telemere/README.md

329 lines
21 KiB
Markdown
Raw Normal View History

2023-10-16 08:50:36 +00:00
<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>
2024-05-08 07:54:08 +00:00
[**API**][cljdoc docs] | [**Wiki**][GitHub wiki] | [Latest releases](#latest-releases) | [Slack channel][]
2023-10-16 08:50:36 +00:00
2024-04-15 07:57:40 +00:00
# <img src="https://raw.githubusercontent.com/taoensso/telemere/master/imgs/telemere-logo.svg" alt="Telemere logo" width="360"/>
2023-10-16 08:50:36 +00:00
### Structured telemetry library for Clojure/Script
2024-08-24 08:57:56 +00:00
**Telemere** is a next-generation replacement for [Timbre](https://www.taoensso.com/timbre) that offers one simple **unified API** for **traditional logging**, **structured logging**, **tracing**, and **basic performance monitoring**.
2024-03-06 10:54:51 +00:00
2024-08-24 08:57:56 +00:00
Friendly enough for complete beginners, but flexible enough for the most complex and performance-sensitive real-world projects.
It helps enable Clojure/Script systems that are easily **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.
Supports Clojure, ClojureScript, [GraalVM](https://en.wikipedia.org/wiki/GraalVM), but not ([yet](../../wiki/6-FAQ#does-telemere-work-with-babashka)) [Babashka](https://github.com/babashka/babashka).
2024-03-06 10:54:51 +00:00
2024-08-22 07:02:21 +00:00
See [here](../../wiki/1-Getting-started) for **full introduction**.
2023-10-16 08:50:36 +00:00
## Latest release/s
2024-08-26 10:34:53 +00:00
- `2024-08-26` `v1.0.0-beta21`: [release info](../../releases/tag/v1.0.0-beta21) (for early adopters/feedback)
2023-10-16 08:50:36 +00:00
[![Main tests][Main tests SVG]][Main tests URL]
[![Graal tests][Graal tests SVG]][Graal tests URL]
See [here][GitHub releases] for earlier releases.
2024-08-22 07:02:21 +00:00
## Quick examples
```clojure
(require '[taoensso.telemere :as t])
2024-08-24 08:57:56 +00:00
;; (Just works / no config necessary for typical use cases)
2024-08-22 07:02:21 +00:00
;; Without structured data
(t/log! :info "Hello world!") ; %> Basic log signal (has message)
(t/event! ::my-id :debug) ; %> Basic event signal (just id)
;; With structured data
(t/log! {:level :info, :data {...}} "Hello again!")
(t/event! ::my-id {:level :debug, :data {...}})
2024-08-24 08:57:56 +00:00
;; Trace (auto interops with OpenTelemetry)
;; Tracks form runtime, return value, and (nested) parent tree
2024-08-22 07:02:21 +00:00
(t/trace! {:id ::my-id :data {...}}
(do-some-work))
2024-08-24 08:57:56 +00:00
;; Check resulting signal content for debug/tests
2024-08-22 07:02:21 +00:00
(t/with-signal (t/event! ::my-id)) ; => {:keys [ns level id data msg_ ...]}
;; Transform signals
(t/set-middleware! (fn [signal] (assoc signal :my-key "my-val")))
;; Filter signals by returning nil
(t/set-middleware! (fn [signal] (when-not (-> signal :data :skip-me?) signal)))
;; Getting fancy (all costs are conditional!)
(t/log!
{:level :debug
:sample-rate (my-dynamic-sample-rate)
:when (my-conditional)
:rate-limit {"1 per sec" [1 1000]
"5 per min" [5 60000]}
:do (inc-my-metric!)
:let
[diagnostics (my-expensive-diagnostics)
formatted (my-expensive-format diagnostics)]
:data
{:diagnostics diagnostics
:formatted formatted
:local-state *my-dynamic-context*}}
;; Message string or vector to join as string
["Something interesting happened!" formatted])
```
2023-10-16 08:50:36 +00:00
## Why Telemere?
2024-08-22 07:02:21 +00:00
### Ergonomics
2024-04-08 08:48:33 +00:00
2024-04-11 09:05:24 +00:00
- Elegant, lightweight API that's **easy to use**, **easy to configure**, and **deeply flexible**.
2024-04-08 08:48:33 +00:00
- **Sensible defaults** to make getting started **fast and easy**.
2024-04-11 09:05:24 +00:00
- Extensive **beginner-oriented** [documentation][GitHub wiki], [docstrings](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso), and error messages.
2024-04-08 08:48:33 +00:00
2024-08-22 07:02:21 +00:00
### Interop
2024-04-08 08:48:33 +00:00
2024-08-19 16:33:17 +00:00
- 1st-class **out-the-box interop** with [SLF4J v2](../../wiki/3-Config#java-logging), [tools.logging](../../wiki/3-Config#toolslogging), [OpenTelemetry](../../wiki/3-Config#opentelemetry), and [Tufte](../../wiki/3-Config#tufte).
2024-04-15 07:57:40 +00:00
- Included [shim](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.timbre) for easy/gradual [migration from Timbre](../../wiki/5-Migrating).
2024-05-08 07:54:08 +00:00
- Extensive set of [handlers](../../wiki/4-Handlers#included-handlers) included out-the-box.
2024-04-08 08:48:33 +00:00
2024-08-22 07:02:21 +00:00
### Scaling
2024-04-08 08:48:33 +00:00
2024-04-19 11:16:01 +00:00
- Hyper-optimized and **blazing fast**, see [benchmarks](#benchmarks).
2024-04-11 09:05:24 +00:00
- An API that **scales comfortably** from the smallest disposable code, to the most massive and complex real-world production environments.
2024-08-05 10:59:18 +00:00
- Auto [handler stats](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#get-handlers-stats) for debugging performance and other issues at scale.
2024-04-08 08:48:33 +00:00
2024-08-22 07:02:21 +00:00
### Flexibility
2024-04-08 08:48:33 +00:00
2024-04-11 09:05:24 +00:00
- Config via plain **Clojure vals and fns** for easy customization, composition, and REPL debugging.
2024-05-24 22:01:05 +00:00
- Unmatched [environmental config](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:environmental-config) support: JVM properties, environment variables, or classpath resources. Per platform, or cross-platform.
- Unmatched [filtering](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:filters) support: by namespace, id pattern, level, level by namespace pattern, etc. At runtime and compile-time.
- Fully [configurable](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:handler-dispatch-options) **a/sync dispatch support**: blocking, dropping, sliding, etc.
- Turn-key **sampling**, **rate-limiting**, and **back-pressure monitoring** with sensible defaults.
2024-03-06 10:54:51 +00:00
2024-08-22 07:02:21 +00:00
## Comparisons
2024-08-07 10:00:22 +00:00
2024-08-22 07:02:21 +00:00
- Telemere [compared](../../wiki/5-Migrating#from-timbre) to [Timbre](https://www.taoensso.com/timbre) (Telemere's predecessor)
- Telemere [compared](../../wiki/6-FAQ#how-does-telemere-compare-to-mulog) to [Mulog](https://github.com/BrunoBonacci/mulog) (Structured micro-logging library)
2024-08-05 10:59:18 +00:00
2024-08-22 07:02:21 +00:00
## Next-gen observability
2024-03-06 10:54:51 +00:00
2024-08-22 07:02:21 +00:00
A key hurdle in building **observable systems** is that it's often inconvenient and costly to get out the kind of **detailed info** that we need when debugging.
2024-04-11 09:05:24 +00:00
2024-08-22 07:02:21 +00:00
Telemere's strategy to address this is to:
2024-04-11 09:05:24 +00:00
2024-08-22 07:02:21 +00:00
1. Provide **lean, low-fuss syntax** to let you conveniently convey program state.
2. Use the unique power of **Lisp macros** to let you **dynamically filter costs as you filter signals** (pay only for what you need, when you need it).
3. For those signals that *do* pass filtering: move costs from the callsite to a/sync handlers with explicit [threading and back-pressure semantics](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:handler-dispatch-options) and [performance monitoring](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#get-handlers-stats).
2024-04-11 09:05:24 +00:00
2024-08-22 07:02:21 +00:00
The effect is more than impressive micro-benchmarks. This approach enables a fundamental (qualitative) change in one's approach to observability.
2024-04-11 09:05:24 +00:00
2024-08-22 07:02:21 +00:00
It enables you to write code that is **information-verbose by default**.
2024-05-08 07:54:08 +00:00
2024-08-22 07:02:21 +00:00
## Video demo
2024-08-05 10:59:18 +00:00
2024-08-22 07:02:21 +00:00
See for intro and basic usage:
2024-08-05 10:59:18 +00:00
2024-08-22 07:02:21 +00:00
<a href="https://www.youtube.com/watch?v=-L9irDG8ysM" target="_blank">
<img src="https://img.youtube.com/vi/-L9irDG8ysM/maxresdefault.jpg" alt="Telemere demo video" width="480" border="0" />
</a>
2024-08-05 10:59:18 +00:00
2024-08-22 07:02:21 +00:00
## More examples
2024-04-11 09:05:24 +00:00
2024-08-22 07:02:21 +00:00
```clojure
;; Set minimum level
(t/set-min-level! :warn) ; For all signals
(t/set-min-level! :log :debug) ; For `log!` signals only
2024-04-11 09:05:24 +00:00
2024-08-22 07:02:21 +00:00
;; Set namespace and id filters
2024-08-05 10:59:18 +00:00
(t/set-ns-filter! {:disallow "taoensso.*" :allow "taoensso.sente.*"})
(t/set-id-filter! {:allow #{::my-particular-id "my-app/*"}})
2024-04-11 09:05:24 +00:00
2024-08-22 07:02:21 +00:00
;; Set minimum level for `event!` signals for particular ns pattern
2024-04-11 09:05:24 +00:00
(t/set-min-level! :event "taoensso.sente.*" :warn)
2024-08-05 10:59:18 +00:00
;; See `t/help:filters` docstring for more
2024-08-22 07:02:21 +00:00
;; Use middleware to:
;; - Transform signals
;; - Filter signals by arb conditions (incl. data/content)
2024-08-05 10:59:18 +00:00
(t/set-middleware!
(fn [signal]
2024-08-22 07:02:21 +00:00
(if (-> signal :data :skip-me?)
nil ; Filter signal (don't handle)
2024-08-05 10:59:18 +00:00
(assoc signal :passed-through-middleware? true))))
2024-08-22 07:02:21 +00:00
(t/with-signal (t/event! ::my-id {:data {:skip-me? true}})) ; => nil
(t/with-signal (t/event! ::my-id {:data {:skip-me? false}})) ; => {...}
;; Signal handlers
(t/get-handlers) ; => {<handler-id> {:keys [handler-fn handler-stats_ dispatch-opts]}}
(t/add-handler! :my-console-handler
(t/handler:console {}) ; Returns handler fn, has many opts
{:async {:mode :dropping, :buffer-size 1024, :n-threads 1}
:priority 100
:sample-rate 0.5
:min-level :info
:ns-filter {:disallow "taoensso.*"}
:rate-limit {"1 per sec" [1 1000]}
;; See `t/help:handler-dispatch-options` for more
})
2024-08-24 08:57:56 +00:00
;; Print human-readable output to console
(t/add-handler! :my-console-handler
(t/handler:console
{:output-fn (t/format-signal-fn {...})}))
;; Print edn to console
(t/add-handler! :my-console-handler
(t/handler:console
{:output-fn (t/pr-signal-fn {:pr-fn :edn})}))
;; Print JSON to console
;; Ref. <https://github.com/metosin/jsonista> (or any alt JSON lib)
#?(:clj (require '[jsonista.core :as jsonista]))
(t/add-handler! :my-console-handler
(t/handler:console
{:output-fn
#?(:cljs :json ; Use js/JSON.stringify
:clj jsonista/write-value-as-string)}))
2024-04-11 09:05:24 +00:00
```
2024-08-22 07:02:21 +00:00
See [examples.cljc](https://github.com/taoensso/telemere/blob/master/examples.cljc) for REPL-ready snippets!
2024-04-15 07:57:40 +00:00
2024-04-11 09:05:24 +00:00
## API overview
2024-04-15 07:57:40 +00:00
See relevant docstrings (links below) for usage info-
2024-04-11 09:05:24 +00:00
### Creating signals
2024-04-11 09:05:24 +00:00
2024-04-17 14:16:14 +00:00
| 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 |
2024-03-06 10:54:51 +00:00
2024-04-11 09:05:24 +00:00
### Internal help
2024-08-05 10:59:18 +00:00
Detailed help is available without leaving your IDE:
2024-08-22 07:02:21 +00:00
| Var | Help with |
| :---------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------- |
| [`help:signal-creators`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-creators) | Creating signals |
| [`help:signal-options`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-options) | Options when creating signals |
| [`help:signal-content`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-content) | Signal content (map given to middleware/handlers) |
| [`help:filters`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:filters) | Signal filtering and transformation |
| [`help:handlers`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:handlers) | Signal handler management |
| [`help:handler-dispatch-options`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:handler-dispatch-options) | Signal handler dispatch options |
| [`help:environmental-config`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:environmental-config) | Config via JVM properties, environment variables, or classpath resources |
2024-04-11 09:05:24 +00:00
2024-05-08 07:54:08 +00:00
### Included handlers
2024-04-11 09:05:24 +00:00
2024-08-22 07:02:21 +00:00
See ✅ links below for **features and usage**,
2024-08-24 08:57:56 +00:00
See ❤️ links below to **vote on future handlers**:
| Target (↓) | Clj | Cljs |
| :--------------------------------------------- | :-----------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------: |
| [Apache Kafka](https://kafka.apache.org/) | [❤️](https://github.com/taoensso/roadmap/issues/12) | - |
| [AWS Kinesis](https://aws.amazon.com/kinesis/) | [❤️](https://github.com/taoensso/roadmap/issues/12) | - |
| Console | [](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:console) | [](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:console) |
| Console (raw) | - | [](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:console-raw) |
| [Datadog](https://www.datadoghq.com/) | [❤️](https://github.com/taoensso/roadmap/issues/12) | [❤️](https://github.com/taoensso/roadmap/issues/12) |
| Email | [](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.postal#handler:postal) | - |
| [Graylog](https://graylog.org/) | [❤️](https://github.com/taoensso/roadmap/issues/12) | - |
| [Jaeger](https://www.jaegertracing.io/) | [❤️](https://github.com/taoensso/roadmap/issues/12) | - |
| [Logstash](https://www.elastic.co/logstash) | [❤️](https://github.com/taoensso/roadmap/issues/12) | - |
| [OpenTelemetry](https://opentelemetry.io/) | [](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.open-telemetry#handler:open-telemetry) | [❤️](https://github.com/taoensso/roadmap/issues/12) |
| [Redis](https://redis.io/) | [❤️](https://github.com/taoensso/roadmap/issues/12) | - |
| SQL | [❤️](https://github.com/taoensso/roadmap/issues/12) | - |
| [Slack](https://slack.com/) | [](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.slack#handler:slack) | - |
| TCP socket | [](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.sockets#handler:tcp-socket) | - |
| UDP socket | [](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.sockets#handler:udp-socket) | - |
| [Zipkin](https://zipkin.io/) | [❤️](https://github.com/taoensso/roadmap/issues/12) | - |
2024-08-20 12:00:45 +00:00
You can also easily [write your own handlers](../../wiki/4-Handlers#writing-handlers).
2024-04-11 09:05:24 +00:00
2024-08-19 16:33:17 +00:00
## Community
My plan for Telemere is to offer a **stable core of limited scope**, then to focus on making it as easy for the **community** to write additional stuff like handlers, middleware, and utils.
See [here](../../wiki/8-Community) for community resources.
2024-04-11 09:05:24 +00:00
## Documentation
2024-04-15 07:57:40 +00:00
- [Wiki][GitHub wiki] (getting started, usage, etc.)
2024-08-07 10:00:22 +00:00
- API reference via [cljdoc][cljdoc docs] or [Codox][Codox docs]
- Extensive [internal help](#internal-help) (no need to leave your IDE)
2024-08-19 16:33:17 +00:00
- Support via [Slack channel][] or [GitHub issues][]
2024-05-08 07:54:08 +00:00
- [General observability tips](../../wiki/7-Tips) (advice on building and maintaining observable Clojure/Script systems, and getting the most out of Telemere)
2024-03-06 10:54:51 +00:00
2024-04-08 08:48:33 +00:00
## Benchmarks
2024-03-06 10:54:51 +00:00
2024-08-15 11:09:34 +00:00
Telemere is **highly optimized** and offers great performance at any scale:
2024-03-06 10:54:51 +00:00
2024-08-15 11:09:34 +00:00
| Compile-time filtering? | Runtime filtering? | Profile? | Trace? | nsecs |
| :---------------------: | :----------------: | :------: | :----: | ----: |
| ✓ (elide) | - | - | - | 0 |
| - | ✓ | - | - | 350 |
| - | ✓ | ✓ | - | 450 |
| - | ✓ | ✓ | ✓ | 1000 |
2024-03-06 10:54:51 +00:00
Measurements:
- Are **~nanoseconds per signal call** (= milliseconds per 1e6 calls)
2024-05-24 22:01:05 +00:00
- Exclude [handler runtime](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#get-handlers-stats) (which depends on handler/s, is usually async)
2024-08-15 11:09:34 +00:00
- Taken on a 2020 Macbook Pro M1, running Clojure v1.12 and OpenJDK v22
2024-03-06 10:54:51 +00:00
2024-08-15 11:09:34 +00:00
### Performance philosophy
2024-08-22 07:02:21 +00:00
Telemere is optimized for *real-world* performance. This means **prioritizing flexibility** and realistic usage over synthetic micro-benchmarks.
2024-08-15 11:09:34 +00:00
Large applications can produce absolute *heaps* of data, not all equally valuable. Quickly processing infinite streams of unmanageable junk is an anti-pattern. As scale and complexity increase, it becomes more important to **strategically plan** what data to collect, when, in what quantities, and how to manage it.
Telemere is designed to help with all that. It offers [rich data](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-content) and unmatched [filtering](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:filters) support - including per-signal and per-handler **sampling** and **rate-limiting**.
Use these to ensure that you're not capturing useless/low-value/high-noise information in production! With appropriate planning, Telemere is designed to scale to systems of any size and complexity.
See [here](../../wiki/7-Tips) for detailed tips on real-world usage.
2023-10-16 08:50:36 +00:00
## Funding
You can [help support][sponsor] continued work on this project, thank you!! 🙏
## License
Copyright &copy; 2023-2024 [Peter Taoussanis][].
Licensed under [EPL 1.0](LICENSE.txt) (same as Clojure).
<!-- Common -->
[GitHub releases]: ../../releases
[GitHub issues]: ../../issues
[GitHub wiki]: ../../wiki
2024-04-15 07:57:40 +00:00
[Slack channel]: https://www.taoensso.com/telemere/slack
2023-10-16 08:50:36 +00:00
[Peter Taoussanis]: https://www.taoensso.com
[sponsor]: https://www.taoensso.com/sponsor
<!-- Project -->
[Codox docs]: https://taoensso.github.io/telemere/
2024-04-11 09:05:24 +00:00
[cljdoc docs]: https://cljdoc.org/d/com.taoensso/telemere/
2023-10-16 08:50:36 +00:00
[Clojars SVG]: https://img.shields.io/clojars/v/com.taoensso/telemere.svg
[Clojars URL]: https://clojars.org/com.taoensso/telemere
[Main tests SVG]: https://github.com/taoensso/telemere/actions/workflows/main-tests.yml/badge.svg
[Main tests URL]: https://github.com/taoensso/telemere/actions/workflows/main-tests.yml
[Graal tests SVG]: https://github.com/taoensso/telemere/actions/workflows/graal-tests.yml/badge.svg
2024-08-20 07:18:59 +00:00
[Graal tests URL]: https://github.com/taoensso/telemere/actions/workflows/graal-tests.yml