Skip to content

Commit

Permalink
[doc] Misc improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
ptaoussanis committed Aug 31, 2024
1 parent 0ae0199 commit fce34a3
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 71 deletions.
20 changes: 12 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@

### 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 all of the 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 occurrences, with optional data)
- **Tracing** (nested flow tracking, with optional data)
- Basic **performance monitoring** (nested form runtimes)

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

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 +26,7 @@ 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.-->

## Quick examples

Expand Down Expand Up @@ -85,7 +89,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 Down Expand Up @@ -289,7 +293,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
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
22 changes: 10 additions & 12 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:
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):

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
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
53 changes: 43 additions & 10 deletions wiki/9-Authors.md
Original file line number Diff line number Diff line change
@@ -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!
Expand All @@ -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:

```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>.
The main trade-off is that your signal calls will be more verbose.
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 an example and more info.
See [here](https://cljdoc.org/d/com.taoensso/telemere-api/CURRENT/api/taoensso.telemere.api) for more info.

0 comments on commit fce34a3

Please sign in to comment.