mirror of
https://github.com/taoensso/telemere.git
synced 2025-12-16 17:41:12 +00:00
[doc] Documentation work
This commit is contained in:
parent
356a7f97b6
commit
ee8505933c
22 changed files with 1023 additions and 101 deletions
61
README.md
61
README.md
|
|
@ -1,15 +1,15 @@
|
|||
<a href="https://www.taoensso.com/clojure" title="More stuff by @ptaoussanis at www.taoensso.com"><img src="https://www.taoensso.com/open-source.png" alt="Taoensso open source" width="340"/></a>
|
||||
[**Documentation**](#documentation) | [Latest releases](#latest-releases) | [Get support][GitHub issues]
|
||||
[**Documentation**](#documentation) | [Latest releases](#latest-releases) | [Slack channel][]
|
||||
|
||||
# Telemere
|
||||
# <img src="https://raw.githubusercontent.com/taoensso/telemere/master/imgs/telemere-logo.svg" alt="Telemere logo" width="360"/>
|
||||
|
||||
### Structured telemetry library for Clojure/Script
|
||||
|
||||
**Telemere** is a next-generation replacement for [Timbre](https://www.taoensso.com/timbre). It handles **structured and traditional logging**, **tracing**, and **basic performance monitoring** with a simple unified API.
|
||||
|
||||
It helps enable the creation of Clojure/Script systems that are highly **observable**, **robust**, and **debuggable** - and it represents the refinement and culmination of ideas brewing over 12+ years in [Timbre](https://github.com/taoensso/timbre), [Tufte](https://github.com/taoensso/tufte), [Truss](https://github.com/taoensso/truss), etc.
|
||||
It helps enable Clojure/Script systems that are **observable**, **robust**, and **debuggable** - and it represents the refinement and culmination of ideas brewing over 12+ years in [Timbre](https://www.taoensso.com/timbre), [Tufte](https://www.taoensso.com/tufte), [Truss](https://www.taoensso.com/truss), etc.
|
||||
|
||||
> [Terminology] *Telemetry* is derived from the Greek roots *tele* (remote) and *metron* (measure). It refers to the collection of *in situ* (in position) information, for transmission to other systems for monitoring/analysis. *Logs* are the most common form of software telemetry. So think of telemetry as the *superset of logging-like activities* that help monitor and understand (software) systems.
|
||||
> [Terminology] *Telemetry* derives from the Greek *tele* (remote) and *metron* (measure). It refers to the collection of *in situ* (in position) data, for transmission to other systems for monitoring/analysis. *Logs* are the most common form of software telemetry. So think of telemetry as the *superset of logging-like activities* that help monitor and understand (software) systems.
|
||||
|
||||
## Latest release/s
|
||||
|
||||
|
|
@ -32,8 +32,8 @@ See [here][GitHub releases] for earlier releases.
|
|||
|
||||
#### Interop
|
||||
|
||||
- 1st-class **out-the-box interop** with [SLF4J v2](https://www.slf4j.org/), [clojure.tools.logging](https://github.com/clojure/tools.logging), [OpenTelemetry](https://opentelemetry.io/), and [Tufte](https://www.taoensso.com/tufte) (soon).
|
||||
- Included shim for easy/gradual [migration from Timbre](../../wiki/7-Migrating).
|
||||
- 1st-class **out-the-box interop** with [SLF4J v2](https://www.slf4j.org/), [clojure.tools.logging](https://github.com/clojure/tools.logging), [OpenTelemetry](https://opentelemetry.io/), and [Tufte](https://www.taoensso.com/tufte).
|
||||
- Included [shim](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.timbre) for easy/gradual [migration from Timbre](../../wiki/5-Migrating).
|
||||
|
||||
#### Scaling
|
||||
|
||||
|
|
@ -43,7 +43,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 **environmental config** (JVM props, ENV vars, edn resources, etc.).
|
||||
- Unmatched support for **system-level config** (JVM props, ENV vars, 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**.
|
||||
|
|
@ -53,7 +53,7 @@ See [here][GitHub releases] for earlier releases.
|
|||
|
||||
See for intro and usage:
|
||||
|
||||
**TODO**: soon
|
||||
**TODO**: coming soon
|
||||
|
||||
<!--
|
||||
<a href="https://www.youtube.com/watch?v=TODO" target="_blank">
|
||||
|
|
@ -108,21 +108,23 @@ See for intro and usage:
|
|||
(t/set-min-level! :event "taoensso.sente.*" :warn)
|
||||
```
|
||||
|
||||
See [examples.cljc](https://github.com/taoensso/telemere/blob/master/examples.cljc) for more REPL-ready snippets.
|
||||
|
||||
## API overview
|
||||
|
||||
See links below for docstrings/usage, and [documentation](#documentation) for lots more info.
|
||||
See relevant docstrings (links below) for usage info-
|
||||
|
||||
### Signal creators
|
||||
|
||||
| Name | Signal kind | Main arg | Optional arg | Returns
|
||||
:-- | :-- | :-- | :-- | :--
|
||||
| [`signal!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#signal!) | `<arb>` | `opts` | - | Signal allowed? / form result (value or throw)
|
||||
| [`log!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#log!) | `:log` | `msg` | `opts`/`level` | Signal allowed?
|
||||
| [`event!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#event!) | `:event` | `id` | `opts`/`level` | Signal allowed?
|
||||
| [`error!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#error!) | `:error` | `error` | `opts`/`id` | Given error
|
||||
| [`trace!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#trace!) | `:trace` | `form` | `opts`/`id` | Form result
|
||||
| [`spy!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#spy!) | `:spy` | `form` | `opts`/`level` | Form result
|
||||
| [`catch->error!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#catch-%3Eerror!) | `:error` | `form` | `opts`/`id` | Form value or given fallback
|
||||
| [`signal!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#signal!) | `<arb>` | `opts` | - | Depends on opts
|
||||
|
||||
### Signal filters
|
||||
|
||||
|
|
@ -135,12 +137,11 @@ See links below for docstrings/usage, and [documentation](#documentation) for lo
|
|||
|
||||
### Internal help
|
||||
|
||||
Telemere includes extensive internal help docstrings:
|
||||
|
||||
| Var | Help with
|
||||
| :-- | :--
|
||||
| [`help:signal-options`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-options) | Options for standard signal creators
|
||||
| [`help:signal-content`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-content) | Content of signal maps
|
||||
| [`help:signal-creators`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-creators) | List of signal creators
|
||||
| [`help:signal-options`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-options) | Options for signal creators
|
||||
| [`help:signal-content`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-content) | Signal map content
|
||||
| [`help:signal-flow`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-flow) | Ordered flow from signal creation to handling
|
||||
| [`help:signal-filters`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-filters) | API for configuring signal filters
|
||||
| [`help:signal-handlers`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-handlers) | API for configuring signal handlers
|
||||
|
|
@ -152,33 +153,42 @@ Telemere includes extensive internal help docstrings:
|
|||
(t/log! {:id ::my-id, :data {:x1 :x2}} "My message") =>
|
||||
```
|
||||
|
||||
##### Default Clojure console handler:
|
||||
#### Clj console handler
|
||||
|
||||
String output:
|
||||
|
||||
```
|
||||
2024-04-11T10:54:57.202869Z INFO LOG Schrebermann.local examples(56,1) ::my-id - My message
|
||||
data: {:x1 :x2}
|
||||
```
|
||||
|
||||
##### Default ClojureScript console handler in Chrome:
|
||||
#### Cljs console handler
|
||||
|
||||
<img src="/imgs/handler-output-cljs-console.png" alt="Default ClojureScript console handler output" width="640"/>
|
||||
Chrome console:
|
||||
|
||||
##### Raw ClojureScript console handler in Chrome, with [cljs-devtools](https://github.com/binaryage/cljs-devtools):
|
||||
<img src="https://raw.githubusercontent.com/taoensso/telemere/master/imgs/handler-output-cljs-console.png" alt="Default ClojureScript console handler output" width="640"/>
|
||||
|
||||
<img src="/imgs/handler-output-cljs-console-raw.png" alt="Raw ClojureScript console handler output" width="640"/>
|
||||
#### Cljs raw console handler
|
||||
|
||||
##### Default Clojure file handler in MacOS terminal:
|
||||
Chrome console, with [cljs-devtools](https://github.com/binaryage/cljs-devtools):
|
||||
|
||||
<img src="/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-cljs-console-raw.png" alt="Raw ClojureScript console handler output" width="640"/>
|
||||
|
||||
#### Clj file handler
|
||||
|
||||
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"/>
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Wiki][GitHub wiki] (getting started, usage, etc.) (**TODO:** soon)
|
||||
- API reference: [cljdoc][cljdoc docs], [Codox][Codox docs]
|
||||
- [Wiki][GitHub wiki] (getting started, usage, etc.)
|
||||
- API reference: [cljdoc][cljdoc docs] or [Codox][Codox docs]
|
||||
- Support: [Slack channel][] or [GitHub issues][]
|
||||
|
||||
## Observability tips
|
||||
|
||||
See [here](/../../wiki/8-Tips) for general advice re: building and maintaining observable Clojure/Script systems.
|
||||
See [here](../../wiki/7-Tips) for general advice re: building and maintaining observable Clojure/Script systems.
|
||||
|
||||
## Benchmarks
|
||||
|
||||
|
|
@ -197,7 +207,7 @@ Measurements:
|
|||
- Exclude handler runtime (which depends on handler/s, is usually async)
|
||||
- Taken on a 2020 Macbook Pro M1, running OpenJDK 21
|
||||
|
||||
**Tip**: Telemere offers extensive per-call and per-handler **filtering**, **sampling**, and **rate-limiting**. Use these to ensure that you're not capturing useless/low-value information in production. See [here](/TODO) for more tips!
|
||||
**Tip**: Telemere offers extensive per-call and per-handler **filtering**, **sampling**, and **rate-limiting**. Use these to ensure that you're not capturing useless/low-value information in production. See [here](../../wiki/7-Tips) for more tips!
|
||||
|
||||
## Funding
|
||||
|
||||
|
|
@ -213,6 +223,7 @@ Licensed under [EPL 1.0](LICENSE.txt) (same as Clojure).
|
|||
[GitHub releases]: ../../releases
|
||||
[GitHub issues]: ../../issues
|
||||
[GitHub wiki]: ../../wiki
|
||||
[Slack channel]: https://www.taoensso.com/telemere/slack
|
||||
|
||||
[Peter Taoussanis]: https://www.taoensso.com
|
||||
[sponsor]: https://www.taoensso.com/sponsor
|
||||
|
|
|
|||
|
|
@ -122,3 +122,65 @@
|
|||
|
||||
;; Deny all signals in matching namespaces
|
||||
(t/set-ns-filter! {:deny "some.nosy.namespace.*"})
|
||||
|
||||
;;; Configuring handlers
|
||||
|
||||
;; Create a test signal
|
||||
(def my-signal
|
||||
(t/with-signal
|
||||
(t/log! {:id ::my-id, :data {:x1 :x2}} "My message")))
|
||||
|
||||
;; Create console handler with default opts (writes formatted string)
|
||||
(def my-handler (t/handler:console))
|
||||
|
||||
;; Test handler, remember it's just a (fn [signal])
|
||||
(my-handler my-signal) ; =>
|
||||
;; 2024-04-11T10:54:57.202869Z INFO LOG Schrebermann.local examples(56,1) ::my-id - My message
|
||||
;; data: {:x1 :x2}
|
||||
|
||||
;; Create console which writes signals as edn
|
||||
(def my-handler
|
||||
(t/handler:console
|
||||
{:format-signal-fn (taoensso.telemere.utils/format-signal->edn-fn)}))
|
||||
|
||||
(my-handler my-signal) ; =>
|
||||
;; {:inst #inst "2024-04-11T10:54:57.202869Z", :msg_ "My message", :ns "examples", ...}
|
||||
|
||||
;; Create console which writes signals as JSON
|
||||
(def my-handler
|
||||
(t/handler:console
|
||||
{:format-signal-fn
|
||||
(taoensso.telemere.utils/format-signal->json-fn
|
||||
{:pr-json-fn jsonista.core/write-value-as-string})}))
|
||||
|
||||
(my-handler my-signal) ; =>
|
||||
;; {"inst":"2024-04-11T10:54:57.202869Z","msg_":"My message","ns":"examples", ...}
|
||||
|
||||
;;; Writing handlers
|
||||
|
||||
(defn handler:my-handler ; Note naming convention
|
||||
"Returns a (fn handler [signal] that:
|
||||
- Does something.
|
||||
|
||||
Options:
|
||||
`:option1` - Description
|
||||
`:option2` - Description"
|
||||
|
||||
([] (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.
|
||||
(let []
|
||||
|
||||
(fn a-handler:my-handler ; Note naming convention
|
||||
|
||||
;; Shutdown arity - called by Telemere exactly once when the handler is
|
||||
;; to be shut down. This is your opportunity to finalize/free resources, etc.
|
||||
([])
|
||||
|
||||
;; Main arity - called by Telemere whenever the handler should handle the
|
||||
;; given signal. Never called after shutdown.
|
||||
([signal]
|
||||
;; TODO Do something with given signal
|
||||
)))))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
Unconditionally executes given form and-
|
||||
If form succeeds: return the form's result.
|
||||
If form throws:
|
||||
Call `error!` with the thrown error and the given signal options [1],
|
||||
Call `error!` with the thrown error and the given signal options [2],
|
||||
then return (:catch-val opts) if it exists, or rethrow the error.
|
||||
|
||||
API: [form] [id-or-opts form] => form's result (value/throw) (unconditional), or (:catch-val opts)
|
||||
|
|
@ -25,11 +25,14 @@ Examples:
|
|||
Tips:
|
||||
|
||||
- Test using `with-signal`: (with-signal (catch->error! ...)).
|
||||
- Supports the same options as other signals [1].
|
||||
- Supports the same options [2] as other signals [1].
|
||||
|
||||
- Useful for recording errors in forms, futures, callbacks, etc.
|
||||
|
||||
See also `error!`.
|
||||
|
||||
---------------------------------------
|
||||
[1] See `help:signal-options` docstring
|
||||
-------------------------------------------------------------------
|
||||
[1] See `help:signal-creators` - (`signal!`, `log!`, `event!`, ...)
|
||||
[2] See `help:signal-options` - {:keys [kind level id data ...]}
|
||||
[3] See `help:signal-content` - {:keys [kind level id data ...]}
|
||||
[4] See `help:signal-flow` - (filters, handling, etc.)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
"Error" signal creator, focused on error + id.
|
||||
"Error" signal creator, emphasizing error + id.
|
||||
|
||||
API: [error] [id-or-opts error] => given error (unconditional)
|
||||
Default kind: `:error`
|
||||
|
|
@ -19,12 +19,13 @@ Examples:
|
|||
Tips:
|
||||
|
||||
- Test using `with-signal`: (with-signal (error! ...)).
|
||||
- Supports the same options as other signals [3].
|
||||
- Supports the same options [2] as other signals [1].
|
||||
|
||||
- `error` arg is a platform error (`java.lang.Throwable` or `js/Error`).
|
||||
- Can conveniently be wrapped by `throw`: (throw (error! ...)).
|
||||
|
||||
----------------------------------------
|
||||
[1] See `help:signal-flow` docstring
|
||||
[2] See `help:signal-content` docstring
|
||||
[3] See `help:signal-options` docstring
|
||||
-------------------------------------------------------------------
|
||||
[1] See `help:signal-creators` - (`signal!`, `log!`, `event!`, ...)
|
||||
[2] See `help:signal-options` - {:keys [kind level id data ...]}
|
||||
[3] See `help:signal-content` - {:keys [kind level id data ...]}
|
||||
[4] See `help:signal-flow` - (filters, handling, etc.)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
"Event" signal creator, focused on id + level.
|
||||
"Event" signal creator, emphasizing id + level.
|
||||
|
||||
API: [id] [id level-or-opts] => true iff signal was allowed
|
||||
Default kind: `:event`
|
||||
Default level: `:info`
|
||||
|
||||
When filtering conditions are met [1], creates a Telemere signal [2] and
|
||||
When filtering conditions are met [4], creates a Telemere signal [3] and
|
||||
dispatches it to registered handlers for processing (e.g. writing to
|
||||
console/file/queue/db, etc.).
|
||||
|
||||
|
|
@ -20,16 +20,17 @@ Examples:
|
|||
Tips:
|
||||
|
||||
- Test using `with-signal`: (with-signal (event! ...)).
|
||||
- Supports the same options as other signals [3].
|
||||
- Supports the same options [2] as other signals [1].
|
||||
|
||||
- A good general-purpose signal, prefer to `log!` by default, since it
|
||||
better encourages structured data over unstructured messages.
|
||||
- `log!` and `event!` are both good default/general-purpose signal creators.
|
||||
- `log!` emphasizes messages, while `event!` emphasizes ids.
|
||||
|
||||
- Has a different 2-arity arg order to all other signals!
|
||||
Mnemonic: the arg that's typically larger is *always* in the rightmost
|
||||
position, and for `event!` that's the `level-or-opts` arg.
|
||||
|
||||
----------------------------------------
|
||||
[1] See `help:signal-flow` docstring
|
||||
[2] See `help:signal-content` docstring
|
||||
[3] See `help:signal-options` docstring
|
||||
-------------------------------------------------------------------
|
||||
[1] See `help:signal-creators` - (`signal!`, `log!`, `event!`, ...)
|
||||
[2] See `help:signal-options` - {:keys [kind level id data ...]}
|
||||
[3] See `help:signal-content` - {:keys [kind level id data ...]}
|
||||
[4] See `help:signal-flow` - (filters, handling, etc.)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
"Log" signal creator, focused on message + level.
|
||||
"Log" signal creator, emphasizing message + level.
|
||||
|
||||
API: [msg] [level-or-opts msg] => true iff signal was allowed.
|
||||
Default kind: `:log`
|
||||
Default level: `:info`
|
||||
|
||||
When filtering conditions are met [1], creates a Telemere signal [2] and
|
||||
When filtering conditions are met [4], creates a Telemere signal [3] and
|
||||
dispatches it to registered handlers for processing (e.g. writing to
|
||||
console/file/queue/db, etc.).
|
||||
|
||||
|
|
@ -21,15 +21,16 @@ Examples:
|
|||
Tips:
|
||||
|
||||
- Test using `with-signal`: (with-signal (log! ...)).
|
||||
- Supports the same options as other signals [3].
|
||||
- Supports the same options [2] as other signals [1].
|
||||
|
||||
- Prefer `event!` to `log!` by default, since it better encourages structured
|
||||
data over unstructured messages.
|
||||
- `log!` and `event!` are both good default/general-purpose signal creators.
|
||||
- `log!` emphasizes messages, while `event!` emphasizes ids.
|
||||
|
||||
- `msg` arg may be a string, or vector of strings to join with `\space`.
|
||||
- See also `msg-splice`, `msg-skip` utils.
|
||||
|
||||
----------------------------------------
|
||||
[1] See `help:signal-flow` docstring
|
||||
[2] See `help:signal-content` docstring
|
||||
[3] See `help:signal-options` docstring
|
||||
-------------------------------------------------------------------
|
||||
[1] See `help:signal-creators` - (`signal!`, `log!`, `event!`, ...)
|
||||
[2] See `help:signal-options` - {:keys [kind level id data ...]}
|
||||
[3] See `help:signal-content` - {:keys [kind level id data ...]}
|
||||
[4] See `help:signal-flow` - (filters, handling, etc.)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
Low-level generic signal creator.
|
||||
|
||||
API: [opts] => depends on options [3]
|
||||
API: [opts] => depends on options [2]
|
||||
Default kind: none (optional)
|
||||
Default level: none (must be provided)
|
||||
|
||||
When filtering conditions are met [1], creates a Telemere signal [2] and
|
||||
When filtering conditions are met [4], creates a Telemere signal [3] and
|
||||
dispatches it to registered handlers for processing (e.g. writing to
|
||||
console/file/queue/db, etc.).
|
||||
|
||||
|
|
@ -14,23 +14,15 @@ If `:run` option is provided: returns value of given run form, or throws.
|
|||
Generic signals are fairly low-level and useful mostly for library authors or
|
||||
advanced users writing their own wrapper macros. Regular users will typically
|
||||
prefer one of the higher-level signal creators optimized for ease-of-use in
|
||||
common cases.
|
||||
|
||||
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):
|
||||
|
||||
`event!` - (id + opts/level) => true iff signal was created (allowed)
|
||||
`log!` - (message + opts/level) => true iff signal was created (allowed)
|
||||
`error!` - (error + opts/id) => given error (unconditional)
|
||||
`trace!` - (form + opts/id) => form's result (value/throw) (unconditional)
|
||||
`spy!` - (form + opts/level) => form's result (value/throw) (unconditional)
|
||||
common cases [1].
|
||||
|
||||
Tips:
|
||||
|
||||
- Test using `with-signal`: (with-signal (signal! ...)).
|
||||
- Supports the same options as other signals [3].
|
||||
- Supports the same options [2] as other signals [1].
|
||||
|
||||
---------------------------------------
|
||||
[1] See `help:signal-flow` docstring
|
||||
[2] See `help:signal-content` docstring
|
||||
[3] See `help:signal-options` docstring
|
||||
-------------------------------------------------------------------
|
||||
[1] See `help:signal-creators` - (`signal!`, `log!`, `event!`, ...)
|
||||
[2] See `help:signal-options` - {:keys [kind level id data ...]}
|
||||
[3] See `help:signal-content` - {:keys [kind level id data ...]}
|
||||
[4] See `help:signal-flow` - (filters, handling, etc.)
|
||||
|
|
|
|||
32
resources/signal-docstrings/signal-creators.txt
Normal file
32
resources/signal-docstrings/signal-creators.txt
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
Call a Telemere signal creator to conditionally create a signal at that callsite.
|
||||
|
||||
When filtering conditions are met [4], the call creates a Telemere signal [3]
|
||||
and dispatches it to registered handlers for processing (e.g. writing to
|
||||
console/file/queue/db, etc.).
|
||||
|
||||
Telemere doesn't make a hard distinction between different kinds of signals
|
||||
(log, event, error, etc.) - they're all just plain Clojure/Script maps with
|
||||
various keys:
|
||||
|
||||
- All signal creators offer the same options [2], and
|
||||
- All signal kinds can contain the same content [3]
|
||||
|
||||
Creators vary only in in their default options and call APIs (expected args
|
||||
and return values), making them more/less convenient for certain use cases:
|
||||
|
||||
`log!` ---------- [message + opts/level] => true iff signal was created (allowed)
|
||||
`event!` -------- [id + opts/level] => true iff signal was created (allowed)
|
||||
`error!` -------- [error + opts/id ] => given error (unconditional)
|
||||
`trace!` -------- [form + opts/id ] => form result (value/throw) (unconditional)
|
||||
`spy!` ---------- [form + opts/level] => form result (value/throw) (unconditional)
|
||||
`catch->error!` - [error + opts/id ] => form value, or given fallback
|
||||
`signal!` ------- [ opts ] => depends on options
|
||||
|
||||
- `log!` and `event!` are both good default/general-purpose signal creators.
|
||||
- `log!` emphasizes messages, while `event!` emphasizes ids.
|
||||
- `signal!` is the generic creator, and is used by all the others.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
[2] See `help:signal-options` - {:keys [kind level id data ...]}
|
||||
[3] See `help:signal-content` - {:keys [kind level id data ...]}
|
||||
[4] See `help:signal-flow` - (filters, handling, etc.)
|
||||
|
|
@ -13,14 +13,14 @@ 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
|
||||
This 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:
|
||||
- On signal filters, see: `help:filters` docstring
|
||||
- On handler filters, see: `help:handlers` docstring
|
||||
- On signal filters, see: `help:signal-filters` docstring
|
||||
- On handler filters, see: `help:signal-handlers` docstring
|
||||
|
||||
If anything is unclear, please ping me (@ptaoussanis) so that I can
|
||||
improve these docs!
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
"Spy" signal creator, focused on form + level.
|
||||
"Spy" signal creator, emphasizing form + level.
|
||||
|
||||
API: [form] [level-or-opts form] => form's result (value/throw) (unconditional)
|
||||
Default kind: `:spy`
|
||||
Default level: `:info`
|
||||
|
||||
When filtering conditions are met [1], creates a Telemere signal [2] and
|
||||
When filtering conditions are met [4], creates a Telemere signal [3] and
|
||||
dispatches it to registered handlers for processing (e.g. writing to
|
||||
console/file/queue/db, etc.).
|
||||
|
||||
|
|
@ -23,9 +23,9 @@ Examples:
|
|||
Tips:
|
||||
|
||||
- Test using `with-signal`: (with-signal (spy! ...)).
|
||||
- Supports the same options as other signals [3].
|
||||
- Supports the same options [2] as other signals [1].
|
||||
|
||||
- Identical to `trace!`, but focused on form + level rather than form + id.
|
||||
- Identical to `trace!`, but emphasizing form + level rather than form + id.
|
||||
|
||||
- Useful for debugging/monitoring forms, and tracing (nested) execution flow.
|
||||
- Execution of `form` arg may create additional (nested) signals.
|
||||
|
|
@ -34,7 +34,8 @@ Tips:
|
|||
- Can be useful to wrap with `catch->error!`:
|
||||
(catch->error! ::error-id (spy! ...)).
|
||||
|
||||
---------------------------------------
|
||||
[1] See `help:signal-flow` docstring
|
||||
[2] See `help:signal-content` docstring
|
||||
[3] See `help:signal-options` docstring
|
||||
-------------------------------------------------------------------
|
||||
[1] See `help:signal-creators` - (`signal!`, `log!`, `event!`, ...)
|
||||
[2] See `help:signal-options` - {:keys [kind level id data ...]}
|
||||
[3] See `help:signal-content` - {:keys [kind level id data ...]}
|
||||
[4] See `help:signal-flow` - (filters, handling, etc.)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
"Trace" signal creator, focused on form + id.
|
||||
"Trace" signal creator, emphasizing form + id.
|
||||
|
||||
API: [form] [id-or-opts form] => form's result (value/throw) (unconditional)
|
||||
Default kind: `:trace`
|
||||
Default level: `:info` (intentionally NOT `:trace`!)
|
||||
|
||||
When filtering conditions are met [1], creates a Telemere signal [2] and
|
||||
When filtering conditions are met [4], creates a Telemere signal [3] and
|
||||
dispatches it to registered handlers for processing (e.g. writing to
|
||||
console/file/queue/db, etc.).
|
||||
|
||||
|
|
@ -23,9 +23,9 @@ Examples:
|
|||
Tips:
|
||||
|
||||
- Test using `with-signal`: (with-signal (trace! ...)).
|
||||
- Supports the same options as other signals [3].
|
||||
- Supports the same options [2] as other signals [1].
|
||||
|
||||
- Identical to `spy!`, but focused on form + id rather than form + level.
|
||||
- Identical to `spy!`, but emphasizing form + id rather than form + level.
|
||||
|
||||
- Useful for debugging/monitoring forms, and tracing (nested) execution flow.
|
||||
- Execution of `form` arg may create additional (nested) signals.
|
||||
|
|
@ -38,7 +38,8 @@ Tips:
|
|||
refers to the general action of tracing program flow rather than to the
|
||||
common logging level of the same name.
|
||||
|
||||
---------------------------------------
|
||||
[1] See `help:signal-flow` docstring
|
||||
[2] See `help:signal-content` docstring
|
||||
[3] See `help:signal-options` docstring
|
||||
-------------------------------------------------------------------
|
||||
[1] See `help:signal-creators` - (`signal!`, `log!`, `event!`, ...)
|
||||
[2] See `help:signal-options` - {:keys [kind level id data ...]}
|
||||
[3] See `help:signal-content` - {:keys [kind level id data ...]}
|
||||
[4] See `help:signal-flow` - (filters, handling, etc.)
|
||||
|
|
|
|||
|
|
@ -100,7 +100,9 @@
|
|||
|
||||
;;;; Help
|
||||
|
||||
(impl/defhelp help:signal-creators :signal-creators)
|
||||
(impl/defhelp help:signal-options :signal-options)
|
||||
(impl/defhelp help:signal-flow :signal-flow)
|
||||
(impl/defhelp help:signal-content :signal-content)
|
||||
(enc/defalias help:signal-filters help:filters) ; Via Encore
|
||||
(enc/defalias help:signal-handlers help:handlers) ; Via Encore
|
||||
|
|
@ -109,7 +111,8 @@
|
|||
;;;; Context
|
||||
|
||||
(enc/defonce default-ctx
|
||||
"Advanced feature. Default root (base) value of `*ctx*` var, controlled by:
|
||||
"Default root (base) value of `*ctx*` var.
|
||||
Defaults to `nil`, controlled by:
|
||||
(get-env {:as :edn} :taoensso.telemere/default-ctx<.platform><.edn>)
|
||||
|
||||
See `get-env` for details."
|
||||
|
|
@ -375,7 +378,7 @@
|
|||
#?(:clj
|
||||
(enc/compile-when
|
||||
(do (require '[taoensso.telemere.tools-logging :as ttl]) true)
|
||||
(enc/defalias ttl/tools-logging->telemere!)
|
||||
(enc/defalias ttl/tools-logging->telemere!) ; Incl. `get-env` docs
|
||||
(when (enc/get-env {:as :bool} :clojure.tools.logging->telemere?)
|
||||
(ttl/tools-logging->telemere!))))
|
||||
|
||||
|
|
|
|||
|
|
@ -39,11 +39,10 @@
|
|||
(defn ^:public tools-logging->telemere!
|
||||
"Configures `clojure.tools.logging` to use Telemere as its logging implementation.
|
||||
|
||||
Will be AUTOMATICALLY called if `clojure.tools.logging` is present and any of the
|
||||
following are \"true\":
|
||||
- `clojure.tools.logging->telemere?` JVM propety value
|
||||
- `CLOJURE_TOOLS_LOGGING_>TELEMERE?` Environment variable
|
||||
- `clojure.tools.logging->telemere?` Classpath resource content"
|
||||
Called automatically if the following is true:
|
||||
(get-env {:as :bool} :clojure.tools.logging->telemere?)
|
||||
|
||||
See `get-env` for details."
|
||||
[]
|
||||
(impl/signal!
|
||||
{:kind :event
|
||||
|
|
|
|||
|
|
@ -1,3 +1,74 @@
|
|||
<img src="https://raw.githubusercontent.com/taoensso/telemere/master/imgs/telemere-logo.svg" alt="Telemere logo" width="360"/>
|
||||
|
||||
# Introduction
|
||||
|
||||
Telemere is a **structured telemetry** library and next-generation replacement for [Timbre](https://www.taoensso.com/timbre). It helps enable the creation of Clojure/Script systems that are highly **observable**, **robust**, and **debuggable**.
|
||||
|
||||
Its key function is to help:
|
||||
|
||||
1. **Capture data** in your running Clojure/Script programs, and
|
||||
2. **Facilitate processing** of that data into **useful information / insight**.
|
||||
|
||||
## Signals
|
||||
|
||||
The basic unit of data in Telemere is the **signal**.
|
||||
|
||||
Signals include **traditional log messages**, **structured log messages**, and **events**. Telemere doesn't make a hard distinction between these - *they're all just signals* with [various attributes](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-content).
|
||||
|
||||
And they're represented by plain **Clojure/Script maps** with those attributes (keys).
|
||||
|
||||
Fundamentally **all signals**:
|
||||
|
||||
- Occur or are observed at a particular **location** in your code (file, namespace, line, column).
|
||||
- Occur or are observed *within* a particular **program state** / context.
|
||||
- Convey something of value *about* that **program state** / context.
|
||||
|
||||
Signals may be *independently* valuable, valuable *in the aggregate* (e.g. statistically), or valuable *in association* with other related signals (e.g. while tracing the flow of some logical activity).
|
||||
|
||||
## Functionality
|
||||
|
||||
The basic tools of Telemere are:
|
||||
|
||||
1. **Signal creators** to conditionally *create* signal maps at points in your code.
|
||||
2. **Signal handlers** to conditionally *handle* those signal maps (analyse, write to
|
||||
console/file/queue/db, etc.).
|
||||
|
||||
This is just a generalization of **traditional logging** which:
|
||||
|
||||
- Conditionally creates **message strings** at points in your code.
|
||||
- Usually *dumps* those message strings somewhere for future parsing by human eyes or automated tools.
|
||||
|
||||
## Data types and structures
|
||||
|
||||
The parsing of traditional log messages is often expensive, fragile, and lossy. So a key principle of **structured logging** is to **avoid parsing**, by instead **preserving data types and structures** whenever possible.
|
||||
|
||||
Telemere embraces this principle by making such preservation *natural and convenient*.
|
||||
|
||||
## Noise reduction
|
||||
|
||||
Not all data is equally valuable.
|
||||
|
||||
Too much low-value data is often actively *harmful*: expensive to process, to store, and to query. Adding noise just interferes with better data, harming your ability to understand your system.
|
||||
|
||||
Telemere embraces this principle by making **effective filtering** likewise *natural and convenient*:
|
||||
|
||||
<img src="https://raw.githubusercontent.com/taoensso/telemere/master/imgs/signal-sampling.svg" alt="Telemere sampling" width="640"/>
|
||||
|
||||
> Telemere uses the term **filtering** as the superset of both random sampling and other forms of data exclusion/reduction.
|
||||
|
||||
## Structured telemetry
|
||||
|
||||
To conclude- Telemere handles **structured and traditional logging**, **tracing**, and **basic performance monitoring** with a simple unified API that:
|
||||
|
||||
- Preserves data types and structures with **rich signals**, and
|
||||
- Offers effective noise reduction with **signal filtering**.
|
||||
|
||||
Its name is a combination of _telemetry_ and _telomere_:
|
||||
|
||||
> *Telemetry* derives from the Greek *tele* (remote) and *metron* (measure). It refers to the collection of *in situ* (in position) data, for transmission to other systems for monitoring/analysis. *Logs* are the most common form of software telemetry. So think of telemetry as the *superset of logging-like activities* that help monitor and understand (software) systems.
|
||||
|
||||
> *Telomere* derives from the Greek *télos* (end) and *méros* (part). It refers to a genetic feature commonly found at the end of linear chromosomes that helps to protect chromosome integrity.
|
||||
|
||||
# Setup
|
||||
|
||||
Add the [relevant dependency](../#latest-releases) to your project:
|
||||
|
|
@ -10,5 +81,140 @@ deps.edn: com.taoensso/telemere {:mvn/version "x-y-z"}
|
|||
And setup your namespace imports:
|
||||
|
||||
```clojure
|
||||
(ns my-app (:require [taoensso.telemere :as tm]))
|
||||
(ns my-app (:require [taoensso.telemere :as t]))
|
||||
```
|
||||
|
||||
# Default config
|
||||
|
||||
Telemere is configured sensibly out-the-box.
|
||||
See section [3-Config](./3-Config) for customization.
|
||||
|
||||
**Default minimum level**: `:info` (signals with lower levels will no-op).
|
||||
|
||||
**Default signal handlers**:
|
||||
|
||||
> Signal handlers process created signals to *do something with them* (analyse them, write them to console/file/queue/db, etc.)
|
||||
|
||||
| Platform | Condition | Handler
|
||||
| --- | --- | ---
|
||||
| Clj | Always | [Console handler](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:console) that prints signals to `*out*` or `*err*`.
|
||||
| Cljs | Always | [Console handler](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:console) that prints signals to the **browser console**.
|
||||
| Clj | [OpenTelemetry API](https://mvnrepository.com/artifact/io.opentelemetry/opentelemetry-api) present | [OpenTelemetry handler](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:open-telemetry-logger) that emits signals as log records to a configured [`LoggerProvider`](https://opentelemetry.io/docs/specs/otel/logs/sdk/#loggerprovider).
|
||||
|
||||
**Default signal intakes**:
|
||||
|
||||
> Telemere can create signals from relevant **external API calls**, etc.
|
||||
|
||||
| Platform | Condition | Signals from
|
||||
| --- | --- | ---
|
||||
| Clj | [SLF4J API](https://mvnrepository.com/artifact/org.slf4j/slf4j-api) and [Telemere SLF4J backend](https://clojars.org/com.taoensso/slf4j-telemere) present | [SLF4J](https://www.slf4j.org/) logging calls.
|
||||
| Clj | [clojure.tools.logging](https://mvnrepository.com/artifact/org.clojure/tools.logging) present and [`tools-logging->telemere!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#tools-logging-%3Etelemere!) called | [clojure.tools.logging](https://github.com/clojure/tools.logging) logging calls.
|
||||
| Clj | [`streams->telemere!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#streams-%3Etelemere!) called | Output to `System/out` and `System/err` streams.
|
||||
|
||||
Run [`check-intakes`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#check-intakes) to help verify/debug:
|
||||
|
||||
```clojure
|
||||
(check-intakes) ; =>
|
||||
{:tools-logging {:present? false}
|
||||
:slf4j {:sending->telemere? true, :telemere-receiving? true}
|
||||
:system/out {:sending->telemere? false, :telemere-receiving? false}
|
||||
:system/err {:sending->telemere? false, :telemere-receiving? false}}
|
||||
```
|
||||
|
||||
# Usage
|
||||
|
||||
## Create signals
|
||||
|
||||
Use whichever signal creator is most convenient for your needs:
|
||||
|
||||
| Name | Signal kind | Main arg | Optional arg | Returns
|
||||
:-- | :-- | :-- | :-- | :--
|
||||
| [`log!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#log!) | `:log` | `msg` | `opts`/`level` | Signal allowed?
|
||||
| [`event!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#event!) | `:event` | `id` | `opts`/`level` | Signal allowed?
|
||||
| [`error!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#error!) | `:error` | `error` | `opts`/`id` | Given error
|
||||
| [`trace!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#trace!) | `:trace` | `form` | `opts`/`id` | Form result
|
||||
| [`spy!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#spy!) | `:spy` | `form` | `opts`/`level` | Form result
|
||||
| [`catch->error!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#catch-%3Eerror!) | `:error` | `form` | `opts`/`id` | Form value or given fallback
|
||||
| [`signal!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#signal!) | `<arb>` | `opts` | - | Depends on opts
|
||||
|
||||
- See [`help:signal-creators`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-creators) for more info on signal creators.
|
||||
- See [`help:signal-options`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-options) for signal options (shared by all creators).
|
||||
- See relevant docstrings (links above) for usage info.
|
||||
- See [examples.cljc](https://github.com/taoensso/telemere/blob/master/examples.cljc) for REPL-ready examples.
|
||||
|
||||
## Check signals
|
||||
|
||||
Use the [`with-signal`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#with-signal) or (advanced) [`with-signals`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#with-signals) utils to help test/debug the signals that you're creating:
|
||||
|
||||
```clojure
|
||||
(t/with-signal
|
||||
(t/log!
|
||||
{:let [x "x"]
|
||||
:data {:x x}}
|
||||
["My msg:" x]))
|
||||
|
||||
;; => {:keys [ns inst data msg_ ...]} ; The signal
|
||||
```
|
||||
|
||||
- [`with-signal`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#with-signal) will return the **last** signal created by the given form.
|
||||
- [`with-signals`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#with-signals) will return **all** signals created by the given form.
|
||||
|
||||
Both have several options, see their docstrings (links above) for details.
|
||||
|
||||
## Filter signals
|
||||
|
||||
A signal will be provided to a handler iff ALL of the following are true:
|
||||
|
||||
1. Signal **creation** is allowed by **compile-time** filter config
|
||||
2. Signal **creation** is allowed by **runtime** filter config
|
||||
3. Signal **handling** is allowed by **handler** filter config
|
||||
4. Signal **middleware** does not suppress the signal (return nil)
|
||||
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
|
||||
|
||||
Quick examples of some basic filtering:
|
||||
|
||||
```clojure
|
||||
(t/set-min-level! :info) ; Set global minimum level
|
||||
(t/with-signal (t/event! ::my-id1 :info)) ; => {:keys [inst id ...]}
|
||||
(t/with-signal (t/event! ::my-id1 :debug)) ; => nil (signal not allowed)
|
||||
|
||||
(t/with-min-level :trace ; Override global minimum level
|
||||
(t/with-signal (t/event! ::my-id1 :debug))) ; => {:keys [inst id ...]}
|
||||
|
||||
;; Deny all signals in matching namespaces
|
||||
(t/set-ns-filter! {:deny "some.nosy.namespace.*"})
|
||||
```
|
||||
|
||||
- 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.
|
||||
- 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:
|
||||
|
||||
| Global | Dynamic | Filters by
|
||||
| :-- | :-- | :--
|
||||
| [`set-kind-filter!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#set-kind-filter!) | [`with-kind-filter`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#with-kind-filter) | Signal kind (`:log`, `:event`, etc.)
|
||||
| [`set-ns-filter!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#set-ns-filter!) | [`with-ns-filter`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#with-ns-filter) | Signal namespace
|
||||
| [`set-id-filter!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#set-id-filter!) | [`with-id-filter`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#with-id-filter) | Signal id
|
||||
| [`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).
|
||||
|
||||
# Internal help
|
||||
|
||||
Telemere includes extensive internal help docstrings:
|
||||
|
||||
| Var | Help with
|
||||
| :-- | :--
|
||||
| [`help:signal-creators`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-creators) | List of signal creators
|
||||
| [`help:signal-options`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-options) | Options for signal creators
|
||||
| [`help:signal-content`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-content) | Signal map content
|
||||
| [`help:signal-flow`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-flow) | Ordered flow from signal creation to handling
|
||||
| [`help:signal-filters`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-filters) | API for configuring signal filters
|
||||
| [`help:signal-handlers`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-handlers) | API for configuring signal handlers
|
||||
| [`help:signal-formatters`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-formatters) | Signal formatters for use by handlers
|
||||
31
wiki/2-Architecture.md
Normal file
31
wiki/2-Architecture.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
Telemere's key function is to help:
|
||||
|
||||
1. **Capture information** in your running Clojure/Script programs, and
|
||||
2. **Facilitate processing** of that information to support **insight**.
|
||||
|
||||
Its basic tools:
|
||||
|
||||
1. **Signal creators** to conditionally *create* signal maps at points in your code.
|
||||
2. **Signal handlers** to conditionally *handle* those signal maps (analyse, write to
|
||||
console/file/queue/db, etc.).
|
||||
|
||||
So you *call* a *signal creator* to (conditionally) create a *signal* (map) which is then dispatched to registered _signal handlers_ for (conditional) handling.
|
||||
|
||||
This flow is described by [`help:signal-flow`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-flow), and is visualized below:
|
||||
|
||||
<img src="https://raw.githubusercontent.com/taoensso/telemere/master/imgs/signal-flow.svg" alt="Telemere signal flowchart" width="640"/>
|
||||
|
||||
- `A/sync queue` semantics are configured per handler when calling [`add-handler!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#add-handler!). Default semantics are: async with a dropping buffer, and 1 handler thread.
|
||||
- The shared **signal middleware cache** is super useful when doing signal transformations that are expensive and/or involve side effects (like syncing with another service/db to get a unique tx id, etc.).
|
||||
|
||||
For more info see:
|
||||
|
||||
| Var | Help with
|
||||
| :-- | :--
|
||||
| [`help:signal-creators`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-creators) | List of signal creators
|
||||
| [`help:signal-options`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-options) | Options for signal creators
|
||||
| [`help:signal-content`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-content) | Signal map content
|
||||
| [`help:signal-flow`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-flow) | Ordered flow from signal creation to handling
|
||||
| [`help:signal-filters`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-filters) | API for configuring signal filters
|
||||
| [`help:signal-handlers`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-handlers) | API for configuring signal handlers
|
||||
| [`help:signal-formatters`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-formatters) | Signal formatters for use by handlers
|
||||
123
wiki/3-Config.md
Normal file
123
wiki/3-Config.md
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
See below for config by topic-
|
||||
|
||||
# Signal filtering
|
||||
|
||||
A signal will be provided to a handler iff ALL of the following are true:
|
||||
|
||||
1. Signal **creation** is allowed by **compile-time** filter config
|
||||
2. Signal **creation** is allowed by **runtime** filter config
|
||||
3. Signal **handling** is allowed by **handler** filter config
|
||||
4. Signal **middleware** does not suppress the signal (return nil)
|
||||
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
|
||||
|
||||
- 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 [`add-handler!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#add-handler!) for info on signal handler filters.
|
||||
|
||||
# Signal handlers
|
||||
|
||||
See section [4-Handlers](./4-Handlers).
|
||||
|
||||
# Interop
|
||||
|
||||
## clojure.tools.logging
|
||||
|
||||
[`clojure.tools.logging`](https://github.com/clojure/tools.logging) can use Telemere as its logging implementation.
|
||||
|
||||
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-%3Etelemere!), or set the relevant system config as described in its docstring.
|
||||
|
||||
Note that the `tools-logging->telemere!` var will be present **only if** the `clojure.tools.logging` dependency is present.
|
||||
|
||||
Verify successful intake with [`check-intakes`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#check-intakes):
|
||||
|
||||
```clojure
|
||||
(check-intakes) ; =>
|
||||
{:tools-logging {:sending->telemere? true, :telemere-receiving? true}}
|
||||
```
|
||||
|
||||
## Java logging
|
||||
|
||||
[`SLF4J`](https://www.slf4j.org/) can use Telemere as its logging backend.
|
||||
|
||||
To do this, ensure that you have the following dependencies:
|
||||
|
||||
```clojure
|
||||
[org.slf4j/slf4j-api "x.y.z"] ; >= 2.0.0
|
||||
[com.taoensso/slf4j-telemere "x.y.z"]
|
||||
```
|
||||
|
||||
When `com.taoensso/slf4j-telemere` is on your classpath AND no other SLF4J backends are, SLF4J will direct all its logging calls to Telemere.
|
||||
|
||||
Verify successful intake with [`check-intakes`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#check-intakes):
|
||||
|
||||
```clojure
|
||||
(check-intakes) ; =>
|
||||
{:slf4j {:sending->telemere? true, :telemere-receiving? true}}
|
||||
```
|
||||
|
||||
For other (non-SLF4J) logging like [Log4j](https://logging.apache.org/log4j/2.x/), [`java.util.logging`](https://docs.oracle.com/javase/8/docs/api/java/util/logging/package-summary.html) (JUL), and [Apache Commons Logging](https://commons.apache.org/proper/commons-logging/) (JCL), use an appropriate [SLF4J bridge](https://www.slf4j.org/legacy.html) and the normal SLF4J config as above.
|
||||
|
||||
In this case logging will be forwarded:
|
||||
|
||||
1. From Log4j/JUL/JCL/etc. to SLF4J, and
|
||||
2. From SLF4J to Telemere
|
||||
|
||||
## System streams
|
||||
|
||||
The JVM's `System/out` and/or `System/err` streams can be set to flush to Telemere signals.
|
||||
|
||||
To do this, call [`streams->telemere!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#streams-%3Etelemere!).
|
||||
|
||||
Note that Clojure's `*out*`, `*err*` are **not** necessarily automatically affected.
|
||||
|
||||
Verify successful intake with [`check-intakes`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#check-intakes):
|
||||
|
||||
```clojure
|
||||
(check-intakes) ; =>
|
||||
{:system/out {:sending->telemere? true, :telemere-receiving? true}
|
||||
:system/err {:sending->telemere? true, :telemere-receiving? true}}
|
||||
```
|
||||
|
||||
## OpenTelemetry
|
||||
|
||||
Telemere can send signals as [`LogRecords`](https://opentelemetry.io/docs/specs/otel/logs/data-model/) to [OpenTelemetry](https://opentelemetry.io/).
|
||||
|
||||
To do this:
|
||||
|
||||
1. Ensure that you have the [OpenTelemetry Java](https://github.com/open-telemetry/opentelemetry-java) dependency.
|
||||
2. Use [`handler:open-telemetry-logger`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:open-telemetry-logger) to create an appropriately configured handler, and register it with [`add-handler!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#add-handler!).
|
||||
|
||||
Note that the `handler:open-telemetry-logger` var will be present **only if** the OpenTelemetry Java dependency is present.
|
||||
|
||||
## Tufte
|
||||
|
||||
> [Tufte](https:/www.taoensso.com/tufte) is a simple performance monitoring library for Clojure/Script by the author of Telemere.
|
||||
|
||||
Telemere can easily incorporate Tufte performance data in its signals, just like any other data:
|
||||
|
||||
```clojure
|
||||
(let [[_ perf-data] (tufte/profiled <opts> <form>)]
|
||||
(t/log! "Performance data" {:perf-data perf-data}))
|
||||
```
|
||||
|
||||
Telemere and Tufte work great together:
|
||||
|
||||
- Their functionality is complementary.
|
||||
- The [upcoming](https:/www.taoensso.com/roadmap) Tufte v4 will share the same core as Telemere and offer an **identical API** for managing filters and handlers.
|
||||
|
||||
## Truss
|
||||
|
||||
> [Truss](https://www.taoensso.com/truss) is an assertions micro-library for Clojure/Script by the author of Telemere.
|
||||
|
||||
Telemere can easily incorporate Truss assertion failure information in its signals, just like any other (error) data.
|
||||
|
||||
The [`catch->error!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#catch-%3Eerror!) signal creator can be particularly convenient for this:
|
||||
|
||||
```clojure
|
||||
(t/catch->error! <form-with-truss-assertion/s>)
|
||||
```
|
||||
184
wiki/4-Handlers.md
Normal file
184
wiki/4-Handlers.md
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
Signal handlers process created signals to *do something with them* (analyse them, write them to console/file/queue/db, etc.).
|
||||
|
||||
# Included handlers
|
||||
|
||||
The following handlers are 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#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.
|
||||
|
||||
# Configuring handlers
|
||||
|
||||
There's two kinds of config relevant to all signal handlers:
|
||||
|
||||
1. **Dispatch** opts (common to all handlers), and
|
||||
2. **Handler-specific** opts
|
||||
|
||||
## Dispatch opts
|
||||
|
||||
Dispatch opts includes dispatch priority, handler filtering, handler middleware, queue semantics, back-pressure opts, etc.
|
||||
|
||||
This is all specified when calling [`add-handler!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#add-handler!) - and documented there.
|
||||
|
||||
Note that handler middleware in particular is an often overlooked but powerful feature, allowing you to arbitrarily transform and/or filter every [signal map](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-content) before it is given to the handler.
|
||||
|
||||
## Handler-specific opts
|
||||
|
||||
Handler-specific opts are specified when calling a particular **handler constructor** (like [`handler:console`](https://cljdoc.org/d/com.taoensso/telemere/CONSOLE/api/taoensso.telemere#handler:console)) - and documented by the constructor.
|
||||
|
||||
Note that it's common for Telemere handlers to be customized by providing *Clojure/Script functions* to the relevant handler constructor call.
|
||||
|
||||
See the [utils namespace](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.utils) for tools useful for customizing and writing signal handlers.
|
||||
|
||||
### Example
|
||||
|
||||
The standard Clj/s console handler ([`handler:console`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:console)) writes signals **as strings** to `*out*`/`*err` or browser console.
|
||||
|
||||
By default it writes formatted strings intended for human consumption:
|
||||
|
||||
```clojure
|
||||
;; Create a test signal
|
||||
(def my-signal
|
||||
(t/with-signal
|
||||
(t/log! {:id ::my-id, :data {:x1 :x2}} "My message")))
|
||||
|
||||
;; Create console handler with default opts (writes formatted string)
|
||||
(def my-handler (t/handler:console))
|
||||
|
||||
;; Test handler, remember it's just a (fn [signal])
|
||||
(my-handler my-signal) ; =>
|
||||
;; 2024-04-11T10:54:57.202869Z INFO LOG Schrebermann.local examples(56,1) ::my-id - My message
|
||||
;; data: {:x1 :x2}
|
||||
```
|
||||
|
||||
To instead writes signals as edn:
|
||||
|
||||
```clojure
|
||||
;; Create console which writes edn
|
||||
(def my-handler
|
||||
(t/handler:console
|
||||
{:format-signal-fn (taoensso.telemere.utils/format-signal->edn-fn)}))
|
||||
|
||||
(my-handler my-signal) ; =>
|
||||
;; {:inst #inst "2024-04-11T10:54:57.202869Z", :msg_ "My message", :ns "examples", ...}
|
||||
```
|
||||
|
||||
To instead writes signals as JSON:
|
||||
|
||||
```clojure
|
||||
;; Create console which writes JSON
|
||||
(def my-handler
|
||||
(t/handler:console
|
||||
{:format-signal-fn
|
||||
(taoensso.telemere.utils/format-signal->json-fn
|
||||
{:pr-json-fn jsonista.core/write-value-as-string})}))
|
||||
|
||||
(my-handler my-signal) ; =>
|
||||
;; {"inst":"2024-04-11T10:54:57.202869Z","msg_":"My message","ns":"examples", ...}
|
||||
```
|
||||
|
||||
Note that when writing JSON with Clojure, you *must* specify a `pr-json-fn`. This lets you plug in the JSON serializer of your choice ([jsonista](https://github.com/metosin/jsonista) is my default recommendation).
|
||||
|
||||
# Managing handlers
|
||||
|
||||
See [`help:signal-handlers`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-handlers) for info on handler management.
|
||||
|
||||
## Managing handlers on startup
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
Use this to easily check your own cross-platform system config, and make whatever conditional handler management decisions you'd like.
|
||||
|
||||
# Writing handlers
|
||||
|
||||
Writing your own signal handlers for Telemere is straightforward, and a reasonable choice if you prefer customizing behaviour that way, or want to write signals to a DB/format/service for which a ready-made handler isn't available.
|
||||
|
||||
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.
|
||||
|
||||
Here's a simple Telemere handler:
|
||||
|
||||
```clojure
|
||||
(fn my-handler [signal] (println signal))
|
||||
```
|
||||
|
||||
For more complex cases, or for handlers that you want to make available for use by other folks, here's the general template that Telemere uses for all its included handlers:
|
||||
|
||||
```clojure
|
||||
(defn handler:my-handler ; Note naming convention
|
||||
"Returns a (fn handler [signal] that:
|
||||
- Does something.
|
||||
|
||||
Options:
|
||||
`:option1` - Description
|
||||
`:option2` - Description"
|
||||
|
||||
([] (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.
|
||||
(let []
|
||||
|
||||
(fn a-handler:my-handler ; Note naming convention
|
||||
|
||||
;; Shutdown arity - called by Telemere exactly once when the handler is
|
||||
;; to be shut down. This is your opportunity to finalize/free resources, etc.
|
||||
([])
|
||||
|
||||
;; Main arity - called by Telemere whenever the handler should handle the
|
||||
;; given signal. Never called after shutdown.
|
||||
([signal]
|
||||
;; TODO Do something with given signal
|
||||
)))))
|
||||
```
|
||||
|
||||
- See [`help:signal-content`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-content) for signal map content.
|
||||
- See the [utils namespace](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.utils) for tools useful for customizing and writing signal handlers.
|
||||
- See section [8-Community](8-Community.md) for PRs to link to community-authored handlers.
|
||||
|
||||
# Example output
|
||||
|
||||
```clojure
|
||||
(t/log! {:id ::my-id, :data {:x1 :x2}} "My message") =>
|
||||
```
|
||||
|
||||
## Clj console handler
|
||||
|
||||
String output:
|
||||
|
||||
```
|
||||
2024-04-11T10:54:57.202869Z INFO LOG Schrebermann.local examples(56,1) ::my-id - My message
|
||||
data: {:x1 :x2}
|
||||
```
|
||||
|
||||
## Cljs console handler
|
||||
|
||||
Chrome console:
|
||||
|
||||
<img src="https://raw.githubusercontent.com/taoensso/telemere/master/imgs/handler-output-cljs-console.png" alt="Default ClojureScript console handler output" width="640"/>
|
||||
|
||||
## Cljs raw console handler
|
||||
|
||||
Chrome console, with [cljs-devtools](https://github.com/binaryage/cljs-devtools):
|
||||
|
||||
<img src="https://raw.githubusercontent.com/taoensso/telemere/master/imgs/handler-output-cljs-console-raw.png" alt="Raw ClojureScript console handler output" width="640"/>
|
||||
|
||||
## Clj file handler
|
||||
|
||||
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"/>
|
||||
70
wiki/5-Migrating.md
Normal file
70
wiki/5-Migrating.md
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
# From Timbre
|
||||
|
||||
While [Timbre](https://taoensso.com/timbre) will **continue to be maintained and supported** (and will even receive some improvements back-ported from Telemere), most Timbre users will want to at least *consider* updating to Telemere.
|
||||
|
||||
Telemere's functionality is a **superset of Timbre**, and offers *many* improvements including:
|
||||
|
||||
- Significantly better performance
|
||||
- A cleaner and more flexible API
|
||||
- Better support for structured logging
|
||||
- Much better documentation
|
||||
- Better built-in handlers
|
||||
- Easier configuration in many cases
|
||||
- A more robust architecture, free from all historical constraints
|
||||
|
||||
Migrating from Timbre to Telemere should be straightforward **unless you depend on specific/custom appenders** that might not be available for Telemere (yet).
|
||||
|
||||
## Checklist
|
||||
|
||||
### 1. Appenders
|
||||
|
||||
Where Timbre uses the term "appender", Telemere uses the more general "handler". Functionally they're the same thing.
|
||||
|
||||
Check which **Timbre appenders** you use, and whether a similar handler is [currently included](./4-Handlers#included-handlers) with Telemere or available via the [community](./8-Community).
|
||||
|
||||
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).
|
||||
|
||||
### 2. Imports
|
||||
|
||||
Switch your Timbre namespace imports:
|
||||
|
||||
```clojure
|
||||
(ns my-ns
|
||||
(:require [taoensso.timbre :as timbre :refer [...]]) ; Old
|
||||
(:require [taoensso.telemere.timbre :as timbre :refer [...]]) ; New
|
||||
)
|
||||
```
|
||||
|
||||
The [`taoensso.telemere.timbre`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.timbre) namespace contains a shim of most of Timbre's API.
|
||||
|
||||
Feel free to keep using this shim API **as long as you like**, there's no need to rewrite any of your existing code unless you specifically want to use features that are only possible with Telemere's [signal creators](./1-Getting-started#create-signals), etc.
|
||||
|
||||
### 3. Config
|
||||
|
||||
You *may* need to update code related to filter config and/or handler management.
|
||||
|
||||
This is usually only a few lines of code, and *should* be straightforward.
|
||||
|
||||
See section [3-Config](./3-Config) for more info on configuring Telemere.
|
||||
|
||||
### 4. Testing
|
||||
|
||||
While I believe that the Timbre shim above *should* be robust, it's of course possible that I missed something.
|
||||
|
||||
So **please test carefully** before switching to Telemere in production, and **please [report](https://github.com/taoensso/telemere/issues) any issues**! 🙏
|
||||
|
||||
In particular - note that Telemere's **handler output** may be **completely different**, so if you have any code/systems (e.g. log aggregators) that depend on the specific output format - **these must also be tested**.
|
||||
|
||||
If for any reason your tests are unsuccessful, please don't feel pressured to migrate. Again, I will **continue to maintain and support Timbre**. I have applications running Timbre that I plan to **never migrate** since they're completely stable.
|
||||
|
||||
# From tools.logging
|
||||
|
||||
This is easy, see [here](./3-Config#clojuretoolslogging).
|
||||
|
||||
# From Java logging
|
||||
|
||||
This is easy, see [here](./3-Config#java-logging).
|
||||
55
wiki/6-FAQ.md
Normal file
55
wiki/6-FAQ.md
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# Does Telemere replace Timbre?
|
||||
|
||||
> [Timbre](https:/www.taoensso.com/timbre) is a pure Clojure/Script logging library, and ancestor of Telemere.
|
||||
|
||||
**Yes**, Telemere's functionality is a **superset of Timbre**, and offers *many* improvements over Timbre.
|
||||
|
||||
But Timbre will **continue to be maintained and supported**, and will even receive some backwards-compatible improvements back-ported from Telemere.
|
||||
|
||||
There is **no pressure to migrate** if you'd prefer not to.
|
||||
|
||||
See section [5-Migrating](./5-Migrating#from-timbre) for migration info.
|
||||
|
||||
# Why not just update Timbre?
|
||||
|
||||
> [Timbre](https:/www.taoensso.com/timbre) is a pure Clojure/Script logging library, and ancestor of Telemere.
|
||||
|
||||
Why release Telemere as a *new library* instead of just updating Timbre?
|
||||
|
||||
Timbre was first released 12+ years ago, and has mostly attempted to keep breaks in that time minimal. Which means that its fundamental design is now 12+ years old.
|
||||
|
||||
I've learnt a lot since then, and would write Timbre differently if I were doing it again today. There's many improvements I've wanted to make over the years, but held back both because of the effort involved and because of not wanting to break Timbre users that are happy with it the way it is.
|
||||
|
||||
Since receiving [open source funding](https://www.taoensso.com/my-work), undertaking larger projects became feasible - so I decided to experiment with a proof-of-concept rewrite free of all historical constraints.
|
||||
|
||||
That eventually grew into Telemere.
|
||||
|
||||
I will **continue to maintain and support** Timbre for users that are happy with it, though I've also tried to make [migration](./5-Migrating#from-timbre) as easy as possible.
|
||||
|
||||
Over time, I also intend to back-port many backwards-compatible improvements from Telemere to Timbre. For one, Telemere's core was actually written as a library that will eventually be used by Telemere, Timbre, and also [Tufte](https://taoensso.com/tufte).
|
||||
|
||||
This will eventually ease long-term maintenance, increase reliability, and help provide unified capabilities across all 3.
|
||||
|
||||
# Does Telemere replace Tufte?
|
||||
|
||||
> [Tufte](https:/www.taoensso.com/tufte) is a simple performance monitoring library for Clojure/Script by the author of Telemere.
|
||||
|
||||
**No**, Telemere does **not** replace [Tufte](https:/www.taoensso.com/tufte). They work great together, and the [upcoming](https:/www.taoensso.com/roadmap) Tufte v4 will share the same core as Telemere and offer an **identical API** for managing filters and handlers.
|
||||
|
||||
There is **some feature overlap** though since Telemere offers basic performance measurement as part of its tracing features.
|
||||
|
||||
For comparison:
|
||||
|
||||
- Telemere offers dynamic profiling of a single form to a simple `:runtime-nsecs`.
|
||||
- Tufte offers dynamic and thread-local profiling of *arbitrary nested forms* to *detailed and mergeable runtime stats*.
|
||||
|
||||
Basically, Tufte has much richer performance monitoring capabilities.
|
||||
|
||||
They're focused on complementary things. When both are in use:
|
||||
|
||||
- Tufte can be used for detailed performance measurement, and
|
||||
- Telemere can be used for conveying (aggregate) performance information as part of your system's general observability signals.
|
||||
|
||||
# Other questions?
|
||||
|
||||
Please [open a Github issue](https://github.com/taoensso/telemere/issues). I'll regularly update the FAQ to add common questions.
|
||||
111
wiki/7-Tips.md
Normal file
111
wiki/7-Tips.md
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
Building **observable systems** can be tough, there's no magic solutions. But the benefits can be large, often dramatically **outweighing the costs**.
|
||||
|
||||
I'll present some basic/fundamental info here and can expand on this more in future if there's interest.
|
||||
|
||||
# General guidance
|
||||
|
||||
## Consider what info you (will) need
|
||||
|
||||
Try be as **concrete as possible** about what info is (or will be) most valuable about your system. **Get agreement on examples**.
|
||||
|
||||
Info may be needed for:
|
||||
|
||||
- Debugging
|
||||
- Business intelligence
|
||||
- Testing/staging/validation
|
||||
- Customer support
|
||||
- Quality management
|
||||
- Etc.
|
||||
|
||||
Try be clear on:
|
||||
|
||||
- Who *exactly* will need what information
|
||||
- In what time frame
|
||||
- In what form
|
||||
- And for what purpose (i.e. how will the information **be actionable**)
|
||||
|
||||
Always try involve the **final consumer of information** in your design process.
|
||||
|
||||
Always try map examples of **expected information** to **expected actionable decisions**, and document these mappings.
|
||||
|
||||
The more clear the expected actionable decisions, the more clear the **information's value**.
|
||||
|
||||
## Consider data dependencies
|
||||
|
||||
Not all data is inherently *useful* (and so valuable).
|
||||
|
||||
Be clear on which data is:
|
||||
|
||||
- Useful *independently*
|
||||
- Useful *in the aggregate* (e.g. statistically)
|
||||
- Useful *in association* with other related data (e.g. while tracing the flow of some logical activity)
|
||||
|
||||
Remember to always **question assertions of usefulness**!!
|
||||
|
||||
Useful for what **precise purpose** and by **whom**? Can a clear example be provided **mapping information to decisions**?
|
||||
|
||||
When aggregates or associations are needed- does a plan exist for producing them from the raw data? Some forethought (e.g. appropriate identifiers and/or indexes) can help avoid big headaches!
|
||||
|
||||
## Consider cardinality
|
||||
|
||||
Too much low-value data is often actively *harmful*: expensive to process, to store, and to query. Adding noise just interferes with better data, harming your ability to understand your system.
|
||||
|
||||
Consider the **quantities** of data that'd best suit your needs *for that data*:
|
||||
|
||||
<img src="https://raw.githubusercontent.com/taoensso/telemere/master/imgs/signal-sampling.svg" alt="Telemere sampling" width="640"/>
|
||||
|
||||
Telemere offers [extensive filtering](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-flow) capabilities that help you easily express the **conditions** and **quantities** that make sense for your needs. *Use these* both for their effects and as a *form of documentation*.
|
||||
|
||||
## Consider evolution
|
||||
|
||||
Both data and needs **evolve over time**.
|
||||
|
||||
Consider **what is likely subject to change**, and how that might impact your observability needs and therefore design.
|
||||
|
||||
Consider the **downstream effects** on data services/storage when something does change.
|
||||
|
||||
**Use schemas** when appropriate. Use **version identifiers** when reasonable.
|
||||
|
||||
Consider the [differences](https://www.youtube.com/watch?v=oyLBGkS5ICk) between **accretion** and **breakage**.
|
||||
|
||||
# Telemere usage tips
|
||||
|
||||
- [`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.
|
||||
|
||||
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).
|
||||
|
||||
- Keep a documented **index** of all your **signal ids** under version control.
|
||||
|
||||
This way you can see all your ids in one place, and precise info on when ids were added/removed/changed.
|
||||
|
||||
- Use **signal middleware** to your advantage.
|
||||
|
||||
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**.
|
||||
|
||||
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.
|
||||
|
||||
- Middleware can return any type, but it's best to return only `nil` or a map.
|
||||
- Middleware can be used to **filter signals** by returning `nil`.
|
||||
- Middleware can be used to **split signals**.
|
||||
|
||||
Your middleware can *call signal creators* like any other code. Return `nil` after to filter the source signal. Just be aware that new signals will re-enter your handler queue/s as would any other signal - and so may be subject to handling delay and normal handler queue back-pressure.
|
||||
|
||||
- Levels can be **arbitrary integers**.
|
||||
|
||||
See the value of [`level-aliases`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#level-aliases) to see how the standard keywords (`:info`, `:warn`, etc.) map to integers.
|
||||
|
||||
- Signals with an `:error` value need not have `:error` level and vice versa.
|
||||
|
||||
Telemere doesn't couple the presence of an error value to signal level. This can be handy, but means that you need to be clear on what constitutes an "error signal" for your use case. See also the [`error-signal?`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#error-signal) util.
|
||||
|
||||
- Signal `kind` can be useful in advanced cases.
|
||||
|
||||
Every signal has a `kind` key set by default by each signal creator- `log!` calls produce signals with a `:log` kind, etc.
|
||||
|
||||
Most folks won't use this, but it can be handy in advanced environments since it allows you to specify a custom taxonomy of signals separate from ids and namespaces.
|
||||
|
||||
Signals can be filtered by kind, and minimum levels specified by kind.
|
||||
20
wiki/8-Community.md
Normal file
20
wiki/8-Community.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
My plan is for Telemere to offer a **stable core of limited scope**.
|
||||
|
||||
If there's demand, additional stuff can then be authored by Telemere's *community*.
|
||||
|
||||
**PRs very welcome** to add links to this page for:
|
||||
|
||||
- Handlers (see [Writing handlers](./4-Handlers#writing-handlers))
|
||||
- Handler utils (formatters, etc.)
|
||||
- Middleware
|
||||
- Tutorials / demos / etc.
|
||||
- Anything else relevant :-)
|
||||
|
||||
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
|
||||
23
wiki/Home.md
23
wiki/Home.md
|
|
@ -1,8 +1,23 @@
|
|||
See the menu to the right for content 👉
|
||||
# Content
|
||||
|
||||
See the **Pages menu to the right** for content 👉
|
||||
|
||||
# Attention!
|
||||
|
||||
This wiki is designed for viewing from GitHub's **Wiki** UI.
|
||||
|
||||
Viewing from GitHub's file browser will result in **broken links**.
|
||||
|
||||
# Please report errors
|
||||
|
||||
I'm currently maintaining a lot of documentation! Typos, broken links, or obsolete info *will* sneak in from time-to-time.
|
||||
|
||||
If you run into something that looks like an error, please [report](../issues) it! 🙏
|
||||
|
||||
Thank you! \- [Peter Taoussanis](https://www.taoensso.com)
|
||||
|
||||
# Contributions welcome
|
||||
|
||||
**PRs very welcome** to help improve this documentation!
|
||||
See the [wiki](../tree/master/wiki) folder in the main repo for the relevant files.
|
||||
**PRs very welcome** to help improve this documentation!
|
||||
|
||||
\- [Peter Taoussanis](https://www.taoensso.com)
|
||||
See the [wiki](../tree/master/wiki) folder in the main repo for the relevant files.
|
||||
Loading…
Reference in a new issue