mirror of
https://github.com/taoensso/telemere.git
synced 2026-01-27 16:50:34 +00:00
[doc] Expand, polish documentation
This commit is contained in:
parent
f5f5daeb29
commit
46a468d329
11 changed files with 116 additions and 102 deletions
|
|
@ -31,4 +31,5 @@ Tips:
|
||||||
|
|
||||||
See also `error!`.
|
See also `error!`.
|
||||||
|
|
||||||
|
---------------------------------------
|
||||||
[1] See `help:signal-options` docstring
|
[1] See `help:signal-options` docstring
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
"Error" signal call, focused on error + id.
|
"Error" signal creator, focused on error + id.
|
||||||
|
|
||||||
API: [error] [id-or-opts error] => given error (unconditional)
|
API: [error] [id-or-opts error] => given error (unconditional)
|
||||||
Default kind: `:error`
|
Default kind: `:error`
|
||||||
|
|
@ -25,6 +25,6 @@ Tips:
|
||||||
- Can conveniently be wrapped by `throw`: (throw (error! ...)).
|
- Can conveniently be wrapped by `throw`: (throw (error! ...)).
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
[1] See `help:signal-flow` docstring
|
[1] See `help:signal-flow` docstring
|
||||||
[2] See `help:signal-content` docstring
|
[2] See `help:signal-content` docstring
|
||||||
[3] See `help:signal-options` docstring
|
[3] See `help:signal-options` docstring
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
"Event" signal call, focused on id + level.
|
"Event" signal creator, focused on id + level.
|
||||||
|
|
||||||
API: [id] [id level-or-opts] => true iff signal call was allowed
|
API: [id] [id level-or-opts] => true iff signal was allowed
|
||||||
Default kind: `:event`
|
Default kind: `:event`
|
||||||
Default level: `:info`
|
Default level: `:info`
|
||||||
|
|
||||||
When conditions are met [1], creates a Telemere signal [2] and dispatches it to
|
When filtering conditions are met [1], creates a Telemere signal [2] and
|
||||||
registered handlers for processing (writing to console/disk/db, etc.).
|
dispatches it to registered handlers for processing (e.g. writing to
|
||||||
|
console/file/queue/db, etc.).
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
|
|
@ -29,6 +30,6 @@ Tips:
|
||||||
position, and for `event!` that's the `level-or-opts` arg.
|
position, and for `event!` that's the `level-or-opts` arg.
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
[1] See `help:signal-flow` docstring
|
[1] See `help:signal-flow` docstring
|
||||||
[2] See `help:signal-content` docstring
|
[2] See `help:signal-content` docstring
|
||||||
[3] See `help:signal-options` docstring
|
[3] See `help:signal-options` docstring
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
"Log" signal call, focused on message + level.
|
"Log" signal creator, focused on message + level.
|
||||||
|
|
||||||
API: [msg] [level-or-opts msg] => true iff signal call was allowed.
|
API: [msg] [level-or-opts msg] => true iff signal was allowed.
|
||||||
Default kind: `:log`
|
Default kind: `:log`
|
||||||
Default level: `:info`
|
Default level: `:info`
|
||||||
|
|
||||||
When conditions are met [1], creates a Telemere signal [2] and dispatches it to
|
When filtering conditions are met [1], creates a Telemere signal [2] and
|
||||||
registered handlers for processing (writing to console/disk/db, etc.).
|
dispatches it to registered handlers for processing (e.g. writing to
|
||||||
|
console/file/queue/db, etc.).
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
|
|
@ -29,6 +30,6 @@ Tips:
|
||||||
- See also `msg-splice`, `msg-skip` utils.
|
- See also `msg-splice`, `msg-skip` utils.
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
[1] See `help:signal-flow` docstring
|
[1] See `help:signal-flow` docstring
|
||||||
[2] See `help:signal-content` docstring
|
[2] See `help:signal-content` docstring
|
||||||
[3] See `help:signal-options` docstring
|
[3] See `help:signal-options` docstring
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,26 @@
|
||||||
Low-level generic signal call.
|
Low-level generic signal creator.
|
||||||
|
|
||||||
API: [opts] => depends on options [3]
|
API: [opts] => depends on options [3]
|
||||||
Default kind: none (optional)
|
Default kind: none (optional)
|
||||||
Default level: none (must be provided)
|
Default level: none (must be provided)
|
||||||
|
|
||||||
When conditions are met [1], creates a Telemere signal [2] and dispatches it to
|
When filtering conditions are met [1], creates a Telemere signal [2] and
|
||||||
registered handlers for processing (writing to console/disk/db, etc.).
|
dispatches it to registered handlers for processing (e.g. writing to
|
||||||
|
console/file/queue/db, etc.).
|
||||||
|
|
||||||
If `:run` option is provided: returns value of given run form, or throws.
|
If `:run` option is provided: returns value of given run form, or throws.
|
||||||
Otherwise: returns true iff call conditions were met.
|
Otherwise: returns true iff signal was created (allowed).
|
||||||
|
|
||||||
Generic signals are fairly low-level and useful mostly for library authors or
|
Generic signals are fairly low-level and useful mostly for library authors or
|
||||||
advanced users writing their own wrapper macros. Regular users will typically
|
advanced users writing their own wrapper macros. Regular users will typically
|
||||||
prefer one of the provided wrapper macros optimized for ease-of-use in
|
prefer one of the higher-level signal creators optimized for ease-of-use in
|
||||||
common cases.
|
common cases.
|
||||||
|
|
||||||
These all use `signal!` underneath and offer the same options, but vary in
|
These all use `signal!` underneath and offer the same options, but vary in
|
||||||
their defaults and the focus of their call APIs (args and return values):
|
their defaults and the focus of their call APIs (args and return values):
|
||||||
|
|
||||||
`event!` - (id + opts/level) => true iff signal call was allowed
|
`event!` - (id + opts/level) => true iff signal was created (allowed)
|
||||||
`log!` - (message + opts/level) => true iff signal call was allowed
|
`log!` - (message + opts/level) => true iff signal was created (allowed)
|
||||||
`error!` - (error + opts/id) => given error (unconditional)
|
`error!` - (error + opts/id) => given error (unconditional)
|
||||||
`trace!` - (form + opts/id) => form's result (value/throw) (unconditional)
|
`trace!` - (form + opts/id) => form's result (value/throw) (unconditional)
|
||||||
`spy!` - (form + opts/level) => form's result (value/throw) (unconditional)
|
`spy!` - (form + opts/level) => form's result (value/throw) (unconditional)
|
||||||
|
|
@ -29,7 +30,7 @@ Tips:
|
||||||
- Test using `with-signal`: (with-signal (signal! ...)).
|
- Test using `with-signal`: (with-signal (signal! ...)).
|
||||||
- Supports the same options as other signals [3].
|
- Supports the same options as other signals [3].
|
||||||
|
|
||||||
----------------------------------------
|
---------------------------------------
|
||||||
[1] See `help:signal-flow` docstring
|
[1] See `help:signal-flow` docstring
|
||||||
[2] See `help:signal-content` docstring
|
[2] See `help:signal-content` docstring
|
||||||
[3] See `help:signal-options` docstring
|
[3] See `help:signal-options` docstring
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,34 @@
|
||||||
Signals are initially maps with {:keys [inst id ns level data msg_ ...]},
|
Signals are maps with {:keys [inst id ns level data msg_ ...]},
|
||||||
though they can be modified by call and/or handler middleware.
|
though they can be modified by signal and/or handler middleware.
|
||||||
|
|
||||||
Default keys:
|
Default signal keys:
|
||||||
|
|
||||||
`:schema` - Int version of signal schema (current: 1)
|
`:schema` ------ Int version of signal schema (current: 1)
|
||||||
`:inst` - Platform instant [1] when signal was created
|
`:inst` -------- Platform instant [1] when signal was created
|
||||||
`:level` - Signal level ∈ #{<int> :trace :debug :info :warn :error :fatal :report ...}
|
`:level` ------- Signal level ∈ #{<int> :trace :debug :info :warn :error :fatal :report ...}
|
||||||
`:kind` - Signal ?kind ∈ #{nil :event :error :log :trace :spy <user-val> ...}
|
`:kind` -------- Signal ?kind ∈ #{nil :event :error :log :trace :spy <user-val> ...}
|
||||||
`:id` - ?id of signal call (common to all signals created by signal call, contrast with `:uid`)
|
`:id` ---------- ?id of signal (common to all signals created at callsite, contrast with `:uid`)
|
||||||
`:uid` - ?id of signal instance (unique to each signal created by signal call, contrast with `:id`)
|
`:uid` --------- ?id of signal instance (unique to each signal created at callsite, contrast with `:id`)
|
||||||
|
|
||||||
`:msg` - Arb user-level message ?str given to signal call
|
`:msg` --------- Arb user-level message ?str given to signal creator
|
||||||
`:data` - Arb user-level data ?val (usu. a map) given to signal call
|
`:data` -------- Arb user-level data ?val (usu. a map) given to signal creator
|
||||||
`:error` - Arb user-level platform ?error [2] given to signal call
|
`:error` ------- Arb user-level platform ?error [2] given to signal creator
|
||||||
|
|
||||||
`:run-form` - Unevaluated ?form given to signal call as `:run`
|
`:run-form` ---- Unevaluated ?form given to signal creator as `:run`
|
||||||
`:run-val` - Successful return ?val of `:run` ?form
|
`:run-val` ----- Successful return ?val of `:run` ?form
|
||||||
`:run-nsecs`- ?int nanosecs runtime of `:run` ?form
|
`:run-nsecs` --- ?int nanosecs runtime of `:run` ?form
|
||||||
`:end-inst` - Platform ?instant [1] when `:run` ?form completed
|
`:end-inst` ---- Platform ?instant [1] when `:run` ?form completed
|
||||||
|
|
||||||
`:ctx` - ?val of `*ctx*` (arb user-level state) when signal was created
|
`:ctx` --------- ?val of `*ctx*` (arb user-level state) when signal was created
|
||||||
`:parent` - ?{:keys [id uid]} of parent signal, present in nested signals when tracing
|
`:parent` ------ ?{:keys [id uid]} of parent signal, present in nested signals when tracing
|
||||||
`:location` - ?{:keys [ns file line column]} signal call location
|
`:location` ---- ?{:keys [ns file line column]} signal creator callsite
|
||||||
`:ns` - ?str namespace of signal call, same as (:ns location)
|
`:ns` ---------- ?str namespace of signal creator callsite, same as (:ns location)
|
||||||
`:line` - ?int line of signal call, same as (:line location)
|
`:line` -------- ?int line of signal creator callsite, same as (:line location)
|
||||||
`:column` - ?int column of signal call, same as (:column location)
|
`:column` ------ ?int column of signal creator callsite, same as (:column location)
|
||||||
`:file` - ?str filename of signal call, same as (:file location)
|
`:file` -------- ?str filename of signal creator callsite, same as (:file location)
|
||||||
|
`:sample-rate` - ?rate ∈ℝ[0,1] for combined signal AND handler sampling (0.75 => allow 75% of signals, nil => allow all)
|
||||||
|
|
||||||
`:sample-rate` - ?rate ∈ℝ[0,1] for combined call AND handler sampling (0.75 => allow 75% of signals, nil => allow all)
|
<kvs> ---------- Arb other user-level ?kvs given to signal creator
|
||||||
|
|
||||||
<kvs> - Arb other user-level ?kvs given to signal call
|
|
||||||
|
|
||||||
If anything is unclear, please ping me (@ptaoussanis) so that I can improve these docs!
|
If anything is unclear, please ping me (@ptaoussanis) so that I can improve these docs!
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,26 @@
|
||||||
A signal will be provided to a handler iff ALL of the following are true:
|
A signal will be provided to a handler iff ALL of the following are true:
|
||||||
|
1. Signal (creation) is allowed by compile-time filters
|
||||||
|
2. Signal (creation) is allowed by runtime filters
|
||||||
|
3. Signal (handling) is allowed by handler filters
|
||||||
|
|
||||||
1. Signal call is allowed by compile-time filters
|
4. Signal middleware does not suppress the signal (return nil)
|
||||||
2. Signal call is allowed by runtime filters
|
5. Handler middleware does not suppress the signal (return nil)
|
||||||
3. Handler is allowed by runtime filters
|
|
||||||
|
|
||||||
4. Signal call middleware does not suppress the signal (return nil)
|
For 1-3, filtering may depend on (in order):
|
||||||
5. Handler middleware does not suppress the signal (return nil)
|
Sample rate → namespace → kind → id → level → when form/fn → rate limit
|
||||||
|
|
||||||
|
Note that sample rates are multiplicative:
|
||||||
|
If a signal is created with 20% sampling and a handler handles 50%
|
||||||
|
of given signals, then 10% of possible signals will be handled.
|
||||||
|
|
||||||
|
This multiplicative rate is helpfully reflected in the signal's final
|
||||||
|
`:sample-rate` value.
|
||||||
|
|
||||||
|
For a visual flowchart, see: Ref. <https://www.taoensso.com/telemere/flow>
|
||||||
|
|
||||||
For more info:
|
For more info:
|
||||||
|
- On signal filters, see: `help:filters` docstring
|
||||||
- On call filters, see: `help:filters` docstring
|
|
||||||
- On handler filters, see: `help:handlers` docstring
|
- On handler filters, see: `help:handlers` docstring
|
||||||
- On signal flow, see: Ref. <https://www.taoensso.com/telemere/flowchart>
|
|
||||||
|
|
||||||
If anything is unclear, please ping me (@ptaoussanis) so that I can
|
If anything is unclear, please ping me (@ptaoussanis) so that I can
|
||||||
improve these docs!
|
improve these docs!
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,31 @@
|
||||||
Signal options (shared by `signal!`, `event!`, ...):
|
Signal options (shared by all signal creators):
|
||||||
|
|
||||||
`:inst` - Platform instant [1] when signal was created, ∈ #{nil :auto <user-val>}
|
`:inst` -------- Platform instant [1] when signal was created, ∈ #{nil :auto <user-val>}
|
||||||
`:level` - Signal level ∈ #{<int> :trace :debug :info :warn :error :fatal :report ...}
|
`:level` ------- Signal level ∈ #{<int> :trace :debug :info :warn :error :fatal :report ...}
|
||||||
`:kind` - Signal ?kind ∈ #{nil :event :error :log :trace :spy <user-val> ...}
|
`:kind` -------- Signal ?kind ∈ #{nil :event :error :log :trace :spy <user-val> ...}
|
||||||
`:id` - ?id of signal call (common to all signals created by signal call, contrast with `:uid`)
|
`:id` ---------- ?id of signal (common to all signals created at callsite, contrast with `:uid`)
|
||||||
`:uid` - ?id of signal instance (unique to each signal created by signal call, contrast with `:id`)
|
`:uid` --------- ?id of signal instance (unique to each signal created at callsite, contrast with `:id`)
|
||||||
|
|
||||||
`:msg` - Arb user-level ?message to incl. in signal: str or vec of strs to join (with `\space`)
|
`:msg` --------- Arb user-level ?message to incl. in signal: str or vec of strs to join (with `\space`)
|
||||||
`:data` - Arb user-level ?data to incl. in signal: usu. a map
|
`:data` -------- Arb user-level ?data to incl. in signal: usu. a map
|
||||||
`:error` - Arb user-level ?error to incl. in signal: platform error [2]
|
`:error` ------- Arb user-level ?error to incl. in signal: platform error [2]
|
||||||
|
|
||||||
`:run` - ?form to execute UNCONDITIONALLY; will incl. `:run-value` in signal
|
`:run` --------- ?form to execute UNCONDITIONALLY; will incl. `:run-value` in signal
|
||||||
`:do` - ?form to execute conditionally (iff signal allowed), before establishing `:let` ?binding
|
`:do` ---------- ?form to execute conditionally (iff signal allowed), before establishing `:let` ?binding
|
||||||
`:let` - ?binding to establish conditionally (iff signal allowed), BEFORE evaluating `:data` and `:msg` (useful!)
|
`:let` --------- ?bindings to establish conditionally (iff signal allowed), BEFORE evaluating `:data` and `:msg` (useful!)
|
||||||
|
|
||||||
`:ctx` - Custom ?val to override auto (dynamic `*ctx*`) in signal
|
`:ctx` --------- Custom ?val to override auto (dynamic `*ctx*`) in signal
|
||||||
`:parent` - Custom ?{:keys [id uid]} to override auto (dynamic) parent signal info in signal
|
`:parent` ------ Custom ?{:keys [id uid]} to override auto (dynamic) parent signal info in signal
|
||||||
`:location` - Custom ?{:keys [ns line column file]} to override auto signal call location
|
`:location` ---- Custom ?{:keys [ns line column file]} to override auto signal call location
|
||||||
|
|
||||||
`:elidable?` - Should signal call be subject to compile-time elision? (Default: true)
|
`:elidable?` --- Should signal be subject to compile-time elision? (Default: true).
|
||||||
`:sample-rate` - ?rate ∈ℝ[0,1] for call sampling (0.75 => allow 75% of signals, nil => allow all)
|
`:sample-rate` - ?rate ∈ℝ[0,1] for call sampling (0.75 => allow 75% of signals, nil => allow all)
|
||||||
`:when` - Arb ?form; when present, form must return truthy to allow signal
|
`:when` -------- Arb ?form; when present, form must return truthy to allow signal
|
||||||
`:rate-limit` - ?spec as given to `taoensso.telemere/rate-limiter`, see its docstring for details
|
`:rate-limit` -- ?spec as given to `taoensso.telemere/rate-limiter`, see its docstring for details
|
||||||
`:middleware` - ?[(fn [signal])=>modified-signal ...] call middleware
|
`:middleware` -- ?[(fn [signal])=>modified-signal ...] signal middleware
|
||||||
`:trace?` - Should tracing be enabled for `:run` form?
|
`:trace?` ------ Should tracing be enabled for `:run` form?
|
||||||
|
|
||||||
<kvs> - Arb other user-level ?kvs to incl. in signal
|
<kvs> ---------- Arb other user-level ?kvs to incl. in signal
|
||||||
|
|
||||||
If anything is unclear, please ping me (@ptaoussanis) so that I can improve these docs!
|
If anything is unclear, please ping me (@ptaoussanis) so that I can improve these docs!
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
"Spy" signal call, focused on form + level.
|
"Spy" signal creator, focused on form + level.
|
||||||
|
|
||||||
API: [form] [level-or-opts form] => form's result (value/throw) (unconditional)
|
API: [form] [level-or-opts form] => form's result (value/throw) (unconditional)
|
||||||
Default kind: `:spy`
|
Default kind: `:spy`
|
||||||
Default level: `:info`
|
Default level: `:info`
|
||||||
|
|
||||||
When conditions are met [1], creates a Telemere signal [2] and dispatches it to
|
When filtering conditions are met [1], creates a Telemere signal [2] and
|
||||||
registered handlers for processing (writing to console/disk/db, etc.).
|
dispatches it to registered handlers for processing (e.g. writing to
|
||||||
|
console/file/queue/db, etc.).
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
|
|
@ -27,13 +28,13 @@ Tips:
|
||||||
- Identical to `trace!`, but focused on form + level rather than form + id.
|
- Identical to `trace!`, but focused on form + level rather than form + id.
|
||||||
|
|
||||||
- Useful for debugging/monitoring forms, and tracing (nested) execution flow.
|
- Useful for debugging/monitoring forms, and tracing (nested) execution flow.
|
||||||
- Execution of `form` arg may trigger additional (nested) signals.
|
- Execution of `form` arg may create additional (nested) signals.
|
||||||
Each signal's `:parent` key will indicate its immediate parent.
|
Each signal's `:parent` key will indicate its immediate parent.
|
||||||
|
|
||||||
- Can be useful to wrap with `catch->error!`:
|
- Can be useful to wrap with `catch->error!`:
|
||||||
(catch->error! ::error-id (spy! ...)).
|
(catch->error! ::error-id (spy! ...)).
|
||||||
|
|
||||||
----------------------------------------
|
---------------------------------------
|
||||||
[1] See `help:signal-flow` docstring
|
[1] See `help:signal-flow` docstring
|
||||||
[2] See `help:signal-content` docstring
|
[2] See `help:signal-content` docstring
|
||||||
[3] See `help:signal-options` docstring
|
[3] See `help:signal-options` docstring
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
"Trace" signal call, focused on form + id.
|
"Trace" signal creator, focused on form + id.
|
||||||
|
|
||||||
API: [form] [id-or-opts form] => form's result (value/throw) (unconditional)
|
API: [form] [id-or-opts form] => form's result (value/throw) (unconditional)
|
||||||
Default kind: `:trace`
|
Default kind: `:trace`
|
||||||
Default level: `:info` (intentionally NOT `:trace`!)
|
Default level: `:info` (intentionally NOT `:trace`!)
|
||||||
|
|
||||||
When conditions are met [1], creates a Telemere signal [2] and dispatches it to
|
When filtering conditions are met [1], creates a Telemere signal [2] and
|
||||||
registered handlers for processing (writing to console/disk/db, etc.).
|
dispatches it to registered handlers for processing (e.g. writing to
|
||||||
|
console/file/queue/db, etc.).
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
|
|
@ -27,7 +28,7 @@ Tips:
|
||||||
- Identical to `spy!`, but focused on form + id rather than form + level.
|
- Identical to `spy!`, but focused on form + id rather than form + level.
|
||||||
|
|
||||||
- Useful for debugging/monitoring forms, and tracing (nested) execution flow.
|
- Useful for debugging/monitoring forms, and tracing (nested) execution flow.
|
||||||
- Execution of `form` arg may trigger additional (nested) signals.
|
- Execution of `form` arg may create additional (nested) signals.
|
||||||
Each signal's `:parent` key will indicate its immediate parent.
|
Each signal's `:parent` key will indicate its immediate parent.
|
||||||
|
|
||||||
- Can be useful to wrap with `catch->error!`:
|
- Can be useful to wrap with `catch->error!`:
|
||||||
|
|
@ -37,7 +38,7 @@ Tips:
|
||||||
refers to the general action of tracing program flow rather than to the
|
refers to the general action of tracing program flow rather than to the
|
||||||
common logging level of the same name.
|
common logging level of the same name.
|
||||||
|
|
||||||
----------------------------------------
|
---------------------------------------
|
||||||
[1] See `help:signal-flow` docstring
|
[1] See `help:signal-flow` docstring
|
||||||
[2] See `help:signal-content` docstring
|
[2] See `help:signal-content` docstring
|
||||||
[3] See `help:signal-options` docstring
|
[3] See `help:signal-options` docstring
|
||||||
|
|
|
||||||
|
|
@ -267,12 +267,12 @@
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(defmacro ^:public with-signal
|
(defmacro ^:public with-signal
|
||||||
"Experimental.
|
"Experimental.
|
||||||
Executes given form, trapping errors. Returns the LAST signal triggered by form.
|
Executes given form, trapping errors. Returns the LAST signal created by form.
|
||||||
Useful for tests/debugging.
|
Useful for tests/debugging.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
`trap-signals?` (default: false)
|
`trap-signals?` (default: false)
|
||||||
Should ALL signals triggered by form be trapped to prevent normal dispatch
|
Should ALL signals created by form be trapped to prevent normal dispatch
|
||||||
to registered handlers?
|
to registered handlers?
|
||||||
|
|
||||||
`raw-msg?` (default: false)
|
`raw-msg?` (default: false)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue