mirror of
https://github.com/taoensso/telemere.git
synced 2025-12-18 18:11:12 +00:00
Compare commits
48 commits
v1.0.0-RC5
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
798e0fddc4 | ||
|
|
6030c472ae | ||
|
|
1d6bdaf7e5 | ||
|
|
6cc54527f2 | ||
|
|
56e35f3f58 | ||
|
|
a6fc4adf6a | ||
|
|
6155713fde | ||
|
|
a883df3c41 | ||
|
|
e6ce33dd4e | ||
|
|
cc680b06f5 | ||
|
|
47af80319d | ||
|
|
b56e1c4529 | ||
|
|
8a3ae14f45 | ||
|
|
917b1b408e | ||
|
|
125e006753 | ||
|
|
f7006f31fe | ||
|
|
c6a71652d7 | ||
|
|
4cc4f45e7c | ||
|
|
ff9e3f4007 | ||
|
|
eb28d365a8 | ||
|
|
d9ad1ba379 | ||
|
|
b2a8b66cc0 | ||
|
|
1bcd46adf3 | ||
|
|
f6ec872f7c | ||
|
|
b7b3a25a82 | ||
|
|
070fe88abb | ||
|
|
dcfeba5b91 | ||
|
|
9d655bb9ce | ||
|
|
75a90c6b6d | ||
|
|
269c58d8fe | ||
|
|
7603ae2fcf | ||
|
|
6fb18bd3b9 | ||
|
|
d6264afe7c | ||
|
|
f08b60bce4 | ||
|
|
3746de8039 | ||
|
|
1bdb667b6c | ||
|
|
2e0a2938b7 | ||
|
|
9d040d70cd | ||
|
|
475e5ba6c2 | ||
|
|
51e8a1062f | ||
|
|
31a4fc26d2 | ||
|
|
94fec57c9e | ||
|
|
345b125f6b | ||
|
|
248e91f982 | ||
|
|
32e8909e42 | ||
|
|
e8f02ac13e | ||
|
|
254cd6471b | ||
|
|
c2e7d0c2d6 |
43 changed files with 872 additions and 793 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
name: Main tests
|
name: Clj tests
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
@ -7,7 +7,6 @@ jobs:
|
||||||
matrix:
|
matrix:
|
||||||
java: ['17', '19', '21']
|
java: ['17', '19', '21']
|
||||||
os: [ubuntu-latest]
|
os: [ubuntu-latest]
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
@ -15,17 +14,14 @@ jobs:
|
||||||
with:
|
with:
|
||||||
distribution: 'corretto'
|
distribution: 'corretto'
|
||||||
java-version: ${{ matrix.java }}
|
java-version: ${{ matrix.java }}
|
||||||
|
|
||||||
- uses: DeLaGuardo/setup-clojure@12.5
|
- uses: DeLaGuardo/setup-clojure@12.5
|
||||||
with:
|
with:
|
||||||
lein: latest
|
lein: latest
|
||||||
|
|
||||||
- uses: actions/cache@v4
|
- uses: actions/cache@v4
|
||||||
id: cache-deps
|
id: cache-deps
|
||||||
with:
|
with:
|
||||||
path: ~/.m2/repository
|
path: ~/.m2/repository
|
||||||
key: deps-${{ hashFiles('main/project.clj') }}
|
key: deps-${{ hashFiles('main/project.clj') }}
|
||||||
restore-keys: deps-
|
restore-keys: deps-
|
||||||
|
- run: lein test-clj
|
||||||
- run: lein test-all
|
|
||||||
working-directory: main
|
working-directory: main
|
||||||
27
.github/workflows/cljs-tests.yml
vendored
Normal file
27
.github/workflows/cljs-tests.yml
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
name: Cljs tests
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tests:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
java: ['21']
|
||||||
|
os: [ubuntu-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: 'corretto'
|
||||||
|
java-version: ${{ matrix.java }}
|
||||||
|
- uses: DeLaGuardo/setup-clojure@12.5
|
||||||
|
with:
|
||||||
|
lein: latest
|
||||||
|
- uses: actions/cache@v4
|
||||||
|
id: cache-deps
|
||||||
|
with:
|
||||||
|
path: ~/.m2/repository
|
||||||
|
key: deps-${{ hashFiles('main/project.clj') }}
|
||||||
|
restore-keys: deps-
|
||||||
|
- run: lein test-cljs
|
||||||
|
working-directory: main
|
||||||
9
.github/workflows/graal-tests.yml
vendored
9
.github/workflows/graal-tests.yml
vendored
|
|
@ -2,7 +2,7 @@ name: Graal tests
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
tests:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
java: ['17']
|
java: ['17']
|
||||||
|
|
@ -29,5 +29,10 @@ jobs:
|
||||||
key: deps-${{ hashFiles('main/project.clj') }}
|
key: deps-${{ hashFiles('main/project.clj') }}
|
||||||
restore-keys: deps-
|
restore-keys: deps-
|
||||||
|
|
||||||
- run: bb graal-tests
|
- name: Run Graal tests
|
||||||
|
run: bb graal-tests
|
||||||
working-directory: main
|
working-directory: main
|
||||||
|
|
||||||
|
# - name: Run Babashka tests
|
||||||
|
# run: bb bb-tests
|
||||||
|
# working-directory: main
|
||||||
|
|
|
||||||
311
CHANGELOG.md
311
CHANGELOG.md
|
|
@ -2,94 +2,195 @@ This project uses [**Break Versioning**](https://www.taoensso.com/break-versioni
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# `v1.0.0-RC5` (2025-03-10)
|
# `v1.2.1` (2025-12-16)
|
||||||
|
|
||||||
## 📦 Dependencies
|
## 📦 Dependencies
|
||||||
|
|
||||||
Available on Clojars:
|
Available on Clojars:
|
||||||
|
|
||||||
1. [Telemere](https://clojars.org/com.taoensso/telemere/versions/1.0.0-RC5) - main dependency.
|
1. [Telemere](https://clojars.org/com.taoensso/telemere/versions/1.2.1) - main dep
|
||||||
2. [SLF4J provider](https://clojars.org/com.taoensso/telemere-slf4j/versions/1.0.0-RC5) - additional dependency for users that want their Java logging [to go to](https://github.com/taoensso/telemere/wiki/3-Config#java-logging) Telemere.
|
2. [SLF4J provider](https://clojars.org/com.taoensso/telemere-slf4j/versions/1.2.1) - extra dep to [send Java logging](https://github.com/taoensso/telemere/wiki/3-Config#java-logging) to Telemere
|
||||||
|
|
||||||
This project uses [Break Versioning](https://www.taoensso.com/break-versioning).
|
This project uses [Break Versioning](https://www.taoensso.com/break-versioning).
|
||||||
|
|
||||||
## Release notes
|
## Release notes
|
||||||
|
|
||||||
RC5 includes **breaking changes** (indicated by ➤), apologies!
|
This is a **hotfix release** to fix a regression in v1.2.0 that prevented errors from correctly appearing via the Timbre->Telemere appender.
|
||||||
|
|
||||||
Big thanks to everyone that's been helping test and give feedback. And as always, please **report any unexpected problems** on [GitHub](https://github.com/taoensso/telemere/issues) or the [Slack channel](https://www.taoensso.com/telemere/slack) 🙏
|
This should be a safe upgrade for users of v1.2.0, apologies for the trouble! - Peter Taoussanis
|
||||||
|
|
||||||
Thanks for the patience awaiting v1 final! I hope and believe that it'll have been worth the time + effort.
|
---
|
||||||
|
|
||||||
|
# `v1.2.0` (2025-12-09)
|
||||||
|
|
||||||
|
## 📦 Dependencies
|
||||||
|
|
||||||
|
Available on Clojars:
|
||||||
|
|
||||||
|
1. [Telemere](https://clojars.org/com.taoensso/telemere/versions/1.2.0) - main dep
|
||||||
|
2. [SLF4J provider](https://clojars.org/com.taoensso/telemere-slf4j/versions/1.2.0) - extra dep to [send Java logging](https://github.com/taoensso/telemere/wiki/3-Config#java-logging) to Telemere
|
||||||
|
|
||||||
|
This project uses [Break Versioning](https://www.taoensso.com/break-versioning).
|
||||||
|
|
||||||
|
## Release notes
|
||||||
|
|
||||||
|
This is a **maintenance and feature release** that should be a safe upgrade for users of v1.1.x, though there have been a few small **changes to signal content** relevant to a very small number of users (see the ➤ items below).
|
||||||
|
|
||||||
|
Please **report any unexpected problems** on [GitHub](https://github.com/taoensso/telemere/issues) or the [Slack channel](https://www.taoensso.com/telemere/slack) 🙏 - [Peter Taoussanis](https://www.taoensso.com)
|
||||||
|
|
||||||
|
## Since `v1.1.0` (2025-08-22)
|
||||||
|
|
||||||
|
- ➤ **\[mod]** SLF4J->Telemere backend: move noisy stuff out of signal data \[e6ce33d]
|
||||||
|
- ➤ **\[mod]** Timbre shim API: move noisy `:vargs` out of signal data \[cc680b0
|
||||||
|
- \[mod] [fix] Timbre->Telemere appender: de-duplicate output formatting \[47af803]
|
||||||
|
- \[mod] [fix] Timbre->Telemere appender: fix callsite coords \[b56e1c4]
|
||||||
|
- \[fix] OpenTelemetry handler: add missing line info to output \[6155713]
|
||||||
|
- \[fix] Correctly handle nil `:run` opt \[8a3ae14]
|
||||||
|
- \[new] OpenTelemetry handler: support spans created outside Telemere \[a6fc4ad]
|
||||||
|
- \[new] [#68] Add config to skip host and/or thread info \[a883df3]
|
||||||
|
- \[doc] Clarify that signal content is lazy \[917b1b4]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# `v1.1.0` (2025-08-22)
|
||||||
|
|
||||||
|
## 📦 Dependencies
|
||||||
|
|
||||||
|
Available on Clojars:
|
||||||
|
|
||||||
|
1. [Telemere](https://clojars.org/com.taoensso/telemere/versions/1.1.0) - main dep
|
||||||
|
2. [SLF4J provider](https://clojars.org/com.taoensso/telemere-slf4j/versions/1.1.0) - extra dep to [send Java logging](https://github.com/taoensso/telemere/wiki/3-Config#java-logging) to Telemere
|
||||||
|
|
||||||
|
This project uses [Break Versioning](https://www.taoensso.com/break-versioning).
|
||||||
|
|
||||||
|
## Release notes
|
||||||
|
|
||||||
|
This is a **maintenance release** that fixes a few minor issues, improves docs, and adds some extra API flexibility. It should be a safe upgrade for all users of v1.x.
|
||||||
|
|
||||||
|
Please **report any unexpected problems** on [GitHub](https://github.com/taoensso/telemere/issues) or the [Slack channel](https://www.taoensso.com/telemere/slack) 🙏 - [Peter Taoussanis](https://www.taoensso.com)
|
||||||
|
|
||||||
|
## Since v1.0.1 (2025-05-27)
|
||||||
|
|
||||||
|
- \[fix] `:trace` level JS console logging \[b2a8b66]
|
||||||
|
- \[fix] Clj-kondo warnings for `with-signal/s` \[269c58d]
|
||||||
|
- \[new] `with-ctx/+` now takes `& body` instead of a single form (via Encore update)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# `v1.0.1` (2025-05-27)
|
||||||
|
|
||||||
|
## 📦 Dependencies
|
||||||
|
|
||||||
|
Available on Clojars:
|
||||||
|
|
||||||
|
1. [Telemere](https://clojars.org/com.taoensso/telemere/versions/1.0.1) - main dependency.
|
||||||
|
2. [SLF4J provider](https://clojars.org/com.taoensso/telemere-slf4j/versions/1.0.1) - additional dependency for users that want their Java logging [to go to](https://github.com/taoensso/telemere/wiki/3-Config#java-logging) Telemere.
|
||||||
|
|
||||||
|
This project uses [Break Versioning](https://www.taoensso.com/break-versioning).
|
||||||
|
|
||||||
|
## Release notes
|
||||||
|
|
||||||
|
This is a **hotfix release** that fixes a few issues, and improves some documentation. It should be a safe upgrade for all users of v1.0.0.
|
||||||
|
|
||||||
|
## Since `v1` (2025-04-30)
|
||||||
|
|
||||||
|
* \[fix] [#65] Fix broken callsite `:limit` option \[f08b60b]
|
||||||
|
* \[fix] Fix bad `signal-content-fn` parent formatting \[3746de8]
|
||||||
|
* \[doc] Add extra docs re: debugging filtering \[1bdb667]
|
||||||
|
* \[doc] [#64] Hide some unimportant vars from API docs (@marksto) \[2e0a293]
|
||||||
|
* \[doc] [#63] Add link to community Axiom handler (@marksto) \[9d040d7]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# `v1.0.0` (2025-04-30)
|
||||||
|
|
||||||
|
## 📦 Dependencies
|
||||||
|
|
||||||
|
Available on Clojars:
|
||||||
|
|
||||||
|
1. [Telemere](https://clojars.org/com.taoensso/telemere/versions/1.0.0) - main dependency.
|
||||||
|
2. [SLF4J provider](https://clojars.org/com.taoensso/telemere-slf4j/versions/1.0.0) - additional dependency for users that want their Java logging [to go to](https://github.com/taoensso/telemere/wiki/3-Config#java-logging) Telemere.
|
||||||
|
|
||||||
|
This project uses [Break Versioning](https://www.taoensso.com/break-versioning).
|
||||||
|
|
||||||
|
## Release notes
|
||||||
|
|
||||||
|
This is the first stable release of Telemere v1! 🍾🥳🎉
|
||||||
|
|
||||||
|
Sincere thanks to everyone that's been helping test and give feedback. As always, please **report any unexpected problems** on [GitHub](https://github.com/taoensso/telemere/issues) or the [Slack channel](https://www.taoensso.com/telemere/slack) 🙏
|
||||||
|
|
||||||
|
Telemere is part of a suite of practical and complementary **observability tools** for modern Clojure and ClojureScript applications:
|
||||||
|
|
||||||
|
- [Telemere](https://www.taoensso.com/telemere) for logging, tracing, and general telemetry
|
||||||
|
- [Tufte](https://www.taoensso.com/tufte) for performance monitoring ([v3 RC1 just released](https://github.com/taoensso/tufte/releases/tag/v3.0.0-RC1))
|
||||||
|
- [Truss](https://www.taoensso.com/truss) for assertions and error handling ([v2 recently released](https://github.com/taoensso/truss/releases/tag/v2.1.0))
|
||||||
|
|
||||||
|
New to Telemere? [Start here](https://github.com/taoensso/telemere/wiki/1-Getting-started)!
|
||||||
|
Upgrading from an earlier version? See the list of changes below👇
|
||||||
|
|
||||||
|
Cheers! :-)
|
||||||
|
|
||||||
\- [Peter Taoussanis](https://www.taoensso.com)
|
\- [Peter Taoussanis](https://www.taoensso.com)
|
||||||
|
|
||||||
## Recent CHANGES
|
## Changes since `v1 RC1`
|
||||||
|
|
||||||
### Since `v1.0.0-RC1` (2024-10-29)
|
> See linked commits for more info:
|
||||||
|
|
||||||
#### ➤ Changes to API since `v1.0.0-RC1`
|
In **v1 stable** (2025-04-30):
|
||||||
|
- \[fix] [#61] OpenTelemetry handler not cancelling timer on shutdown \[51e8a10]
|
||||||
|
- \[fix] [#32] Fix clj-kondo declaration typo (@icp1994) \[254cd64]
|
||||||
|
- \[new] Support `:host`, `:thread` override \[31a4fc2]
|
||||||
|
- \[new] Add callsite info to compile-time errors \[345b125]
|
||||||
|
- \[doc] Use consistent style for docstring opts \[94fec57]
|
||||||
|
|
||||||
Please read carefully in case you may be affected:
|
In **v1 RC5** (2025-03-10):
|
||||||
|
* \[mod] Rename `:rate-limit` -> `:limit` \[f37f54e] (RC5)
|
||||||
|
* \[mod] Rename `:sample-rate` -> `:sample` \[1f4b49a] (RC5)
|
||||||
|
* \[mod] Rename `:middleware` -> `:xfn` \[7cccf67] (RC5)
|
||||||
|
* \[mod] [#56] `utils/clean-signal-fn` exclude `:schema` by default \[c78eb07] (RC5)
|
||||||
|
* \[fix] [#57] File handling: use nio API to create missing parent dirs \[af45ffc] (RC5)
|
||||||
|
* \[fix] [#55] SLF4J signals should include `*ctx*` \[79173a6] (RC5)
|
||||||
|
* \[fix] [#32] Fix clj-kondo warnings \[c60f33e] (RC5)
|
||||||
|
* \[new] [#57] File handling: make file stream more robust \[82f4c31] (RC5)
|
||||||
|
|
||||||
* ➤ **\[mod]** Rename `:rate-limit` -> `:limit` \[f37f54e] (**RC5**)
|
In **v1 RC4** (2025-03-03):
|
||||||
* ➤ **\[mod]** Rename `:sample-rate` -> `:sample` \[1f4b49a] (**RC5**)
|
* \[mod] `log!`, `event!` now always return nil \[ac5feb4] (RC4)
|
||||||
* ➤ **\[mod]** Rename `:middleware` -> `:xfn` \[7cccf67] (**RC5**)
|
* \[mod] [#51] Make default console handler sync by default \[78ed4d7] (RC4)
|
||||||
* ➤ **\[mod]** `log!`, `event!` now always return nil \[ac5feb4] (**RC4**)
|
* \[mod] [#52] `signal-preamble-fn` now ignores nil `:kind` (@marksto) \[634cc53] (RC4)
|
||||||
* ➤ **\[mod]** Signal content: drop `:location`, add `:coords` \[fda22ce] (**RC3**)
|
* \[fix] [#52] `signal-preamble-fn` should use host info in signal (@marksto) \[410ed89] (RC4)
|
||||||
* ➤ **\[mod]** Signal options: drop `:location`, add `:coords` \[1f99f71] (**RC3**)
|
* \[new]Add `log!?`, `event!?` \[ac5feb4] (RC4)
|
||||||
* ➤ **\[mod]** OpenTelemetry: use standard attr names when possible \[bb715fb] (**RC3**)
|
* \[new] Alias `keep-callsite`, mention in `signal!` docs \[bfea515] (RC4)
|
||||||
|
* \[doc][#50] Expand docs for `set-min-level!` (via Encore update) (RC4)
|
||||||
|
* \[doc] Mention `:inst` monotonicity \[6b0e0b9] (RC4)
|
||||||
|
|
||||||
|
In **v1 RC3** (2025-02-27):
|
||||||
|
* \[mod] Signal content: drop `:location`, add `:coords` \[fda22ce] (RC3)
|
||||||
|
* \[mod] Signal options: drop `:location`, add `:coords` \[1f99f71] (RC3)
|
||||||
|
* \[mod] OpenTelemetry: use standard attr names when possible \[bb715fb] (RC3)
|
||||||
|
* \[fix] Timbre shim: rename `spy!` -> `spy` (@lvh) \[3a9ffc6] (RC3)
|
||||||
|
* \[fix] Timbre shim: don't attach empty `:vargs` data \[0e642ba] (RC3)
|
||||||
|
* \[fix] Fix environment val docs \[db26a5d] (RC3)
|
||||||
|
* \[fix] `spy!` docstring typo (@rafd) \[35606d9] (RC3)
|
||||||
|
* \[new] Use [Truss](https://www.taoensso.com/truss) v2 and [contextual exceptions](https://cljdoc.org/d/com.taoensso/truss/CURRENT/api/taoensso.truss#ex-info) when relevant (RC3)
|
||||||
|
* \[new] [#44] Open Telemetry handler: add span kind option (@farcaller) \[413cce8] (RC3)
|
||||||
|
* \[new] Reduced Cljs build sizes in some cases (RC3)
|
||||||
|
* \[doc]Timbre shim: document different `spy` error handling \[1517f30] (RC3)
|
||||||
|
* \[doc] [#43] ns filters work for SLF4J logger names (@lvh) \[db0498b] (RC3)
|
||||||
|
|
||||||
|
In **v1 RC2** (2024-12-24):
|
||||||
* \[mod] [#39] Discontinued separate "shell" library \[096c432] (RC2)
|
* \[mod] [#39] Discontinued separate "shell" library \[096c432] (RC2)
|
||||||
* \[mod] Change return value of experimental [`with-signals`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#with-signals) \[cb6a5d9] (RC2)
|
* \[mod] Change return value of experimental [`with-signals`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#with-signals) \[cb6a5d9] (RC2)
|
||||||
* \[mod] Remove rarely-used advanced options from [`catch->error!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#catch-%3Eerror!) \[0de5c09] (RC2)
|
* \[mod] Remove rarely-used advanced options from [`catch->error!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#catch-%3Eerror!) \[0de5c09] (RC2)
|
||||||
|
|
||||||
#### Changes to default output since `v1.0.0-RC1`
|
|
||||||
|
|
||||||
* ➤ **\[mod]** [#56] `utils/clean-signal-fn` exclude `:schema` by default \[c78eb07] (**RC5**)
|
|
||||||
* ➤ **\[mod]** [#52] `signal-preamble-fn` now ignores nil `:kind` (@marksto) \[634cc53] (**RC4**)
|
|
||||||
* \[mod] Remove "- " msg separator from default [preamble](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.utils#signal-preamble-fn) output \[d61f6c2] (RC2)
|
* \[mod] Remove "- " msg separator from default [preamble](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.utils#signal-preamble-fn) output \[d61f6c2] (RC2)
|
||||||
* \[mod] [Postal handler](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.postal#handler:postal) now uses default preamble fn for email subject \[706a8b6] (RC2)
|
* \[mod] [Postal handler](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.postal#handler:postal) now uses default preamble fn for email subject \[706a8b6] (RC2)
|
||||||
* \[mod] Default [`signal-content-fn`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.utils#signal-content-fn): omit redundant parent/root id namespaces \[55323f1] (RC2)
|
* \[mod] Default [`signal-content-fn`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.utils#signal-content-fn): omit redundant parent/root id namespaces \[55323f1] (RC2)
|
||||||
* \[mod] Default [`signal-content-fn`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.utils#signal-content-fn): swap `ctx`, `kvs` position \[b208532] (RC2)
|
* \[mod] Default [`signal-content-fn`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.utils#signal-content-fn): swap `ctx`, `kvs` position \[b208532] (RC2)
|
||||||
* \[mod] Default [`signal-content-fn`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.utils#signal-content-fn): omit `:root` if it's same as parent \[0464285] (RC2)
|
* \[mod] Default [`signal-content-fn`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.utils#signal-content-fn): omit `:root` if it's same as parent \[0464285] (RC2)
|
||||||
* \[mod] Omit empty `:data`, `:ctx` from [signal content](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.utils#signal-content-fn) output \[d78663a] (RC2)
|
* \[mod] Omit empty `:data`, `:ctx` from [signal content](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.utils#signal-content-fn) output \[d78663a] (RC2)
|
||||||
|
* \[fix] Broken signal string representation \[8c701d4] (RC2)
|
||||||
### Other changes
|
* \[fix] Trace formatting: always include root info \[f522307] (RC2)
|
||||||
|
* \[fix] Trace formatting: properly format nil ids \[68a894e] (RC2)
|
||||||
* **\[mod]** [#51] Make default console handler sync by default \[78ed4d7] (**RC4**)
|
* \[fix] [#36] Fix missing cljdoc docstrings \[b58ec73] (RC2)
|
||||||
* \[mod] Update [`pr-signal-fn`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#pr-signal-fn) to use [`clean-signal-fn`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#clean-signal-fn) \[f70363091] (beta 23)
|
|
||||||
* \[mod] Rename `taoensso.telemere.api` -> `taoensso.telemere.shell` \[a9005e7f1] (beta 23)
|
|
||||||
* \[mod] Move dep: `com.taoensso/slf4j-telemere` -> [com.taoensso/telemere-slf4j](https://clojars.org/com.taoensso/telemere-slf4j) \[77ed27cfd] (beta 22)
|
|
||||||
* \[mod] Generalize "intake", rename -> "interop" \[ef678bcc] (beta 20)
|
|
||||||
* \[mod] Make `:host` output opt-in for default signal handlers \[88eb5211] (beta 20)
|
|
||||||
* \[mod] [OpenTelemetry handler](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.open-telemetry#handler:open-telemetry): rename (generalize) \[064ef323] (beta 19)
|
|
||||||
* \[mod] [OpenTelemetry handler](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.open-telemetry#handler:open-telemetry): revert #10 \[599236f4] (beta 18)
|
|
||||||
* \[mod] Decrease level of :on-init signals \[4d2b5d46] (beta 18)
|
|
||||||
* \[mod] Removed `*auto-stop-handlers?*` var (beta 15)
|
|
||||||
* \[mod] Removed `:needs-stopping?` [handler dispatch opt](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:handler-dispatch-options) (beta 15)
|
|
||||||
* \[mod] Cljs handlers MUST now include stop (0) arity (beta 15)
|
|
||||||
* \[mod] Users MUST now **manually call** [`stop-handlers!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#stop-handlers!) (beta 15)
|
|
||||||
* \[mod] SLF4J and `tools.logging` signals now have a custom `:kind` and no `:id` (beta 14)
|
|
||||||
* \[mod] Renamed `get-min-level` -> [`get-min-levels`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#get-min-levels) (beta 13)
|
|
||||||
* \[mod] Renamed `shut-down-handlers!` -> [`stop-handlers!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#stop-handlers!) (beta 13)
|
|
||||||
* \[mod] Changed default **handler back-pressure** mechanism from `:dropping` to `:blocking` (eaiser for most users to understand and detect; override when calling [`add-handler!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#add-handler!)) (beta 11)
|
|
||||||
* \[mod] [`pr-signal-fn`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.utils#pr-signal-fn) now takes only a **single opts map** (beta 10)
|
|
||||||
* \[mod] [User-level kvs](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-options) are **no longer included by default** in handler output. `:incl-kvs?` option has been added to [`format-signal-fn`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.utils#format-signal-fn) and [`pr-signal-fn`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.utils#pr-signal-fn) (beta 7)
|
|
||||||
* \[mod] Middleware must now be a **single fn**, use [`comp-middleware`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#comp-middleware) to create one fn from many (beta 7)
|
|
||||||
* \[mod] [OpenTelemetry handler](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.open-telemetry#handler:open-telemetry) is **no longer auto added** (beta 1)
|
|
||||||
* \[mod] Various API improvements to [included handlers](https://github.com/taoensso/telemere/wiki/4-Handlers#included-handlers) and [utils](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.utils)
|
|
||||||
|
|
||||||
## Recent additions
|
|
||||||
|
|
||||||
### Since `v1.0.0-RC1` (2024-10-29)
|
|
||||||
|
|
||||||
#### Misc improvements
|
|
||||||
|
|
||||||
* **\[new]** [#57] File handling: make file stream more robust \[82f4c31] (**RC5**)
|
|
||||||
* **\[new]** Add `log!?`, `event!?` \[ac5feb4] (**RC4**)
|
|
||||||
* **\[new]** Alias `keep-callsite`, mention in `signal!` docs \[bfea515] (**RC4**)
|
|
||||||
* **\[new]** Use [Truss](https://www.taoensso.com/truss) v2 and [contextual exceptions](https://cljdoc.org/d/com.taoensso/truss/CURRENT/api/taoensso.truss#ex-info) when relevant (**RC3**)
|
|
||||||
* **\[new]** [#44] Open Telemetry handler: add span kind option (@farcaller) \[413cce8] (**RC3**)
|
|
||||||
* **\[new]** Reduced Cljs build sizes in some cases (**RC3**)
|
|
||||||
* \[new] Add [`timbre->telemere`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.timbre#timbre->telemere-appender) appender and update docs \[ace6e2d] (RC2)
|
* \[new] Add [`timbre->telemere`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.timbre#timbre->telemere-appender) appender and update docs \[ace6e2d] (RC2)
|
||||||
* \[new] All signal creators can now take single opts map \[d2386d6] (RC2)
|
* \[new] All signal creators can now take single opts map \[d2386d6] (RC2)
|
||||||
* \[new] Add `& opts` support to [`signal!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#signal!), [`signal-allowed?`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#signal-allowed?) \[a04f255] (RC2)
|
* \[new] Add `& opts` support to [`signal!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#signal!), [`signal-allowed?`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#signal-allowed?) \[a04f255] (RC2)
|
||||||
|
|
@ -99,102 +200,12 @@ Please read carefully in case you may be affected:
|
||||||
* \[new] Allow manual `:run-val` override \[9dc883d] (RC2)
|
* \[new] Allow manual `:run-val` override \[9dc883d] (RC2)
|
||||||
* \[new] [#34] Add new [`signal-preamble-fn`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.utils#signal-preamble-fn) opts (@Knotschi) \[0822217] (RC2)
|
* \[new] [#34] Add new [`signal-preamble-fn`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.utils#signal-preamble-fn) opts (@Knotschi) \[0822217] (RC2)
|
||||||
* \[new] Alias low-level formatters in utils ns \[9dc9a46] (RC2)
|
* \[new] Alias low-level formatters in utils ns \[9dc9a46] (RC2)
|
||||||
|
|
||||||
#### Doc improvements
|
|
||||||
|
|
||||||
* **\[doc]** [#50] Expand docs for `set-min-level!` (via Encore update) (**RC4**)
|
|
||||||
* **\[doc]** Mention `:inst` monotonicity \[6b0e0b9] (**RC4**)
|
|
||||||
* **\[doc]** Timbre shim: document different `spy` error handling \[1517f30] (**RC3**)
|
|
||||||
* **\[doc]** [#43] ns filters work for SLF4J logger names (@lvh) \[db0498b] (**RC3**)
|
|
||||||
* \[doc] [#33] Add community examples link to [Bling Gist](https://gist.github.com/ptaoussanis/f8a80f85d3e0f89b307a470ce6e044b5) \[8cd4ca9] (RC2)
|
* \[doc] [#33] Add community examples link to [Bling Gist](https://gist.github.com/ptaoussanis/f8a80f85d3e0f89b307a470ce6e044b5) \[8cd4ca9] (RC2)
|
||||||
* \[doc] Better document pattern of using [`trace!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#trace!)/[`spy!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#spy!) with [`catch->error!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#catch-%3Eerror!) \[5c977a3] (RC2)
|
* \[doc] Better document pattern of using [`trace!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#trace!)/[`spy!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#spy!) with [`catch->error!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#catch-%3Eerror!) \[5c977a3] (RC2)
|
||||||
* \[doc] [#35] Emphasize that [signal opts](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-options) must be a compile-time map \[55720ac] (RC2)
|
* \[doc] [#35] Emphasize that [signal opts](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-options) must be a compile-time map \[55720ac] (RC2)
|
||||||
* \[doc] Add [FAQ item](https://github.com/taoensso/telemere/wiki/6-FAQ#why-the-unusual-arg-order-for-event) re: [`event!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#event!) arg order \[822032d] (RC2)
|
* \[doc] Add [FAQ item](https://github.com/taoensso/telemere/wiki/6-FAQ#why-the-unusual-arg-order-for-event) re: [`event!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#event!) arg order \[822032d] (RC2)
|
||||||
* \[doc] Document that `:msg` may be a delay \[13d9dbf] (RC2)
|
* \[doc] Document that `:msg` may be a delay \[13d9dbf] (RC2)
|
||||||
|
|
||||||
### Earlier improvements
|
|
||||||
|
|
||||||
* \[new] Add `:ctx+`, `:middleware+` signal options \[5a8c407] (RC1)
|
|
||||||
* \[new] OpenTelemetry handler: try print map vals as EDN \[c1e1c1e] (RC1)
|
|
||||||
* \[new] [#28] OpenTelemetry handler: support custom signal attrs \[5ef4f12] (RC1)
|
|
||||||
* \[new] Simplify default OpenTelemetry providers code, expose SDK \[19548d3] (RC1)
|
|
||||||
* \[new] Add `dispatch-signal!` util \[5ac8725] (RC1)
|
|
||||||
* \[new] `writeable-file!`: resolve sym links, etc. \[9965450] (RC1)
|
|
||||||
* \[new] Extend IIFE-wrap to Clj \[d0ad99d] (RC1)
|
|
||||||
* \[new] Numerous improvements to docs and examples (RC1)
|
|
||||||
* \[new] Add `:rate-limit-by` option to all signal creators \[d9c358363] (beta 23)
|
|
||||||
* \[new] Add `clean-signal-fn` util \[be55f44a8] (beta 23)
|
|
||||||
* \[new] Add `signal-allowed?` util \[d12b0b145] (beta 23)
|
|
||||||
* \[new] Allow compile-time config of uid kind \[965c2277f] (beta 23)
|
|
||||||
* \[new] Avoid duplicated trace bodies \[c9e84e8b3] (beta 23)
|
|
||||||
* \[new] Cap length of displayed run-form when tracing \[85772f733] (beta 23)
|
|
||||||
* \[new] Added experimental [shell API](https://cljdoc.org/d/com.taoensso/telemere-shell/CURRENT/api/taoensso.telemere.api) for library authors \[ece51b2ef] (beta 22)
|
|
||||||
* \[new] Auto stop existing handler when replacing it (beta 22)
|
|
||||||
* \[new] Added `"(.*)"` wildcard syntax to kind/ns/id filters (beta 22)
|
|
||||||
* \[new] Internal and doc improvements: \[8066776a8], \[b4b06f324], \[3068ccf8d] (beta 21)
|
|
||||||
* \[new] OpenTelemetry handler: improve span interop \[84957c6d] (beta 20)
|
|
||||||
* \[new] OpenTelemetry handler: add experimental trace output \[67cb4941] (beta 18)
|
|
||||||
* \[new] Improve uid control, switch to nano-style by default \[5ab2736c] (beta 18)
|
|
||||||
* \[new] Add host info to signal content \[1cef1957] (beta 18)
|
|
||||||
* \[new] Add extra tracing info to signal content \[d635318f] (beta 18)
|
|
||||||
* \[new] Ongoing [API](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere) and [wiki](https://github.com/taoensso/telemere/wiki) doc improvements (beta 15)
|
|
||||||
* \[new] [#5] Added [comparison to Mulog](https://github.com/taoensso/telemere/wiki/6-FAQ#how-does-telemere-compare-to-mulog) (beta 15)
|
|
||||||
* \[new] SLF4J and `tools.logging` signals now have a namespace (from logger name) (beta 14)
|
|
||||||
* \[new] Added [`get-handlers-stats`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#get-handlers-stats) (beta 13)
|
|
||||||
* \[new] [`add-handler!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#add-handler!) can now specify per-handler `:drain-msecs` (beta 13)
|
|
||||||
* \[new] Added [`*auto-stop-handlers?*`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#*auto-stop-handlers?*) (beta 13)
|
|
||||||
* \[new] [`remove-handler!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#remove-handler!) now auto stops relevant handlers after removal (beta 13)
|
|
||||||
* \[new] [`with-handler`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#with-handler) and [`with-handler+`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#with-handler+) now auto stops relevant handlers after use (beta 12)
|
|
||||||
* \[new] (Advanced) Handler fns can now include `:dispatch-opts` metadata, useful for handler authors that want to set defaults for use by [`add-handler!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#add-handler!) (beta 8)
|
|
||||||
* \[new] Added [Slack handler](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.slack#handler:slack) (beta 8)
|
|
||||||
* \[new] Added [TCP](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.sockets#handler:tcp-socket) and [UDP](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.sockets#handler:udp-socket) socket handlers (beta 7)
|
|
||||||
* \[new] Clj [signal content](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-content) now includes `:thread {:keys [group name id]}` key (beta 7)
|
|
||||||
* \[new] Added [postal (email) handler](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.postal#handler:postal) (beta 5)
|
|
||||||
* \[new] Handlers now block to try drain their signal queues on shutdown (beta 3)
|
|
||||||
* \[new] Rate limiter performance improvements (via Encore) (beta 3)
|
|
||||||
|
|
||||||
## Recent fixes
|
|
||||||
|
|
||||||
### Since `v1.0.0-RC1` (2024-10-29)
|
|
||||||
|
|
||||||
* **\[fix]** [#57] File handling: use nio API to create missing parent dirs \[af45ffc] (**RC5**)
|
|
||||||
* **\[fix]** [#55] SLF4J signals should include `*ctx*` \[79173a6] (**RC5**)
|
|
||||||
* **\[fix]** [#32] Fix clj-kondo warnings \[c60f33e] (**RC5**)
|
|
||||||
* **\[fix]** [#52] `signal-preamble-fn` should use host info in signal (@marksto) \[410ed89] (**RC4**)
|
|
||||||
* **\[fix]** Timbre shim: rename `spy!` -> `spy` (@lvh) \[3a9ffc6] (**RC3**)
|
|
||||||
* **\[fix]** Timbre shim: don't attach empty `:vargs` data \[0e642ba] (**RC3**)
|
|
||||||
* **\[fix]** Fix environment val docs \[db26a5d] (**RC3**)
|
|
||||||
* **\[fix]** `spy!` docstring typo (@rafd) \[35606d9] (**RC3**)
|
|
||||||
* \[fix] Broken signal string representation \[8c701d4] (RC2)
|
|
||||||
* \[fix] Trace formatting: always include root info \[f522307] (RC2)
|
|
||||||
* \[fix] Trace formatting: properly format nil ids \[68a894e] (RC2)
|
|
||||||
* \[fix] [#36] Fix missing cljdoc docstrings \[b58ec73] (RC2)
|
|
||||||
|
|
||||||
### Earlier fixes
|
|
||||||
|
|
||||||
* \[fix] `signal-opts`: allow map forms as intended \[f7a5663] (RC1)
|
|
||||||
* \[fix] `uncaught->error!` wasn't working (@benalbrecht) \[7f52cb1] (RC1)
|
|
||||||
* \[fix] Regression affecting deprecated `rate-limiter*` (beta 25)
|
|
||||||
* \[fix] Don't try count non-list tracing bodies \[88f7a3c7d] (beta 24)
|
|
||||||
* \[fix] [#21] Work around issue with use in Cljs `core.async/go` bodies \[cbab57be6] (beta 23)
|
|
||||||
* \[fix] [#20] Wrong :arglists meta on `spy!` \[568906c96] (beta 23)
|
|
||||||
* \[fix] [#18] Support `{:uid :auto}` for non-tracing signal creators \[f52a04b4d] (beta 23)
|
|
||||||
* \[fix] Runtime Clj env config now works correctly in uberjars (beta 23)
|
|
||||||
* \[fix] Signal `:line` info missing for some wrapped-macro cases \[0f09b797e] (beta 22)
|
|
||||||
* \[fix] OpenTelemetry handler: use signal callsite Context as root span parent \[a8e92303] (beta 19)
|
|
||||||
* \[fix] [#16] OpenTelemetry handler: coerce line attrs (@flyingmachine) \[17349a08] (beta 19)
|
|
||||||
* \[fix] Decrease min Java version (11->8) (@flyingmachine) \[a1c50f10] (beta 19)
|
|
||||||
* \[fix] Broken handler ns and kind filters \[23194238] (beta 16)
|
|
||||||
* \[fix] [#10] OpenTelemetry handler: render keywords as plain strings \[6e94215e] (beta 15)
|
|
||||||
* \[fix] [#11] OpenTelemetry handler: signals without message fail \[863cea15] (beta 15)
|
|
||||||
* \[fix] [#14] File handler: Don't truncate gzip output \[2d4b0497] (beta 15)
|
|
||||||
* \[fix] Don't drop signals while draining async buffer during shutdown, add tests (via Encore) (beta 12, beta 13)
|
|
||||||
* \[fix] [`pr-signal-fn`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.utils#pr-signal-fn) wasn't realizing delayed messages, add tests \[cf72017a] (beta 11)
|
|
||||||
* \[fix] [`pr-signal-fn`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.utils#pr-signal-fn) broken custom pr-fn support, add tests \[e7cce0c1] (beta 10)
|
|
||||||
* \[fix] [#6] Missing root stack trace, add tests \[213c6470] (beta 9)
|
|
||||||
* \[fix] Broken AOT support, add tests \[ffea1a30] (beta 1)
|
|
||||||
* \[fix] SLF4J broken timestamps, add tests \[e222297a] (beta 1)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# `v1.0.0-RC1` (2024-10-29)
|
# `v1.0.0-RC1` (2024-10-29)
|
||||||
|
|
|
||||||
205
README.md
205
README.md
|
|
@ -1,77 +1,73 @@
|
||||||
<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>
|
<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>
|
||||||
[**API**][cljdoc] | [**Wiki**][GitHub wiki] | [Latest releases](#latest-releases) | [Slack channel][]
|
[**API**][cljdoc] | [**Wiki**][GitHub wiki] | [Slack][] | Latest release: [v1.2.1](../../releases/tag/v1.2.1) (2025-12-16)
|
||||||
|
|
||||||
|
[![Clj tests][Clj tests SVG]][Clj tests URL]
|
||||||
|
[![Cljs tests][Cljs tests SVG]][Cljs tests URL]
|
||||||
|
[![Graal tests][Graal tests SVG]][Graal tests URL]
|
||||||
|
|
||||||
# <img src="https://raw.githubusercontent.com/taoensso/telemere/master/imgs/telemere-logo.svg" alt="Telemere logo" width="360"/>
|
# <img src="https://raw.githubusercontent.com/taoensso/telemere/master/imgs/telemere-logo.svg" alt="Telemere logo" width="360"/>
|
||||||
|
|
||||||
### Structured logs and telemetry for Clojure/Script
|
### Structured logs and telemetry for Clojure/Script
|
||||||
|
|
||||||
**Telemere** is a **pure Clojure/Script library** that offers an elegant and simple **unified API** to cover:
|
**Telemere** is the next-gen version of [Timbre](https://www.taoensso.com/timbre). It offers **one API** to cover:
|
||||||
|
|
||||||
- **Traditional logging** (string messages)
|
- **Traditional logging** (string messages)
|
||||||
- **Structured logging** (rich Clojure data types and structures)
|
- **Structured logging** (rich Clojure data types and structures)
|
||||||
- **Events** (named thing happened, with optional data)
|
|
||||||
- **Tracing** (nested flow tracking, with optional data)
|
- **Tracing** (nested flow tracking, with optional data)
|
||||||
- Basic **performance monitoring** (nested form runtimes)
|
- Basic **performance monitoring** (nested form runtimes)
|
||||||
- Any combination of the above
|
|
||||||
|
|
||||||
It's small, *super* fast, easy to learn, easy to use, and **absurdly flexible**.
|
It's pure Clj/s, small, **easy to use**, super fast, and **seriously flexible**:
|
||||||
|
|
||||||
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** (concepts, terminology, getting started).
|
|
||||||
|
|
||||||
## Latest release/s
|
|
||||||
|
|
||||||
- `2025-03-10` `v1.0.0-RC5`: [release info](../../releases/tag/v1.0.0-RC5)
|
|
||||||
|
|
||||||
[![Main tests][Main tests SVG]][Main tests URL]
|
|
||||||
[![Graal tests][Graal tests SVG]][Graal tests URL]
|
|
||||||
|
|
||||||
<!--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
|
|
||||||
|
|
||||||
> (Or see [examples.cljc](https://github.com/taoensso/telemere/blob/master/examples.cljc) for REPL-ready snippets)
|
|
||||||
|
|
||||||
<details open><summary>Create signals</summary><br/>
|
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
(require '[taoensso.telemere :as t])
|
(tel/log! {:level :info, :id ::login, :data {:user-id 1234}, :msg "User logged in!"})
|
||||||
|
```
|
||||||
|
|
||||||
;; (Just works / no config necessary for typical use cases)
|
Works great with:
|
||||||
|
|
||||||
;; Without structured data
|
- [Trove](https://www.taoensso.com/trove) for logging by **library authors**
|
||||||
(t/log! :info "Hello world!") ; %> Basic log signal (has message)
|
- [Tufte](https://www.taoensso.com/tufte) for rich **performance monitoring**
|
||||||
(t/event! ::my-id :debug) ; %> Basic event signal (just id)
|
- [Truss](https://www.taoensso.com/truss) for **assertions** and error handling
|
||||||
|
|
||||||
;; With structured data
|
## Why structured logging?
|
||||||
(t/log! {:level :info, :data {...}} "Hello again!")
|
|
||||||
(t/event! ::my-id {:level :debug, :data {...}})
|
- Traditional logging outputs **strings** (messages).
|
||||||
|
- Structured logging in contrast outputs **data**. It retains **rich data types and (nested) structures** throughout the logging pipeline from logging callsite → filters → middleware → handlers.
|
||||||
|
|
||||||
|
A data-oriented pipeline can make a huge difference - supporting **easier filtering**, **transformation**, and **analysis**. It’s also usually **faster**, since you only pay for serialization if/when you need it. In a lot of cases you can avoid serialization altogether if your final target (DB, etc.) supports the relevant types.
|
||||||
|
|
||||||
|
The structured (data-oriented) approach is inherently more flexible, faster, and well suited to the tools and idioms offered by Clojure and ClojureScript.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
See [examples.cljc](https://github.com/taoensso/telemere/blob/master/examples.cljc) for REPL-ready snippets, or expand below:
|
||||||
|
|
||||||
|
<details><summary>Create signals</summary><br/>
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
(require '[taoensso.telemere :as tel])
|
||||||
|
|
||||||
|
;; No config needed for typical use cases!!
|
||||||
|
;; Signals print to console by default for both Clj and Cljs
|
||||||
|
|
||||||
|
;; Traditional style logging (data formatted into message string):
|
||||||
|
(tel/log! {:level :info, :msg (str "User " 1234 " logged in!")})
|
||||||
|
|
||||||
|
;; Modern/structured style logging (explicit id and data)
|
||||||
|
(tel/log! {:level :info, :id :auth/login, :data {:user-id 1234}})
|
||||||
|
|
||||||
|
;; Mixed style (explicit id and data, with message string)
|
||||||
|
(tel/log! {:level :info, :id :auth/login, :data {:user-id 1234}, :msg "User logged in!"})
|
||||||
|
|
||||||
;; Trace (can interop with OpenTelemetry)
|
;; Trace (can interop with OpenTelemetry)
|
||||||
;; Tracks form runtime, return value, and (nested) parent tree
|
;; Tracks form runtime, return value, and (nested) parent tree
|
||||||
(t/trace! {:id ::my-id :data {...}}
|
(tel/trace! {:id ::my-id :data {...}}
|
||||||
(do-some-work))
|
(do-some-work))
|
||||||
|
|
||||||
;; Check resulting signal content for debug/tests
|
;; Check resulting signal content for debug/tests
|
||||||
(t/with-signal (t/event! ::my-id)) ; => {:keys [ns level id data msg_ ...]}
|
(tel/with-signal (tel/log! {...})) ; => {:keys [ns level id data msg_ ...]}
|
||||||
|
|
||||||
;; Getting fancy (all costs are conditional!)
|
;; Getting fancy (all costs are conditional!)
|
||||||
(t/log!
|
(tel/log!
|
||||||
{:level :debug
|
{:level :debug
|
||||||
:sample 0.75 ; 75% sampling (noop 25% of the time)
|
:sample 0.75 ; 75% sampling (noop 25% of the time)
|
||||||
:when (my-conditional)
|
:when (my-conditional)
|
||||||
|
|
@ -99,33 +95,33 @@ It enables you to write code that is **information-verbose by default**.
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
;; Set minimum level
|
;; Set minimum level
|
||||||
(t/set-min-level! :warn) ; For all signals
|
(tel/set-min-level! :warn) ; For all signals
|
||||||
(t/set-min-level! :log :debug) ; For `log!` signals only
|
(tel/set-min-level! :log :debug) ; For `log!` signals specifically
|
||||||
|
|
||||||
;; Set id and namespace filters
|
;; Set id and namespace filters
|
||||||
(t/set-id-filter! {:allow #{::my-particular-id "my-app/*"}})
|
(tel/set-id-filter! {:allow #{::my-particular-id "my-app/*"}})
|
||||||
(t/set-ns-filter! {:disallow "taoensso.*" :allow "taoensso.sente.*"})
|
(tel/set-ns-filter! {:disallow "taoensso.*" :allow "taoensso.sente.*"})
|
||||||
|
|
||||||
;; SLF4J signals will have their `:ns` key set to the logger's name
|
;; SLF4J signals will have their `:ns` key set to the logger's name
|
||||||
;; (typically a source class)
|
;; (typically a source class)
|
||||||
(t/set-ns-filter! {:disallow "com.noisy.java.package.*"})
|
(tel/set-ns-filter! {:disallow "com.noisy.java.package.*"})
|
||||||
|
|
||||||
;; Set minimum level for `event!` signals for particular ns pattern
|
;; Set minimum level for `log!` signals for particular ns pattern
|
||||||
(t/set-min-level! :event "taoensso.sente.*" :warn)
|
(tel/set-min-level! :log "taoensso.sente.*" :warn)
|
||||||
|
|
||||||
;; Use transforms (xfns) to filter and/or arbitrarily modify signals
|
;; Use transforms (xfns) to filter and/or arbitrarily modify signals
|
||||||
;; by signal data/content/etc.
|
;; by signal data/content/etc.
|
||||||
|
|
||||||
(t/set-xfn!
|
(tel/set-xfn!
|
||||||
(fn [signal]
|
(fn [signal]
|
||||||
(if (-> signal :data :skip-me?)
|
(if (-> signal :data :skip-me?)
|
||||||
nil ; Filter signal (don't handle)
|
nil ; Filter signal (don't handle)
|
||||||
(assoc signal :transformed? true))))
|
(assoc signal :transformed? true))))
|
||||||
|
|
||||||
(t/with-signal (t/event! ::my-id {:data {:skip-me? true}})) ; => nil
|
(tel/with-signal (tel/log! {... :data {:skip-me? true}})) ; => nil
|
||||||
(t/with-signal (t/event! ::my-id {:data {:skip-me? false}})) ; => {...}
|
(tel/with-signal (tel/log! {... :data {:skip-me? false}})) ; => {...}
|
||||||
|
|
||||||
;; See `t/help:filters` docstring for more filtering options
|
;; See `tel/help:filters` docstring for more filtering options
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
@ -134,13 +130,13 @@ It enables you to write code that is **information-verbose by default**.
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
;; Add your own signal handler
|
;; Add your own signal handler
|
||||||
(t/add-handler! :my-handler
|
(tel/add-handler! :my-handler
|
||||||
(fn
|
(fn
|
||||||
([signal] (println signal))
|
([signal] (println signal))
|
||||||
([] (println "Handler has shut down"))))
|
([] (println "Handler has shut down"))))
|
||||||
|
|
||||||
;; Use `add-handler!` to set handler-level filtering and back-pressure
|
;; Use `add-handler!` to set handler-level filtering and back-pressure
|
||||||
(t/add-handler! :my-handler
|
(tel/add-handler! :my-handler
|
||||||
(fn
|
(fn
|
||||||
([signal] (println signal))
|
([signal] (println signal))
|
||||||
([] (println "Handler has shut down")))
|
([] (println "Handler has shut down")))
|
||||||
|
|
@ -151,27 +147,27 @@ It enables you to write code that is **information-verbose by default**.
|
||||||
:min-level :info
|
:min-level :info
|
||||||
:ns-filter {:disallow "taoensso.*"}
|
:ns-filter {:disallow "taoensso.*"}
|
||||||
:limit {"1 per sec" [1 1000]}
|
:limit {"1 per sec" [1 1000]}
|
||||||
;; See `t/help:handler-dispatch-options` for more
|
;; See `tel/help:handler-dispatch-options` for more
|
||||||
})
|
})
|
||||||
|
|
||||||
;; See current handlers
|
;; See current handlers
|
||||||
(t/get-handlers) ; => {<handler-id> {:keys [handler-fn handler-stats_ dispatch-opts]}}
|
(tel/get-handlers) ; => {<handler-id> {:keys [handler-fn handler-stats_ dispatch-opts]}}
|
||||||
|
|
||||||
;; Add console handler to print signals as human-readable text
|
;; Add console handler to print signals as human-readable text
|
||||||
(t/add-handler! :my-handler
|
(tel/add-handler! :my-handler
|
||||||
(t/handler:console
|
(tel/handler:console
|
||||||
{:output-fn (t/format-signal-fn {})}))
|
{:output-fn (tel/format-signal-fn {})}))
|
||||||
|
|
||||||
;; Add console handler to print signals as edn
|
;; Add console handler to print signals as edn
|
||||||
(t/add-handler! :my-handler
|
(tel/add-handler! :my-handler
|
||||||
(t/handler:console
|
(tel/handler:console
|
||||||
{:output-fn (t/pr-signal-fn {:pr-fn :edn})}))
|
{:output-fn (tel/pr-signal-fn {:pr-fn :edn})}))
|
||||||
|
|
||||||
;; Add console handler to print signals as JSON
|
;; Add console handler to print signals as JSON
|
||||||
;; Ref. <https://github.com/metosin/jsonista> (or any alt JSON lib)
|
;; Ref. <https://github.com/metosin/jsonista> (or any alt JSON lib)
|
||||||
#?(:clj (require '[jsonista.core :as jsonista]))
|
#?(:clj (require '[jsonista.core :as jsonista]))
|
||||||
(t/add-handler! :my-handler
|
(tel/add-handler! :my-handler
|
||||||
(t/handler:console
|
(tel/handler:console
|
||||||
{:output-fn
|
{:output-fn
|
||||||
#?(:cljs :json ; Use js/JSON.stringify
|
#?(:cljs :json ; Use js/JSON.stringify
|
||||||
:clj jsonista/write-value-as-string)}))
|
:clj jsonista/write-value-as-string)}))
|
||||||
|
|
@ -183,34 +179,28 @@ It enables you to write code that is **information-verbose by default**.
|
||||||
|
|
||||||
### Ergonomics
|
### Ergonomics
|
||||||
|
|
||||||
- Elegant, lightweight API that's **easy to use**, **easy to configure**, and **deeply flexible**.
|
- Elegant unified API that's **easy to use** and **deeply flexible**.
|
||||||
- **Sensible defaults** to make getting started **fast and easy**.
|
- Pure **Clojure vals and fns** for easy config, composition, and REPL debugging.
|
||||||
- Extensive **beginner-oriented** [documentation][GitHub wiki], [docstrings](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere), and error messages.
|
- **Sensible defaults** to get started fast.
|
||||||
|
- **Beginner-oriented** [documentation][GitHub wiki], [docstrings](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere), and error messages.
|
||||||
|
|
||||||
### Interop
|
### Interop
|
||||||
|
|
||||||
- 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).
|
- **Interop ready** 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).
|
- [Timbre 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.
|
- Extensive set of [handlers](../../wiki/4-Handlers#included-handlers) included out-the-box.
|
||||||
|
|
||||||
### Scaling
|
### Scaling
|
||||||
|
|
||||||
- Hyper-optimized and **blazing fast**, see [performance](#performance).
|
- Rich [filtering](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:filters) by namespace, id pattern, level, level by namespace pattern, etc.
|
||||||
- An API that **scales comfortably** from the smallest disposable code, to the most massive and complex real-world production environments.
|
- Fully [configurable](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:handler-dispatch-options) **a/sync dispatch support** with per-handler [performance monitoring](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#get-handlers-stats).
|
||||||
- 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.
|
- Turn-key **sampling**, **rate limiting**, and **back-pressure monitoring**.
|
||||||
|
- Highly optimized and [blazing fast](#performance)!
|
||||||
### Flexibility
|
|
||||||
|
|
||||||
- Config via plain **Clojure vals and fns** for easy customization, composition, and REPL debugging.
|
|
||||||
- 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.
|
|
||||||
|
|
||||||
## Comparisons
|
## Comparisons
|
||||||
|
|
||||||
- Telemere [compared](../../wiki/5-Migrating#from-timbre) to [Timbre](https://www.taoensso.com/timbre) (Telemere's predecessor)
|
- 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)
|
- Telemere [compared](../../wiki/6-FAQ#how-does-telemere-compare-to-%CE%BClog) to [μ/log](https://github.com/BrunoBonacci/mulog) (structured micro-logging library)
|
||||||
|
|
||||||
## Videos
|
## Videos
|
||||||
|
|
||||||
|
|
@ -228,19 +218,21 @@ It enables you to write code that is **information-verbose by default**.
|
||||||
|
|
||||||
## API overview
|
## API overview
|
||||||
|
|
||||||
See relevant docstrings (links below) for usage info-
|
|
||||||
|
|
||||||
### Creating signals
|
### Creating signals
|
||||||
|
|
||||||
| Name | Kind | Args | Returns |
|
80% of Telemere's functionality is available through one macro: [`signal!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#signal!) and a rich set of [opts](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-options).
|
||||||
| :---------------------------------------------------------------------------------------------------------- | :--------- | :--------------- | :--------------------------- |
|
|
||||||
| [`log!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#log!) | `:log` | `?level` + `msg` | nil |
|
Use that directly, or any of the wrapper macros that you find most convenient. They're **semantically equivalent** but have ergonomics slightly tweaked for different common use cases:
|
||||||
| [`event!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#event!) | `:event` | `id` + `?level` | nil |
|
|
||||||
| [`trace!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#trace!) | `:trace` | `?id` + `run` | Form result |
|
| Name | Args | Returns |
|
||||||
| [`spy!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#spy!) | `:spy` | `?level` + `run` | Form result |
|
| :---------------------------------------------------------------------------------------------------------- | :------------------------- | :--------------------------- |
|
||||||
| [`error!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#error!) | `:error` | `?id` + `error` | Given error |
|
| [`log!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#log!) | `[opts]` or `[?level msg]` | nil |
|
||||||
| [`catch->error!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#catch-%3Eerror!) | `:error` | `?id` | Form value or given fallback |
|
| [`event!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#event!) | `[opts]` or `[id ?level]` | nil |
|
||||||
| [`signal!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#signal!) | `:generic` | `opts` | Depends on opts |
|
| [`trace!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#trace!) | `[opts]` or `[?id run]` | Form result |
|
||||||
|
| [`spy!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#spy!) | `[opts]` or `[?level run]` | Form result |
|
||||||
|
| [`error!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#error!) | `[opts]` or `[?id error]` | Given error |
|
||||||
|
| [`catch->error!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#catch-%3Eerror!) | `[opts]` or `[?id error]` | Form value or given fallback |
|
||||||
|
| [`signal!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#signal!) | `[opts]` | Depends on opts |
|
||||||
|
|
||||||
### Internal help
|
### Internal help
|
||||||
|
|
||||||
|
|
@ -322,12 +314,12 @@ See [here](../../wiki/8-Community) for community resources.
|
||||||
- [Wiki][GitHub wiki] (getting started, usage, etc.)
|
- [Wiki][GitHub wiki] (getting started, usage, etc.)
|
||||||
- API reference via [cljdoc][cljdoc]
|
- API reference via [cljdoc][cljdoc]
|
||||||
- Extensive [internal help](#internal-help) (no need to leave your IDE)
|
- Extensive [internal help](#internal-help) (no need to leave your IDE)
|
||||||
- Support via [Slack channel][] or [GitHub issues][]
|
- Support via [Slack][] 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)
|
- [General observability tips](../../wiki/7-Tips) (advice on building and maintaining observable Clojure/Script systems, and getting the most out of Telemere)
|
||||||
|
|
||||||
## Funding
|
## Funding
|
||||||
|
|
||||||
You can [help support][sponsor] continued work on this project, thank you!! 🙏
|
You can [help support][sponsor] continued work on this project and [others][my work], thank you!! 🙏
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
@ -339,10 +331,11 @@ Licensed under [EPL 1.0](LICENSE.txt) (same as Clojure).
|
||||||
[GitHub releases]: ../../releases
|
[GitHub releases]: ../../releases
|
||||||
[GitHub issues]: ../../issues
|
[GitHub issues]: ../../issues
|
||||||
[GitHub wiki]: ../../wiki
|
[GitHub wiki]: ../../wiki
|
||||||
[Slack channel]: https://www.taoensso.com/telemere/slack
|
[Slack]: https://www.taoensso.com/telemere/slack
|
||||||
|
|
||||||
[Peter Taoussanis]: https://www.taoensso.com
|
[Peter Taoussanis]: https://www.taoensso.com
|
||||||
[sponsor]: https://www.taoensso.com/sponsor
|
[sponsor]: https://www.taoensso.com/sponsor
|
||||||
|
[my work]: https://www.taoensso.com/clojure-libraries
|
||||||
|
|
||||||
<!-- Project -->
|
<!-- Project -->
|
||||||
|
|
||||||
|
|
@ -351,7 +344,9 @@ Licensed under [EPL 1.0](LICENSE.txt) (same as Clojure).
|
||||||
[Clojars SVG]: https://img.shields.io/clojars/v/com.taoensso/telemere.svg
|
[Clojars SVG]: https://img.shields.io/clojars/v/com.taoensso/telemere.svg
|
||||||
[Clojars URL]: https://clojars.org/com.taoensso/telemere
|
[Clojars URL]: https://clojars.org/com.taoensso/telemere
|
||||||
|
|
||||||
[Main tests SVG]: https://github.com/taoensso/telemere/actions/workflows/main-tests.yml/badge.svg
|
[Clj tests SVG]: https://github.com/taoensso/telemere/actions/workflows/clj-tests.yml/badge.svg
|
||||||
[Main tests URL]: https://github.com/taoensso/telemere/actions/workflows/main-tests.yml
|
[Clj tests URL]: https://github.com/taoensso/telemere/actions/workflows/clj-tests.yml
|
||||||
|
[Cljs tests SVG]: https://github.com/taoensso/telemere/actions/workflows/cljs-tests.yml/badge.svg
|
||||||
|
[Cljs tests URL]: https://github.com/taoensso/telemere/actions/workflows/cljs-tests.yml
|
||||||
[Graal tests SVG]: https://github.com/taoensso/telemere/actions/workflows/graal-tests.yml/badge.svg
|
[Graal tests SVG]: https://github.com/taoensso/telemere/actions/workflows/graal-tests.yml/badge.svg
|
||||||
[Graal tests URL]: https://github.com/taoensso/telemere/actions/workflows/graal-tests.yml
|
[Graal tests URL]: https://github.com/taoensso/telemere/actions/workflows/graal-tests.yml
|
||||||
|
|
|
||||||
|
|
@ -10,4 +10,4 @@ Please report possible security vulnerabilities [via GitHub](https://github.com/
|
||||||
|
|
||||||
Thank you!
|
Thank you!
|
||||||
|
|
||||||
\- [Peter Taoussanis](https://www.taoensso.com)
|
\- [Peter Taoussanis](https://www.taoensso.com)
|
||||||
190
examples.cljc
190
examples.cljc
|
|
@ -1,33 +1,35 @@
|
||||||
(ns examples
|
(ns examples
|
||||||
"Basic Telemere usage examples that appear in the Wiki or docstrings."
|
"Basic Telemere usage examples that appear in the Wiki or docstrings."
|
||||||
(:require [taoensso.telemere :as t]))
|
(:require [taoensso.telemere :as tel]))
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
|
|
||||||
;;;; README "Quick examples"
|
;;;; README "Quick examples"
|
||||||
|
|
||||||
(require '[taoensso.telemere :as t])
|
(require '[taoensso.telemere :as tel])
|
||||||
|
|
||||||
;; (Just works / no config necessary for typical use cases)
|
;; No config needed for typical use cases!!
|
||||||
|
;; Signals print to console by default for both Clj and Cljs
|
||||||
|
|
||||||
;; Without structured data
|
;; Traditional style logging (data formatted into message string):
|
||||||
(t/log! :info "Hello world!") ; %> Basic log signal (has message)
|
(tel/log! {:level :info, :msg (str "User " 1234 " logged in!")})
|
||||||
(t/event! ::my-id :debug) ; %> Basic event signal (just id)
|
|
||||||
|
|
||||||
;; With structured data
|
;; Modern/structured style logging (explicit id and data)
|
||||||
(t/log! {:level :info, :data {}} "Hello again!")
|
(tel/log! {:level :info, :id :auth/login, :data {:user-id 1234}})
|
||||||
(t/event! ::my-id {:level :debug, :data {}})
|
|
||||||
|
|
||||||
;; Trace (auto interops with OpenTelemetry)
|
;; Mixed style (explicit id and data, with message string)
|
||||||
|
(tel/log! {:level :info, :id :auth/login, :data {:user-id 1234}, :msg "User logged in!"})
|
||||||
|
|
||||||
|
;; Trace (can interop with OpenTelemetry)
|
||||||
;; Tracks form runtime, return value, and (nested) parent tree
|
;; Tracks form runtime, return value, and (nested) parent tree
|
||||||
(t/trace! {:id ::my-id :data {}}
|
(tel/trace! {:id ::my-id :data {...}}
|
||||||
(do-some-work))
|
(do-some-work))
|
||||||
|
|
||||||
;; Check resulting signal content for debug/tests
|
;; Check resulting signal content for debug/tests
|
||||||
(t/with-signal (t/event! ::my-id)) ; => {:keys [ns level id data msg_ ...]}
|
(tel/with-signal (tel/log! {...})) ; => {:keys [ns level id data msg_ ...]}
|
||||||
|
|
||||||
;; Getting fancy (all costs are conditional!)
|
;; Getting fancy (all costs are conditional!)
|
||||||
(t/log!
|
(tel/log!
|
||||||
{:level :debug
|
{:level :debug
|
||||||
:sample 0.75 ; 75% sampling (noop 25% of the time)
|
:sample 0.75 ; 75% sampling (noop 25% of the time)
|
||||||
:when (my-conditional)
|
:when (my-conditional)
|
||||||
|
|
@ -47,42 +49,45 @@
|
||||||
|
|
||||||
;; Message string or vector to join as string
|
;; Message string or vector to join as string
|
||||||
["Something interesting happened!" formatted])
|
["Something interesting happened!" formatted])
|
||||||
|
)
|
||||||
|
|
||||||
;; Set minimum level
|
;; Set minimum level
|
||||||
(t/set-min-level! :warn) ; For all signals
|
(tel/set-min-level! :warn) ; For all signals
|
||||||
(t/set-min-level! :log :debug) ; For `log!` signals only
|
(tel/set-min-level! :log :debug) ; For `log!` signals specifically
|
||||||
|
|
||||||
;; Set namespace and id filters
|
;; Set id and namespace filters
|
||||||
(t/set-ns-filter! {:disallow "taoensso.*" :allow "taoensso.sente.*"})
|
(tel/set-id-filter! {:allow #{::my-particular-id "my-app/*"}})
|
||||||
(t/set-id-filter! {:allow #{::my-particular-id "my-app/*"}})
|
(tel/set-ns-filter! {:disallow "taoensso.*" :allow "taoensso.sente.*"})
|
||||||
|
|
||||||
;; Set minimum level for `event!` signals for particular ns pattern
|
;; SLF4J signals will have their `:ns` key set to the logger's name
|
||||||
(t/set-min-level! :event "taoensso.sente.*" :warn)
|
;; (typically a source class)
|
||||||
|
(tel/set-ns-filter! {:disallow "com.noisy.java.package.*"})
|
||||||
|
|
||||||
|
;; Set minimum level for `log!` signals for particular ns pattern
|
||||||
|
(tel/set-min-level! :log "taoensso.sente.*" :warn)
|
||||||
|
|
||||||
;; Use transforms (xfns) to filter and/or arbitrarily modify signals
|
;; Use transforms (xfns) to filter and/or arbitrarily modify signals
|
||||||
;; by signal data/content/etc.
|
;; by signal data/content/etc.
|
||||||
|
|
||||||
(t/set-xfn!
|
(tel/set-xfn!
|
||||||
(fn [signal]
|
(fn [signal]
|
||||||
(if (-> signal :data :skip-me?)
|
(if (-> signal :data :skip-me?)
|
||||||
nil ; Filter signal (don't handle)
|
nil ; Filter signal (don't handle)
|
||||||
(assoc signal :transformed? true))))
|
(assoc signal :transformed? true))))
|
||||||
|
|
||||||
(t/with-signal (t/event! ::my-id {:data {:skip-me? true}})) ; => nil
|
(tel/with-signal (tel/log! {... :data {:skip-me? true}})) ; => nil
|
||||||
(t/with-signal (t/event! ::my-id {:data {:skip-me? false}})) ; => {...}
|
(tel/with-signal (tel/log! {... :data {:skip-me? false}})) ; => {...}
|
||||||
|
|
||||||
;; See `t/help:filters` docstring for more filtering options
|
;; See `tel/help:filters` docstring for more filtering options
|
||||||
|
|
||||||
;;;; README "More examples"
|
|
||||||
|
|
||||||
;; Add your own signal handler
|
;; Add your own signal handler
|
||||||
(t/add-handler! :my-handler
|
(tel/add-handler! :my-handler
|
||||||
(fn
|
(fn
|
||||||
([signal] (println signal))
|
([signal] (println signal))
|
||||||
([] (println "Handler has shut down"))))
|
([] (println "Handler has shut down"))))
|
||||||
|
|
||||||
;; Use `add-handler!` to set handler-level filtering and back-pressure
|
;; Use `add-handler!` to set handler-level filtering and back-pressure
|
||||||
(t/add-handler! :my-handler
|
(tel/add-handler! :my-handler
|
||||||
(fn
|
(fn
|
||||||
([signal] (println signal))
|
([signal] (println signal))
|
||||||
([] (println "Handler has shut down")))
|
([] (println "Handler has shut down")))
|
||||||
|
|
@ -93,79 +98,79 @@
|
||||||
:min-level :info
|
:min-level :info
|
||||||
:ns-filter {:disallow "taoensso.*"}
|
:ns-filter {:disallow "taoensso.*"}
|
||||||
:limit {"1 per sec" [1 1000]}
|
:limit {"1 per sec" [1 1000]}
|
||||||
;; See `t/help:handler-dispatch-options` for more
|
;; See `tel/help:handler-dispatch-options` for more
|
||||||
})
|
})
|
||||||
|
|
||||||
;; See current handlers
|
;; See current handlers
|
||||||
(t/get-handlers) ; => {<handler-id> {:keys [handler-fn handler-stats_ dispatch-opts]}}
|
(tel/get-handlers) ; => {<handler-id> {:keys [handler-fn handler-stats_ dispatch-opts]}}
|
||||||
|
|
||||||
;; Add console handler to print signals as human-readable text
|
;; Add console handler to print signals as human-readable text
|
||||||
(t/add-handler! :my-handler
|
(tel/add-handler! :my-handler
|
||||||
(t/handler:console
|
(tel/handler:console
|
||||||
{:output-fn (t/format-signal-fn {})}))
|
{:output-fn (tel/format-signal-fn {})}))
|
||||||
|
|
||||||
;; Add console handler to print signals as edn
|
;; Add console handler to print signals as edn
|
||||||
(t/add-handler! :my-handler
|
(tel/add-handler! :my-handler
|
||||||
(t/handler:console
|
(tel/handler:console
|
||||||
{:output-fn (t/pr-signal-fn {:pr-fn :edn})}))
|
{:output-fn (tel/pr-signal-fn {:pr-fn :edn})}))
|
||||||
|
|
||||||
;; Add console handler to print signals as JSON
|
;; Add console handler to print signals as JSON
|
||||||
;; Ref. <https://github.com/metosin/jsonista> (or any alt JSON lib)
|
;; Ref. <https://github.com/metosin/jsonista> (or any alt JSON lib)
|
||||||
#?(:clj (require '[jsonista.core :as jsonista]))
|
#?(:clj (require '[jsonista.core :as jsonista]))
|
||||||
(t/add-handler! :my-handler
|
(tel/add-handler! :my-handler
|
||||||
(t/handler:console
|
(tel/handler:console
|
||||||
{:output-fn
|
{:output-fn
|
||||||
#?(:cljs :json ; Use js/JSON.stringify
|
#?(:cljs :json ; Use js/JSON.stringify
|
||||||
:clj jsonista/write-value-as-string)}))
|
:clj jsonista/write-value-as-string)}))
|
||||||
|
|
||||||
;;;; Docstring examples
|
;;;; Docstring examples
|
||||||
|
|
||||||
(t/with-signal (t/event! ::my-id))
|
(tel/with-signal (tel/event! ::my-id))
|
||||||
(t/with-signal (t/event! ::my-id :warn))
|
(tel/with-signal (tel/event! ::my-id :warn))
|
||||||
(t/with-signal
|
(tel/with-signal
|
||||||
(t/event! ::my-id
|
(tel/event! ::my-id
|
||||||
{:let [x "x"] ; Available to `:data` and `:msg`
|
{:let [x "x"] ; Available to `:data` and `:msg`
|
||||||
:data {:x x}
|
:data {:x x}
|
||||||
:msg ["My msg:" x]}))
|
:msg ["My msg:" x]}))
|
||||||
|
|
||||||
(t/with-signal (t/log! "My msg"))
|
(tel/with-signal (tel/log! "My msg"))
|
||||||
(t/with-signal (t/log! :warn "My msg"))
|
(tel/with-signal (tel/log! :warn "My msg"))
|
||||||
(t/with-signal
|
(tel/with-signal
|
||||||
(t/log!
|
(tel/log!
|
||||||
{:let [x "x"] ; Available to `:data` and `:msg`
|
{:let [x "x"] ; Available to `:data` and `:msg`
|
||||||
:data {:x x}}
|
:data {:x x}}
|
||||||
["My msg:" x]))
|
["My msg:" x]))
|
||||||
|
|
||||||
(t/with-signal (throw (t/error! (ex-info "MyEx" {}))))
|
(tel/with-signal (throw (tel/error! (ex-info "MyEx" {}))))
|
||||||
(t/with-signal (throw (t/error! ::my-id (ex-info "MyEx" {}))))
|
(tel/with-signal (throw (tel/error! ::my-id (ex-info "MyEx" {}))))
|
||||||
(t/with-signal
|
(tel/with-signal
|
||||||
(throw
|
(throw
|
||||||
(t/error!
|
(tel/error!
|
||||||
{:let [x "x"] ; Available to `:data` and `:msg`
|
{:let [x "x"] ; Available to `:data` and `:msg`
|
||||||
:data {:x x}
|
:data {:x x}
|
||||||
:msg ["My msg:" x]}
|
:msg ["My msg:" x]}
|
||||||
(ex-info "MyEx" {}))))
|
(ex-info "MyEx" {}))))
|
||||||
|
|
||||||
(t/with-signal (t/trace! (+ 1 2)))
|
(tel/with-signal (tel/trace! (+ 1 2)))
|
||||||
(t/with-signal (t/trace! ::my-id (+ 1 2)))
|
(tel/with-signal (tel/trace! ::my-id (+ 1 2)))
|
||||||
(t/with-signal
|
(tel/with-signal
|
||||||
(t/trace!
|
(tel/trace!
|
||||||
{:let [x "x"] ; Available to `:data` and `:msg`
|
{:let [x "x"] ; Available to `:data` and `:msg`
|
||||||
:data {:x x}}
|
:data {:x x}}
|
||||||
(+ 1 2)))
|
(+ 1 2)))
|
||||||
|
|
||||||
(t/with-signal (t/spy! (+ 1 2)))
|
(tel/with-signal (tel/spy! (+ 1 2)))
|
||||||
(t/with-signal (t/spy! :debug (+ 1 2)))
|
(tel/with-signal (tel/spy! :debug (+ 1 2)))
|
||||||
(t/with-signal
|
(tel/with-signal
|
||||||
(t/spy!
|
(tel/spy!
|
||||||
{:let [x "x"] ; Available to `:data` and `:msg`
|
{:let [x "x"] ; Available to `:data` and `:msg`
|
||||||
:data {:x x}}
|
:data {:x x}}
|
||||||
(+ 1 2)))
|
(+ 1 2)))
|
||||||
|
|
||||||
(t/with-signal (t/catch->error! (/ 1 0)))
|
(tel/with-signal (tel/catch->error! (/ 1 0)))
|
||||||
(t/with-signal (t/catch->error! ::my-id (/ 1 0)))
|
(tel/with-signal (tel/catch->error! ::my-id (/ 1 0)))
|
||||||
(t/with-signal
|
(tel/with-signal
|
||||||
(t/catch->error!
|
(tel/catch->error!
|
||||||
{:let [x "x"] ; Available to `:data` and `:msg`
|
{:let [x "x"] ; Available to `:data` and `:msg`
|
||||||
:data {:x x}
|
:data {:x x}
|
||||||
:msg ["My msg:" x]
|
:msg ["My msg:" x]
|
||||||
|
|
@ -176,25 +181,25 @@
|
||||||
|
|
||||||
;;; Filter signals
|
;;; Filter signals
|
||||||
|
|
||||||
(t/set-min-level! :info) ; Set global minimum level
|
(tel/set-min-level! :info) ; Set global minimum level
|
||||||
(t/with-signal (t/event! ::my-id1 :info)) ; => {:keys [inst id ...]}
|
(tel/with-signal (tel/event! ::my-id1 :info)) ; => {:keys [inst id ...]}
|
||||||
(t/with-signal (t/event! ::my-id1 :debug)) ; => nil (signal not allowed)
|
(tel/with-signal (tel/event! ::my-id1 :debug)) ; => nil (signal not allowed)
|
||||||
|
|
||||||
(t/with-min-level :trace ; Override global minimum level
|
(tel/with-min-level :trace ; Override global minimum level
|
||||||
(t/with-signal (t/event! ::my-id1 :debug))) ; => {:keys [inst id ...]}
|
(tel/with-signal (tel/event! ::my-id1 :debug))) ; => {:keys [inst id ...]}
|
||||||
|
|
||||||
;; Disallow all signals in matching namespaces
|
;; Disallow all signals in matching namespaces
|
||||||
(t/set-ns-filter! {:disallow "some.nosy.namespace.*"})
|
(tel/set-ns-filter! {:disallow "some.nosy.namespace.*"})
|
||||||
|
|
||||||
;;; Configuring handlers
|
;;; Configuring handlers
|
||||||
|
|
||||||
;; Create a test signal
|
;; Create a test signal
|
||||||
(def my-signal
|
(def my-signal
|
||||||
(t/with-signal
|
(tel/with-signal
|
||||||
(t/log! {:id ::my-id, :data {:x1 :x2}} "My message")))
|
(tel/log! {:id ::my-id, :data {:x1 :x2}} "My message")))
|
||||||
|
|
||||||
;; Create console handler with default opts (writes formatted string)
|
;; Create console handler with default opts (writes formatted string)
|
||||||
(def my-handler (t/handler:console {}))
|
(def my-handler (tel/handler:console {}))
|
||||||
|
|
||||||
;; Test handler, remember it's just a (fn [signal])
|
;; Test handler, remember it's just a (fn [signal])
|
||||||
(my-handler my-signal) ; %>
|
(my-handler my-signal) ; %>
|
||||||
|
|
@ -203,8 +208,8 @@
|
||||||
|
|
||||||
;; Create console handler which writes signals as edn
|
;; Create console handler which writes signals as edn
|
||||||
(def my-handler
|
(def my-handler
|
||||||
(t/handler:console
|
(tel/handler:console
|
||||||
{:output-fn (t/pr-signal-fn {:pr-fn :edn})}))
|
{:output-fn (tel/pr-signal-fn {:pr-fn :edn})}))
|
||||||
|
|
||||||
(my-handler my-signal) ; %>
|
(my-handler my-signal) ; %>
|
||||||
;; {:inst #inst "2024-04-11T10:54:57.202869Z", :msg_ "My message", :ns "examples", ...}
|
;; {:inst #inst "2024-04-11T10:54:57.202869Z", :msg_ "My message", :ns "examples", ...}
|
||||||
|
|
@ -213,9 +218,9 @@
|
||||||
;; Ref. <https://github.com/metosin/jsonista> (or any alt JSON lib)
|
;; Ref. <https://github.com/metosin/jsonista> (or any alt JSON lib)
|
||||||
#?(:clj (require '[jsonista.core :as jsonista]))
|
#?(:clj (require '[jsonista.core :as jsonista]))
|
||||||
(def my-handler
|
(def my-handler
|
||||||
(t/handler:console
|
(tel/handler:console
|
||||||
{:output-fn
|
{:output-fn
|
||||||
(t/pr-signal-fn
|
(tel/pr-signal-fn
|
||||||
{:pr-fn
|
{:pr-fn
|
||||||
#?(:cljs :json ; Use js/JSON.stringify
|
#?(:cljs :json ; Use js/JSON.stringify
|
||||||
:clj jsonista/write-value-as-string)})}))
|
:clj jsonista/write-value-as-string)})}))
|
||||||
|
|
@ -224,19 +229,20 @@
|
||||||
;; {"inst":"2024-04-11T10:54:57.202869Z","msg_":"My message","ns":"examples", ...}
|
;; {"inst":"2024-04-11T10:54:57.202869Z","msg_":"My message","ns":"examples", ...}
|
||||||
|
|
||||||
;; Deregister the default console handler
|
;; Deregister the default console handler
|
||||||
(t/remove-handler! :default/console)
|
(tel/remove-handler! :defaultel/console)
|
||||||
|
|
||||||
;; Register our custom console handler
|
;; Register our custom console handler
|
||||||
(t/add-handler! :my-handler my-handler
|
(tel/add-handler! :my-handler my-handler
|
||||||
;; Lots of options here for filtering, etc.
|
;; Lots of options here for filtering, etc.
|
||||||
;; See `help:handler-dispatch-options` docstring!
|
;; See `help:handler-dispatch-options` docstring!
|
||||||
{})
|
{})
|
||||||
|
|
||||||
;; NB make sure to always stop handlers at the end of your
|
;; NB make sure to always stop handlers at the end of your
|
||||||
;; `-main` or shutdown procedure
|
;; `-main` or shutdown procedure
|
||||||
(t/call-on-shutdown! t/stop-handlers!)
|
(tel/call-on-shutdown!
|
||||||
|
(fn [] (tel/stop-handlers!)))
|
||||||
|
|
||||||
;; See `t/help:handlers` docstring for more
|
;; See `tel/help:handlers` docstring for more
|
||||||
|
|
||||||
;;; Writing handlers
|
;;; Writing handlers
|
||||||
|
|
||||||
|
|
@ -298,18 +304,18 @@
|
||||||
;;; Message building
|
;;; Message building
|
||||||
|
|
||||||
;; A fixed message (string arg)
|
;; A fixed message (string arg)
|
||||||
(t/log! "A fixed message") ; %> {:msg "A fixed message"}
|
(tel/log! "A fixed message") ; %> {:msg "A fixed message"}
|
||||||
|
|
||||||
;; A joined message (vector arg)
|
;; A joined message (vector arg)
|
||||||
(let [user-arg "Bob"]
|
(let [user-arg "Bob"]
|
||||||
(t/log! ["User" (str "`" user-arg "`") "just logged in!"]))
|
(tel/log! ["User" (str "`" user-arg "`") "just logged in!"]))
|
||||||
;; %> {:msg_ "User `Bob` just logged in!` ...}
|
;; %> {:msg_ "User `Bob` just logged in!` ...}
|
||||||
|
|
||||||
;; With arg prep
|
;; With arg prep
|
||||||
(let [user-arg "Bob"
|
(let [user-arg "Bob"
|
||||||
usd-balance-str "22.4821"]
|
usd-balance-str "22.4821"]
|
||||||
|
|
||||||
(t/log!
|
(tel/log!
|
||||||
{:let
|
{:let
|
||||||
[username (clojure.string/upper-case user-arg)
|
[username (clojure.string/upper-case user-arg)
|
||||||
usd-balance (parse-double usd-balance-str)]
|
usd-balance (parse-double usd-balance-str)]
|
||||||
|
|
@ -322,16 +328,16 @@
|
||||||
|
|
||||||
;; %> {:msg "User BOB has balance: $22" ...}
|
;; %> {:msg "User BOB has balance: $22" ...}
|
||||||
|
|
||||||
(t/log! (str "This message " "was built " "by `str`"))
|
(tel/log! (str "This message " "was built " "by `str`"))
|
||||||
;; %> {:msg "This message was built by `str`"}
|
;; %> {:msg "This message was built by `str`"}
|
||||||
|
|
||||||
(t/log! (enc/format "This message was built by `%s`" "format"))
|
(tel/log! (enc/format "This message was built by `%s`" "format"))
|
||||||
;; %> {:msg "This message was built by `format`"}
|
;; %> {:msg "This message was built by `format`"}
|
||||||
|
|
||||||
;;; App-level kvs
|
;;; App-level kvs
|
||||||
|
|
||||||
(t/with-signal
|
(tel/with-signal
|
||||||
(t/event! ::my-id
|
(tel/event! ::my-id
|
||||||
{:my-data-for-xfn "foo"
|
{:my-data-for-xfn "foo"
|
||||||
:my-data-for-handler "bar"}))
|
:my-data-for-handler "bar"}))
|
||||||
|
|
||||||
|
|
@ -346,20 +352,20 @@
|
||||||
|
|
||||||
;;;; Misc extra examples
|
;;;; Misc extra examples
|
||||||
|
|
||||||
(t/log! {:id ::my-id, :data {:x1 :x2}} ["My 2-part" "message"]) ; %>
|
(tel/log! {:id ::my-id, :data {:x1 :x2}} ["My 2-part" "message"]) ; %>
|
||||||
;; 2024-04-11T10:54:57.202869Z INFO LOG MyHost examples(56,1) ::my-id - My 2-part message
|
;; 2024-04-11T10:54:57.202869Z INFO LOG MyHost examples(56,1) ::my-id - My 2-part message
|
||||||
;; data: {:x1 :x2}
|
;; data: {:x1 :x2}
|
||||||
|
|
||||||
;; `:let` bindings are available to `:data` and message, but only paid
|
;; `:let` bindings are available to `:data` and message, but only paid
|
||||||
;; for when allowed by minimum level and other filtering criteria
|
;; for when allowed by minimum level and other filtering criteria
|
||||||
(t/log!
|
(tel/log!
|
||||||
{:level :info
|
{:level :info
|
||||||
:let [expensive (reduce * (range 1 12))] ; 12 factorial
|
:let [expensive (reduce * (range 1 12))] ; 12 factorial
|
||||||
:data {:my-metric expensive}}
|
:data {:my-metric expensive}}
|
||||||
["Message with metric:" expensive])
|
["Message with metric:" expensive])
|
||||||
|
|
||||||
;; With sampling 50% and 1/sec rate limiting
|
;; With sampling 50% and 1/sec rate limiting
|
||||||
(t/log!
|
(tel/log!
|
||||||
{:sample 0.5
|
{:sample 0.5
|
||||||
:limit {"1 per sec" [1 1000]}}
|
:limit {"1 per sec" [1 1000]}}
|
||||||
"This signal will be sampled and rate limited")
|
"This signal will be sampled and rate limited")
|
||||||
|
|
@ -368,8 +374,8 @@
|
||||||
;; All offer the same options, but each has an API optimized
|
;; All offer the same options, but each has an API optimized
|
||||||
;; for a particular use case:
|
;; for a particular use case:
|
||||||
|
|
||||||
(t/log! {:level :info, :id ::my-id} "Hi!") ; [msg] or [level-or-opts msg]
|
(tel/log! {:level :info, :id ::my-id} "Hi!") ; [msg] or [level-or-opts msg]
|
||||||
(t/event! ::my-id {:level :info, :msg "Hi!"}) ; [id] or [id level-or-opts]
|
(tel/event! ::my-id {:level :info, :msg "Hi!"}) ; [id] or [id level-or-opts]
|
||||||
(t/signal! {:level :info, :id ::my-id, :msg "Hi!"}) ; [opts]
|
(tel/signal! {:level :info, :id ::my-id, :msg "Hi!"}) ; [opts]
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 384 KiB After Width: | Height: | Size: 340 KiB |
Binary file not shown.
|
|
@ -1,4 +1,4 @@
|
||||||
(defproject com.taoensso/telemere "1.0.0-RC5"
|
(defproject com.taoensso/telemere "1.2.1"
|
||||||
:author "Peter Taoussanis <https://www.taoensso.com>"
|
:author "Peter Taoussanis <https://www.taoensso.com>"
|
||||||
:description "Structured logs and telemetry for Clojure/Script"
|
:description "Structured logs and telemetry for Clojure/Script"
|
||||||
:url "https://www.taoensso.com/telemere"
|
:url "https://www.taoensso.com/telemere"
|
||||||
|
|
@ -10,15 +10,15 @@
|
||||||
:scm {:name "git" :url "https://github.com/taoensso/telemere"}
|
:scm {:name "git" :url "https://github.com/taoensso/telemere"}
|
||||||
|
|
||||||
:dependencies
|
:dependencies
|
||||||
[[com.taoensso/encore "3.139.0"]]
|
[[com.taoensso/encore "3.159.0"]]
|
||||||
|
|
||||||
:test-paths ["test" #_"src"]
|
:test-paths ["test" #_"src"]
|
||||||
|
|
||||||
:profiles
|
:profiles
|
||||||
{;; :default [:base :system :user :provided :dev]
|
{;; :default [:base :system :user :provided :dev]
|
||||||
:provided {:dependencies [[org.clojure/clojurescript "1.11.132"]
|
:provided {:dependencies [[org.clojure/clojurescript "1.12.134"]
|
||||||
[org.clojure/clojure "1.11.4"]]}
|
[org.clojure/clojure "1.11.4"]]}
|
||||||
:c1.12 {:dependencies [[org.clojure/clojure "1.12.0"]]}
|
:c1.12 {:dependencies [[org.clojure/clojure "1.12.3"]]}
|
||||||
:c1.11 {:dependencies [[org.clojure/clojure "1.11.4"]]}
|
:c1.11 {:dependencies [[org.clojure/clojure "1.11.4"]]}
|
||||||
:c1.10 {:dependencies [[org.clojure/clojure "1.10.3"]]}
|
:c1.10 {:dependencies [[org.clojure/clojure "1.10.3"]]}
|
||||||
|
|
||||||
|
|
@ -46,19 +46,19 @@
|
||||||
*unchecked-math* false #_:warn-on-boxed}
|
*unchecked-math* false #_:warn-on-boxed}
|
||||||
|
|
||||||
:dependencies
|
:dependencies
|
||||||
[[org.clojure/core.async "1.7.701"]
|
[[org.clojure/core.async "1.8.741"]
|
||||||
[org.clojure/test.check "1.1.1"]
|
[org.clojure/test.check "1.1.2"]
|
||||||
[org.clojure/tools.logging "1.3.0"]
|
[org.clojure/tools.logging "1.3.0"]
|
||||||
[org.slf4j/slf4j-api "2.0.17"]
|
[org.slf4j/slf4j-api "2.0.17"]
|
||||||
[com.taoensso/telemere-slf4j "1.0.0-RC5"]
|
[com.taoensso/telemere-slf4j "1.2.1"]
|
||||||
#_[org.slf4j/slf4j-simple "2.0.16"]
|
#_[org.slf4j/slf4j-simple "2.0.16"]
|
||||||
#_[org.slf4j/slf4j-nop "2.0.16"]
|
#_[org.slf4j/slf4j-nop "2.0.16"]
|
||||||
#_[io.github.paintparty/bling "0.4.2"]
|
#_[io.github.paintparty/bling "0.4.2"]
|
||||||
|
|
||||||
;;; For optional handlers
|
;;; For optional handlers
|
||||||
[io.opentelemetry/opentelemetry-api "1.48.0"]
|
[io.opentelemetry/opentelemetry-api "1.57.0"]
|
||||||
[io.opentelemetry/opentelemetry-sdk-extension-autoconfigure "1.48.0"]
|
[io.opentelemetry/opentelemetry-sdk-extension-autoconfigure "1.57.0"]
|
||||||
[io.opentelemetry/opentelemetry-exporter-otlp "1.48.0"]
|
[io.opentelemetry/opentelemetry-exporter-otlp "1.57.0"]
|
||||||
#_[io.opentelemetry/opentelemetry-exporters-jaeger "0.9.1"]
|
#_[io.opentelemetry/opentelemetry-exporters-jaeger "0.9.1"]
|
||||||
[metosin/jsonista "0.3.13"]
|
[metosin/jsonista "0.3.13"]
|
||||||
[com.draines/postal "2.0.5"]
|
[com.draines/postal "2.0.5"]
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,17 @@
|
||||||
Signals are maps with {:keys [inst id ns level data msg_ ...]}, though they
|
Telemere signals are maps with {:keys [inst id ns level data msg_ ...]},
|
||||||
can be modified by call and/or handler transform (xfns).
|
though they can be modified by call and/or handler transform (xfns).
|
||||||
|
|
||||||
Default signal keys:
|
Default signal keys:
|
||||||
|
|
||||||
`:schema` ------ Int version of signal schema (current: 1)
|
`:schema` ------ Int version of signal schema (current: 1)
|
||||||
`:inst` -------- Platform instant [1] when signal was created, monotonicity depends on system clock
|
`:inst` -------- Platform instant [1] when signal was created, monotonicity depends on system clock
|
||||||
`:level` ------- Signal level ∈ #{<int> :trace :debug :info :warn :error :fatal :report ...}
|
`:ns` ---------- ?str namespace of signal callsite
|
||||||
|
`:coords` ------ ?[line column] of signal callsite
|
||||||
|
|
||||||
`:kind` -------- Signal ?kind ∈ #{nil :event :error :log :trace :spy :slf4j :tools-logging <app-val> ...}
|
`:kind` -------- Signal ?kind ∈ #{nil :event :error :log :trace :spy :slf4j :tools-logging <app-val> ...}
|
||||||
`:id` ---------- ?id of signal (common to all signals created at callsite, contrast with `:uid`)
|
`:level` ------- Signal level ∈ #{<int> :trace :debug :info :warn :error :fatal :report ...}
|
||||||
`:uid` --------- ?id of signal instance (unique to each signal created at callsite when tracing, contrast with `:id`)
|
`:id` ---------- Signal callsite ?id (usu. keyword) (common to all signals created at callsite, contrast with `:uid`)
|
||||||
|
`:uid` --------- Signal instance ?id (usu. string) (unique to each signal created at callsite when tracing, contrast with `:id`)
|
||||||
|
|
||||||
`:msg_` -------- Arb app-level message ?str given to signal creator - may be a delay, always use `force` to unwrap!
|
`:msg_` -------- Arb app-level message ?str given to signal creator - may be a delay, always use `force` to unwrap!
|
||||||
`:data` -------- Arb app-level data ?val (usu. a map) given to signal creator
|
`:data` -------- Arb app-level data ?val (usu. a map) given to signal creator
|
||||||
|
|
@ -23,9 +26,6 @@ Default signal keys:
|
||||||
`:root` -------- ?{:keys [id uid]} of root signal, present in nested signals when tracing
|
`:root` -------- ?{:keys [id uid]} of root signal, present in nested signals when tracing
|
||||||
`:ctx` --------- ?val of `*ctx*` (arb app-level state) when signal was created
|
`:ctx` --------- ?val of `*ctx*` (arb app-level state) when signal was created
|
||||||
|
|
||||||
`:ns` ---------- ?str namespace of signal callsite
|
|
||||||
`:coords` ------ ?[line column] of signal callsite
|
|
||||||
|
|
||||||
`:host` -------- (Clj only) {:keys [name ip]} info for network host
|
`:host` -------- (Clj only) {:keys [name ip]} info for network host
|
||||||
`:thread` ------ (Clj only) {:keys [name id group]} info for thread that created signal
|
`:thread` ------ (Clj only) {:keys [name id group]} info for thread that created signal
|
||||||
|
|
||||||
|
|
@ -11,8 +11,8 @@ various keys:
|
||||||
- All signal creators offer the same options [2], and
|
- All signal creators offer the same options [2], and
|
||||||
- All signal kinds can contain the same content [3]
|
- All signal kinds can contain the same content [3]
|
||||||
|
|
||||||
Creators vary only in in their default options and call APIs (expected args
|
Creators vary only in in their default `:kind` value and call APIs (expected
|
||||||
and return values), making them more/less convenient for certain use cases:
|
args and return values), making them more/less convenient for certain use cases:
|
||||||
|
|
||||||
`log!` ------------- ?level + msg => nil
|
`log!` ------------- ?level + msg => nil
|
||||||
`event!` ----------- id + ?level => nil
|
`event!` ----------- id + ?level => nil
|
||||||
|
|
@ -9,12 +9,12 @@ All options are available for all signal creator calls:
|
||||||
Defaults to `:auto` for tracing signals, and nil otherwise
|
Defaults to `:auto` for tracing signals, and nil otherwise
|
||||||
|
|
||||||
`:msg` --------- Arb app-level ?message to incl. in signal: str or vec of strs to join (with `\space`), may be a delay
|
`:msg` --------- Arb app-level ?message to incl. in signal: str or vec of strs to join (with `\space`), may be a delay
|
||||||
`:data` -------- Arb app-level ?data to incl. in signal: usu. a map
|
`:data` -------- Arb app-level ?data to incl. in signal: usu. a map, LAZY! [3]
|
||||||
`:error` ------- Arb app-level ?error to incl. in signal: platform error [2]
|
`:error` ------- Arb app-level ?error to incl. in signal: platform error [2]
|
||||||
|
|
||||||
`:run` --------- ?form to execute UNCONDITIONALLY; will incl. `:run-val` in signal
|
`:run` --------- ?form to execute UNCONDITIONALLY; will incl. `:run-val` in signal
|
||||||
`:do` ---------- ?form to execute conditionally (iff signal allowed), before establishing `:let` ?binding
|
`:do` ---------- ?form to execute conditionally (iff signal allowed) and LAZILY [3], before establishing `:let` ?binding
|
||||||
`:let` --------- ?bindings to establish conditionally (iff signal allowed), BEFORE evaluating `:data` and `:msg` (useful!)
|
`:let` --------- ?bindings to establish conditionally (iff signal allowed) and LAZILY [3], BEFORE evaluating `:data` and `:msg` (useful!)
|
||||||
|
|
||||||
`:parent` ------ Custom ?{:keys [id uid]} to override auto (dynamic) parent signal tracing info
|
`:parent` ------ Custom ?{:keys [id uid]} to override auto (dynamic) parent signal tracing info
|
||||||
`:root` -------- Custom ?{:keys [id uid]} to override auto (dynamic) root signal tracing info
|
`:root` -------- Custom ?{:keys [id uid]} to override auto (dynamic) root signal tracing info
|
||||||
|
|
@ -24,10 +24,11 @@ All options are available for all signal creator calls:
|
||||||
`:ns` ---------- Custom ?str namespace to override auto signal callsite info
|
`:ns` ---------- Custom ?str namespace to override auto signal callsite info
|
||||||
`:coords` ------ Custom ?[line column] to override auto signal callsite info
|
`:coords` ------ Custom ?[line column] to override auto signal callsite info
|
||||||
|
|
||||||
`:elidable?` --- Should signal be subject to compile-time elision? (Default: true)
|
`:elidable?` --- Should signal be subject to compile-time elision? (default true)
|
||||||
|
`:allow?` ------ Custom override for usual runtime filtering (true => ALWAYS create signal)
|
||||||
`:trace?` ------ Should tracing be enabled for `:run` form?
|
`:trace?` ------ Should tracing be enabled for `:run` form?
|
||||||
|
|
||||||
`:sample` ------ Sample ?rate ∈ℝ[0,1] for signal sampling (0.75 => allow 75% of signals, nil => allow all)
|
`:sample` ------ Sample ?rate ∈ℝ[0,1] for random signal sampling (0.75 => allow 75% of signals, nil => allow all)
|
||||||
`:when` -------- Arb ?form; when present, form must return truthy to allow signal
|
`:when` -------- Arb ?form; when present, form must return truthy to allow signal
|
||||||
`:limit` ------- Rate limit ?spec given to `taoensso.telemere/rate-limiter`, see its docstring for details
|
`:limit` ------- Rate limit ?spec given to `taoensso.telemere/rate-limiter`, see its docstring for details
|
||||||
`:limit-by` ---- When present, rate limits will be enforced independently for each value (any Clojure value!)
|
`:limit-by` ---- When present, rate limits will be enforced independently for each value (any Clojure value!)
|
||||||
|
|
@ -36,9 +37,13 @@ All options are available for all signal creator calls:
|
||||||
|
|
||||||
<kvs> ---------- Other arb app-level ?kvs to incl. in signal. Typically NOT included in
|
<kvs> ---------- Other arb app-level ?kvs to incl. in signal. Typically NOT included in
|
||||||
handler output, so a great way to provide custom data/opts for use
|
handler output, so a great way to provide custom data/opts for use
|
||||||
(only) by custom transforms/handlers.
|
(only) by custom transforms/handlers. LAZY! [3]
|
||||||
|
|
||||||
If anything is unclear, please ping me (@ptaoussanis) so that I can improve these docs!
|
If anything is unclear, please ping me (@ptaoussanis) so that I can improve these docs!
|
||||||
|
|
||||||
[1] `java.time.Instant` or `js/Date`
|
[1] `java.time.Instant` or `js/Date`
|
||||||
[2] `java.lang.Throwable` or `js/Error`
|
[2] `java.lang.Throwable` or `js/Error`
|
||||||
|
[3] Most Telemere signal content is evaluated CONDITIONALLY (iff signal allowed),
|
||||||
|
LAZILY (when signal is created), and on the HANDLING THREAD (not logging thread).
|
||||||
|
This allows efficient filtering, better control+monitoring of back pressure,
|
||||||
|
conditional effects, etc. Ref. <https://www.taoensso.com/telemere/flow> for visual!
|
||||||
|
|
@ -31,50 +31,54 @@
|
||||||
(remove-ns (symbol (str *ns*)))
|
(remove-ns (symbol (str *ns*)))
|
||||||
(:api (enc/interns-overview)))
|
(:api (enc/interns-overview)))
|
||||||
|
|
||||||
(enc/assert-min-encore-version [3 139 0])
|
(enc/assert-min-encore-version [3 159 0])
|
||||||
|
|
||||||
;;;; Shared signal API
|
;;;; Shared signal API
|
||||||
|
|
||||||
(declare ; Needed to avoid `clj-kondo` "Unresolved var" warnings
|
(declare ; Needed to avoid `clj-kondo` "Unresolved var" warnings
|
||||||
level-aliases
|
level-aliases
|
||||||
help:filters help:handler help:handler-dispatch-options
|
help:filters help:handlers help:handler-dispatch-options
|
||||||
get-filters get-min-levels get-handlers get-handlers-stats
|
get-filters get-min-levels get-handlers get-handlers-stats
|
||||||
|
|
||||||
#?(:clj without-filters)
|
#?(:clj without-filters)
|
||||||
set-kind-filter! #?(:clj with-kind-filter)
|
set-kind-filter! #?(:clj with-kind-filter)
|
||||||
set-ns-filter! #?(:clj with-ns-filter)
|
set-ns-filter! #?(:clj with-ns-filter)
|
||||||
set-id-filter #?(:clj with-id-filter)
|
set-id-filter! #?(:clj with-id-filter)
|
||||||
set-min-level! #?(:clj with-min-level)
|
set-min-level! #?(:clj with-min-level)
|
||||||
|
|
||||||
#?(:clj with-handler) #?(:clj with-handler+)
|
#?(:clj with-handler) #?(:clj with-handler+)
|
||||||
add-handler! remove-handler! stop-handlers!
|
add-handler! remove-handler! stop-handlers!
|
||||||
|
|
||||||
|
with-signal with-signals
|
||||||
|
|
||||||
^:dynamic *ctx* set-ctx! #?(:clj with-ctx) #?(:clj with-ctx+)
|
^:dynamic *ctx* set-ctx! #?(:clj with-ctx) #?(:clj with-ctx+)
|
||||||
^:dynamic *xfn* set-xfn! #?(:clj with-xfn) #?(:clj with-xfn+))
|
^:dynamic *xfn* set-xfn! #?(:clj with-xfn) #?(:clj with-xfn+))
|
||||||
|
|
||||||
|
(def default-handler-dispatch-opts
|
||||||
|
"See `help:handler-dispatch-opts` for details."
|
||||||
|
(dissoc sigs/default-handler-dispatch-opts
|
||||||
|
:convey-bindings? ; We use `enc/bound-delay`
|
||||||
|
))
|
||||||
|
|
||||||
(sigs/def-api
|
(sigs/def-api
|
||||||
{:sf-arity 4
|
{:sf-arity 4
|
||||||
:ct-call-filter impl/ct-call-filter
|
:ct-call-filter impl/ct-call-filter
|
||||||
:*rt-call-filter* impl/*rt-call-filter*
|
:*rt-call-filter* impl/*rt-call-filter*
|
||||||
:*sig-handlers* impl/*sig-handlers*
|
:*sig-handlers* impl/*sig-handlers*
|
||||||
:lib-dispatch-opts
|
:lib-dispatch-opts default-handler-dispatch-opts})
|
||||||
(assoc sigs/default-handler-dispatch-opts
|
|
||||||
:convey-bindings? false ; Handled manually
|
|
||||||
)})
|
|
||||||
|
|
||||||
;;;; Aliases
|
;;;; Aliases
|
||||||
|
|
||||||
(enc/defaliases
|
(enc/defaliases
|
||||||
;; Encore
|
;; Encore
|
||||||
#?(:clj enc/set-var-root!)
|
#?(:clj ^:no-doc enc/set-var-root!)
|
||||||
#?(:clj enc/update-var-root!)
|
#?(:clj ^:no-doc enc/update-var-root!)
|
||||||
#?(:clj enc/get-env)
|
#?(:clj enc/get-env)
|
||||||
#?(:clj enc/call-on-shutdown!)
|
#?(:clj enc/call-on-shutdown!)
|
||||||
enc/chance
|
^:no-doc enc/chance
|
||||||
enc/rate-limiter
|
enc/rate-limiter
|
||||||
enc/newline
|
^:no-doc enc/newline
|
||||||
sigs/comp-xfn
|
sigs/comp-xfn
|
||||||
sigs/default-handler-dispatch-opts
|
|
||||||
#?(:clj truss/keep-callsite)
|
#?(:clj truss/keep-callsite)
|
||||||
|
|
||||||
;; Impl
|
;; Impl
|
||||||
|
|
@ -226,9 +230,9 @@
|
||||||
|
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(defn- args->opts [args]
|
(defn- args->opts [args]
|
||||||
(case (count args)
|
(case (count args)
|
||||||
0 {}
|
0 {}
|
||||||
1 (impl/valid-opts! (first args))
|
1 (first args)
|
||||||
(apply hash-map args))))
|
(apply hash-map args))))
|
||||||
|
|
||||||
#?(:clj
|
#?(:clj
|
||||||
|
|
@ -246,16 +250,18 @@
|
||||||
(log! {:allow? true} \"Message 2\"))"
|
(log! {:allow? true} \"Message 2\"))"
|
||||||
|
|
||||||
;; Used also for interop (tools.logging, SLF4J), etc.
|
;; Used also for interop (tools.logging, SLF4J), etc.
|
||||||
{:arglists (impl/signal-arglists :signal-allowed?)}
|
{:arglists (impl/arglists :signal-allowed?)}
|
||||||
[& args] `(impl/signal-allowed? ~(args->opts args))))
|
[& args]
|
||||||
|
(truss/keep-callsite
|
||||||
|
`(impl/signal-allowed? ~(args->opts args)))))
|
||||||
|
|
||||||
(comment (macroexpand '(signal-allowed? {:ns "my-ns"})))
|
(comment (macroexpand '(signal-allowed? {:ns "my-ns"})))
|
||||||
|
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(defmacro signal!
|
(defmacro signal!
|
||||||
"opts => allowed? / run result (value or throw)."
|
"opts => allowed? / run result (value or throw)."
|
||||||
{:doc (impl/signal-docstring :signal!)
|
{:doc (impl/docstring :signal!)
|
||||||
:arglists (impl/signal-arglists :signal!)}
|
:arglists (impl/arglists :signal!)}
|
||||||
[& args]
|
[& args]
|
||||||
(truss/keep-callsite
|
(truss/keep-callsite
|
||||||
`(impl/signal! ~(args->opts args)))))
|
`(impl/signal! ~(args->opts args)))))
|
||||||
|
|
@ -273,8 +279,8 @@
|
||||||
(let [base-opts {:kind :log, :level :info}]
|
(let [base-opts {:kind :log, :level :info}]
|
||||||
(defmacro log!?
|
(defmacro log!?
|
||||||
"?level + msg => allowed?"
|
"?level + msg => allowed?"
|
||||||
{:doc (impl/signal-docstring :log!)
|
{:doc (impl/docstring :log!)
|
||||||
:arglists (impl/signal-arglists :log!)}
|
:arglists (impl/arglists :log!)}
|
||||||
([opts-or-msg ] `(impl/signal! ~(merge-or-assoc-opts base-opts &form :msg opts-or-msg)))
|
([opts-or-msg ] `(impl/signal! ~(merge-or-assoc-opts base-opts &form :msg opts-or-msg)))
|
||||||
([opts-or-level msg] `(impl/signal! ~(assoc (merge-or-assoc-opts base-opts &form :level opts-or-level) :msg msg))))))
|
([opts-or-level msg] `(impl/signal! ~(assoc (merge-or-assoc-opts base-opts &form :level opts-or-level) :msg msg))))))
|
||||||
|
|
||||||
|
|
@ -283,8 +289,8 @@
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(defmacro log!
|
(defmacro log!
|
||||||
"Like `log!?` but always returns nil."
|
"Like `log!?` but always returns nil."
|
||||||
{:doc (impl/signal-docstring :log!)
|
{:doc (impl/docstring :log!)
|
||||||
:arglists (impl/signal-arglists :log!)}
|
:arglists (impl/arglists :log!)}
|
||||||
[& args] `(do ~(truss/keep-callsite `(log!? ~@args)) nil)))
|
[& args] `(do ~(truss/keep-callsite `(log!? ~@args)) nil)))
|
||||||
|
|
||||||
(comment (:coords (with-signal (log! :info "My msg"))))
|
(comment (:coords (with-signal (log! :info "My msg"))))
|
||||||
|
|
@ -293,8 +299,8 @@
|
||||||
(let [base-opts {:kind :event, :level :info}]
|
(let [base-opts {:kind :event, :level :info}]
|
||||||
(defmacro event!?
|
(defmacro event!?
|
||||||
"id + ?level => allowed? Note unique arg order: [x opts] rather than [opts x]!"
|
"id + ?level => allowed? Note unique arg order: [x opts] rather than [opts x]!"
|
||||||
{:doc (impl/signal-docstring :event!)
|
{:doc (impl/docstring :event!)
|
||||||
:arglists (impl/signal-arglists :event!)}
|
:arglists (impl/arglists :event!)}
|
||||||
([ opts-or-id] `(impl/signal! ~(merge-or-assoc-opts base-opts &form :id opts-or-id)))
|
([ opts-or-id] `(impl/signal! ~(merge-or-assoc-opts base-opts &form :id opts-or-id)))
|
||||||
([id opts-or-level] `(impl/signal! ~(assoc (merge-or-assoc-opts base-opts &form :level opts-or-level) :id id))))))
|
([id opts-or-level] `(impl/signal! ~(assoc (merge-or-assoc-opts base-opts &form :level opts-or-level) :id id))))))
|
||||||
|
|
||||||
|
|
@ -303,8 +309,8 @@
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(defmacro event!
|
(defmacro event!
|
||||||
"Like `event!?` but always returns nil."
|
"Like `event!?` but always returns nil."
|
||||||
{:doc (impl/signal-docstring :event!)
|
{:doc (impl/docstring :event!)
|
||||||
:arglists (impl/signal-arglists :event!)}
|
:arglists (impl/arglists :event!)}
|
||||||
[& args] `(do ~(truss/keep-callsite `(event!? ~@args)) nil)))
|
[& args] `(do ~(truss/keep-callsite `(event!? ~@args)) nil)))
|
||||||
|
|
||||||
(comment (:coords (with-signal (event! ::my-id :info))))
|
(comment (:coords (with-signal (event! ::my-id :info))))
|
||||||
|
|
@ -313,8 +319,8 @@
|
||||||
(let [base-opts {:kind :trace, :level :info, :msg `impl/default-trace-msg}]
|
(let [base-opts {:kind :trace, :level :info, :msg `impl/default-trace-msg}]
|
||||||
(defmacro trace!
|
(defmacro trace!
|
||||||
"?id + run => run result (value or throw)."
|
"?id + run => run result (value or throw)."
|
||||||
{:doc (impl/signal-docstring :trace!)
|
{:doc (impl/docstring :trace!)
|
||||||
:arglists (impl/signal-arglists :trace!)}
|
:arglists (impl/arglists :trace!)}
|
||||||
([opts-or-run] `(impl/signal! ~(merge-or-assoc-opts base-opts &form :run opts-or-run)))
|
([opts-or-run] `(impl/signal! ~(merge-or-assoc-opts base-opts &form :run opts-or-run)))
|
||||||
([opts-or-id run] `(impl/signal! ~(assoc (merge-or-assoc-opts base-opts &form :id opts-or-id) :run run))))))
|
([opts-or-id run] `(impl/signal! ~(assoc (merge-or-assoc-opts base-opts &form :id opts-or-id) :run run))))))
|
||||||
|
|
||||||
|
|
@ -324,8 +330,8 @@
|
||||||
(let [base-opts {:kind :spy, :level :info, :msg `impl/default-trace-msg}]
|
(let [base-opts {:kind :spy, :level :info, :msg `impl/default-trace-msg}]
|
||||||
(defmacro spy!
|
(defmacro spy!
|
||||||
"?level + run => run result (value or throw)."
|
"?level + run => run result (value or throw)."
|
||||||
{:doc (impl/signal-docstring :spy!)
|
{:doc (impl/docstring :spy!)
|
||||||
:arglists (impl/signal-arglists :spy!)}
|
:arglists (impl/arglists :spy!)}
|
||||||
([opts-or-run] `(impl/signal! ~(merge-or-assoc-opts base-opts &form :run opts-or-run)))
|
([opts-or-run] `(impl/signal! ~(merge-or-assoc-opts base-opts &form :run opts-or-run)))
|
||||||
([opts-or-level run] `(impl/signal! ~(assoc (merge-or-assoc-opts base-opts &form :level opts-or-level) :run run))))))
|
([opts-or-level run] `(impl/signal! ~(assoc (merge-or-assoc-opts base-opts &form :level opts-or-level) :run run))))))
|
||||||
|
|
||||||
|
|
@ -335,8 +341,8 @@
|
||||||
(let [base-opts {:kind :error, :level :error}]
|
(let [base-opts {:kind :error, :level :error}]
|
||||||
(defmacro error!
|
(defmacro error!
|
||||||
"?id + error => given error."
|
"?id + error => given error."
|
||||||
{:doc (impl/signal-docstring :error!)
|
{:doc (impl/docstring :error!)
|
||||||
:arglists (impl/signal-arglists :error!)}
|
:arglists (impl/arglists :error!)}
|
||||||
([opts-or-id error] `(error! ~(assoc (merge-or-assoc-opts base-opts &form :id opts-or-id) :error error)))
|
([opts-or-id error] `(error! ~(assoc (merge-or-assoc-opts base-opts &form :id opts-or-id) :error error)))
|
||||||
([opts-or-error]
|
([opts-or-error]
|
||||||
(let [opts (merge-or-assoc-opts base-opts &form :error opts-or-error)
|
(let [opts (merge-or-assoc-opts base-opts &form :error opts-or-error)
|
||||||
|
|
@ -352,8 +358,8 @@
|
||||||
(let [base-opts {:kind :error, :level :error}]
|
(let [base-opts {:kind :error, :level :error}]
|
||||||
(defmacro catch->error!
|
(defmacro catch->error!
|
||||||
"?id + run => run value or ?catch-val."
|
"?id + run => run value or ?catch-val."
|
||||||
{:doc (impl/signal-docstring :catch->error!)
|
{:doc (impl/docstring :catch->error!)
|
||||||
:arglists (impl/signal-arglists :catch->error!)}
|
:arglists (impl/arglists :catch->error!)}
|
||||||
([opts-or-id run] `(catch->error! ~(assoc (merge-or-assoc-opts base-opts &form :id opts-or-id) :run run)))
|
([opts-or-id run] `(catch->error! ~(assoc (merge-or-assoc-opts base-opts &form :id opts-or-id) :run run)))
|
||||||
([opts-or-run]
|
([opts-or-run]
|
||||||
(let [opts (merge-or-assoc-opts base-opts &form :run opts-or-run)
|
(let [opts (merge-or-assoc-opts base-opts &form :run opts-or-run)
|
||||||
|
|
@ -395,7 +401,7 @@
|
||||||
uncaught JVM errors.
|
uncaught JVM errors.
|
||||||
|
|
||||||
See `uncaught->handler!` and `error!` for details."
|
See `uncaught->handler!` and `error!` for details."
|
||||||
{:arglists (impl/signal-arglists :uncaught->error!)}
|
{:arglists (impl/arglists :uncaught->error!)}
|
||||||
([ ] (truss/keep-callsite `(uncaught->error! {})))
|
([ ] (truss/keep-callsite `(uncaught->error! {})))
|
||||||
([opts-or-id]
|
([opts-or-id]
|
||||||
(let [opts (merge-or-assoc-opts base-opts &form :id opts-or-id)]
|
(let [opts (merge-or-assoc-opts base-opts &form :id opts-or-id)]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
(ns ^:no-doc taoensso.telemere.consoles
|
(ns ^:no-doc taoensso.telemere.consoles
|
||||||
"Private ns, implementation detail.
|
"Telemere -> console handlers."
|
||||||
Core console handlers, aliased in main Telemere ns."
|
|
||||||
(:require
|
(:require
|
||||||
[taoensso.truss :as truss]
|
[taoensso.truss :as truss]
|
||||||
[taoensso.encore :as enc]
|
[taoensso.encore :as enc]
|
||||||
|
|
@ -14,7 +13,6 @@
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(defn ^:public handler:console
|
(defn ^:public handler:console
|
||||||
"Alpha, subject to change.
|
"Alpha, subject to change.
|
||||||
|
|
||||||
Returns a signal handler that:
|
Returns a signal handler that:
|
||||||
- Takes a Telemere signal (map).
|
- Takes a Telemere signal (map).
|
||||||
- Writes the signal as a string to specified stream.
|
- Writes the signal as a string to specified stream.
|
||||||
|
|
@ -24,11 +22,11 @@
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
`:output-fn` - (fn [signal]) => string, see `format-signal-fn` or `pr-signal-fn`
|
`:output-fn` - (fn [signal]) => string, see `format-signal-fn` or `pr-signal-fn`
|
||||||
`:stream` - `java.io.writer`
|
`:stream` ---- `java.io.writer`
|
||||||
Defaults to `*err*` if `utils/error-signal?` is true, and `*out*` otherwise."
|
Defaults to `*err*` if `utils/error-signal?` is true, and `*out*` otherwise."
|
||||||
|
|
||||||
([] (handler:console nil))
|
([] (handler:console nil))
|
||||||
([{:keys [stream output-fn ]
|
([{:keys [stream output-fn]
|
||||||
:or
|
:or
|
||||||
{stream :auto
|
{stream :auto
|
||||||
output-fn (utils/format-signal-fn)}}]
|
output-fn (utils/format-signal-fn)}}]
|
||||||
|
|
@ -52,7 +50,6 @@
|
||||||
:cljs
|
:cljs
|
||||||
(defn ^:public handler:console
|
(defn ^:public handler:console
|
||||||
"Alpha, subject to change.
|
"Alpha, subject to change.
|
||||||
|
|
||||||
If `js/console` exists, returns a signal handler that:
|
If `js/console` exists, returns a signal handler that:
|
||||||
- Takes a Telemere signal (map).
|
- Takes a Telemere signal (map).
|
||||||
- Writes the signal as a string to JavaScript console.
|
- Writes the signal as a string to JavaScript console.
|
||||||
|
|
@ -89,7 +86,6 @@
|
||||||
#?(:cljs
|
#?(:cljs
|
||||||
(defn ^:public handler:console-raw
|
(defn ^:public handler:console-raw
|
||||||
"Alpha, subject to change.
|
"Alpha, subject to change.
|
||||||
|
|
||||||
If `js/console` exists, returns a signal handler that:
|
If `js/console` exists, returns a signal handler that:
|
||||||
- Takes a Telemere signal (map).
|
- Takes a Telemere signal (map).
|
||||||
- Writes the raw signal to JavaScript console.
|
- Writes the raw signal to JavaScript console.
|
||||||
|
|
@ -98,7 +94,7 @@
|
||||||
Ref. <https://github.com/binaryage/cljs-devtools>.
|
Ref. <https://github.com/binaryage/cljs-devtools>.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
`:preamble-fn` - (fn [signal]) => string, see [1].
|
`:preamble-fn` ----- (fn [signal]) => string, see [1].
|
||||||
`:format-nsecs-fn` - (fn [nanosecs]) => string.
|
`:format-nsecs-fn` - (fn [nanosecs]) => string.
|
||||||
|
|
||||||
[1] `taoensso.telemere.utils/signal-preamble-fn`, etc."
|
[1] `taoensso.telemere.utils/signal-preamble-fn`, etc."
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
(ns ^:no-doc taoensso.telemere.files
|
(ns ^:no-doc taoensso.telemere.files
|
||||||
"Private ns, implementation detail.
|
"Telemere -> file handler."
|
||||||
Core file handler, aliased in main Telemere ns."
|
|
||||||
(:require
|
(:require
|
||||||
[taoensso.truss :as truss]
|
[taoensso.truss :as truss]
|
||||||
[taoensso.encore :as enc]
|
[taoensso.encore :as enc]
|
||||||
|
|
@ -286,18 +285,18 @@
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
`:output-fn`- (fn [signal]) => string, see `format-signal-fn` or `pr-signal-fn`
|
`:output-fn`- (fn [signal]) => string, see `format-signal-fn` or `pr-signal-fn`
|
||||||
`:path` - Path string of the target output file (default `logs/telemere.log`)
|
`:path` ----- Path string of the target output file (default `logs/telemere.log`)
|
||||||
|
|
||||||
`:interval` - ∈ #{nil :daily :weekly :monthly} (default `:monthly`)
|
`:interval` - ∈ #{nil :daily :weekly :monthly} (default `:monthly`)
|
||||||
When non-nil, causes interval-based archives to be maintained.
|
When non-nil, causes interval-based archives to be maintained.
|
||||||
|
|
||||||
`:max-file-size` ∈ #{nil <pos-int>} (default 4MB)
|
`:max-file-size` - ∈ #{nil <pos-int>} (default 4MB)
|
||||||
When `path` file size > ~this many bytes, rotates old content to numbered archives.
|
When `path` file size > ~this many bytes, rotates old content to numbered archives.
|
||||||
|
|
||||||
`:max-num-parts` ∈ #{nil <pos-int>} (default 8)
|
`:max-num-parts` - ∈ #{nil <pos-int>} (default 8)
|
||||||
Maximum number of numbered archives to retain for any particular interval.
|
Maximum number of numbered archives to retain for any particular interval.
|
||||||
|
|
||||||
`:max-num-intervals` ∈ #{nil <pos-int>} (default 6)
|
`:max-num-intervals` - ∈ #{nil <pos-int>} (default 6)
|
||||||
Maximum number of intervals (days/weeks/months) to retain."
|
Maximum number of intervals (days/weeks/months) to retain."
|
||||||
|
|
||||||
([] (handler:file nil))
|
([] (handler:file nil))
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,10 @@
|
||||||
(def enabled:otel-tracing?
|
(def enabled:otel-tracing?
|
||||||
"Documented at `taoensso.telemere/otel-tracing?`."
|
"Documented at `taoensso.telemere/otel-tracing?`."
|
||||||
(enc/get-env {:as :bool, :default present:otel?}
|
(enc/get-env {:as :bool, :default present:otel?}
|
||||||
:taoensso.telemere/otel-tracing<.platform>))))
|
:taoensso.telemere/otel-tracing<.platform>))
|
||||||
|
|
||||||
|
(def enabled:incl-host-info? "Include `:host` info in signals by default?" (enc/get-env {:as :bool, :default true} :taoensso.telemere/incl-host-info))
|
||||||
|
(def enabled:incl-thread-info? "Include `:thread` info in signals by default?" (enc/get-env {:as :bool, :default true} :taoensso.telemere/incl-thread-info))))
|
||||||
|
|
||||||
(def uid-kind
|
(def uid-kind
|
||||||
"Documented at `taoensso.telemere/*uid-fn*`."
|
"Documented at `taoensso.telemere/*uid-fn*`."
|
||||||
|
|
@ -170,8 +173,8 @@
|
||||||
(defn default-trace-msg
|
(defn default-trace-msg
|
||||||
[form value error nsecs]
|
[form value error nsecs]
|
||||||
(if error
|
(if error
|
||||||
(str form " !> " (truss/ex-type error))
|
(str (if (nil? form) "nil" form) " !> " (truss/ex-type error))
|
||||||
(str form " => " value)))
|
(str (if (nil? form) "nil" form) " => " (if (nil? value) "nil" value))))
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
(default-trace-msg "(+ 1 2)" 3 nil 12345)
|
(default-trace-msg "(+ 1 2)" 3 nil 12345)
|
||||||
|
|
@ -253,28 +256,19 @@
|
||||||
sample, kind id level, ctx parent root, data kvs msg_,
|
sample, kind id level, ctx parent root, data kvs msg_,
|
||||||
error run-form run-val end-inst run-nsecs]
|
error run-form run-val end-inst run-nsecs]
|
||||||
|
|
||||||
Object (toString [sig] (str "taoensso.telemere.Signal" (into {} sig))))
|
Object (toString [sig] (str "taoensso.telemere.Signal" (enc/pr-edn* (into {} sig)))))
|
||||||
|
|
||||||
;; NB intentionally verbose constructors for readability, to support extra keys
|
;; Verbose constructors for readability + to support extra keys
|
||||||
(do (enc/def-print-impl [sig Signal] (str "#taoensso.telemere.Signal" (pr-str (into {} sig)))))
|
(do (enc/def-print-impl [sig Signal] (str "#taoensso.telemere.Signal" (enc/pr-edn* (into {} sig)))))
|
||||||
#?(:clj (enc/def-print-dup [sig Signal] (str "#taoensso.telemere.impl.Signal" (pr-str (into {} sig)))))
|
#?(:clj (enc/def-print-dup [sig Signal] (str "#taoensso.telemere.impl.Signal" (enc/pr-edn* (into {} sig)))))
|
||||||
|
|
||||||
|
(defn signal? #?(:cljs {:tag 'boolean}) [x] (instance? Signal x))
|
||||||
|
|
||||||
(def impl-signal-keys #{:_otel-context})
|
(def impl-signal-keys #{:_otel-context})
|
||||||
(def standard-signal-keys
|
(def standard-signal-keys
|
||||||
(set/difference (set (keys (map->Signal {:schema 0})))
|
(set/difference (set (keys (map->Signal {:schema 0})))
|
||||||
impl-signal-keys))
|
impl-signal-keys))
|
||||||
|
|
||||||
(comment
|
|
||||||
(def s1 (with-signal (signal! {:level :info, :my-k1 :my-v1})))
|
|
||||||
(read-string (str (assoc s1 :my-k2 :my-v2)))
|
|
||||||
(read-string (pr-str (assoc s1 :my-k2 :my-v2)))
|
|
||||||
(read-string (binding [*print-dup* true] (pr-str (assoc s1 :my-k2 :my-v2))))
|
|
||||||
|
|
||||||
(defrecord MyRec [x])
|
|
||||||
(read-string ; Non-verbose will fail on any extra keys
|
|
||||||
(binding [*print-dup* true, *verbose-defrecords* false]
|
|
||||||
(pr-str (assoc (MyRec. :x) :y :y)))))
|
|
||||||
|
|
||||||
(deftype #_defrecord WrappedSignal
|
(deftype #_defrecord WrappedSignal
|
||||||
[kind ns id level signal-value_]
|
[kind ns id level signal-value_]
|
||||||
sigs/ISignalHandling
|
sigs/ISignalHandling
|
||||||
|
|
@ -380,28 +374,27 @@
|
||||||
(sigs/call-handlers! *sig-handlers* signal)
|
(sigs/call-handlers! *sig-handlers* signal)
|
||||||
:dispatched))
|
:dispatched))
|
||||||
|
|
||||||
;;;; Signal API helpers
|
;;;; API helpers
|
||||||
|
|
||||||
#?(:clj (defmacro signal-docstring [ rname] (enc/slurp-resource (str "signal-docstrings/" (name rname) ".txt"))))
|
#?(:clj (defmacro docstring [ rname] (enc/slurp-resource (str "docs/" (name rname) ".txt"))))
|
||||||
#?(:clj (defmacro defhelp [sym rname] `(enc/def* ~sym {:doc ~(eval `(signal-docstring ~rname))} "See docstring")))
|
#?(:clj (defmacro defhelp [sym rname] `(enc/def* ~sym {:doc ~(eval `(docstring ~rname))} "See docstring")))
|
||||||
|
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(defn signal-arglists [macro-id]
|
(defn arglists [macro-id]
|
||||||
|
;; + Undocumented [elide? allow? callsite-id host thread otel/context]
|
||||||
(case macro-id
|
(case macro-id
|
||||||
|
|
||||||
:signal-allowed? ; opts => allowed?
|
:signal-allowed? ; opts => allowed?
|
||||||
'( [& opts-kvs]
|
'( [& opts-kvs]
|
||||||
[{:as opts-map :keys
|
[{:as opts-map :keys
|
||||||
[#_elide? #_allow? #_callsite-id, ; Undocumented
|
[elidable? coords #_inst #_uid #_xfn #_xfn+,
|
||||||
elidable? coords #_inst #_uid #_xfn #_xfn+,
|
|
||||||
sample kind ns id level when limit limit-by,
|
sample kind ns id level when limit limit-by,
|
||||||
#_ctx #_ctx+ #_parent #_root #_trace?, #_do #_let #_data #_msg #_error #_run #_& #_kvs]}])
|
#_ctx #_ctx+ #_parent #_root #_trace?, #_do #_let #_data #_msg #_error #_run #_& #_kvs]}])
|
||||||
|
|
||||||
:signal! ; opts => allowed? / run result (value or throw)
|
:signal! ; opts => allowed? / run result (value or throw)
|
||||||
'( [& opts-kvs]
|
'( [& opts-kvs]
|
||||||
[{:as opts-map :keys
|
[{:as opts-map :keys
|
||||||
[#_elide? #_allow? #_callsite-id, ; Undocumented
|
[elidable? coords inst uid xfn xfn+ #_kvs+,
|
||||||
elidable? coords inst uid xfn xfn+,
|
|
||||||
sample kind ns id level when limit limit-by,
|
sample kind ns id level when limit limit-by,
|
||||||
ctx ctx+ parent root trace?, do let data msg error run & kvs]}])
|
ctx ctx+ parent root trace?, do let data msg error run & kvs]}])
|
||||||
|
|
||||||
|
|
@ -409,8 +402,7 @@
|
||||||
'([opts-or-msg]
|
'([opts-or-msg]
|
||||||
[level msg]
|
[level msg]
|
||||||
[{:as opts-map :keys
|
[{:as opts-map :keys
|
||||||
[#_elide? #_allow? #_callsite-id,
|
[elidable? coords inst uid xfn xfn+ #_kvs+,
|
||||||
elidable? coords inst uid xfn xfn+,
|
|
||||||
sample kind ns id level when limit limit-by,
|
sample kind ns id level when limit limit-by,
|
||||||
ctx ctx+ parent root trace?, do let data msg error #_run & kvs]}
|
ctx ctx+ parent root trace?, do let data msg error #_run & kvs]}
|
||||||
msg])
|
msg])
|
||||||
|
|
@ -420,8 +412,7 @@
|
||||||
[id level]
|
[id level]
|
||||||
[id
|
[id
|
||||||
{:as opts-map :keys
|
{:as opts-map :keys
|
||||||
[#_elide? #_allow? #_callsite-id,
|
[elidable? coords inst uid xfn xfn+ #_kvs+,
|
||||||
elidable? coords inst uid xfn xfn+,
|
|
||||||
sample kind ns id level when limit limit-by,
|
sample kind ns id level when limit limit-by,
|
||||||
ctx ctx+ parent root trace?, do let data msg error #_run & kvs]}])
|
ctx ctx+ parent root trace?, do let data msg error #_run & kvs]}])
|
||||||
|
|
||||||
|
|
@ -429,8 +420,7 @@
|
||||||
'([opts-or-run]
|
'([opts-or-run]
|
||||||
[id run]
|
[id run]
|
||||||
[{:as opts-map :keys
|
[{:as opts-map :keys
|
||||||
[#_elide? #_allow? #_callsite-id,
|
[elidable? coords inst uid xfn xfn+ #_kvs+,
|
||||||
elidable? coords inst uid xfn xfn+,
|
|
||||||
sample kind ns id level when limit limit-by,
|
sample kind ns id level when limit limit-by,
|
||||||
ctx ctx+ parent root trace?, do let data msg error run & kvs]}
|
ctx ctx+ parent root trace?, do let data msg error run & kvs]}
|
||||||
run])
|
run])
|
||||||
|
|
@ -439,8 +429,7 @@
|
||||||
'([opts-or-run]
|
'([opts-or-run]
|
||||||
[level run]
|
[level run]
|
||||||
[{:as opts-map :keys
|
[{:as opts-map :keys
|
||||||
[#_elide? #_allow? #_callsite-id,
|
[elidable? coords inst uid xfn xfn+ #_kvs+,
|
||||||
elidable? coords inst uid xfn xfn+,
|
|
||||||
sample kind ns id level when limit limit-by,
|
sample kind ns id level when limit limit-by,
|
||||||
ctx ctx+ parent root trace?, do let data msg error run & kvs]}
|
ctx ctx+ parent root trace?, do let data msg error run & kvs]}
|
||||||
run])
|
run])
|
||||||
|
|
@ -449,8 +438,7 @@
|
||||||
'([opts-or-error]
|
'([opts-or-error]
|
||||||
[id error]
|
[id error]
|
||||||
[{:as opts-map :keys
|
[{:as opts-map :keys
|
||||||
[#_elide? #_allow? #_callsite-id,
|
[elidable? coords inst uid xfn xfn+ #_kvs+,
|
||||||
elidable? coords inst uid xfn xfn+,
|
|
||||||
sample kind ns id level when limit limit-by,
|
sample kind ns id level when limit limit-by,
|
||||||
ctx ctx+ parent root trace?, do let data msg error #_run & kvs]}
|
ctx ctx+ parent root trace?, do let data msg error #_run & kvs]}
|
||||||
error])
|
error])
|
||||||
|
|
@ -459,8 +447,8 @@
|
||||||
'([opts-or-run]
|
'([opts-or-run]
|
||||||
[id run]
|
[id run]
|
||||||
[{:as opts-map :keys
|
[{:as opts-map :keys
|
||||||
[#_elide? #_allow? #_callsite-id, catch-val,
|
[catch-val,
|
||||||
elidable? coords inst uid xfn xfn+,
|
elidable? coords inst uid xfn xfn+ #_kvs+,
|
||||||
sample kind ns id level when limit limit-by,
|
sample kind ns id level when limit limit-by,
|
||||||
ctx ctx+ parent root trace?, do let data msg error #_run & kvs]}
|
ctx ctx+ parent root trace?, do let data msg error #_run & kvs]}
|
||||||
run])
|
run])
|
||||||
|
|
@ -469,8 +457,7 @@
|
||||||
'([]
|
'([]
|
||||||
[opts-or-id]
|
[opts-or-id]
|
||||||
[{:as opts-map :keys
|
[{:as opts-map :keys
|
||||||
[#_elide? #_allow? #_callsite-id,
|
[elidable? coords inst uid xfn xfn+ #_kvs+,
|
||||||
elidable? coords inst uid xfn xfn+,
|
|
||||||
sample kind ns id level when limit limit-by,
|
sample kind ns id level when limit limit-by,
|
||||||
ctx ctx+ parent root trace?, do let data msg error #_run & kvs]}])
|
ctx ctx+ parent root trace?, do let data msg error #_run & kvs]}])
|
||||||
|
|
||||||
|
|
@ -499,12 +486,12 @@
|
||||||
(comment (enc/qb 1e6 (inst+nsecs (enc/now-inst) 1e9)))
|
(comment (enc/qb 1e6 (inst+nsecs (enc/now-inst) 1e9)))
|
||||||
|
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(defn valid-opts! [x]
|
(defn- valid-opts! [macro-form macro-env caller opts]
|
||||||
(if (map? x)
|
(if (map? opts)
|
||||||
(do x)
|
(do opts)
|
||||||
;; We require const map keys, but vals may require eval
|
(truss/ex-info!
|
||||||
(truss/ex-info! "Telemere signal opts must be a map with const (compile-time) keys."
|
(str "`" caller "` needs compile-time map opts at "
|
||||||
{:opts (truss/typed-val x)}))))
|
(sigs/format-callsite (enc/get-source macro-form macro-env)))))))
|
||||||
|
|
||||||
#?(:clj (defn- auto-> [form auto-form] (if (= form :auto) auto-form form)))
|
#?(:clj (defn- auto-> [form auto-form] (if (= form :auto) auto-form form)))
|
||||||
#?(:clj
|
#?(:clj
|
||||||
|
|
@ -513,8 +500,8 @@
|
||||||
Wrapped for public API."
|
Wrapped for public API."
|
||||||
([ opts] (truss/keep-callsite `(signal-allowed? nil ~opts)))
|
([ opts] (truss/keep-callsite `(signal-allowed? nil ~opts)))
|
||||||
([base-opts opts]
|
([base-opts opts]
|
||||||
(valid-opts! (or base-opts {}))
|
(valid-opts! &form &env 'telemere/signal-allowed? (or base-opts {}))
|
||||||
(valid-opts! (or opts {}))
|
(valid-opts! &form &env 'telemere/signal-allowed? (or opts {}))
|
||||||
(let [opts (merge {:kind :generic, :level :info} base-opts opts)
|
(let [opts (merge {:kind :generic, :level :info} base-opts opts)
|
||||||
{:keys [#_callsite-id elide? allow?]}
|
{:keys [#_callsite-id elide? allow?]}
|
||||||
(sigs/filter-call
|
(sigs/filter-call
|
||||||
|
|
@ -534,20 +521,22 @@
|
||||||
"Generic low-level signal creator. Wrapped for public API."
|
"Generic low-level signal creator. Wrapped for public API."
|
||||||
([ opts] (truss/keep-callsite `(signal! nil ~opts)))
|
([ opts] (truss/keep-callsite `(signal! nil ~opts)))
|
||||||
([base-opts opts]
|
([base-opts opts]
|
||||||
(valid-opts! (or base-opts {}))
|
(valid-opts! &form &env 'telemere/signal! (or base-opts {}))
|
||||||
(valid-opts! (or opts {}))
|
(valid-opts! &form &env 'telemere/signal! (or opts {}))
|
||||||
(let [cljs? (boolean (:ns &env))
|
(let [cljs? (boolean (:ns &env))
|
||||||
clj? (not cljs?)
|
clj? (not cljs?)
|
||||||
|
|
||||||
opts (merge {:kind :generic, :level :info} base-opts opts)
|
opts (merge {:kind :generic, :level :info} base-opts opts)
|
||||||
{run-form :run} opts
|
|
||||||
|
run-form? (contains? opts :run)
|
||||||
|
run-form (get opts :run)
|
||||||
|
|
||||||
ns-form* (get opts :ns :auto)
|
ns-form* (get opts :ns :auto)
|
||||||
ns-form (auto-> ns-form* (str *ns*))
|
ns-form (auto-> ns-form* (str *ns*))
|
||||||
|
|
||||||
show-run-val (get opts :run-val '_run-val)
|
show-run-val (get opts :run-val '_run-val)
|
||||||
show-run-form
|
show-run-form
|
||||||
(when run-form
|
(when run-form?
|
||||||
(get opts :run-form
|
(get opts :run-form
|
||||||
(if (and
|
(if (and
|
||||||
(enc/list-form? run-form)
|
(enc/list-form? run-form)
|
||||||
|
|
@ -573,29 +562,23 @@
|
||||||
|
|
||||||
(if elide?
|
(if elide?
|
||||||
run-form
|
run-form
|
||||||
(let [coords
|
(let [coords (get opts :coords (when (= ns-form* :auto) (truss/callsite-coords &form)))
|
||||||
(get opts :coords
|
|
||||||
(when (= ns-form* :auto)
|
|
||||||
;; Auto coords iff auto ns
|
|
||||||
(truss/callsite-coords &form)))
|
|
||||||
|
|
||||||
{inst-form :inst
|
{inst-form :inst
|
||||||
level-form :level
|
|
||||||
kind-form :kind
|
kind-form :kind
|
||||||
id-form :id} opts
|
id-form :id
|
||||||
|
level-form :level} opts
|
||||||
|
|
||||||
trace? (get opts :trace? (boolean run-form))
|
trace? (get opts :trace? run-form?)
|
||||||
_
|
_
|
||||||
(when-not (contains? #{true false nil} trace?)
|
(when-not (contains? #{true false nil} trace?)
|
||||||
(truss/unexpected-arg! trace?
|
(truss/ex-info!
|
||||||
{:param 'trace?
|
(str "Signal needs compile-time `:trace?` value at "
|
||||||
:context `signal!
|
(sigs/format-callsite ns-form coords))))
|
||||||
:msg "Expected constant (compile-time) `:trace?` boolean"}))
|
|
||||||
|
|
||||||
thread-form (when clj? `(enc/thread-info))
|
host-form (auto-> (get opts :host :auto) (when (and clj? enabled:incl-host-info?) `(enc/host-info)))
|
||||||
|
thread-form (auto-> (get opts :thread :auto) (when (and clj? enabled:incl-thread-info?) `(enc/thread-info)))
|
||||||
inst-form (get opts :inst :auto)
|
inst-form (auto-> (get opts :inst :auto) `(enc/now-inst*))
|
||||||
inst-form (auto-> inst-form `(enc/now-inst*))
|
|
||||||
|
|
||||||
parent-form (get opts :parent `*trace-parent*)
|
parent-form (get opts :parent `*trace-parent*)
|
||||||
root-form0 (get opts :root `*trace-root*)
|
root-form0 (get opts :root `*trace-root*)
|
||||||
|
|
@ -624,39 +607,47 @@
|
||||||
(get opts :xfn `taoensso.telemere/*xfn*))
|
(get opts :xfn `taoensso.telemere/*xfn*))
|
||||||
|
|
||||||
kvs-form
|
kvs-form
|
||||||
(not-empty
|
(let [base
|
||||||
(dissoc opts
|
(not-empty
|
||||||
:elidable? :coords :inst :uid :xfn :xfn+,
|
(dissoc opts
|
||||||
:sample :ns :kind :id :level :filter :when #_:limit #_:limit-by,
|
:elidable? :coords :inst :uid :xfn :xfn+ :kvs+,
|
||||||
:ctx :ctx+ :parent #_:trace?, :do :let :data :msg :error,
|
:sample :ns :kind :id :level :filter :when #_:limit #_:limit-by,
|
||||||
:run :run-form :run-val, :elide? :allow? #_:callsite-id :otel/context))
|
:ctx :ctx+ :parent :trace?, :do :let :data :msg :error,
|
||||||
|
:run :run-form :run-val, :elide? :allow? #_:callsite-id,
|
||||||
|
:host :thread :otel/context))]
|
||||||
|
|
||||||
|
(if-let [kvs+ (get opts :kvs+)] ; Undocumented
|
||||||
|
(if base
|
||||||
|
`(not-empty (conj ~base ~kvs+))
|
||||||
|
`(not-empty ~kvs+))
|
||||||
|
base))
|
||||||
|
|
||||||
_ ; Compile-time validation
|
_ ; Compile-time validation
|
||||||
(do
|
(do
|
||||||
(when (and run-form error-form) ; Ambiguous source of error
|
(when (and run-form? error-form) ; Ambiguous source of error
|
||||||
(truss/ex-info! "Signals cannot have both `:run` and `:error` opts at the same time"
|
(truss/ex-info!
|
||||||
{:run-form run-form
|
(str "Signal cannot have both `:run` and `:error` opts at "
|
||||||
:error-form error-form
|
(sigs/format-callsite ns-form coords))))
|
||||||
:ns ns-form
|
|
||||||
:coords coords
|
|
||||||
:other-opts (dissoc opts :run :error)}))
|
|
||||||
|
|
||||||
(when-let [e (find opts :msg_)] ; Common typo/confusion
|
(when-let [e (find opts :msg_)] ; Common typo/confusion
|
||||||
(truss/ex-info! "Signals cannot have `:msg_` opt (did you mean `:msg`?))"
|
(truss/ex-info!
|
||||||
{:msg_ (truss/typed-val (val e))})))
|
(str "Signal cannot have `:msg_` opt (did you mean `:msg`?) at "
|
||||||
|
(sigs/format-callsite ns-form coords)))))
|
||||||
|
|
||||||
signal-form
|
signal-form
|
||||||
(let [record-form
|
(let [record-form
|
||||||
(let [clause [(if run-form :run :no-run) (if clj? :clj :cljs)]]
|
(let [clause [(if run-form? :run :no-run) (if clj? :clj :cljs)]]
|
||||||
(case clause
|
(case clause
|
||||||
[:run :clj ] `(Signal. 1 ~'__inst ~'__uid, ~'__ns ~coords (enc/host-info) ~'__thread ~'__otel-context1, ~sample-form, ~'__kind ~'__id ~'__level, ~ctx-form ~parent-form ~'__root1, ~data-form ~kvs-form ~'_msg_, ~'_run-err '~show-run-form ~show-run-val ~'_end-inst ~'_run-nsecs)
|
[:run :clj ] `(Signal. 1 ~'__inst ~'__uid, ~'__ns ~coords ~host-form ~'__thread ~'__otel-context, ~sample-form, ~'__kind ~'__id ~'__level, ~ctx-form ~parent-form ~'__root1, ~data-form ~kvs-form ~'_msg_, ~'_run-err '~show-run-form ~show-run-val ~'_end-inst ~'_run-nsecs)
|
||||||
[:run :cljs] `(Signal. 1 ~'__inst ~'__uid, ~'__ns ~coords ~sample-form, ~'__kind ~'__id ~'__level, ~ctx-form ~parent-form ~'__root1, ~data-form ~kvs-form ~'_msg_, ~'_run-err '~show-run-form ~show-run-val ~'_end-inst ~'_run-nsecs)
|
[:run :cljs] `(Signal. 1 ~'__inst ~'__uid, ~'__ns ~coords ~sample-form, ~'__kind ~'__id ~'__level, ~ctx-form ~parent-form ~'__root1, ~data-form ~kvs-form ~'_msg_, ~'_run-err '~show-run-form ~show-run-val ~'_end-inst ~'_run-nsecs)
|
||||||
[:no-run :clj ] `(Signal. 1 ~'__inst ~'__uid, ~'__ns ~coords (enc/host-info) ~'__thread ~'__otel-context1, ~sample-form, ~'__kind ~'__id ~'__level, ~ctx-form ~parent-form ~'__root1, ~data-form ~kvs-form ~msg-form, ~error-form nil nil nil nil)
|
[:no-run :clj ] `(Signal. 1 ~'__inst ~'__uid, ~'__ns ~coords ~host-form ~'__thread ~'__otel-context, ~sample-form, ~'__kind ~'__id ~'__level, ~ctx-form ~parent-form ~'__root1, ~data-form ~kvs-form ~msg-form, ~error-form nil nil nil nil)
|
||||||
[:no-run :cljs] `(Signal. 1 ~'__inst ~'__uid, ~'__ns ~coords ~sample-form, ~'__kind ~'__id ~'__level, ~ctx-form ~parent-form ~'__root1, ~data-form ~kvs-form ~msg-form, ~error-form nil nil nil nil)
|
[:no-run :cljs] `(Signal. 1 ~'__inst ~'__uid, ~'__ns ~coords ~sample-form, ~'__kind ~'__id ~'__level, ~ctx-form ~parent-form ~'__root1, ~data-form ~kvs-form ~msg-form, ~error-form nil nil nil nil)
|
||||||
(truss/unexpected-arg! clause {:context :signal-constructor-args})))
|
(truss/ex-info!
|
||||||
|
(str "Unexpected signal constructor args at "
|
||||||
|
(sigs/format-callsite ns-form coords)))))
|
||||||
|
|
||||||
record-form
|
record-form
|
||||||
(if-not run-form
|
(if-not run-form?
|
||||||
record-form
|
record-form
|
||||||
`(let [~(with-meta '_run-result {:tag `RunResult}) ~'__run-result
|
`(let [~(with-meta '_run-result {:tag `RunResult}) ~'__run-result
|
||||||
~'_run-nsecs (.-run-nsecs ~'_run-result)
|
~'_run-nsecs (.-run-nsecs ~'_run-result)
|
||||||
|
|
@ -684,21 +675,35 @@
|
||||||
|
|
||||||
;; Final unwrapped signal value visible to users/handler-fns, allow to throw
|
;; Final unwrapped signal value visible to users/handler-fns, allow to throw
|
||||||
(if-let [xfn# ~xfn-form]
|
(if-let [xfn# ~xfn-form]
|
||||||
(xfn# signal#) ; Apply call transform, can throw
|
(xfn# signal#)
|
||||||
(do signal#)))))
|
(do signal#)))))
|
||||||
|
|
||||||
;; Trade-off: avoid double `run-form` expansion
|
;; Trade-off: avoid double `run-form` expansion
|
||||||
run-fn-form (when run-form `(fn [] ~run-form))
|
run-fn-form (when run-form? `(fn [] ~run-form))
|
||||||
run-form* (when run-form `(~'__run-fn-form))
|
run-form* (when run-form? `(~'__run-fn-form))
|
||||||
|
|
||||||
into-let-form
|
binds-form-base
|
||||||
|
`[~'__inst ~inst-form
|
||||||
|
~'__thread ~thread-form
|
||||||
|
~'__root0 ~root-form0 ; ?{:keys [id uid]}
|
||||||
|
|
||||||
|
~'__otel-context
|
||||||
|
~(when (and clj? enabled:otel-tracing?)
|
||||||
|
(if run-form?
|
||||||
|
`(otel-context+span ~'__id ~'__inst ~(get opts :otel/context `(otel-context)) ~(get opts :otel/span-kind))
|
||||||
|
(do (get opts :otel/context `(otel-context)))))
|
||||||
|
|
||||||
|
~'__uid
|
||||||
|
~(if (and clj? enabled:otel-tracing? trace?)
|
||||||
|
(auto-> uid-form `(or (otel-span-id ~'__otel-context) (com.taoensso.encore.Ids/genHexId16)))
|
||||||
|
(auto-> uid-form `(taoensso.telemere/*uid-fn* (if ~'__root0 false true))))]
|
||||||
|
|
||||||
|
binds-form-more
|
||||||
(enc/cond!
|
(enc/cond!
|
||||||
(not trace?) ; Don't trace
|
(not trace?) ; Non-tracing signal
|
||||||
`[~'__otel-context1 nil
|
`[~'__root1 ~'__root0 ; Retain, but don't establish
|
||||||
~'__uid ~(auto-> uid-form `(taoensso.telemere/*uid-fn* (if ~'__root0 false true)))
|
|
||||||
~'__root1 ~'__root0 ; Retain, but don't establish
|
|
||||||
~'__run-result
|
~'__run-result
|
||||||
~(when run-form
|
~(when run-form?
|
||||||
`(let [t0# (enc/now-nano*)]
|
`(let [t0# (enc/now-nano*)]
|
||||||
(truss/try*
|
(truss/try*
|
||||||
(do (RunResult. ~run-form* nil (- (enc/now-nano*) t0#)))
|
(do (RunResult. ~run-form* nil (- (enc/now-nano*) t0#)))
|
||||||
|
|
@ -706,11 +711,9 @@
|
||||||
|
|
||||||
;; Trace without OpenTelemetry
|
;; Trace without OpenTelemetry
|
||||||
(or cljs? (not enabled:otel-tracing?))
|
(or cljs? (not enabled:otel-tracing?))
|
||||||
`[~'__otel-context1 nil
|
`[~'__root1 (or ~'__root0 ~(when trace? `{:id ~'__id, :uid ~'__uid}))
|
||||||
~'__uid ~(auto-> uid-form `(taoensso.telemere/*uid-fn* (if ~'__root0 false true)))
|
|
||||||
~'__root1 (or ~'__root0 ~(when trace? `{:id ~'__id, :uid ~'__uid}))
|
|
||||||
~'__run-result
|
~'__run-result
|
||||||
~(when run-form
|
~(when run-form?
|
||||||
`(binding [*trace-root* ~'__root1
|
`(binding [*trace-root* ~'__root1
|
||||||
*trace-parent* {:id ~'__id, :uid ~'__uid}]
|
*trace-parent* {:id ~'__id, :uid ~'__uid}]
|
||||||
(let [t0# (enc/now-nano*)]
|
(let [t0# (enc/now-nano*)]
|
||||||
|
|
@ -720,57 +723,46 @@
|
||||||
|
|
||||||
;; Trace with OpenTelemetry
|
;; Trace with OpenTelemetry
|
||||||
(and clj? enabled:otel-tracing?)
|
(and clj? enabled:otel-tracing?)
|
||||||
`[~'__otel-context0 ~(get opts :otel/context `(otel-context)) ; Context
|
`[~'__root1
|
||||||
~'__otel-context1 ~(if run-form `(otel-context+span ~'__id ~'__inst ~'__otel-context0 ~(get opts :otel/span-kind)) ~'__otel-context0)
|
|
||||||
~'__uid ~(auto-> uid-form `(or (otel-span-id ~'__otel-context1) (com.taoensso.encore.Ids/genHexId16)))
|
|
||||||
~'__root1
|
|
||||||
(or ~'__root0
|
(or ~'__root0
|
||||||
~(when trace?
|
~(when trace?
|
||||||
`{:id ~'__id, :uid (or (otel-trace-id ~'__otel-context1) (com.taoensso.encore.Ids/genHexId32))}))
|
`{:id ~'__id, :uid (or (otel-trace-id ~'__otel-context) (com.taoensso.encore.Ids/genHexId32))}))
|
||||||
|
|
||||||
~'__run-result
|
~'__run-result
|
||||||
~(when run-form
|
~(when run-form?
|
||||||
`(binding [*otel-context* ~'__otel-context1
|
`(binding [*otel-context* ~'__otel-context
|
||||||
*trace-root* ~'__root1
|
*trace-root* ~'__root1
|
||||||
*trace-parent* {:id ~'__id, :uid ~'__uid}]
|
*trace-parent* {:id ~'__id, :uid ~'__uid}]
|
||||||
(let [otel-scope# (.makeCurrent ~'__otel-context1)
|
(let [otel-scope# (.makeCurrent ~'__otel-context)
|
||||||
t0# (enc/now-nano*)]
|
t0# (enc/now-nano*)]
|
||||||
(truss/try*
|
(truss/try*
|
||||||
(do (RunResult. ~run-form* nil (- (enc/now-nano*) t0#)))
|
(do (RunResult. ~run-form* nil (- (enc/now-nano*) t0#)))
|
||||||
(catch :all t# (RunResult. nil t# (- (enc/now-nano*) t0#)))
|
(catch :all t# (RunResult. nil t# (- (enc/now-nano*) t0#)))
|
||||||
(finally (.close otel-scope#))))))])
|
(finally (.close otel-scope#))))))])]
|
||||||
|
|
||||||
final-form
|
`((fn [] ; iife for better IoC compatibility
|
||||||
;; Unless otherwise specified, allow errors to throw on call
|
;; Unless otherwise specified, allow errors to throw on call
|
||||||
`(let [~'__run-fn-form ~run-fn-form
|
(let [~'__run-fn-form ~run-fn-form
|
||||||
~'__kind ~kind-form
|
~'__kind ~kind-form
|
||||||
~'__ns ~ns-form
|
~'__ns ~ns-form
|
||||||
~'__id ~id-form
|
~'__id ~id-form
|
||||||
~'__level ~level-form]
|
~'__level ~level-form]
|
||||||
|
|
||||||
(enc/if-not ~allow?
|
(enc/if-not ~allow?
|
||||||
~run-form*
|
~run-form*
|
||||||
(let [~'__inst ~inst-form
|
(let [~@binds-form-base
|
||||||
~'__thread ~thread-form
|
~@binds-form-more
|
||||||
~'__root0 ~root-form0 ; ?{:keys [id uid]}
|
signal# ~signal-delay-form]
|
||||||
|
|
||||||
~@into-let-form ; Inject conditional bindings
|
(dispatch-signal!
|
||||||
signal# ~signal-delay-form]
|
;; Unconditionally send same wrapped signal to all handlers.
|
||||||
|
;; Each handler will use wrapper for handler filtering,
|
||||||
|
;; unwrapping (realizing) only allowed signals.
|
||||||
|
(WrappedSignal. ~'__kind ~'__ns ~'__id ~'__level signal#))
|
||||||
|
|
||||||
(dispatch-signal!
|
(if ~'__run-result
|
||||||
;; Unconditionally send same wrapped signal to all handlers.
|
( ~'__run-result signal#)
|
||||||
;; Each handler will use wrapper for handler filtering,
|
true))))))))))))
|
||||||
;; unwrapping (realizing) only allowed signals.
|
|
||||||
(WrappedSignal. ~'__kind ~'__ns ~'__id ~'__level signal#))
|
|
||||||
|
|
||||||
(if ~'__run-result
|
|
||||||
( ~'__run-result signal#)
|
|
||||||
true))))]
|
|
||||||
|
|
||||||
(if-let [iife-wrap? true #_cljs?]
|
|
||||||
;; Small perf hit to improve compatibility within `go` and other IOC-style bodies
|
|
||||||
`((fn [] ~final-form))
|
|
||||||
(do final-form))))))))
|
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
(with-signal (signal! {:level :warn :let [x :x] :msg ["Test" "message" x] :data {:a :A :x x} :run (+ 1 2)}))
|
(with-signal (signal! {:level :warn :let [x :x] :msg ["Test" "message" x] :data {:a :A :x x} :run (+ 1 2)}))
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
(ns taoensso.telemere.open-telemetry
|
(ns taoensso.telemere.open-telemetry
|
||||||
"OpenTelemetry handler using `opentelemetry-java`,
|
"Telemere -> OpenTelemetry handler using `opentelemetry-java`,
|
||||||
Ref. <https://github.com/open-telemetry/opentelemetry-java>,
|
Ref. <https://github.com/open-telemetry/opentelemetry-java>,
|
||||||
<https://javadoc.io/doc/io.opentelemetry/opentelemetry-api/latest/index.html>"
|
<https://javadoc.io/doc/io.opentelemetry/opentelemetry-api/latest/index.html>
|
||||||
|
|
||||||
|
Telemere will attempt to load this ns automatically when possible."
|
||||||
(:require
|
(:require
|
||||||
[clojure.string :as str]
|
[clojure.string :as str]
|
||||||
[clojure.set :as set]
|
[clojure.set :as set]
|
||||||
|
|
@ -217,15 +219,15 @@
|
||||||
|
|
||||||
(if (or common-attrs trace-attrs)
|
(if (or common-attrs trace-attrs)
|
||||||
(let [ab (Attributes/builder)]
|
(let [ab (Attributes/builder)]
|
||||||
(when-let [ns (get signal :ns)] (.put ab "ns" (str ns)))
|
(when-let [ns (get signal :ns)] (.put ab "ns" (str ns)))
|
||||||
(when-let [line (get signal :line)] (.put ab "line" (long line)))
|
(when-let [line (enc/get-in* signal [:coords 0])] (.put ab "line" (long line)))
|
||||||
(when-let [attrs common-attrs] (put-attrs! ab attrs))
|
(when-let [attrs common-attrs] (put-attrs! ab attrs))
|
||||||
(when-let [attrs trace-attrs] (put-attrs! ab attrs))
|
(when-let [attrs trace-attrs] (put-attrs! ab attrs))
|
||||||
(.build ab))
|
(.build ab))
|
||||||
|
|
||||||
;; Common case
|
;; Common case
|
||||||
(when-let [ns (get signal :ns)]
|
(when-let [ns (get signal :ns)]
|
||||||
(if-let [line (get signal :line)]
|
(if-let [line (enc/get-in* signal [:coords 0])]
|
||||||
(Attributes/of ak-ns ns, ak-line (long line))
|
(Attributes/of ak-ns ns, ak-line (long line))
|
||||||
(Attributes/of ak-ns ns)))))))
|
(Attributes/of ak-ns ns)))))))
|
||||||
|
|
||||||
|
|
@ -254,10 +256,10 @@
|
||||||
(see `telemere/otel-default-providers_` for default).
|
(see `telemere/otel-default-providers_` for default).
|
||||||
|
|
||||||
Optional signal keys:
|
Optional signal keys:
|
||||||
`:otel/attrs` - Attributes [1] to add to log records AND tracing spans/events
|
`:otel/attrs` ------- Attributes [1] to add to log records AND tracing spans/events
|
||||||
`:otel/log-attrs` - Attributes [1] to add to log records ONLY
|
`:otel/log-attrs` --- Attributes [1] to add to log records ONLY
|
||||||
`:otel/trace-attrs` - Attributes [1] to add to tracing spans/events ONLY
|
`:otel/trace-attrs` - Attributes [1] to add to tracing spans/events ONLY
|
||||||
`:otel/span-kind` - Span kind ∈ #{:internal (default) :client :server :consumer :producer}
|
`:otel/span-kind` --- Span kind ∈ #{:internal (default) :client :server :consumer :producer}
|
||||||
|
|
||||||
[1] `io.opentelemetry.api.common.Attributes` or Clojure map with str/kw keys and vals ∈
|
[1] `io.opentelemetry.api.common.Attributes` or Clojure map with str/kw keys and vals ∈
|
||||||
#{nil boolean keyword string UUID long double string-vec long-vec double-vec boolean-vec}.
|
#{nil boolean keyword string UUID long double string-vec long-vec double-vec boolean-vec}.
|
||||||
|
|
@ -298,8 +300,8 @@
|
||||||
(when-let [drained (enc/reset-in! span-buffer1_ #{})]
|
(when-let [drained (enc/reset-in! span-buffer1_ #{})]
|
||||||
(when-not (empty? drained)
|
(when-not (empty? drained)
|
||||||
(span-buffer2_ (fn [old] (set/union old drained)))))))
|
(span-buffer2_ (fn [old] (set/union old drained)))))))
|
||||||
|
3000 3000)
|
||||||
3000 3000)))
|
t3s))
|
||||||
|
|
||||||
stop-tracing!
|
stop-tracing!
|
||||||
(fn stop-tracing! []
|
(fn stop-tracing! []
|
||||||
|
|
@ -382,9 +384,9 @@
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
(do
|
(do
|
||||||
(require '[taoensso.telemere :as t])
|
(require '[taoensso.telemere :as tel])
|
||||||
(def h1 (handler:open-telemetry))
|
(def h1 (handler:open-telemetry))
|
||||||
(let [{[s1 s2] :signals} (t/with-signals (t/trace! ::id1 (t/trace! ::id2 "form2")))]
|
(let [{[s1 s2] :signals} (tel/with-signals (tel/trace! ::id1 (tel/trace! ::id2 "form2")))]
|
||||||
(def s1 s1)
|
(def s1 s1)
|
||||||
(def s2 s2)))
|
(def s2 s2)))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
(ns taoensso.telemere.postal
|
(ns taoensso.telemere.postal
|
||||||
"Email handler using `postal`,
|
"Telemere -> email handler using `postal`,
|
||||||
Ref. <https://github.com/drewr/postal>."
|
Ref. <https://github.com/drewr/postal>."
|
||||||
(:require
|
(:require
|
||||||
[taoensso.truss :as truss]
|
[taoensso.truss :as truss]
|
||||||
[taoensso.encore :as enc]
|
[taoensso.encore :as enc]
|
||||||
|
|
@ -58,7 +58,7 @@
|
||||||
:cc \"engineering@example.com\"
|
:cc \"engineering@example.com\"
|
||||||
:X-MyHeader \"A custom header\"}
|
:X-MyHeader \"A custom header\"}
|
||||||
|
|
||||||
`:subject-fn` - (fn [signal]) => email subject string
|
`:subject-fn` ------ (fn [signal]) => email subject string
|
||||||
`:subject-max-len` - Truncate subjects beyond this length (default 90)
|
`:subject-max-len` - Truncate subjects beyond this length (default 90)
|
||||||
|
|
||||||
`:body-fn` - (fn [signal]) => email body content string,
|
`:body-fn` - (fn [signal]) => email body content string,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
(ns taoensso.telemere.slack
|
(ns taoensso.telemere.slack
|
||||||
"Slack handler using `clj-slack`,
|
"Telemere -> Slack handler using `clj-slack`,
|
||||||
Ref. <https://github.com/julienXX/clj-slack>"
|
Ref. <https://github.com/julienXX/clj-slack>"
|
||||||
(:require
|
(:require
|
||||||
[taoensso.truss :as truss]
|
[taoensso.truss :as truss]
|
||||||
[taoensso.encore :as enc]
|
[taoensso.encore :as enc]
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
(ns taoensso.telemere.sockets
|
(ns taoensso.telemere.sockets
|
||||||
"Basic TCP/UDP socket handlers."
|
"Telemere -> TCP/UDP socket handlers."
|
||||||
(:require
|
(:require
|
||||||
[taoensso.truss :as truss]
|
[taoensso.truss :as truss]
|
||||||
[taoensso.encore :as enc]
|
[taoensso.encore :as enc]
|
||||||
|
|
@ -25,11 +25,11 @@
|
||||||
Can output signals as human or machine-readable (edn, JSON) strings.
|
Can output signals as human or machine-readable (edn, JSON) strings.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
`:output-fn` - (fn [signal]) => string, see `format-signal-fn` or `pr-signal-fn`
|
`:output-fn` --- (fn [signal]) => string, see `format-signal-fn` or `pr-signal-fn`
|
||||||
`:socket-opts` - {:keys [host port ssl? connect-timeout-msecs]}
|
`:socket-opts` - {:keys [host port ssl? connect-timeout-msecs]}
|
||||||
`:host` - Destination TCP socket hostname string
|
`:host` ------ Destination TCP socket hostname string
|
||||||
`:port` - Destination TCP socket port int
|
`:port` ------ Destination TCP socket port int
|
||||||
`:ssl?` - Use SSL/TLS (default false)
|
`:ssl?` ------ Use SSL/TLS (default false)
|
||||||
`:connect-timeout-msecs` - Connection timeout (default 3000 msecs)
|
`:connect-timeout-msecs` - Connection timeout (default 3000 msecs)
|
||||||
|
|
||||||
Limitations:
|
Limitations:
|
||||||
|
|
@ -59,10 +59,10 @@
|
||||||
Can output signals as human or machine-readable (edn, JSON) strings.
|
Can output signals as human or machine-readable (edn, JSON) strings.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
`:output-fn` - (fn [signal]) => string, see `format-signal-fn` or `pr-signal-fn`
|
`:output-fn` ---------- (fn [signal]) => string, see `format-signal-fn` or `pr-signal-fn`
|
||||||
`:socket-opts` - {:keys [host port max-packet-bytes]}
|
`:socket-opts` -------- {:keys [host port max-packet-bytes]}
|
||||||
`:host` - Destination UDP socket hostname string
|
`:host` ------------- Destination UDP socket hostname string
|
||||||
`:port` - Destination UDP socket port int
|
`:port` ------------- Destination UDP socket port int
|
||||||
`:max-packet-bytes` - Max packet size (in bytes) before truncating output (default 512)
|
`:max-packet-bytes` - Max packet size (in bytes) before truncating output (default 512)
|
||||||
|
|
||||||
`:truncation-warning-fn`
|
`:truncation-warning-fn`
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
(ns taoensso.telemere.streams
|
(ns taoensso.telemere.streams
|
||||||
"Interop support for standard stream/s -> Telemere."
|
"Standard streams -> Telemere interop."
|
||||||
(:require
|
(:require
|
||||||
[taoensso.encore :as truss]
|
[taoensso.encore :as truss]
|
||||||
[taoensso.encore :as enc]
|
[taoensso.encore :as enc]
|
||||||
|
|
|
||||||
|
|
@ -35,21 +35,21 @@
|
||||||
(enc/format* pattern vargs)
|
(enc/format* pattern vargs)
|
||||||
(enc/str-join " " (map arg-str) vargs)))]
|
(enc/str-join " " (map arg-str) vargs)))]
|
||||||
|
|
||||||
[error msg {:vargs vargs}])
|
[error msg vargs])
|
||||||
|
|
||||||
(let [md (if (and (map? v0) (get (meta v0) :meta)) v0 nil)
|
(let [md (if (and (map? v0) (get (meta v0) :meta)) v0 nil)
|
||||||
error (get md :err)
|
error (get md :err)
|
||||||
md (dissoc md :err)
|
md (dissoc md :err)
|
||||||
vargs (if md (enc/vrest vargs) vargs)
|
vargs (if md (enc/vrest vargs) vargs)
|
||||||
pattern (if format-msg? (let [[v0] vargs] v0) nil)
|
pattern (if format-msg? (let [[v0] vargs] v0) nil)
|
||||||
vargs (if format-msg? (enc/vrest vargs) vargs)
|
vargs (if format-msg? (enc/vrest vargs) vargs)
|
||||||
msg
|
msg
|
||||||
(delay
|
(delay
|
||||||
(if format-msg?
|
(if format-msg?
|
||||||
(enc/format* pattern vargs)
|
(enc/format* pattern vargs)
|
||||||
(enc/str-join " " (map arg-str) vargs)))]
|
(enc/str-join " " (map arg-str) vargs)))]
|
||||||
|
|
||||||
[error msg (when-not (empty? vargs) {:vargs vargs})])))))
|
[error msg (when-not (empty? vargs) vargs)])))))
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
(parse-vargs true [ "hello %s" "stu"])
|
(parse-vargs true [ "hello %s" "stu"])
|
||||||
|
|
@ -63,13 +63,13 @@
|
||||||
[level format-msg? vargs]
|
[level format-msg? vargs]
|
||||||
(truss/keep-callsite
|
(truss/keep-callsite
|
||||||
`(when (impl/signal-allowed? {:kind :log, :level ~level, :id shim-id})
|
`(when (impl/signal-allowed? {:kind :log, :level ~level, :id shim-id})
|
||||||
(let [[error# msg# data#] (parse-vargs ~format-msg? ~vargs)]
|
(let [[error# msg# vargs#] (parse-vargs ~format-msg? ~vargs)]
|
||||||
(tel/log!
|
(tel/log!
|
||||||
{:allow? true
|
{:allow? true
|
||||||
:level ~level
|
:level ~level
|
||||||
:id shim-id
|
:id shim-id
|
||||||
:error error#
|
:error error#
|
||||||
:data data#}
|
:timbre/vargs vargs#}
|
||||||
msg#)
|
msg#)
|
||||||
nil)))))
|
nil)))))
|
||||||
|
|
||||||
|
|
@ -190,8 +190,11 @@
|
||||||
:min-level nil
|
:min-level nil
|
||||||
:fn
|
:fn
|
||||||
(fn [data]
|
(fn [data]
|
||||||
(let [{:keys [instant level context ?err output_
|
(let [{:keys [instant level context ?err msg-type vargs
|
||||||
?ns-str ?file ?line ?column]} data]
|
?ns-str ?file ?line ?column]} data
|
||||||
|
|
||||||
|
format-msg? (enc/identical-kw? msg-type :f)
|
||||||
|
[_error msg vargs] (parse-vargs format-msg? vargs)]
|
||||||
|
|
||||||
(taoensso.telemere/signal!
|
(taoensso.telemere/signal!
|
||||||
{:kind :timbre
|
{:kind :timbre
|
||||||
|
|
@ -200,9 +203,10 @@
|
||||||
:ctx+ context
|
:ctx+ context
|
||||||
|
|
||||||
:ns ?ns-str
|
:ns ?ns-str
|
||||||
:file ?file
|
:coords (when ?line [?line ?column])
|
||||||
:line ?line
|
:file ?file ; Non-standard, goes to kvs
|
||||||
:column ?column
|
|
||||||
|
|
||||||
:error ?err
|
:error ?err
|
||||||
:msg (force output_)})))})
|
:msg (when msg-type msg)
|
||||||
|
|
||||||
|
:timbre/vargs vargs})))})
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
(ns taoensso.telemere.tools-logging
|
(ns taoensso.telemere.tools-logging
|
||||||
"Interop support for tools.logging -> Telemere.
|
"tools.logging -> Telemere interop.
|
||||||
Telemere will attempt to load this ns automatically when possible.
|
Telemere will attempt to load this ns automatically when possible.
|
||||||
|
|
||||||
Naming conventions:
|
Naming conventions:
|
||||||
|
|
|
||||||
|
|
@ -142,14 +142,14 @@
|
||||||
#?(:cljs
|
#?(:cljs
|
||||||
(defn js-console-logger
|
(defn js-console-logger
|
||||||
"Returns JavaScript console logger to match given signal level:
|
"Returns JavaScript console logger to match given signal level:
|
||||||
`:trace` -> `js/console.trace`,
|
`:debug` -> `js/console.debug`,
|
||||||
`:error` -> `js/console.error`, etc.
|
`:error` -> `js/console.error`, etc.
|
||||||
|
|
||||||
Defaults to `js.console.log` for unmatched signal levels.
|
Defaults to `js.console.log` for unmatched signal levels.
|
||||||
NB: assumes that `js/console` exists, handler constructors should check first!"
|
NB: assumes that `js/console` exists, handler constructors should check first!"
|
||||||
[level]
|
[level]
|
||||||
(case level
|
(case level
|
||||||
:trace js/console.trace
|
:trace js/console.debug
|
||||||
:debug js/console.debug
|
:debug js/console.debug
|
||||||
:info js/console.info
|
:info js/console.info
|
||||||
:warn js/console.warn
|
:warn js/console.warn
|
||||||
|
|
@ -352,10 +352,10 @@
|
||||||
Useful for basic handlers that write to a TCP socket, etc.
|
Useful for basic handlers that write to a TCP socket, etc.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
`:ssl?` - Use SSL/TLS?
|
`:ssl?` ------------------ Use SSL/TLS?
|
||||||
`:connect-timeout-msecs` - Connection timeout (default 3000 msecs)
|
`:connect-timeout-msecs` - Connection timeout (default 3000 msecs)
|
||||||
`:socket-fn` - (fn [host port timeout]) => `java.net.Socket`
|
`:socket-fn` ------------- (fn [host port timeout]) => `java.net.Socket`
|
||||||
`:ssl-socket-fn` - (fn [socket host port]) => `java.net.Socket`
|
`:ssl-socket-fn` --------- (fn [socket host port]) => `java.net.Socket`
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
- Writer should be manually closed after use (with zero-arity call).
|
- Writer should be manually closed after use (with zero-arity call).
|
||||||
|
|
@ -493,11 +493,11 @@
|
||||||
{:keys [chain trace]} em]
|
{:keys [chain trace]} em]
|
||||||
|
|
||||||
(let [s+cause (enc/sb-appender sb (str nls "Caused: "))]
|
(let [s+cause (enc/sb-appender sb (str nls "Caused: "))]
|
||||||
(s+ " Root: ")
|
(s+ "Root: ")
|
||||||
(doseq [{:keys [type msg data]} (rseq chain)]
|
(doseq [{:keys [type msg data]} (rseq chain)]
|
||||||
(s+cause type " - " msg)
|
(s+cause type " - " msg)
|
||||||
(when data
|
(when data
|
||||||
(s+ nl " data: " (enc/pr-edn* data)))))
|
(s+ nl "data: " (enc/pr-edn* data)))))
|
||||||
|
|
||||||
(when trace
|
(when trace
|
||||||
(s+ nl nl "Root stack trace:" nl)
|
(s+ nl nl "Root stack trace:" nl)
|
||||||
|
|
@ -522,8 +522,8 @@
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
`:format-inst-fn` - (fn format [instant]) => string.
|
`:format-inst-fn` - (fn format [instant]) => string.
|
||||||
`:format-id-fn` - (fn format [ns id]) => string.
|
`:format-id-fn` --- (fn format [ns id]) => string.
|
||||||
`:format-msg-fn` - (fn format [msg]) => string."
|
`:format-msg-fn` -- (fn format [msg]) => string."
|
||||||
([] (signal-preamble-fn nil))
|
([] (signal-preamble-fn nil))
|
||||||
([{:keys [format-inst-fn format-id-fn format-msg-fn]
|
([{:keys [format-inst-fn format-id-fn format-msg-fn]
|
||||||
:or {format-inst-fn (format-inst-fn)
|
:or {format-inst-fn (format-inst-fn)
|
||||||
|
|
@ -540,17 +540,10 @@
|
||||||
(when kind (s+spc (sigs/upper-qn kind)))
|
(when kind (s+spc (sigs/upper-qn kind)))
|
||||||
|
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(when-let [host (get signal :host)]
|
(when-let [hostname (enc/get-in* signal [:host :name])]
|
||||||
(when-let [hostname (get host :name)]
|
(s+spc hostname)))
|
||||||
(s+spc hostname))))
|
|
||||||
|
|
||||||
(when ns
|
|
||||||
(enc/sb-append sb " " ns)
|
|
||||||
(when-let [[line column] (get signal :coords)]
|
|
||||||
(if column
|
|
||||||
(enc/sb-append sb "[" line "," column "]")
|
|
||||||
(enc/sb-append sb "[" line "]"))))
|
|
||||||
|
|
||||||
|
(when ns (s+spc (sigs/format-callsite ns (get signal :coords))))
|
||||||
(when id (when-let [ff format-id-fn] (s+spc (ff ns id))))
|
(when id (when-let [ff format-id-fn] (s+spc (ff ns id))))
|
||||||
(enc/when-let [ff format-msg-fn
|
(enc/when-let [ff format-msg-fn
|
||||||
msg (force msg_)]
|
msg (force msg_)]
|
||||||
|
|
@ -563,8 +556,12 @@
|
||||||
|
|
||||||
(defn- format-parent [ns {:keys [id uid]}]
|
(defn- format-parent [ns {:keys [id uid]}]
|
||||||
(if id
|
(if id
|
||||||
{:id (symbol (format-id ns id)), :uid uid}
|
(if uid
|
||||||
{:id (symbol (format-id ns id))}))
|
{:id (symbol (format-id ns id)), :uid uid}
|
||||||
|
{:id (symbol (format-id ns id))})
|
||||||
|
(if uid
|
||||||
|
{:uid uid}
|
||||||
|
nil)))
|
||||||
|
|
||||||
(comment (str (format-parent (str *ns*) {:id ::id1 :uid "uid1"})))
|
(comment (str (format-parent (str *ns*) {:id ::id1 :uid "uid1"})))
|
||||||
|
|
||||||
|
|
@ -575,8 +572,8 @@
|
||||||
- Returns a human-readable signal content ?string (incl. data, ctx, etc.).
|
- Returns a human-readable signal content ?string (incl. data, ctx, etc.).
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
`:raw-error?` - Retain unformatted error? (default false)
|
`:raw-error?` ------ Retain unformatted error? (default false)
|
||||||
`:incl-keys` - Subset of signal keys to retain from those
|
`:incl-keys` ------- Subset of signal keys to retain from those
|
||||||
otherwise excluded by default: #{:kvs :host :thread}
|
otherwise excluded by default: #{:kvs :host :thread}
|
||||||
`:format-nsecs-fn` - (fn [nanosecs]) => string.
|
`:format-nsecs-fn` - (fn [nanosecs]) => string.
|
||||||
`:format-error-fn` - (fn [error]) => string."
|
`:format-error-fn` - (fn [error]) => string."
|
||||||
|
|
@ -661,8 +658,8 @@
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
`:incl-nils?` - Include signal's keys with nil values? (default false)
|
`:incl-nils?` - Include signal's keys with nil values? (default false)
|
||||||
`:incl-kvs?` - Include signal's app-level root kvs? (default false)
|
`:incl-kvs?` -- Include signal's app-level root kvs? (default false)
|
||||||
`:incl-keys` - Subset of signal keys to retain from those otherwise
|
`:incl-keys` -- Subset of signal keys to retain from those otherwise
|
||||||
excluded by default: #{:schema :kvs :host :thread}"
|
excluded by default: #{:schema :kvs :host :thread}"
|
||||||
([] (clean-signal-fn nil))
|
([] (clean-signal-fn nil))
|
||||||
([{:keys [incl-kvs? incl-nils? incl-keys] :as opts}]
|
([{:keys [incl-kvs? incl-nils? incl-keys] :as opts}]
|
||||||
|
|
@ -721,8 +718,8 @@
|
||||||
- Returns a machine-readable signal string.
|
- Returns a machine-readable signal string.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
`:pr-fn` - ∈ #{<unary-fn> :edn (default) :json (Cljs only)}
|
`:pr-fn` --------- ∈ #{<unary-fn> :edn (default) :json (Cljs only)}
|
||||||
`:clean-fn` - (fn [signal]) => clean signal map, see [1]
|
`:clean-fn` ------ (fn [signal]) => clean signal map, see [1]
|
||||||
`:incl-newline?` - Include terminating system newline? (default true)
|
`:incl-newline?` - Include terminating system newline? (default true)
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
@ -787,8 +784,8 @@
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
`:incl-newline?` - Include terminating system newline? (default true)
|
`:incl-newline?` - Include terminating system newline? (default true)
|
||||||
`:preamble-fn` - (fn [signal]) => signal preamble string, see [1]
|
`:preamble-fn` --- (fn [signal]) => signal preamble string, see [1]
|
||||||
`:content-fn` - (fn [signal]) => signal content string, see [2]
|
`:content-fn` ---- (fn [signal]) => signal content string, see [2]
|
||||||
|
|
||||||
[1] `taoensso.telemere.utils/signal-preamble-fn`, etc.
|
[1] `taoensso.telemere.utils/signal-preamble-fn`, etc.
|
||||||
[2] `taoensso.telemere.utils/signal-content-fn`, etc.
|
[2] `taoensso.telemere.utils/signal-content-fn`, etc.
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,9 @@
|
||||||
(do (def t2s "2024-02-02T02:02:02.120Z") (def t2 (enc/as-inst t2s)) (def udt2 (enc/as-udt t2)))
|
(do (def t2s "2024-02-02T02:02:02.120Z") (def t2 (enc/as-inst t2s)) (def udt2 (enc/as-udt t2)))
|
||||||
(do (def t3s "2024-03-03T03:03:03.130Z") (def t3 (enc/as-inst t3s)) (def udt3 (enc/as-udt t3)))
|
(do (def t3s "2024-03-03T03:03:03.130Z") (def t3 (enc/as-inst t3s)) (def udt3 (enc/as-udt t3)))
|
||||||
|
|
||||||
(def ex-info-type (truss/ex-type (truss/ex-info "" {})))
|
(def ex-info-type (truss/ex-type (ex-info "" {})))
|
||||||
(def ex1 (truss/ex-info "Ex1" {}))
|
(def ex1 (ex-info "Ex1" {}))
|
||||||
(def ex2 (truss/ex-info "Ex2" {:k2 "v2"} (truss/ex-info "Ex1" {:k1 "v1"})))
|
(def ex2 (ex-info "Ex2" {:k2 "v2"} (ex-info "Ex1" {:k1 "v1"})))
|
||||||
(def ex2-chain (truss/ex-chain :as-map ex2))
|
(def ex2-chain (truss/ex-chain :as-map ex2))
|
||||||
(defn ex1! [] (throw ex1))
|
(defn ex1! [] (throw ex1))
|
||||||
(defn ex1? [x] (= (truss/ex-root x) ex1)))
|
(defn ex1? [x] (= (truss/ex-root x) ex1)))
|
||||||
|
|
@ -281,15 +281,20 @@
|
||||||
(with-sig (sig! {:level :info, :xfn+ #(assoc % :bar true)})))]
|
(with-sig (sig! {:level :info, :xfn+ #(assoc % :bar true)})))]
|
||||||
(is (sm? sv {:foo true, :bar true})))])])
|
(is (sm? sv {:foo true, :bar true})))])])
|
||||||
|
|
||||||
#?(:clj
|
(testing "Printing"
|
||||||
(testing "Printing"
|
[#?(:clj (is (impl/signal? (read-string (binding [*print-dup* true] (pr-str (impl/map->Signal {})))))))
|
||||||
(let [sv1 (dissoc (with-sig (sig! {:level :info, :run (+ 1 2), :my-k1 :my-v1})) :_otel-context)
|
#?(:clj (is (impl/signal? (read-string (binding [*print-dup* true] (pr-str (assoc (impl/map->Signal {}) :k :v)))))))
|
||||||
sv1 ; Ensure instants are printable
|
(is (enc/str-starts-with? (binding [*print-dup* true] (str (assoc (impl/map->Signal {}) :k :v))) "taoensso.telemere.Signal{"))
|
||||||
(-> sv1
|
(is (enc/str-starts-with? (pr-str (assoc (impl/map->Signal {}) :k :v)) "#taoensso.telemere.Signal{"))
|
||||||
(update-in [:inst] enc/inst->udt)
|
(is (enc/str-starts-with? (str (assoc (impl/map->Signal {}) :k :v)) "taoensso.telemere.Signal{"))
|
||||||
(update-in [:end-inst] enc/inst->udt))]
|
|
||||||
|
|
||||||
[(is (= sv1 (read-string (pr-str sv1))))])))])
|
#?(:clj
|
||||||
|
(let [sv1 (dissoc (with-sig (sig! {:level :info, :run (+ 1 2), :my-k1 :my-v1})) :_otel-context)
|
||||||
|
sv1 ; Ensure instants are printable
|
||||||
|
(-> sv1
|
||||||
|
(update-in [:inst] enc/inst->udt)
|
||||||
|
(update-in [:end-inst] enc/inst->udt))]
|
||||||
|
(is (= sv1 (read-string (pr-str sv1))) "Equality holds")))])])
|
||||||
|
|
||||||
(deftest _handlers
|
(deftest _handlers
|
||||||
;; Basic handler tests are in Encore
|
;; Basic handler tests are in Encore
|
||||||
|
|
@ -589,12 +594,13 @@
|
||||||
(let [{rv :value, [sv] :signals} (with-sigs (tel/log!? {:allow? false} "msg")) ] [(is (= rv nil)) (is (nil? sv))])])
|
(let [{rv :value, [sv] :signals} (with-sigs (tel/log!? {:allow? false} "msg")) ] [(is (= rv nil)) (is (nil? sv))])])
|
||||||
|
|
||||||
(testing "trace!" ; ?id + run => unconditional run result (value or throw)
|
(testing "trace!" ; ?id + run => unconditional run result (value or throw)
|
||||||
[(let [{rv :value, [sv] :signals} (with-sigs (tel/trace! (+ 1 2))) ] [(is (= rv 3)) (is (sm? sv {:kind :trace, :coords coords?, :level :info, :id nil, :msg_ "(+ 1 2) => 3"}))])
|
[(let [{rv :value, [sv] :signals} (with-sigs (tel/trace! nil)) ] [(is (= rv nil)) (is (sm? sv {:kind :trace, :coords coords?, :level :info, :id nil, :msg_ "nil => nil"}))])
|
||||||
(let [{rv :value, [sv] :signals} (with-sigs (tel/trace! {:msg nil} (+ 1 2))) ] [(is (= rv 3)) (is (sm? sv {:kind :trace, :coords coords?, :level :info, :id nil, :msg_ nil}))])
|
(let [{rv :value, [sv] :signals} (with-sigs (tel/trace! (+ 1 2))) ] [(is (= rv 3)) (is (sm? sv {:kind :trace, :coords coords?, :level :info, :id nil, :msg_ "(+ 1 2) => 3"}))])
|
||||||
(let [{rv :value, [sv] :signals} (with-sigs (tel/trace! :id1 (+ 1 2))) ] [(is (= rv 3)) (is (sm? sv {:kind :trace, :coords coords?, :level :info, :id :id1}))])
|
(let [{rv :value, [sv] :signals} (with-sigs (tel/trace! {:msg nil} (+ 1 2))) ] [(is (= rv 3)) (is (sm? sv {:kind :trace, :coords coords?, :level :info, :id nil, :msg_ nil}))])
|
||||||
(let [{rv :value, [sv] :signals} (with-sigs (tel/trace! {:id :id1} (+ 1 2))) ] [(is (= rv 3)) (is (sm? sv {:kind :trace, :coords coords?, :level :info, :id :id1}))])
|
(let [{rv :value, [sv] :signals} (with-sigs (tel/trace! :id1 (+ 1 2))) ] [(is (= rv 3)) (is (sm? sv {:kind :trace, :coords coords?, :level :info, :id :id1}))])
|
||||||
(let [{rv :value, [sv] :signals} (with-sigs (tel/trace! {:id :id1, :run (+ 1 2)}))] [(is (= rv 3)) (is (sm? sv {:kind :trace, :coords coords?, :level :info, :id :id1}))])
|
(let [{rv :value, [sv] :signals} (with-sigs (tel/trace! {:id :id1} (+ 1 2))) ] [(is (= rv 3)) (is (sm? sv {:kind :trace, :coords coords?, :level :info, :id :id1}))])
|
||||||
(let [{re :error, [sv] :signals} (with-sigs (tel/trace! :id1 (ex1!))) ] [(is (ex1? re)) (is (sm? sv {:kind :trace, :coords coords?, :level :info, :id :id1, :error ex1,
|
(let [{rv :value, [sv] :signals} (with-sigs (tel/trace! {:id :id1, :run (+ 1 2)}))] [(is (= rv 3)) (is (sm? sv {:kind :trace, :coords coords?, :level :info, :id :id1}))])
|
||||||
|
(let [{re :error, [sv] :signals} (with-sigs (tel/trace! :id1 (ex1!))) ] [(is (ex1? re)) (is (sm? sv {:kind :trace, :coords coords?, :level :info, :id :id1, :error ex1,
|
||||||
:msg_ #?(:clj "(ex1!) !> clojure.lang.ExceptionInfo"
|
:msg_ #?(:clj "(ex1!) !> clojure.lang.ExceptionInfo"
|
||||||
:cljs "(ex1!) !> cljs.core/ExceptionInfo")}))])
|
:cljs "(ex1!) !> cljs.core/ExceptionInfo")}))])
|
||||||
(let [{rv :value, [sv] :signals} (with-sigs (tel/trace! {:allow? false} (+ 1 2)))] [(is (= rv 3)) (is (nil? sv))])
|
(let [{rv :value, [sv] :signals} (with-sigs (tel/trace! {:allow? false} (+ 1 2)))] [(is (= rv 3)) (is (nil? sv))])
|
||||||
|
|
@ -721,7 +727,7 @@
|
||||||
(is (sm? (with-sig (-> (.atWarn sl) (.log "Hello"))) {:level :warn, :ns "my.class", :kind :slf4j, :msg_ "Hello", :inst enc/inst?}) "Fluent API: warn basics")])
|
(is (sm? (with-sig (-> (.atWarn sl) (.log "Hello"))) {:level :warn, :ns "my.class", :kind :slf4j, :msg_ "Hello", :inst enc/inst?}) "Fluent API: warn basics")])
|
||||||
|
|
||||||
(testing "Message formatting"
|
(testing "Message formatting"
|
||||||
(let [msgp "x={},y={}", expected {:msg_ "x=1,y=2", :data {:slf4j/args ["1" "2"]}}]
|
(let [msgp "x={},y={}", expected {:msg_ "x=1,y=2", :slf4j/args (fn [objs] (= (vec objs) ["1" "2"]))}]
|
||||||
[(is (sm? (with-sig (.info sl msgp "1" "2")) expected) "Legacy API: formatted message, raw args")
|
[(is (sm? (with-sig (.info sl msgp "1" "2")) expected) "Legacy API: formatted message, raw args")
|
||||||
(is (sm? (with-sig (-> (.atInfo sl) (.setMessage msgp) (.addArgument "1") (.addArgument "2") (.log))) expected) "Fluent API: formatted message, raw args")]))
|
(is (sm? (with-sig (-> (.atInfo sl) (.setMessage msgp) (.addArgument "1") (.addArgument "2") (.log))) expected) "Fluent API: formatted message, raw args")]))
|
||||||
|
|
||||||
|
|
@ -732,8 +738,8 @@
|
||||||
m2 (#'slf4j/est-marker! "M2")
|
m2 (#'slf4j/est-marker! "M2")
|
||||||
cm (#'slf4j/est-marker! "Compound" "M1" "M2")]
|
cm (#'slf4j/est-marker! "Compound" "M1" "M2")]
|
||||||
|
|
||||||
[(is (sm? (with-sig (.info sl cm "Hello")) {:data {:slf4j/marker-names #{"Compound" "M1" "M2"}}}) "Legacy API: markers")
|
[(is (sm? (with-sig (.info sl cm "Hello")) {:slf4j/markers #{"Compound" "M1" "M2"}}) "Legacy API: markers")
|
||||||
(is (sm? (with-sig (-> (.atInfo sl) (.addMarker m1) (.addMarker cm) (.log))) {:data {:slf4j/marker-names #{"Compound" "M1" "M2"}}}) "Fluent API: markers")]))
|
(is (sm? (with-sig (-> (.atInfo sl) (.addMarker m1) (.addMarker cm) (.log))) {:slf4j/markers #{"Compound" "M1" "M2"}}) "Fluent API: markers")]))
|
||||||
|
|
||||||
(testing "Errors"
|
(testing "Errors"
|
||||||
[(is (sm? (with-sig (.warn sl "An error" ^Throwable ex1)) {:level :warn, :error ex1}) "Legacy API: errors")
|
[(is (sm? (with-sig (.warn sl "An error" ^Throwable ex1)) {:level :warn, :error ex1}) "Legacy API: errors")
|
||||||
|
|
@ -748,15 +754,15 @@
|
||||||
;;;; Timbre shim
|
;;;; Timbre shim
|
||||||
|
|
||||||
(deftest _timbre-shim
|
(deftest _timbre-shim
|
||||||
[(is (sm? (with-sig (timbre/log :warn "x1" nil "x2")) {:kind :log, :level :warn, :id timbre/shim-id, :msg_ "x1 nil x2", :data {:vargs ["x1" nil "x2"]}, :ns string?}))
|
[(is (sm? (with-sig (timbre/log :warn "x1" nil "x2")) {:kind :log, :level :warn, :id timbre/shim-id, :msg_ "x1 nil x2", :timbre/vargs ["x1" nil "x2"], :ns string?}))
|
||||||
(is (sm? (with-sig (timbre/info "x1" nil "x2")) {:kind :log, :level :info, :id timbre/shim-id, :msg_ "x1 nil x2", :data {:vargs ["x1" nil "x2"]}, :ns string?}))
|
(is (sm? (with-sig (timbre/info "x1" nil "x2")) {:kind :log, :level :info, :id timbre/shim-id, :msg_ "x1 nil x2", :timbre/vargs ["x1" nil "x2"], :ns string?}))
|
||||||
(is (sm? (with-sig (timbre/error "x1" nil "x2")) {:kind :log, :level :error, :id timbre/shim-id, :msg_ "x1 nil x2", :data {:vargs ["x1" nil "x2"]}, :ns string?}))
|
(is (sm? (with-sig (timbre/error "x1" nil "x2")) {:kind :log, :level :error, :id timbre/shim-id, :msg_ "x1 nil x2", :timbre/vargs ["x1" nil "x2"], :ns string?}))
|
||||||
|
|
||||||
(is (sm? (with-sig (timbre/logf :warn "%s %s %s" "x1" nil "x2")) {:kind :log, :level :warn, :id timbre/shim-id, :msg_ "x1 nil x2", :data {:vargs ["x1" nil "x2"]}, :ns string?}))
|
(is (sm? (with-sig (timbre/logf :warn "%s %s %s" "x1" nil "x2")) {:kind :log, :level :warn, :id timbre/shim-id, :msg_ "x1 nil x2", :timbre/vargs ["x1" nil "x2"], :ns string?}))
|
||||||
(is (sm? (with-sig (timbre/infof "%s %s %s" "x1" nil "x2")) {:kind :log, :level :info, :id timbre/shim-id, :msg_ "x1 nil x2", :data {:vargs ["x1" nil "x2"]}, :ns string?}))
|
(is (sm? (with-sig (timbre/infof "%s %s %s" "x1" nil "x2")) {:kind :log, :level :info, :id timbre/shim-id, :msg_ "x1 nil x2", :timbre/vargs ["x1" nil "x2"], :ns string?}))
|
||||||
(is (sm? (with-sig (timbre/errorf "%s %s %s" "x1" nil "x2")) {:kind :log, :level :error, :id timbre/shim-id, :msg_ "x1 nil x2", :data {:vargs ["x1" nil "x2"]}, :ns string?}))
|
(is (sm? (with-sig (timbre/errorf "%s %s %s" "x1" nil "x2")) {:kind :log, :level :error, :id timbre/shim-id, :msg_ "x1 nil x2", :timbre/vargs ["x1" nil "x2"], :ns string?}))
|
||||||
|
|
||||||
(is (sm? (with-sig (timbre/info ex1 "x1" "x2")) {:kind :log, :level :info, :error ex1, :msg_ "x1 x2", :data {:vargs ["x1" "x2"]}}) "First-arg error")
|
(is (sm? (with-sig (timbre/info ex1 "x1" "x2")) {:kind :log, :level :info, :error ex1, :msg_ "x1 x2", :timbre/vargs ["x1" "x2"]}) "First-arg error")
|
||||||
|
|
||||||
(is (sm? (with-sig (timbre/spy :info "my-name" (+ 1 2))) {:kind :spy, :level :info, :id timbre/shim-id, :msg_ "my-name => 3", :ns string?}))
|
(is (sm? (with-sig (timbre/spy :info "my-name" (+ 1 2))) {:kind :spy, :level :info, :id timbre/shim-id, :msg_ "my-name => 3", :ns string?}))
|
||||||
(is (sm? (tel/with-min-level :debug (with-sig (timbre/spy (+ 1 2)))) {:kind :spy, :level :debug, :id timbre/shim-id, :msg_ "(+ 1 2) => 3", :ns string?}))
|
(is (sm? (tel/with-min-level :debug (with-sig (timbre/spy (+ 1 2)))) {:kind :spy, :level :debug, :id timbre/shim-id, :msg_ "(+ 1 2) => 3", :ns string?}))
|
||||||
|
|
@ -831,8 +837,8 @@
|
||||||
(testing "format-error-fn"
|
(testing "format-error-fn"
|
||||||
(let [ex2-str ((utils/format-error-fn) ex2)]
|
(let [ex2-str ((utils/format-error-fn) ex2)]
|
||||||
[(is (enc/str-starts-with? ex2-str
|
[(is (enc/str-starts-with? ex2-str
|
||||||
#?(:clj " Root: clojure.lang.ExceptionInfo - Ex1\n data: {:k1 \"v1\"}\n\nCaused: clojure.lang.ExceptionInfo - Ex2\n data: {:k2 \"v2\"}\n\nRoot stack trace:\n"
|
#?(:clj "Root: clojure.lang.ExceptionInfo - Ex1\ndata: {:k1 \"v1\"}\n\nCaused: clojure.lang.ExceptionInfo - Ex2\ndata: {:k2 \"v2\"}\n\nRoot stack trace:\n"
|
||||||
:cljs " Root: cljs.core/ExceptionInfo - Ex1\n data: {:k1 \"v1\"}\n\nCaused: cljs.core/ExceptionInfo - Ex2\n data: {:k2 \"v2\"}\n\nRoot stack trace:\n")))
|
:cljs "Root: cljs.core/ExceptionInfo - Ex1\ndata: {:k1 \"v1\"}\n\nCaused: cljs.core/ExceptionInfo - Ex2\ndata: {:k2 \"v2\"}\n\nRoot stack trace:\n")))
|
||||||
|
|
||||||
(is (enc/str-contains? ex2-str "Root stack trace:"))
|
(is (enc/str-contains? ex2-str "Root stack trace:"))
|
||||||
(is (enc/str-contains? ex2-str "invoke") "Root stack trace includes content")]))
|
(is (enc/str-contains? ex2-str "invoke") "Root stack trace includes content")]))
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
(defproject com.taoensso/telemere-slf4j "1.0.0-RC5"
|
(defproject com.taoensso/telemere-slf4j "1.2.1"
|
||||||
:author "Peter Taoussanis <https://www.taoensso.com>"
|
:author "Peter Taoussanis <https://www.taoensso.com>"
|
||||||
:description "Telemere backend/provider for SLF4J API v2"
|
:description "Telemere backend/provider for SLF4J API v2"
|
||||||
:url "https://www.taoensso.com/telemere"
|
:url "https://www.taoensso.com/telemere"
|
||||||
|
|
@ -16,9 +16,9 @@
|
||||||
:profiles
|
:profiles
|
||||||
{:provided
|
{:provided
|
||||||
{:dependencies
|
{:dependencies
|
||||||
[[org.clojure/clojure "1.12.0"]
|
[[org.clojure/clojure "1.12.3"]
|
||||||
[org.slf4j/slf4j-api "2.0.17"]
|
[org.slf4j/slf4j-api "2.0.17"]
|
||||||
[com.taoensso/telemere "1.0.0-RC5"]]}
|
[com.taoensso/telemere "1.2.1"]]}
|
||||||
|
|
||||||
:dev
|
:dev
|
||||||
{:plugins
|
{:plugins
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
(ns taoensso.telemere.slf4j
|
(ns taoensso.telemere.slf4j
|
||||||
"Interop support for SLF4Jv2 -> Telemere.
|
"SLF4Jv2 -> Telemere interop.
|
||||||
Telemere will attempt to load this ns automatically when possible.
|
Telemere will attempt to load this ns automatically when possible.
|
||||||
|
|
||||||
To use Telemere as your SLF4J backend/provider, just include the
|
To use Telemere as your SLF4J backend/provider, just include the
|
||||||
|
|
@ -63,13 +63,13 @@
|
||||||
|
|
||||||
(comment [(est-marker! "a1" "a2") (get-marker "a1") (= (get-marker "a1") (get-marker "a1"))])
|
(comment [(est-marker! "a1" "a2") (get-marker "a1") (= (get-marker "a1") (get-marker "a1"))])
|
||||||
|
|
||||||
(def ^:private marker-names
|
(def ^:private get-marker-names
|
||||||
"Returns #{<MarkerName>}. Cached => assumes markers NOT modified after creation."
|
"Returns #{<MarkerName>}. Cached => assumes markers NOT modified after creation."
|
||||||
;; We use `BasicMarkerFactory` so:
|
;; We use `BasicMarkerFactory` so:
|
||||||
;; 1. Our markers are just labels (no other content besides their name).
|
;; 1. Our markers are just labels (no other content besides their name).
|
||||||
;; 2. Markers with the same name are identical (enabling caching).
|
;; 2. Markers with the same name are identical (enabling caching).
|
||||||
(enc/fmemoize
|
(enc/fmemoize
|
||||||
(fn marker-names [marker-or-markers]
|
(fn get-marker-names [marker-or-markers]
|
||||||
(if (instance? org.slf4j.Marker marker-or-markers)
|
(if (instance? org.slf4j.Marker marker-or-markers)
|
||||||
|
|
||||||
;; Single marker
|
;; Single marker
|
||||||
|
|
@ -79,15 +79,15 @@
|
||||||
(if-not (.hasReferences m)
|
(if-not (.hasReferences m)
|
||||||
acc
|
acc
|
||||||
(enc/reduce-iterator!
|
(enc/reduce-iterator!
|
||||||
(fn [acc ^org.slf4j.Marker in]
|
(fn [acc ^org.slf4j.Marker in]
|
||||||
(if-not (.hasReferences in)
|
(if-not (.hasReferences in)
|
||||||
(conj acc (.getName in))
|
(conj acc (.getName in))
|
||||||
(into acc (marker-names in))))
|
(into acc (get-marker-names in))))
|
||||||
acc (.iterator m))))
|
acc (.iterator m))))
|
||||||
|
|
||||||
;; Vector of markers
|
;; Vector of markers
|
||||||
(reduce
|
(reduce
|
||||||
(fn [acc in] (into acc (marker-names in)))
|
(fn [acc in] (into acc (get-marker-names in)))
|
||||||
#{} (truss/have vector? marker-or-markers))))))
|
#{} (truss/have vector? marker-or-markers))))))
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
|
|
@ -97,9 +97,9 @@
|
||||||
ms [m1 m2]]
|
ms [m1 m2]]
|
||||||
|
|
||||||
(enc/qb 1e6 ; [45.52 47.48 44.85]
|
(enc/qb 1e6 ; [45.52 47.48 44.85]
|
||||||
(marker-names m1)
|
(get-marker-names m1)
|
||||||
(marker-names cm)
|
(get-marker-names cm)
|
||||||
(marker-names ms))))
|
(get-marker-names ms))))
|
||||||
|
|
||||||
;;;; Interop fns (called by `TelemereLogger`)
|
;;;; Interop fns (called by `TelemereLogger`)
|
||||||
|
|
||||||
|
|
@ -132,11 +132,10 @@
|
||||||
(org.slf4j.helpers.MessageFormatter/basicArrayFormat
|
(org.slf4j.helpers.MessageFormatter/basicArrayFormat
|
||||||
msg-pattern args))
|
msg-pattern args))
|
||||||
|
|
||||||
:data
|
:slf4j/args args ; Object[]
|
||||||
(enc/assoc-some nil
|
:slf4j/markers marker-names ; Usu. used for routing, filtering, xfns, etc.
|
||||||
:slf4j/marker-names marker-names
|
:data (when kvs {:slf4j/kvs kvs})})
|
||||||
:slf4j/args (when args (vec args))
|
|
||||||
:slf4j/kvs kvs)})
|
|
||||||
nil)
|
nil)
|
||||||
|
|
||||||
(defn- log!
|
(defn- log!
|
||||||
|
|
@ -144,24 +143,24 @@
|
||||||
|
|
||||||
;; Modern "fluent" API calls
|
;; Modern "fluent" API calls
|
||||||
([logger-name ^org.slf4j.event.LoggingEvent event]
|
([logger-name ^org.slf4j.event.LoggingEvent event]
|
||||||
(let [inst (or (when-let [ts (.getTimeStamp event)] (java.time.Instant/ofEpochMilli ts)) (enc/now-inst*))
|
(let [inst (or (when-let [ts (.getTimeStamp event)] (java.time.Instant/ofEpochMilli ts)) (enc/now-inst*))
|
||||||
level (.getLevel event)
|
level (.getLevel event)
|
||||||
error (.getThrowable event)
|
error (.getThrowable event)
|
||||||
msg-pattern (.getMessage event)
|
msg-pattern (.getMessage event)
|
||||||
args (when-let [args (.getArgumentArray event)] args)
|
args (when-let [args (.getArgumentArray event)] args)
|
||||||
markers (when-let [markers (.getMarkers event)] (marker-names (vec markers)))
|
marker-names (when-let [markers (.getMarkers event)] (get-marker-names (vec markers)))
|
||||||
kvs (when-let [kvps (.getKeyValuePairs event)]
|
kvs (when-let [kvps (.getKeyValuePairs event)]
|
||||||
(reduce
|
(reduce
|
||||||
(fn [acc ^org.slf4j.event.KeyValuePair kvp]
|
(fn [acc ^org.slf4j.event.KeyValuePair kvp]
|
||||||
(assoc acc (.-key kvp) (.-value kvp)))
|
(assoc acc (.-key kvp) (.-value kvp)))
|
||||||
nil kvps))]
|
nil kvps))]
|
||||||
|
|
||||||
(when-debug (println [:slf4j/fluent-log-call (sig-level level) logger-name]))
|
(when-debug (println [:slf4j/fluent-log-call (sig-level level) logger-name]))
|
||||||
(normalized-log! logger-name level inst error msg-pattern args markers kvs)))
|
(normalized-log! logger-name level inst error msg-pattern args marker-names kvs)))
|
||||||
|
|
||||||
;; Legacy API calls
|
;; Legacy API calls
|
||||||
([logger-name ^org.slf4j.event.Level level error msg-pattern args marker]
|
([logger-name ^org.slf4j.event.Level level error msg-pattern args marker]
|
||||||
(let [marker-names (when marker (marker-names marker))]
|
(let [marker-names (when marker (get-marker-names marker))]
|
||||||
(when-debug (println [:slf4j/legacy-log-call (sig-level level) logger-name]))
|
(when-debug (println [:slf4j/legacy-log-call (sig-level level) logger-name]))
|
||||||
(normalized-log! logger-name level (enc/now-inst*) error msg-pattern args marker-names nil))))
|
(normalized-log! logger-name level (enc/now-inst*) error msg-pattern args marker-names nil))))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ deps.edn: com.taoensso/telemere {:mvn/version "x-y-z"}
|
||||||
And setup your namespace imports:
|
And setup your namespace imports:
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
(ns my-app (:require [taoensso.telemere :as t]))
|
(ns my-app (:require [taoensso.telemere :as tel]))
|
||||||
```
|
```
|
||||||
|
|
||||||
# Default config
|
# Default config
|
||||||
|
|
@ -127,30 +127,34 @@ Interop can be tough to get configured correctly so the [`check-interop`](https:
|
||||||
|
|
||||||
## Creating signals
|
## Creating signals
|
||||||
|
|
||||||
Use whichever signal creator is most convenient for your needs:
|
Telemere's signals are all created using the low-level `signal!` macro. You can use that directly, or one of the wrapper macros like `log!`.
|
||||||
|
|
||||||
| Name | Kind | Args | Returns |
|
Several different wrapper macros are provided. The only difference between them:
|
||||||
| :---------------------------------------------------------------------------------------------------------- | :--------- | :--------------- | :--------------------------- |
|
|
||||||
| [`log!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#log!) | `:log` | `?level` + `msg` | nil |
|
1. They create signals with a different `:kind` value (which can be handy for filtering, etc.).
|
||||||
| [`event!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#event!) | `:event` | `id` + `?level` | nil |
|
2. They have different positional arguments and/or return values optimised for concise calling in different use cases.
|
||||||
| [`trace!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#trace!) | `:trace` | `?id` + `run` | Form result |
|
|
||||||
| [`spy!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#spy!) | `:spy` | `?level` + `run` | Form result |
|
**NB:** ALL wrapper macros can also just be called with a single [opts](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-options) map!
|
||||||
| [`error!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#error!) | `:error` | `?id` + `error` | Given error |
|
|
||||||
| [`catch->error!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#catch-%3Eerror!) | `:error` | `?id` | Form value or given fallback |
|
See the linked docstrings below for more info:
|
||||||
| [`signal!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#signal!) | `:generic` | `opts` | Depends on opts |
|
|
||||||
|
| Name | Args | Returns |
|
||||||
- See relevant docstrings (links above) for usage info.
|
| :---------------------------------------------------------------------------------------------------------- | :------------------------- | :--------------------------- |
|
||||||
- See [`help:signal-creators`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-creators) for more about signal creators.
|
| [`log!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#log!) | `[opts]` or `[?level msg]` | nil |
|
||||||
- See [`help:signal-options`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-options) for options shared by all signal creators.
|
| [`event!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#event!) | `[opts]` or `[id ?level]` | nil |
|
||||||
- See [examples.cljc](https://github.com/taoensso/telemere/blob/master/examples.cljc) for REPL-ready examples.
|
| [`trace!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#trace!) | `[opts]` or `[?id run]` | Form result |
|
||||||
|
| [`spy!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#spy!) | `[opts]` or `[?level run]` | Form result |
|
||||||
|
| [`error!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#error!) | `[opts]` or `[?id error]` | Given error |
|
||||||
|
| [`catch->error!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#catch-%3Eerror!) | `[opts]` or `[?id error]` | Form value or given fallback |
|
||||||
|
| [`signal!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#signal!) | `[opts]` | Depends on opts |
|
||||||
|
|
||||||
## Checking signals
|
## Checking signals
|
||||||
|
|
||||||
Use the [`with-signal`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#with-signal) or (advanced) [`with-signals`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#with-signals) utils to help test/debug the signals that you're creating:
|
Use the [`with-signal`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#with-signal) or (advanced) [`with-signals`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#with-signals) utils to help test/debug the signals that you're creating:
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
(t/with-signal
|
(tel/with-signal
|
||||||
(t/log!
|
(tel/log!
|
||||||
{:let [x "x"]
|
{:let [x "x"]
|
||||||
:data {:x x}}
|
:data {:x x}}
|
||||||
["My msg:" x]))
|
["My msg:" x]))
|
||||||
|
|
@ -165,7 +169,7 @@ Both have several options, see their docstrings (links above) for details.
|
||||||
|
|
||||||
## Filtering
|
## Filtering
|
||||||
|
|
||||||
A signal will be provided to a handler iff ALL of the following are true:
|
A signal will be provided to a handler iff **ALL** of the following are true:
|
||||||
|
|
||||||
- 1. Signal **call filters** pass:
|
- 1. Signal **call filters** pass:
|
||||||
- a. Compile time: sample rate, kind, ns, id, level, when form, rate limit
|
- a. Compile time: sample rate, kind, ns, id, level, when form, rate limit
|
||||||
|
|
@ -178,20 +182,22 @@ A signal will be provided to a handler iff ALL of the following are true:
|
||||||
- 3. **Call transform** `(fn [signal]) => ?modified-signal` returns non-nil
|
- 3. **Call transform** `(fn [signal]) => ?modified-signal` returns non-nil
|
||||||
- 4. **Handler transform** `(fn [signal]) => ?modified-signal` returns non-nil
|
- 4. **Handler transform** `(fn [signal]) => ?modified-signal` returns non-nil
|
||||||
|
|
||||||
> Transform fns provides a flexible way to modify and/or filter signals by arbitrary signal data/content conditions (return nil to skip handling).
|
> 👉 Transform fns provides a flexible way to modify and/or filter signals by arbitrary signal data/content conditions (return nil to skip handling).
|
||||||
|
|
||||||
|
> 👉 Call and handler filters are **additive** - so handlers can be *more* but not *less* restrictive than call filters allow. This makes sense: call filters decide if a signal can be created. Handler filters decide if a particular handler is allowed to handle a created signal.
|
||||||
|
|
||||||
Quick examples of some basic filtering:
|
Quick examples of some basic filtering:
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
(t/set-min-level! :info) ; Set global minimum level
|
(tel/set-min-level! :info) ; Set global minimum level
|
||||||
(t/with-signal (t/event! ::my-id1 :info)) ; => {:keys [inst id ...]}
|
(tel/with-signal (tel/log! {:level :info ...})) ; => {:keys [inst id ...]}
|
||||||
(t/with-signal (t/event! ::my-id1 :debug)) ; => nil (signal not allowed)
|
(tel/with-signal (tel/log! {:level :debug ...})) ; => nil (signal not allowed)
|
||||||
|
|
||||||
(t/with-min-level :trace ; Override global minimum level
|
(tel/with-min-level :trace ; Override global minimum level
|
||||||
(t/with-signal (t/event! ::my-id1 :debug))) ; => {:keys [inst id ...]}
|
(tel/with-signal (tel/log! {:level :debug ...})) ; => {:keys [inst id ...]}
|
||||||
|
|
||||||
;; Disallow all signals in matching namespaces
|
;; Disallow all signals in matching namespaces
|
||||||
(t/set-ns-filter! {:disallow "some.nosy.namespace.*"})
|
(tel/set-ns-filter! {:disallow "some.nosy.namespace.*"})
|
||||||
```
|
```
|
||||||
|
|
||||||
- Filtering is always O(1), except for rate limits which are O(n_windows).
|
- Filtering is always O(1), except for rate limits which are O(n_windows).
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ See below for config by topic-
|
||||||
|
|
||||||
# Filtering
|
# Filtering
|
||||||
|
|
||||||
A signal will be provided to a handler iff ALL of the following are true:
|
A signal will be provided to a handler iff **ALL** of the following are true:
|
||||||
|
|
||||||
- 1. Signal **call filters** pass:
|
- 1. Signal **call filters** pass:
|
||||||
- a. Compile time: sample rate, kind, ns, id, level, when form, rate limit
|
- a. Compile time: sample rate, kind, ns, id, level, when form, rate limit
|
||||||
|
|
@ -15,10 +15,24 @@ A signal will be provided to a handler iff ALL of the following are true:
|
||||||
- 3. **Call transform** `(fn [signal]) => ?modified-signal` returns non-nil
|
- 3. **Call transform** `(fn [signal]) => ?modified-signal` returns non-nil
|
||||||
- 4. **Handler transform** `(fn [signal]) => ?modified-signal` returns non-nil
|
- 4. **Handler transform** `(fn [signal]) => ?modified-signal` returns non-nil
|
||||||
|
|
||||||
> Transform fns provides a flexible way to modify and/or filter signals by arbitrary signal data/content conditions (return nil to skip handling).
|
> 👉 Transform fns provides a flexible way to modify and/or filter signals by arbitrary signal data/content conditions (return nil to skip handling).
|
||||||
|
|
||||||
|
> 👉 Call and handler filters are **additive** - so handlers can be *more* but not *less* restrictive than call filters allow. This makes sense: call filters decide if a signal can be created. Handler filters decide if a particular handler is allowed to handle a created signal.
|
||||||
|
|
||||||
See [`help:filters`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:filters) for more about filtering.
|
See [`help:filters`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:filters) for more about filtering.
|
||||||
|
|
||||||
|
## Debugging filters
|
||||||
|
|
||||||
|
Telemere offers a *lot* of filtering control, so real systems can get quite complex. There's a lot of tools to help debug, including:
|
||||||
|
|
||||||
|
| Util | |
|
||||||
|
| ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ |
|
||||||
|
| [`with-signal`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#with-signal) | To see *last* signal created in body |
|
||||||
|
| [`with-signals`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#with-signals) | To see *all* signals created in body |
|
||||||
|
| [`get-filters`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#get-filters) | To see all call filters in current context |
|
||||||
|
| [`without-filters`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#without-filters) | To disable filters in body |
|
||||||
|
| [`get-handlers-stats`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#get-handlers-stats) | To see handler call stats |
|
||||||
|
|
||||||
# Signal handlers
|
# Signal handlers
|
||||||
|
|
||||||
See section [4-Handlers](./4-Handlers).
|
See section [4-Handlers](./4-Handlers).
|
||||||
|
|
@ -118,7 +132,7 @@ Telemere can easily incorporate Tufte performance data in its signals, just like
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
(let [[_ perf-data] (tufte/profiled <opts> <form>)]
|
(let [[_ perf-data] (tufte/profiled <opts> <form>)]
|
||||||
(t/log! {:perf-data perf-data} "Performance data"))
|
(tel/log! {:perf-data perf-data} "Performance data"))
|
||||||
```
|
```
|
||||||
|
|
||||||
Telemere and Tufte work great together:
|
Telemere and Tufte work great together:
|
||||||
|
|
@ -135,7 +149,7 @@ Telemere can easily incorporate Truss assertion failure information in its signa
|
||||||
The [`catch->error!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#catch-%3Eerror!) signal creator can be particularly convenient for this:
|
The [`catch->error!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#catch-%3Eerror!) signal creator can be particularly convenient for this:
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
(t/catch->error! <form-with-truss-assertion/s>)
|
(tel/catch->error! <form-with-truss-assertion/s>)
|
||||||
```
|
```
|
||||||
|
|
||||||
Telemere also uses [Truss contextual exceptions](https://cljdoc.org/d/com.taoensso/truss/CURRENT/api/taoensso.truss#ex-info) when relevant.
|
Telemere also uses [Truss contextual exceptions](https://cljdoc.org/d/com.taoensso/truss/CURRENT/api/taoensso.truss#ex-info) when relevant.
|
||||||
|
|
@ -71,11 +71,11 @@ By default it writes formatted strings intended for human consumption:
|
||||||
```clojure
|
```clojure
|
||||||
;; Create a test signal
|
;; Create a test signal
|
||||||
(def my-signal
|
(def my-signal
|
||||||
(t/with-signal
|
(tel/with-signal
|
||||||
(t/log! {:id ::my-id, :data {:x1 :x2}} "My message")))
|
(tel/log! {:id ::my-id, :data {:x1 :x2}} "My message")))
|
||||||
|
|
||||||
;; Create console handler with default opts (writes formatted string)
|
;; Create console handler with default opts (writes formatted string)
|
||||||
(def my-handler (t/handler:console {}))
|
(def my-handler (tel/handler:console {}))
|
||||||
|
|
||||||
;; Test handler, remember it's just a (fn [signal])
|
;; Test handler, remember it's just a (fn [signal])
|
||||||
(my-handler my-signal) ; %>
|
(my-handler my-signal) ; %>
|
||||||
|
|
@ -90,8 +90,8 @@ To instead writes signals as [edn](https://github.com/edn-format/edn):
|
||||||
```clojure
|
```clojure
|
||||||
;; Create console handler which writes signals as edn
|
;; Create console handler which writes signals as edn
|
||||||
(def my-handler
|
(def my-handler
|
||||||
(t/handler:console
|
(tel/handler:console
|
||||||
{:output-fn (t/pr-signal-fn {:pr-fn :edn})}))
|
{:output-fn (tel/pr-signal-fn {:pr-fn :edn})}))
|
||||||
|
|
||||||
(my-handler my-signal) ; %>
|
(my-handler my-signal) ; %>
|
||||||
;; {:inst #inst "2024-04-11T10:54:57.202869Z", :msg_ "My message", :ns "examples", ...}
|
;; {:inst #inst "2024-04-11T10:54:57.202869Z", :msg_ "My message", :ns "examples", ...}
|
||||||
|
|
@ -105,9 +105,9 @@ To instead writes signals as JSON:
|
||||||
;; Ref. <https://github.com/metosin/jsonista> (or any alt JSON lib)
|
;; Ref. <https://github.com/metosin/jsonista> (or any alt JSON lib)
|
||||||
#?(:clj (require '[jsonista.core :as jsonista]))
|
#?(:clj (require '[jsonista.core :as jsonista]))
|
||||||
(def my-handler
|
(def my-handler
|
||||||
(t/handler:console
|
(tel/handler:console
|
||||||
{:output-fn
|
{:output-fn
|
||||||
(t/pr-signal-fn
|
(tel/pr-signal-fn
|
||||||
{:pr-fn
|
{:pr-fn
|
||||||
#?(:cljs :json ; Use js/JSON.stringify
|
#?(:cljs :json ; Use js/JSON.stringify
|
||||||
:clj jsonista/write-value-as-string)})}))
|
:clj jsonista/write-value-as-string)})}))
|
||||||
|
|
@ -125,9 +125,10 @@ Telemere includes a handy mechanism for including arbitrary app-level data/opts
|
||||||
Any *non-standard* (app-level) keys you include in your signal constructor opts will automatically be included in created signals, e.g.:
|
Any *non-standard* (app-level) keys you include in your signal constructor opts will automatically be included in created signals, e.g.:
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
(t/with-signal
|
(tel/with-signal
|
||||||
(t/event! ::my-id
|
(tel/log!
|
||||||
{:my-data-for-xfn "foo"
|
{...
|
||||||
|
:my-data-for-xfn "foo"
|
||||||
:my-data-for-handler "bar"}))
|
:my-data-for-handler "bar"}))
|
||||||
|
|
||||||
;; %>
|
;; %>
|
||||||
|
|
@ -250,7 +251,7 @@ If you're making a customizable handler for use by others, it's often handy to d
|
||||||
# Example output
|
# Example output
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
(t/log! {:id ::my-id, :data {:x1 :x2}} "My message") =>
|
(tel/log! {:id ::my-id, :data {:x1 :x2}} "My message") =>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Clj console handler
|
## Clj console handler
|
||||||
|
|
|
||||||
|
|
@ -76,18 +76,18 @@ Examples:
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
;; A fixed message (string arg)
|
;; A fixed message (string arg)
|
||||||
(t/log! "A fixed message") ; %> {:msg "A fixed message"}
|
(tel/log! "A fixed message") ; %> {:msg "A fixed message"}
|
||||||
|
|
||||||
;; A joined message (vector arg)
|
;; A joined message (vector arg)
|
||||||
(let [user-arg "Bob"]
|
(let [user-arg "Bob"]
|
||||||
(t/log! ["User" (str "`" user-arg "`") "just logged in!"]))
|
(tel/log! ["User" (str "`" user-arg "`") "just logged in!"]))
|
||||||
;; %> {:msg_ "User `Bob` just logged in!` ...}
|
;; %> {:msg_ "User `Bob` just logged in!` ...}
|
||||||
|
|
||||||
;; With arg prep
|
;; With arg prep
|
||||||
(let [user-arg "Bob"
|
(let [user-arg "Bob"
|
||||||
usd-balance-str "22.4821"]
|
usd-balance-str "22.4821"]
|
||||||
|
|
||||||
(t/log!
|
(tel/log!
|
||||||
{:let
|
{:let
|
||||||
[username (clojure.string/upper-case user-arg)
|
[username (clojure.string/upper-case user-arg)
|
||||||
usd-balance (parse-double usd-balance-str)]
|
usd-balance (parse-double usd-balance-str)]
|
||||||
|
|
@ -100,10 +100,10 @@ Examples:
|
||||||
|
|
||||||
;; %> {:msg "User BOB has balance: $22" ...}
|
;; %> {:msg "User BOB has balance: $22" ...}
|
||||||
|
|
||||||
(t/log! (str "This message " "was built " "by `str`"))
|
(tel/log! (str "This message " "was built " "by `str`"))
|
||||||
;; %> {:msg "This message was built by `str`"}
|
;; %> {:msg "This message was built by `str`"}
|
||||||
|
|
||||||
(t/log! (format "This message was built by `%s`" "format"))
|
(tel/log! (format "This message was built by `%s`" "format"))
|
||||||
;; %> {:msg "This message was built by `format`"}
|
;; %> {:msg "This message was built by `format`"}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -115,11 +115,11 @@ See also [`msg-skip`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/tao
|
||||||
|
|
||||||
See section [9-Authors](./9-Authors.md).
|
See section [9-Authors](./9-Authors.md).
|
||||||
|
|
||||||
# How does Telemere compare to Mulog?
|
# How does Telemere compare to μ/log?
|
||||||
|
|
||||||
> [Mulog](https://github.com/BrunoBonacci/mulog) is an excellent "micro-logging library" for Clojure that shares many of the same capabilities and objectives as Telemere.
|
> [μ/log](https://github.com/BrunoBonacci/mulog) is an excellent "micro-logging library" for Clojure that shares many of the same capabilities and objectives as Telemere.
|
||||||
|
|
||||||
Some **similarities** between Telemere and Mulog:
|
Some **similarities** between Telemere and μ/log:
|
||||||
|
|
||||||
- Both emphasize **structured data** rather than string messages
|
- Both emphasize **structured data** rather than string messages
|
||||||
- Both offer **tracing** to understand (nested) program flow
|
- Both offer **tracing** to understand (nested) program flow
|
||||||
|
|
@ -127,7 +127,7 @@ Some **similarities** between Telemere and Mulog:
|
||||||
- Both are **fast** and offer **async handling**
|
- Both are **fast** and offer **async handling**
|
||||||
- Both offer a variety of **handlers** and are designed for ease of use
|
- Both offer a variety of **handlers** and are designed for ease of use
|
||||||
|
|
||||||
Some particular **strengths of Mulog** that I'm aware of:
|
Some particular **strengths of μ/log** that I'm aware of:
|
||||||
|
|
||||||
- More **established/mature**
|
- More **established/mature**
|
||||||
- Wider **range of handlers** (incl. Kafka, Kinesis, Prometheus, Zipkin, etc.)
|
- Wider **range of handlers** (incl. Kafka, Kinesis, Prometheus, Zipkin, etc.)
|
||||||
|
|
@ -137,7 +137,7 @@ Some particular **strengths of Mulog** that I'm aware of:
|
||||||
|
|
||||||
Some particular **strengths of Telemere**:
|
Some particular **strengths of Telemere**:
|
||||||
|
|
||||||
- Both **Clj and Cljs support** (Mulog is Clj only)
|
- Both **Clj and Cljs support** (μ/log is Clj only)
|
||||||
- Rich **filtering capabilities** (see [`help:filters`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:filters)) incl. compile-time elision
|
- Rich **filtering capabilities** (see [`help:filters`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:filters)) incl. compile-time elision
|
||||||
- Rich **dispatch control** (see [`help:handler-dispatch-options`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:handler-dispatch-options))
|
- Rich **dispatch control** (see [`help:handler-dispatch-options`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:handler-dispatch-options))
|
||||||
- Rich **environmental config** (see [`help:environmental-config`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:environmental-config)) for all platforms
|
- Rich **environmental config** (see [`help:environmental-config`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:environmental-config)) for all platforms
|
||||||
|
|
@ -148,7 +148,7 @@ Some particular **strengths of Telemere**:
|
||||||
|
|
||||||
**My subjective thoughts**:
|
**My subjective thoughts**:
|
||||||
|
|
||||||
Mulog is an awesome, well-designed library with quality documentation and a solid API. It's **absolutely worth checking out** - you may well prefer it to Telemere!
|
μ/log is an awesome, well-designed library with quality documentation and a solid API. It's **absolutely worth checking out** - you may well prefer it to Telemere!
|
||||||
|
|
||||||
The two libraries have many shared capabilities and objectives.
|
The two libraries have many shared capabilities and objectives.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ Consider the [differences](https://www.youtube.com/watch?v=oyLBGkS5ICk) between
|
||||||
Any non-standard [options](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-options) you give to a signal creator call will be added to the signal it creates:
|
Any non-standard [options](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-options) you give to a signal creator call will be added to the signal it creates:
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
(t/with-signal (t/log! {:my-key "foo"} "My message")))
|
(tel/with-signal (tel/log! {:my-key "foo"} "My message")))
|
||||||
;; => {:my-key "foo", :kvs {:my-key "foo", ...}, ...}
|
;; => {:my-key "foo", :kvs {:my-key "foo", ...}, ...}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
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, transforms, and utils.
|
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, transforms, and utils.
|
||||||
|
|
||||||
**PRs very welcome** to add links to this page!
|
[PRs](../wiki#contributions-welcome) **very welcome** to add links to this page!
|
||||||
|
|
||||||
If you spot issues with any linked resources, please **contact the relevant authors** to let them know! Thank you! 🙏 - [Peter](https://www.taoensso.com)
|
If you spot issues with any linked resources, please **contact the relevant authors** to let them know! Thank you! 🙏 - [Peter](https://www.taoensso.com)
|
||||||
|
|
||||||
# Learning
|
# Learning
|
||||||
|
|
||||||
Includes videos, tutorials, demo projects, etc.
|
Includes videos, tutorials, demo projects, etc.
|
||||||
[PRs](../wiki#contributions-welcome) welcome for additions!
|
|
||||||
|
|
||||||
| Type | Description |
|
| Type | Description |
|
||||||
| ------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
|
@ -20,9 +19,9 @@ Includes videos, tutorials, demo projects, etc.
|
||||||
|
|
||||||
# Handlers and tools
|
# Handlers and tools
|
||||||
|
|
||||||
Includes libraries or examples for handlers (see [Writing handlers](./4-Handlers#writing-handlers)), transforms, handler utils (e.g. formatters), tools for analyzing signals, etc. [PRs](../wiki#contributions-welcome) welcome for additions!
|
Includes libraries or examples for handlers (see [Writing handlers](./4-Handlers#writing-handlers)), transforms, handler utils (e.g. formatters), tools for analyzing signals, etc.
|
||||||
|
|
||||||
| Type | Description |
|
|
||||||
| ---- | :------------------------------------------------------------ |
|
|
||||||
| - | Your link here? [PRs](../wiki#contributions-welcome) welcome! |
|
|
||||||
|
|
||||||
|
| Type | Description |
|
||||||
|
| ------- | :------------------------------------------------------------ |
|
||||||
|
| Handler | [Axiom.co](https://github.com/marksto/telemere.axiom) handler |
|
||||||
|
| - | Your link here? [PRs](../wiki#contributions-welcome) welcome! |
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,30 @@
|
||||||
Are you a library author/maintainer that's considering **using Telemere in your library**?
|
Are you a library author/maintainer that's considering **using Telemere in your library**?
|
||||||
|
|
||||||
You have **two options** below-
|
You have **a few options** below-
|
||||||
|
|
||||||
# Options
|
# Options
|
||||||
## 1. Common logging facade (basic logging only)
|
|
||||||
|
|
||||||
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/).
|
## Modern logging facade
|
||||||
|
|
||||||
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)).
|
[Trove](https://www.taoensso.com/trove) is a minimal, modern alternative to [tools.logging](https://github.com/clojure/tools.logging) that supports all of Telemere's structured logging and rich filtering features.
|
||||||
|
|
||||||
## 2. Telemere as a transitive dependency
|
Basically:
|
||||||
|
|
||||||
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.
|
1. You include the (very small) Trove dependency with your library
|
||||||
|
2. Your library logs using the [Trove API](https://github.com/taoensso/trove#to-choose-a-backend)
|
||||||
|
3. Your users then [choose](https://github.com/taoensso/trove#to-choose-a-backend) their preferred backend (Telemere, etc.)
|
||||||
|
|
||||||
|
This would be my first recommendation, and is what I'm planning to use for future updates to [Sente](https://www.taoensso.com/sente), [Carmine](https://www.taoensso.com/carmine), etc.
|
||||||
|
|
||||||
|
## Traditional logging facade (basic logging only)
|
||||||
|
|
||||||
|
Many libraries need only basic logging. In these cases it can be beneficial to do your logging through a common traditional logging facade like [tools.logging](https://github.com/clojure/tools.logging) or [SLF4J](https://www.slf4j.org/).
|
||||||
|
|
||||||
|
Though these'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)).
|
||||||
|
|
||||||
|
## Telemere as a transitive dependency
|
||||||
|
|
||||||
|
You could just 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 your users are unlikely to need to configure or interact with Telemere much unless they choose to.
|
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.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue