Skip to content

Commit

Permalink
[doc] Misc improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
ptaoussanis committed Sep 3, 2024
1 parent 0ae0199 commit cb53258
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 93 deletions.
51 changes: 28 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 most common telemetry needs:

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

Expand All @@ -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

Expand Down Expand Up @@ -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]}
Expand Down Expand Up @@ -85,7 +104,7 @@ 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.

Expand All @@ -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:
Expand Down Expand Up @@ -289,7 +294,7 @@ Telemere is optimized for *real-world* performance. This means **prioritizing fl

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**.
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.

Expand Down
2 changes: 1 addition & 1 deletion examples.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -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]}
Expand Down
32 changes: 2 additions & 30 deletions projects/api/src/taoensso/telemere/api.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -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]])))
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion projects/main/src/taoensso/telemere/impl.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -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?]}
Expand Down
10 changes: 5 additions & 5 deletions projects/main/src/taoensso/telemere/tools_logging.clj
Original file line number Diff line number Diff line change
@@ -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:
Expand Down Expand Up @@ -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\":
Expand All @@ -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*]
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion projects/main/test/taoensso/telemere_tests.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -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?}))
Expand Down
26 changes: 12 additions & 14 deletions wiki/3-Config.md
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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:

```clojure
[org.slf4j/slf4j-api "x.y.z"] ; >= 2.0.0 only!
[com.taoensso/telemere-slf4j "x.y.z"]
```
To do this:

> 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.
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)

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):

Expand All @@ -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:

Expand Down Expand Up @@ -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

Expand All @@ -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>)
```
```
8 changes: 4 additions & 4 deletions wiki/5-Migrating.md
Original file line number Diff line number Diff line change
Expand Up @@ -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#benchmarks)
- 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).
Expand Down Expand Up @@ -63,7 +63,7 @@ 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

Expand Down
8 changes: 4 additions & 4 deletions wiki/6-FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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.

Expand Down Expand Up @@ -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)
Loading

0 comments on commit cb53258

Please sign in to comment.