[doc] Documentation improvements

This commit is contained in:
Peter Taoussanis 2024-04-25 09:37:55 +02:00
parent c4d9dd09a3
commit 12f4b35d71
16 changed files with 165 additions and 83 deletions

View file

@ -41,7 +41,7 @@ See [here][GitHub releases] for earlier releases.
#### Flexibility
- Config via plain **Clojure vals and fns** for easy customization, composition, and REPL debugging.
- Unmatched support for **system-level config** (JVM props, ENV vars, classpath resources).
- Unmatched **environmental config** support (JVM properties, environment variables, or classpath resources).
- Expressive **per-call** and **per-handler** filtering at both **runtime** and **compile-time**.
- Filter by namespace and id pattern, level, **level by namespace pattern**, etc.
- **Sampling**, **rate-limiting**, and **back-pressure monitoring**.

View file

@ -160,7 +160,8 @@
(defn handler:my-handler ; Note naming convention
"Returns a (fn handler [signal] that:
- Does something.
- Takes a Telemere signal.
- Does something with it.
Options:
`:option1` - Description
@ -169,8 +170,9 @@
([] (handler:my-handler nil)) ; Use default opts
([{:as constructor-opts}]
;; Do expensive prep outside returned handler fn whenever possible -
;; i.e. at (one-off) construction time rather than handling time.
;; Do option validation and expensive prep *outside* returned handler
;; fn whenever possible - i.e. at (one-off) construction time rather than
;; at every handler call.
(let []
(fn a-handler:my-handler ; Note naming convention

View file

@ -0,0 +1,52 @@
Environmental filter config includes:
Kind filter:
JVM property: `taoensso.telemere.rt-kind-filter.edn`
Env variable: `TAOENSSO_TELEMERE_RT_KIND_FILTER_EDN`
Classpath resource: `taoensso.telemere.rt-kind-filter.edn`
Namespace filter:
JVM property: `taoensso.telemere.rt-ns-filter.edn`
Env variable: `TAOENSSO_TELEMERE_RT_NS_FILTER_EDN`
Classpath resource: `taoensso.telemere.rt-ns-filter.edn`
Id filter:
JVM property: `taoensso.telemere.rt-id-filter.edn`
Env variable: `TAOENSSO_TELEMERE_RT_ID_FILTER_EDN`
Classpath resource: `taoensso.telemere.rt-id-filter.edn`
Minimum level:
JVM property: `taoensso.telemere.rt-min-level.edn`
Env variable: `TAOENSSO_TELEMERE_RT_MIN_LEVEL_EDN`
Classpath resource: `taoensso.telemere.rt-min-level.edn`
Examples:
`taoensso.telemere.rt-min-level.edn` -> ":info"
`TAOENSSO_TELEMERE_RT_NS_FILTER_EDN` -> "{:deny \"taoensso.*\"}"
`taoensso.telemere.rt-id-filter.cljs.edn` -> "#{:my-id1 :my-id2}"
`TAOENSSO_TELEMERE_RT_KIND_FILTER_CLJ_EDN` -> "nil"
Tips:
- The above ids are for runtime filters (the most common).
For compile-time filters, change `rt`->`ct` / `RT`->`CT`.
- The above ids will affect both Clj AND Cljs.
For platform-specific filters, use
".clj.edn" / "_CLJ_EDN" or
".cljs.edn" / "_CLJS_EDN" suffixes instead.
- ".edn" / "_EDN" suffixes are optional.
- Config values should be edn. To get the right syntax, first set
your runtime filters using the standard utils (`set-min-level!`,
etc.). Then call `get-filters` and serialize the relevant parts
to edn with `pr-str`.
- All environmental config uses `get-env` underneath.
See the `get-env` docstring for more/advanced details.
- Classpath resources are files accessible on your project's
classpath. This usually includes files in your project's
`resources/` dir.

View file

@ -1,15 +0,0 @@
These include:
Compile-time:
ns-filter: (get-env {:as :edn} :taoensso.telemere/ct-ns-filter<.platform><.edn>)
id-filter: (get-env {:as :edn} :taoensso.telemere/ct-id-filter<.platform><.edn>)
min-level: (get-env {:as :edn} :taoensso.telemere/ct-min-level<.platform><.edn>)
Runtime:
ns-filter: (get-env {:as :edn} :taoensso.telemere/rt-ns-filter<.platform><.edn>)
id-filter: (get-env {:as :edn} :taoensso.telemere/rt-id-filter<.platform><.edn>)
min-level: (get-env {:as :edn, :default :info} :taoensso.telemere/rt-min-level<.platform><.edn>)
See `get-env` for details.

View file

@ -28,7 +28,9 @@ Default signal keys:
`: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)
<kvs> ---------- Arb other user-level ?kvs given to signal creator
<kvs> ---------- Other arb user-level ?kvs given to signal creator. Typically NOT included
in handler output, so a great way to provide custom data/opts for use
(only) by custom middleware/handlers.
If anything is unclear, please ping me (@ptaoussanis) so that I can improve these docs!

View file

@ -1,4 +1,5 @@
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
@ -7,18 +8,40 @@ A signal will be provided to a handler iff ALL of the following are true:
5. Handler middleware does not suppress the signal (return nil)
For 1-3, filtering may depend on (in order):
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.
Compile-time vs runtime filtering:
This multiplicative rate is helpfully reflected in each signal's final
Compile-time filtering is an advanced feature that can be tricky to set
and use correctly. Most folks will want ONLY runtime filtering.
Compile-time filtering works by eliding (completely removing the code for)
disallowed signals. This means zero performance cost for these signals,
but also means that compile-time filtering is PERMANENT once applied.
So if you set `:info` as the compile-time minimum level, that'll REMOVE
CODE for every signal call below `:info` level. To decrease that minimum
level, you'll need to rebuild.
Compile-time filtering can be set ONLY with environmental config
(JVM properties, environment variables, or classpath resources).
Signal and handler sampling is multiplicative:
Both signals and handlers can have independent sample rates, and these
MULTIPLY!
If a signal is created with 20% sampling and a handler handles 50%
of received signals, then 10% of possible signals will be handled
(50% of 20%).
The multiplicative rate is helpfully reflected in each signal's final
`:sample-rate` value.
For a visual flowchart, see: Ref. <https://www.taoensso.com/telemere/flow>
For more info:
- Signal visual flowchart, Ref. <https://www.taoensso.com/telemere/flow>
- On signal filters, see: `help:signal-filters` docstring
- On handler filters, see: `help:signal-handlers` docstring

View file

@ -18,14 +18,18 @@ Signal options (shared by all signal creators):
`: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 creator callsite location
`:elidable?` --- Should signal 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 signal sampling (0.75 => allow 75% of signals, nil => allow all)
`: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
`:middleware` -- ?[(fn [signal])=>modified-signal ...] signal middleware
`:trace?` ------ Should tracing be enabled for `:run` form?
<kvs> ---------- Arb other user-level ?kvs to incl. in signal
<kvs> ---------- Other arb user-level ?kvs to incl. in signal. Typically NOT included in
handler output, so a great way to provide custom data/opts for use
(only) by custom middleware/handlers.
handler-specific data that can just be ignored by other handlers
If anything is unclear, please ping me (@ptaoussanis) so that I can improve these docs!

View file

@ -47,8 +47,8 @@
:ct-sig-filter impl/ct-sig-filter
:*rt-sig-filter* impl/*rt-sig-filter*
:*sig-handlers* impl/*sig-handlers*
:sig-filter-system-vals-info
(impl/signal-docstring :filter-system-vals)})
:sig-filter-env-config-help
(impl/signal-docstring :filter-env-config)})
(comment help:filters)

View file

@ -34,29 +34,35 @@
;;;; Config
#?(:clj
(let [base (enc/get-env {:as :edn} :taoensso.telemere/ct-filters<.platform><.edn>)
ns-filter (enc/get-env {:as :edn} :taoensso.telemere/ct-ns-filter<.platform><.edn>)
id-filter (enc/get-env {:as :edn} :taoensso.telemere/ct-id-filter<.platform><.edn>)
min-level (enc/get-env {:as :edn} :taoensso.telemere/ct-min-level<.platform><.edn>)]
(let [base (enc/get-env {:as :edn} :taoensso.telemere/ct-filters<.platform><.edn>)
kind-filter (enc/get-env {:as :edn} :taoensso.telemere/ct-kind-filter<.platform><.edn>)
ns-filter (enc/get-env {:as :edn} :taoensso.telemere/ct-ns-filter<.platform><.edn>)
id-filter (enc/get-env {:as :edn} :taoensso.telemere/ct-id-filter<.platform><.edn>)
min-level (enc/get-env {:as :edn} :taoensso.telemere/ct-min-level<.platform><.edn>)]
(enc/defonce ct-sig-filter
"`SigFilter` used for compile-time elision, or nil."
(sigs/sig-filter
{:ns-filter (or ns-filter (get base :ns-filter))
:id-filter (or id-filter (get base :id-filter))
:min-level (or min-level (get base :min-level))}))))
{:kind-filter (or kind-filter (get base :kind-filter))
:ns-filter (or ns-filter (get base :ns-filter))
:id-filter (or id-filter (get base :id-filter))
:min-level (or min-level (get base :min-level))}))))
(let [base (enc/get-env {:as :edn} :taoensso.telemere/rt-filters<.platform><.edn>)
ns-filter (enc/get-env {:as :edn} :taoensso.telemere/rt-ns-filter<.platform><.edn>)
id-filter (enc/get-env {:as :edn} :taoensso.telemere/rt-id-filter<.platform><.edn>)
min-level (enc/get-env {:as :edn, :default :info} :taoensso.telemere/rt-min-level<.platform><.edn>)]
(let [base (enc/get-env {:as :edn} :taoensso.telemere/rt-filters<.platform><.edn>)
kind-filter (enc/get-env {:as :edn} :taoensso.telemere/rt-kind-filter<.platform><.edn>)
ns-filter (enc/get-env {:as :edn} :taoensso.telemere/rt-ns-filter<.platform><.edn>)
id-filter (enc/get-env {:as :edn} :taoensso.telemere/rt-id-filter<.platform><.edn>)
min-level (enc/get-env {:as :edn, :default :info} :taoensso.telemere/rt-min-level<.platform><.edn>)]
(enc/defonce ^:dynamic *rt-sig-filter*
"`SigFilter` used for runtime filtering, or nil."
(sigs/sig-filter
{:ns-filter (or ns-filter (get base :ns-filter))
:id-filter (or id-filter (get base :id-filter))
:min-level (or min-level (get base :min-level))})))
{:kind-filter (or kind-filter (get base :kind-filter))
:ns-filter (or ns-filter (get base :ns-filter))
:id-filter (or id-filter (get base :id-filter))
:min-level (or min-level (get base :min-level))})))
(comment (enc/get-env {:as :edn, :return :explain} :taoensso.telemere/rt-filters<.platform><.edn>))
;;;; Context (optional arb app-level state)
;; taoensso.telemere/*ctx*

View file

@ -39,10 +39,10 @@
(defn tools-logging->telemere!
"Configures `clojure.tools.logging` to use Telemere as its logging implementation.
Called automatically if the following is true:
(get-env {:as :bool} :clojure.tools.logging/to-telemere)
See `get-env` for details."
Called automatically if one of the following is \"true\":
JVM property: `clojure.tools.logging.to-telemere`
Env variable: `CLOJURE_TOOLS_LOGGING_TO_TELEMERE`
Classpath resource: `clojure.tools.logging.to-telemere`"
[]
(impl/signal!
{:kind :event

View file

@ -188,11 +188,11 @@ Quick examples of some basic filtering:
```
- Filtering is always O(1), except for rate limits which are O(n_windows).
- 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 each signal's final `:sample-rate` value.
- Signal and handler sampling is *multiplicative*: if a signal is created with *20%* sampling and a handler handles *50%* of received signals, then *10%* of possible signals will be handled. This multiplicative rate is helpfully reflected in each signal's final `:sample-rate` value.
- See [`help:signal-flow`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-flow) for internal docs on signal flow.
- See section [2-Architecture](./2-Architecture) for a flowchart / visual aid.
Runtime signal filters can be configured with:
Runtime signal filters can be set with:
| Global | Dynamic | Filters by |
| :-------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------- |
@ -202,7 +202,7 @@ Runtime signal filters can be configured with:
| [`set-min-level`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#set-min-level) | [`with-min-level`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#with-min-level) | Signal level (minimum can be specified by kind and/or ns) |
- See relevant docstrings (links above) for usage info.
- Compile-time filters are controlled by system-level config, see section [3-Config](./3-Config).
- Compile-time filters are set with environmental config, see section [3-Config](./3-Config).
# Internal help

View file

@ -13,7 +13,7 @@ A signal will be provided to a handler iff ALL of the following are true:
For 1-3, filtering may depend on (in order):
Sample rate → namespace → kind → id → level → when form/fn → rate limit
- See [`help:signal-filters`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-filters) for info on signal creation filters.
- See [`help:signal-filters`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-filters) for info on signal creation filters, **environmental config**, etc.
- See [`add-handler!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#add-handler!) for info on signal handler filters.
# Signal handlers
@ -29,7 +29,7 @@ See section [4-Handlers](./4-Handlers).
To do this:
1. Ensure that you have the `clojure.tools.logging` dependency, and
2. Call [`tools-logging->telemere!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.tools-logging#tools-logging-%3Etelemere!), or set the relevant system config as described in its docstring.
2. Call [`tools-logging->telemere!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.tools-logging#tools-logging-%3Etelemere!), or set the relevant environmental config as described in its docstring.
Verify successful intake with [`check-intakes`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#check-intakes):

View file

@ -2,18 +2,22 @@ Signal handlers process created signals to *do something with them* (analyse the
# Included handlers
The following handlers are included out-the-box:
The following signal handlers are currently included out-the-box:
| Name | Platform | Writes signals to | Writes signals as |
| :------------------------------------------------------------------------------------------------------------------------------------------------------- | :------- | :------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------- |
| [`handler:console`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:console) | Clj | `*out*` or `*err*` | String ([edn](https://github.com/edn-format/edn), JSON, formatted, etc.) |
| [`handler:console`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:console) | Cljs | Browser console | String ([edn](https://github.com/edn-format/edn), JSON, formatted, etc.) |
| [`handler:console-raw`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:console-raw) | Cljs | Browser console | Raw data (for [cljs-devtools](https://github.com/binaryage/cljs-devtools), etc.) |
| [`handler:file`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:file) | Clj | File/s on disk | String ([edn](https://github.com/edn-format/edn), JSON, formatted, etc.) |
| [`handler:open-telemetry-logger`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.open-telemetry#handler:open-telemetry-logger) | Clj | [OpenTelemetry](https://opentelemetry.io/) [Java client](https://github.com/open-telemetry/opentelemetry-java) | [LogRecord](https://opentelemetry.io/docs/specs/otel/logs/data-model/) |
| Name | Platform | Output target | Output format |
| :------------------------------------------------------------------------------------------------------------------------------------------------------- | :------- | :------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------- |
| [`handler:console`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:console) | Clj | `*out*` or `*err*` | Formatted string [1] |
| [`handler:console`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:console) | Cljs | Browser console | Formatted string [1] |
| [`handler:console-raw`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:console-raw) | Cljs | Browser console | Raw signal data [2] |
| [`handler:file`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:file) | Clj | File/s on disk | Formatted string [1] |
| [`handler:postal`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.postal#handler:postal) | Clj | Email (via [postal](https://github.com/drewr/postal)) | Formatted string [1] |
| [`handler:open-telemetry-logger`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.open-telemetry#handler:open-telemetry-logger) | Clj | [OpenTelemetry](https://opentelemetry.io/) [Java client](https://github.com/open-telemetry/opentelemetry-java) | [LogRecord](https://opentelemetry.io/docs/specs/otel/logs/data-model/) |
- See relevant docstrings (links above) for more info.
- See section [8-Community](8-Community.md) for additional handlers.
- \[1] [Configurable](https://cljdoc.org/d/com.taoensso/telemere/1.0.0-beta3/api/taoensso.telemere#help:signal-formatters): human-readable (default), [edn](https://github.com/edn-format/edn), [JSON](https://www.json.org/), etc.
- \[2] For use with browser formatting tools like [cljs-devtools](https://github.com/binaryage/cljs-devtools).
- See relevant docstrings (links above) for features, usage, etc.
- See section [8-Community](8-Community.md) for more (community-supported) handlers.
- If there's other handlers you'd like to see, feel free to [ping me](https://github.com/taoensso/telemere/issues), or ask on the [`#telemere` Slack channel](https://www.taoensso.com/telemere/slack). It helps to know what people most need!
# Configuring handlers
@ -97,11 +101,11 @@ Want to add or remove a particular handler when your application starts?
Just make an appropriate call to [`add-handler!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#add-handler!) or [`remove-handler!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#remove-handler!).
## System-level config
## Environmental config
If you want to manage handlers **conditionally** based on **system-level config** (e.g. JVM prop, ENV var, or classpath resource) - Telemere provides the highly flexible [`get-env`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#get-env) util.
If you want to manage handlers **conditionally** based on **environmental config** (JVM properties, environment variables, or classpath resources) - Telemere provides the highly flexible [`get-env`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#get-env) util.
Use this to easily check your own cross-platform system config, and make whatever conditional handler management decisions you'd like.
Use this to easily define your own arbitrary cross-platform config, and make whatever conditional handler management decisions you'd like.
# Writing handlers
@ -120,7 +124,8 @@ For more complex cases, or for handlers that you want to make available for use
```clojure
(defn handler:my-handler ; Note naming convention
"Returns a (fn handler [signal] that:
- Does something.
- Takes a Telemere signal.
- Does something with it.
Options:
`:option1` - Description
@ -129,8 +134,9 @@ For more complex cases, or for handlers that you want to make available for use
([] (handler:my-handler nil)) ; Use default opts
([{:as constructor-opts}]
;; Do expensive prep outside returned handler fn whenever possible -
;; i.e. at (one-off) construction time rather than handling time.
;; Do option validation and expensive prep *outside* returned handler
;; fn whenever possible - i.e. at (one-off) construction time rather than
;; at every handler call.
(let []
(fn a-handler:my-handler ; Note naming convention
@ -181,4 +187,4 @@ Chrome console, with [cljs-devtools](https://github.com/binaryage/cljs-devtools)
MacOS terminal:
<img src="https://raw.githubusercontent.com/taoensso/telemere/master/imgs/handler-output-clj-file.png" alt="Default Clojure file handler output" width="640"/>
<img src="https://raw.githubusercontent.com/taoensso/telemere/master/imgs/handler-output-clj-file.png" alt="Default Clojure file handler output" width="640"/>

View file

@ -26,7 +26,7 @@ If not, you may need to [write something yourself](./4-Handlers#writing-handlers
This may be easier than it sounds. Remember that signals are just plain Clojure/Script [maps](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-content), and handlers just plain Clojure/Script functions that do something with those maps.
Feel free to [ping me](https://github.com/taoensso/telemere/issues) for assistance, or ask on the [`#telemere` Slack channel](https://clojurians.slack.com/archives/C06ALA6EEUA).
Feel free to [ping me](https://github.com/taoensso/telemere/issues) for assistance, or ask on the [`#telemere` Slack channel](https://www.taoensso.com/telemere/slack).
### 2. Imports

View file

@ -72,7 +72,7 @@ Consider the [differences](https://www.youtube.com/watch?v=oyLBGkS5ICk) between
- [`log!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#log!) and [`event!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#event!) are both **good general-purpose** signal creators.
- Try **always provide an id** for all signals you create.
- **Provide an id** for all signals you create.
Qualified keywords are perfect! Downstream behaviour (e.g. alerts) can then look for these ids rather than messages (which are harder to match and more likely to change).
@ -84,11 +84,13 @@ Consider the [differences](https://www.youtube.com/watch?v=oyLBGkS5ICk) between
The result of signal middleware is cached and *shared between all handlers* making it an efficient place to transform signals. For this reason - prefer signal middleware to handler middleware when possible/convenient.
- **Signal sampling** and **handler sampling** are **multiplicative**.
- Signal and handler **sampling is 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.
Both signals and handlers can have independent sample rates, and these MULTIPLY!
This multiplicative rate is helpfully reflected in each signal's final `:sample-rate` value, making it possible to estimate unsampled cardinalities in relevant cases.
If a signal is created with *20%* sampling and a handler handles *50%* of received signals, then *10%* of possible signals will be handled (50% of 20%).
This multiplicative rate is helpfully reflected in each signal's final `:sample-rate` value, making it possible to estimate *unsampled* cardinalities in relevant cases.
So for `n` randomly sampled signals matching some criteria, you'd have seen an estimated `Σ(1.0/sample-rate_i)` such signals _without_ sampling, etc.
@ -117,7 +119,7 @@ Consider the [differences](https://www.youtube.com/watch?v=oyLBGkS5ICk) between
Note that all user kvs will *also* be available *together* under the signal's `:kvs` key.
User kvs are a great way of controlling the per-signal behaviour of custom/advanced handlers.
User kvs are typically *not* included in handler output, so are a great way of providing custom data/opts for use (only) by custom middleware or handlers.
- Signal `kind` can be useful in advanced cases.

View file

@ -4,7 +4,7 @@ If there's demand, additional stuff can then be authored by Telemere's *communit
**PRs very welcome** to add links to this page for:
- Handlers (see [Writing handlers](./4-Handlers#writing-handlers))
- Handler libraries or examples (see [Writing handlers](./4-Handlers#writing-handlers))
- Handler utils (formatters, etc.)
- Middleware
- Tutorials / demos / etc.
@ -12,9 +12,9 @@ If there's demand, additional stuff can then be authored by Telemere's *communit
If you spot issues with any linked resources, please **contact the relevant authors** to let them know! Thank you! 🙏
| Contributor | Link | Description |
| :--------------------------------------------- | :-------------------------------------------------------------------------- | :------------------------------------------------------------ |
| [@ptaoussanis](https://github.com/ptaoussanis) | [Official Slack channel](https://clojurians.slack.com/archives/C06ALA6EEUA) | For questions, support, etc. |
| [@ptaoussanis](https://github.com/ptaoussanis) | [GitHub issues](https://github.com/taoensso/telemere/issues) | For questions, support, bug reports, PRs, etc. |
| _ | _ | Your link here? [PRs](../wiki#contributions-welcome) welcome! |
| [@username](https://github.com/username) | [Project](https://github.com/username/project) | Short description of resource |
| Contributor | Link | Description |
| :--------------------------------------------- | :---------------------------------------------------------------- | :------------------------------------------------------------ |
| [@ptaoussanis](https://github.com/ptaoussanis) | [Official Slack channel](https://www.taoensso.com/telemere/slack) | For questions, support, etc. |
| [@ptaoussanis](https://github.com/ptaoussanis) | [GitHub issues](https://github.com/taoensso/telemere/issues) | For questions, support, bug reports, PRs, etc. |
| _ | _ | Your link here? [PRs](../wiki#contributions-welcome) welcome! |
| [@username](https://github.com/username) | [Project](https://github.com/username/project) | Short description of resource |