mirror of
https://github.com/taoensso/telemere.git
synced 2025-12-17 01:51:10 +00:00
[doc] Misc improvements
This commit is contained in:
parent
f9564b2fc5
commit
e4a0a41a1b
13 changed files with 142 additions and 127 deletions
113
README.md
113
README.md
|
|
@ -5,15 +5,20 @@
|
|||
|
||||
### Structured telemetry library for Clojure/Script
|
||||
|
||||
**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**.
|
||||
**Telemere** is a **pure Clojure/Script library** that offers an elegant and simple **unified API** to cover:
|
||||
|
||||
Friendly enough for complete beginners, but flexible enough for the most complex and performance-sensitive real-world projects.
|
||||
- **Traditional logging** (string messages)
|
||||
- **Structured logging** (rich Clojure data types and structures)
|
||||
- **Events** (named thing happened, with optional data)
|
||||
- **Tracing** (nested flow tracking, with optional data)
|
||||
- Basic **performance monitoring** (nested form runtimes)
|
||||
- Any combination of the above
|
||||
|
||||
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.
|
||||
It's small, *super* fast, easy to learn, easy to use, and **absurdly flexible**.
|
||||
|
||||
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).
|
||||
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) and [Truss](https://www.taoensso.com/truss).
|
||||
|
||||
See [here](../../wiki/1-Getting-started) for **full introduction**.
|
||||
See [here](../../wiki/1-Getting-started) for **full introduction** (concepts, terminology, getting started).
|
||||
|
||||
## Latest release/s
|
||||
|
||||
|
|
@ -22,7 +27,21 @@ See [here](../../wiki/1-Getting-started) for **full introduction**.
|
|||
[![Main tests][Main tests SVG]][Main tests URL]
|
||||
[![Graal tests][Graal tests SVG]][Graal tests URL]
|
||||
|
||||
See [here][GitHub releases] for earlier releases.
|
||||
<!--See [here][GitHub releases] for earlier releases.-->
|
||||
|
||||
## Next-gen observability
|
||||
|
||||
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.
|
||||
|
||||
Telemere's strategy to address this is to:
|
||||
|
||||
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).
|
||||
|
||||
The effect is more than impressive micro-benchmarks. This approach enables a fundamental (qualitative) change in one's approach to observability.
|
||||
|
||||
It enables you to write code that is **information-verbose by default**.
|
||||
|
||||
## Quick examples
|
||||
|
||||
|
|
@ -39,7 +58,7 @@ See [here][GitHub releases] for earlier releases.
|
|||
(t/log! {:level :info, :data {...}} "Hello again!")
|
||||
(t/event! ::my-id {:level :debug, :data {...}})
|
||||
|
||||
;; Trace (auto interops with OpenTelemetry)
|
||||
;; Trace (can interop with OpenTelemetry)
|
||||
;; Tracks form runtime, return value, and (nested) parent tree
|
||||
(t/trace! {:id ::my-id :data {...}}
|
||||
(do-some-work))
|
||||
|
|
@ -56,7 +75,7 @@ See [here][GitHub releases] for earlier releases.
|
|||
;; Getting fancy (all costs are conditional!)
|
||||
(t/log!
|
||||
{:level :debug
|
||||
:sample-rate (my-dynamic-sample-rate)
|
||||
:sample-rate 0.75 ; 75% sampling (noop 25% of the time)
|
||||
:when (my-conditional)
|
||||
:rate-limit {"1 per sec" [1 1000]
|
||||
"5 per min" [5 60000]}
|
||||
|
|
@ -85,13 +104,13 @@ See [here][GitHub releases] for earlier releases.
|
|||
|
||||
### Interop
|
||||
|
||||
- 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).
|
||||
- 1st-class **out-the-box interop** with [tools.logging](../../wiki/3-Config#toolslogging), [Java logging via SLF4J v2](../../wiki/3-Config#java-logging), [OpenTelemetry](../../wiki/3-Config#opentelemetry), and [Tufte](../../wiki/3-Config#tufte).
|
||||
- Included [shim](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.timbre) for easy/gradual [migration from Timbre](../../wiki/5-Migrating).
|
||||
- Extensive set of [handlers](../../wiki/4-Handlers#included-handlers) included out-the-box.
|
||||
|
||||
### Scaling
|
||||
|
||||
- Hyper-optimized and **blazing fast**, see [benchmarks](#benchmarks).
|
||||
- Hyper-optimized and **blazing fast**, see [performance](#performance).
|
||||
- An API that **scales comfortably** from the smallest disposable code, to the most massive and complex real-world production environments.
|
||||
- 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.
|
||||
|
||||
|
|
@ -108,20 +127,6 @@ See [here][GitHub releases] for earlier releases.
|
|||
- 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)
|
||||
|
||||
## Next-gen observability
|
||||
|
||||
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.
|
||||
|
||||
Telemere's strategy to address this is to:
|
||||
|
||||
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).
|
||||
|
||||
The effect is more than impressive micro-benchmarks. This approach enables a fundamental (qualitative) change in one's approach to observability.
|
||||
|
||||
It enables you to write code that is **information-verbose by default**.
|
||||
|
||||
## Video demo
|
||||
|
||||
See for intro and basic usage:
|
||||
|
|
@ -226,7 +231,36 @@ Detailed help is available without leaving your IDE:
|
|||
| [`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 |
|
||||
|
||||
### Included handlers
|
||||
## Performance
|
||||
|
||||
Telemere is **highly optimized** and offers great performance at any scale, handling up to **4.2 million filtered signals/sec** on a 2020 Macbook Pro M1.
|
||||
|
||||
Signal call benchmarks (per thread):
|
||||
|
||||
| Compile-time filtering? | Runtime filtering? | Profile? | Trace? | nsecs / call |
|
||||
| :---------------------: | :----------------: | :------: | :----: | -----------: |
|
||||
| ✓ (elide) | - | - | - | 0 |
|
||||
| - | ✓ | - | - | 350 |
|
||||
| - | ✓ | ✓ | - | 450 |
|
||||
| - | ✓ | ✓ | ✓ | 1000 |
|
||||
|
||||
- Nanoseconds per signal call ~ **milliseconds per 1e6 calls**
|
||||
- Times 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)
|
||||
- Benched on a 2020 Macbook Pro M1, running Clojure v1.12 and OpenJDK v22
|
||||
|
||||
### Performance philosophy
|
||||
|
||||
Telemere is optimized for *real-world* performance. This means **prioritizing flexibility** and realistic usage over synthetic micro-benchmarks.
|
||||
|
||||
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**, and zero cost compile-time filtering.
|
||||
|
||||
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.
|
||||
|
||||
## Included handlers
|
||||
|
||||
See ✅ links below for **features and usage**,
|
||||
See ❤️ links below to **vote on future handlers**:
|
||||
|
|
@ -266,35 +300,6 @@ See [here](../../wiki/8-Community) for community resources.
|
|||
- Support via [Slack channel][] or [GitHub issues][]
|
||||
- [General observability tips](../../wiki/7-Tips) (advice on building and maintaining observable Clojure/Script systems, and getting the most out of Telemere)
|
||||
|
||||
## Benchmarks
|
||||
|
||||
Telemere is **highly optimized** and offers great performance at any scale:
|
||||
|
||||
| Compile-time filtering? | Runtime filtering? | Profile? | Trace? | nsecs |
|
||||
| :---------------------: | :----------------: | :------: | :----: | ----: |
|
||||
| ✓ (elide) | - | - | - | 0 |
|
||||
| - | ✓ | - | - | 350 |
|
||||
| - | ✓ | ✓ | - | 450 |
|
||||
| - | ✓ | ✓ | ✓ | 1000 |
|
||||
|
||||
Measurements:
|
||||
|
||||
- Are **~nanoseconds per signal call** (= milliseconds per 1e6 calls)
|
||||
- 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)
|
||||
- Taken on a 2020 Macbook Pro M1, running Clojure v1.12 and OpenJDK v22
|
||||
|
||||
### Performance philosophy
|
||||
|
||||
Telemere is optimized for *real-world* performance. This means **prioritizing flexibility** and realistic usage over synthetic micro-benchmarks.
|
||||
|
||||
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.
|
||||
|
||||
## Funding
|
||||
|
||||
You can [help support][sponsor] continued work on this project, thank you!! 🙏
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
;; Getting fancy (all costs are conditional!)
|
||||
(t/log!
|
||||
{:level :debug
|
||||
:sample-rate (my-dynamic-sample-rate)
|
||||
:sample-rate 0.75 ; 75% sampling (noop 25% of the time)
|
||||
:when (my-conditional)
|
||||
:rate-limit {"1 per sec" [1 1000]
|
||||
"5 per min" [5 60000]}
|
||||
|
|
|
|||
|
|
@ -2,35 +2,7 @@
|
|||
"Experimental, subject to change.
|
||||
Minimal Telemere facade API for library authors, etc.
|
||||
Allows library code to use Telemere if it's present, or fall back to
|
||||
something like `tools.logging` otherwise.
|
||||
|
||||
(ns my-lib
|
||||
(:require
|
||||
[taoensso.telemere.api :as t] ; `com.taoensso/telemere-api` dependency
|
||||
[clojure.tools.logging :as ctl] ; `org.clojure/tools.logging` dependency
|
||||
))
|
||||
|
||||
(t/require-telemere-if-present) ; Just below `ns` form
|
||||
|
||||
;; Optional convenience for library users
|
||||
(defn set-min-level!
|
||||
\"If using Telemere, sets Telemere's minimum level for <library> namespaces.
|
||||
Possible levels: #{:trace :debug :info :warn :error :fatal :report}.
|
||||
Default level: `:warn`.
|
||||
[min-level]
|
||||
(t/if-telemere
|
||||
(do (t/set-min-level! nil \"my-lib(.*)\" min-level) true)
|
||||
false))
|
||||
|
||||
(defonce ^:private __set-default-min-level (set-min-level! :warn))
|
||||
|
||||
(signal!
|
||||
{:kind :log, :id :my-id, :level :warn,
|
||||
:let [x :x]
|
||||
:msg [\"Hello\" \"world\" x]
|
||||
:data {:a :A :x x}
|
||||
:fallback (ctl/warn (str \"Hello world\" x))})"
|
||||
|
||||
something like tools.logging otherwise."
|
||||
{:author "Peter Taoussanis (@ptaoussanis)"}
|
||||
#?(:clj (:require [clojure.java.io :as jio])
|
||||
:cljs (:require-macros [taoensso.telemere.api :refer [compile-if]])))
|
||||
|
|
@ -87,7 +59,7 @@
|
|||
Otherwise expands to arbitrary `fallback` opt form.
|
||||
|
||||
Allows library code to use Telemere if it's present, or fall back to
|
||||
something like `tools.logging` otherwise.
|
||||
something like tools.logging otherwise.
|
||||
|
||||
MUST be used with `require-telemere-if-present`, example:
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ Tips:
|
|||
- Test using `with-signal`: (with-signal (catch->error! ...)).
|
||||
- Supports the same options [2] as other signals [1].
|
||||
|
||||
- Useful for recording errors in forms, futures, callbacks, etc.
|
||||
- Useful for preventing errors from going unnoticed in futures, callbacks,
|
||||
agent actions, etc.!: (future (catch->error ::my-future (do-something)))
|
||||
|
||||
See also `error!`.
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ 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.).
|
||||
|
||||
`form` is generally expected to be synchronous and eager (not a lazy seq,
|
||||
async call, or IOT code like a core.async `go` block, etc.).
|
||||
|
||||
Examples:
|
||||
|
||||
(spy! (+ 1 2)) ; %> {:kind :trace, :level :info, :run-form '(+ 1 2),
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ 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.).
|
||||
|
||||
`form` is generally expected to be synchronous and eager (not a lazy seq,
|
||||
async call, or IOT code like a core.async `go` block, etc.).
|
||||
|
||||
Examples:
|
||||
|
||||
(trace! (+ 1 2)) ; %> {:kind :trace, :level :info, :run-form '(+ 1 2),
|
||||
|
|
|
|||
|
|
@ -758,7 +758,7 @@
|
|||
|
||||
#?(:clj
|
||||
(defmacro signal-allowed?
|
||||
"Used only for interop (SLF4J, `tools.logging`, etc.)."
|
||||
"Used only for interop (tools.logging, SLF4J, etc.)."
|
||||
{:arglists (signal-arglists :signal!)}
|
||||
[opts]
|
||||
(let [{:keys [#_expansion-id #_location elide? allow?]}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
(ns taoensso.telemere.tools-logging
|
||||
"Interop support for `tools.logging` -> Telemere.
|
||||
"Interop support for tools.logging -> Telemere.
|
||||
Telemere will attempt to load this ns automatically when possible.
|
||||
|
||||
Naming conventions:
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
(get-logger [_ logger-name] (TelemereLogger. (str logger-name))))
|
||||
|
||||
(defn tools-logging->telemere!
|
||||
"Configures `tools.logging` to use Telemere as its logging
|
||||
"Configures tools.logging to use Telemere as its logging
|
||||
implementation (backend).
|
||||
|
||||
Called automatically if one of the following is \"true\":
|
||||
|
|
@ -53,13 +53,13 @@
|
|||
{:kind :event
|
||||
:level :debug ; < :info since runs on init
|
||||
:id :taoensso.telemere/tools-logging->telemere!
|
||||
:msg "Enabling interop: `tools.logging` -> Telemere"})
|
||||
:msg "Enabling interop: tools.logging -> Telemere"})
|
||||
|
||||
(alter-var-root #'clojure.tools.logging/*logger-factory*
|
||||
(fn [_] (TelemereLoggerFactory.))))
|
||||
|
||||
(defn tools-logging->telemere?
|
||||
"Returns true iff `tools.logging` is configured to use Telemere
|
||||
"Returns true iff tools.logging is configured to use Telemere
|
||||
as its logging implementation (backend)."
|
||||
[]
|
||||
(when-let [lf clojure.tools.logging/*logger-factory*]
|
||||
|
|
@ -73,7 +73,7 @@
|
|||
(let [sending? (tools-logging->telemere?)
|
||||
receiving?
|
||||
(and sending?
|
||||
(impl/test-interop! "`tools.logging` -> Telemere"
|
||||
(impl/test-interop! "tools.logging -> Telemere"
|
||||
#(clojure.tools.logging/info %)))]
|
||||
|
||||
{:present? true
|
||||
|
|
|
|||
|
|
@ -657,7 +657,7 @@
|
|||
|
||||
#?(:clj
|
||||
(deftest _interop
|
||||
[(testing "`tools.logging` -> Telemere"
|
||||
[(testing "tools.logging -> Telemere"
|
||||
[(is (sm? (tel/check-interop) {:tools-logging {:present? true, :sending->telemere? true, :telemere-receiving? true}}))
|
||||
(is (sm? (with-sig (ctl/info "Hello" "x" "y")) {:level :info, :ns "taoensso.telemere-tests", :kind :tools-logging, :msg_ "Hello x y", :inst pinst?}))
|
||||
(is (sm? (with-sig (ctl/warn "Hello" "x" "y")) {:level :warn, :ns "taoensso.telemere-tests", :kind :tools-logging, :msg_ "Hello x y", :inst pinst?}))
|
||||
|
|
|
|||
|
|
@ -25,11 +25,11 @@ See section [4-Handlers](./4-Handlers).
|
|||
|
||||
## tools.logging
|
||||
|
||||
[`tools.logging`](https://github.com/clojure/tools.logging) can use Telemere as its logging implementation (backend). This'll let `tools.logging` calls create Telemere signals.
|
||||
[tools.logging](https://github.com/clojure/tools.logging) can use Telemere as its logging implementation (backend). This'll let tools.logging calls create Telemere signals.
|
||||
|
||||
To do this:
|
||||
|
||||
1. Ensure that you have the `tools.logging` dependency, and
|
||||
1. Ensure that you have the tools.logging [dependency](https://mvnrepository.com/artifact/org.clojure/tools.logging), and
|
||||
2. Call [`tools-logging->telemere!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.tools-logging#tools-logging-%3Etelemere!), or set the relevant environmental config as described in its docstring.
|
||||
|
||||
Verify successful interop with [`check-interop`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#check-interop):
|
||||
|
|
@ -41,18 +41,14 @@ Verify successful interop with [`check-interop`](https://cljdoc.org/d/com.taoens
|
|||
|
||||
## Java logging
|
||||
|
||||
[`SLF4Jv2`](https://www.slf4j.org/) can use Telemere as its logging backend. This'll let SLF4J logging calls create Telemere signals.
|
||||
[SLF4Jv2](https://www.slf4j.org/) can use Telemere as its logging backend. This'll let SLF4J logging calls create Telemere signals.
|
||||
|
||||
To do this, ensure that you have the following dependencies:
|
||||
To do this:
|
||||
|
||||
```clojure
|
||||
[org.slf4j/slf4j-api "x.y.z"] ; >= 2.0.0 only!
|
||||
[com.taoensso/telemere-slf4j "x.y.z"]
|
||||
```
|
||||
1. Ensure that you have the SLF4J [dependency](https://mvnrepository.com/artifact/org.slf4j/slf4j-api) ( v2+ **only**), and
|
||||
2. Ensure that you have the Telemere SLF4J backend [dependency](https://clojars.org/com.taoensso/telemere-slf4j)
|
||||
|
||||
> Telemere needs SLF4J API **version 2 or newer**. If you're seeing `Failed to load class "org.slf4j.impl.StaticLoggerBinder"` it could be that your project is importing the older v1 API, check with `lein deps :tree` or equivalent.
|
||||
|
||||
When `com.taoensso/telemere-slf4j` is on your classpath AND no other SLF4J backends are, SLF4J will direct all its logging calls to Telemere.
|
||||
When `com.taoensso/telemere-slf4j` (2) is on your classpath AND no other SLF4J backends are, SLF4J will automatically direct all its logging calls to Telemere.
|
||||
|
||||
Verify successful interop with [`check-interop`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#check-interop):
|
||||
|
||||
|
|
@ -61,7 +57,9 @@ Verify successful interop with [`check-interop`](https://cljdoc.org/d/com.taoens
|
|||
{: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.
|
||||
> Telemere needs SLF4J API **version 2 or newer**. If you're seeing `Failed to load class "org.slf4j.impl.StaticLoggerBinder"` it could be that your project is importing the older v1 API, check with `lein deps :tree` or equivalent.
|
||||
|
||||
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:
|
||||
|
||||
|
|
@ -96,7 +94,7 @@ This allows output to go (via configured exporters) to a wide variety of targets
|
|||
|
||||
To do this:
|
||||
|
||||
1. Ensure that you have the necessary [OpenTelemetry Java](https://github.com/open-telemetry/opentelemetry-java) dependencies.
|
||||
1. Ensure that you have the necessary [OpenTelemetry Java](https://github.com/open-telemetry/opentelemetry-java) [dependency](https://mvnrepository.com/artifact/io.opentelemetry/opentelemetry-api).
|
||||
2. Ensure that the relevant exporters are [appropriately configured](https://opentelemetry.io/docs/languages/java/configuration/) (this is the trickiest part, but not at all specific to Telemere).
|
||||
3. Create a Telemere signal handler using [`handler:open-telemetry`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.open-telemetry#handler:open-telemetry), and register it using [`add-handler!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#add-handler!).
|
||||
4. Ensure that [`otel-tracing?`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#otel-tracing?) is enabled if you want tracing interop.
|
||||
|
|
@ -117,7 +115,7 @@ Telemere can easily incorporate Tufte performance data in its signals, just like
|
|||
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.
|
||||
- The [upcoming](https:/www.taoensso.com/roadmap) Tufte v3 will share the same core as Telemere and offer an **identical API** for managing filters and handlers.
|
||||
|
||||
## Truss
|
||||
|
||||
|
|
@ -129,4 +127,4 @@ The [`catch->error!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/tao
|
|||
|
||||
```clojure
|
||||
(t/catch->error! <form-with-truss-assertion/s>)
|
||||
```
|
||||
```
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ While [Timbre](https://taoensso.com/timbre) will **continue to be maintained and
|
|||
Telemere's functionality is a **superset of Timbre**, and offers *many* improvements including:
|
||||
|
||||
- Better support for [structured logging](./1-Getting-started#data-types-and-structures)
|
||||
- Better [performance](https://github.com/taoensso/telemere#benchmarks)
|
||||
- Better [documentation](https://github.com/taoensso/telemere#documentation)
|
||||
- Better [included handlers](./4-Handlers##included-handlers)
|
||||
- Much better [performance](https://github.com/taoensso/telemere#performance)
|
||||
- Much better [documentation](https://github.com/taoensso/telemere#documentation)
|
||||
- A more flexible [API](./1-Getting-started#usage) that unifies all telemetry and logging needs
|
||||
- A more robust [architecture](./2-Architecture), free from all historical constraints
|
||||
- Better [included handlers](./4-Handlers##included-handlers)
|
||||
- Easier [configuration](./3-Config)
|
||||
|
||||
Migrating from Timbre to Telemere should be straightforward **unless you depend on specific/custom appenders** that might not be available for Telemere (yet).
|
||||
|
|
@ -63,8 +63,8 @@ If for any reason your tests are unsuccessful, please don't feel pressured to mi
|
|||
|
||||
# From tools.logging
|
||||
|
||||
This is easy, see [here](./3-Config#clojuretoolslogging).
|
||||
This is easy, see [here](./3-Config#toolslogging).
|
||||
|
||||
# From Java logging
|
||||
|
||||
This is easy, see [here](./3-Config#java-logging).
|
||||
This is easy, see [here](./3-Config#java-logging).
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ This will eventually ease long-term maintenance, increase reliability, and help
|
|||
|
||||
> [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.
|
||||
**No**, Telemere does **not** replace [Tufte](https:/www.taoensso.com/tufte). They work great together, and the [upcoming](https:/www.taoensso.com/roadmap) Tufte v3 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.
|
||||
|
||||
|
|
@ -54,13 +54,13 @@ They're focused on complementary things. When both are in use:
|
|||
|
||||
> [GraalVM](https://en.wikipedia.org/wiki/GraalVM) is a JDK alternative with ahead-of-time compilation for faster app initialization and improved runtime performance, etc.
|
||||
|
||||
Yes, this shouldn't be a problem.
|
||||
**Yes**, this shouldn't be a problem.
|
||||
|
||||
# Does Telemere work with Babashka?
|
||||
|
||||
> [Babashka](https://github.com/babashka/babashka) is a native Clojure interpreter for scripting with fast startup.
|
||||
|
||||
Not currently, though support should be possible with a little work. The current bottleneck is a dependency on [Encore](https://github.com/taoensso/encore), though that could actually be removed (also offering benefits re: library size).
|
||||
**No**, not currently - though support should be possible with a little work. The current bottleneck is a dependency on [Encore](https://github.com/taoensso/encore), though that could actually be removed (also offering benefits re: library size).
|
||||
|
||||
If there's interest in this, please [upvote](https://github.com/taoensso/roadmap/issues/22) on my open source roadmap.
|
||||
|
||||
|
|
@ -161,4 +161,4 @@ Ultimately I wrote Telemere because:
|
|||
|
||||
# Other questions?
|
||||
|
||||
Please [open a Github issue](https://github.com/taoensso/telemere/issues) or ping on Telemere's [Slack channel](https://www.taoensso.com/telemere/slack). I'll regularly update the FAQ to add common questions. - [Peter](https://www.taoensso.com)
|
||||
Please [open a Github issue](https://github.com/taoensso/telemere/issues) or ping on Telemere's [Slack channel](https://www.taoensso.com/telemere/slack). I'll regularly update the FAQ to add common questions. - [Peter](https://www.taoensso.com)
|
||||
|
|
|
|||
|
|
@ -1,21 +1,19 @@
|
|||
Are you a library author/maintainer that's considering **using Telemere in your library**?
|
||||
|
||||
# Options
|
||||
## 1. Consider a basic facade
|
||||
## 1. Common logging facade (basic logging only)
|
||||
|
||||
Does your library **really need** Telemere? Many libraries only need very basic logging. In these cases it can be beneficial to do your logging through a common basic facade like [tools.logging](https://github.com/clojure/tools.logging) or [SLF4J](https://www.slf4j.org/).
|
||||
Many libraries need only basic logging. In these cases it can be beneficial to do your logging through a common logging facade like [tools.logging](https://github.com/clojure/tools.logging) or [SLF4J](https://www.slf4j.org/).
|
||||
|
||||
**Pro**: users can then choose and configure their **preferred backend** - including Telemere, which can easily [act as a backend](./3-Config#interop) for both tools.logging and SLF4J.
|
||||
|
||||
**Cons**: you'll be limited by what your facade API offers, and so lose support for Telemere's advanced features like structured logging, [rich filtering](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#get-filters), etc.
|
||||
This'll limit you to basic features (e.g. no structured logging or [rich filtering](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#get-filters)) - but your users will have the freedom to choose and configure their **preferred backend** ([incl. Telemere if they like](./3-Config#interop)).
|
||||
|
||||
## 2. Telemere as a transitive dependency
|
||||
|
||||
Include [Telemere](https://clojars.org/com.taoensso/telemere) in your **library's dependencies**. Your library (and users) will then have access to the full Telemere API.
|
||||
|
||||
Telemere's [default config](./1-Getting-started#default-config) is sensible (with println-like console output), so many of library users won't need to configure or interact with Telemere at all.
|
||||
Telemere's [default config](./1-Getting-started#default-config) is sensible (with println-like console output), so your users are unlikely to need to configure or interact with Telemere much unless they choose to.
|
||||
|
||||
The most common thing library users may want to do is **adjust the minimum level** of signals created by your library. And since your users might not be familiar with Telemere, I'd recommend including something like the following in a convenient place like your library's main API namespace:
|
||||
The most common thing users may want to do is **adjust the minimum level** of signals created by your library. You can help make this as easy as possible by adding a util to your library:
|
||||
|
||||
```clojure
|
||||
(defn set-min-log-level!
|
||||
|
|
@ -39,8 +37,43 @@ This way your users can easily disable, decrease, or increase signal output from
|
|||
|
||||
Include the (super lightweight) [Telemere facade API](https://clojars.org/com.taoensso/telemere-api) in your **library's dependencies**.
|
||||
|
||||
Your library will then be able to take advantage of Telemere **when Telemere is present**, or fall back to something like [tools.logging](https://github.com/clojure/tools.logging) otherwise.
|
||||
Your library will then be able to emit structured logs/telemetry **when Telemere is present**, or fall back to something like [tools.logging](https://github.com/clojure/tools.logging) otherwise.
|
||||
|
||||
The main trade-off is that your signal calls will be more verbose.
|
||||
The main trade-off is that your signal calls will be more verbose:
|
||||
|
||||
See [here](https://cljdoc.org/d/com.taoensso/telemere-api/CURRENT/api/taoensso.telemere.api) for an example and more info.
|
||||
```clojure
|
||||
(ns my-lib
|
||||
(:require
|
||||
[taoensso.telemere.api :as t] ; `com.taoensso/telemere-api` dependency
|
||||
[clojure.tools.logging :as ctl] ; `org.clojure/tools.logging` dependency
|
||||
))
|
||||
|
||||
(t/require-telemere-if-present) ; Just below `ns` form
|
||||
|
||||
;; Optional convenience for library users
|
||||
(defn set-min-level!
|
||||
"If it's present, sets Telemere's minimum level for <my-lib> namespaces.
|
||||
This will affect all signals (logs) created by <my-lib>.
|
||||
|
||||
Possible minimum levels (from most->least verbose):
|
||||
#{:trace :debug :info :warn :error :fatal :report}.
|
||||
|
||||
The default minimum level is `:warn`."
|
||||
[min-level]
|
||||
(t/if-telemere
|
||||
(do (t/set-min-level! nil \"my-lib(.*)\" min-level) true)
|
||||
false))
|
||||
|
||||
(defonce ^:private __set-default-min-level (set-min-level! :warn))
|
||||
|
||||
;; Creates Telemere signal if Telemere is present,
|
||||
;; otherwise logs with tools.logging
|
||||
(signal!
|
||||
{:kind :log, :id :my-id, :level :warn,
|
||||
:let [x :x]
|
||||
:msg [\"Hello\" \"world\" x]
|
||||
:data {:a :A :x x}
|
||||
:fallback (ctl/warn (str \"Hello world\" x))})
|
||||
```
|
||||
|
||||
See [here](https://cljdoc.org/d/com.taoensso/telemere-api/CURRENT/api/taoensso.telemere.api) for more info.
|
||||
Loading…
Reference in a new issue