[fix] Fix broken AOT support, add AOT tests

Thanks to @AdamFrey for reporting this issue!
Ref. <https://clojurians.slack.com/archives/C06ALA6EEUA/p1713805333272469>

Previously:

  Attempting to run AOT'd code using Telemere would result in errors like:
  "Attempting to call unbound fn: #'taoensso.telemere.handlers.open-telemetry/handler:open-telemetry-logger"

The approach I was using of conditionally requiring namespaces and then aliasing vars seems to be inherently
fragile under AOT, and was leading to the remote source var being unbound.

With this commit I've now switched to a simpler approach - where we conditionally require namespaces *without*
the need for any aliasing.
This commit is contained in:
Peter Taoussanis 2024-04-23 11:25:15 +02:00
parent b98e492071
commit ffea1a30ed
12 changed files with 186 additions and 153 deletions

View file

@ -29,10 +29,12 @@
[[org.clojure/clojure "1.11.2"]
[com.github.clj-easy/graal-build-time "1.0.5"]]}
:test {:aot [taoensso.telemere-tests]}
:dev
{:jvm-opts
["-server"
"-Dtaoensso.elide-deprecated=true"
"-Dtaoensso.telemere.auto-handlers=false"
"-Dclojure.tools.logging-to-telemere?=true"]
:global-vars

View file

@ -1,6 +1,6 @@
(ns ^:no-doc taoensso.telemere.slf4j
"Private ns, implementation detail.
Intake support: SLF4J -> Telemere.
(ns taoensso.telemere.slf4j
"Intake support for SLF4J -> Telemere.
Telemere will attempt to load this ns automatically when possible.
To use Telemere as your SLF4J backend/provider, just include the
`com.taoensso/slf4j-telemere` dependency on your classpath.
@ -18,7 +18,11 @@
(:require
[taoensso.encore :as enc :refer [have have?]]
[taoensso.telemere.impl :as impl]))
[taoensso.telemere.impl :as impl])
(:import
[org.slf4j Logger]
[com.taoensso.telemere.slf4j TelemereLogger]))
;;;; Utils
@ -40,10 +44,10 @@
(comment (enc/qb 1e6 (sig-level org.slf4j.event.Level/INFO))) ; 36.47
(defn get-marker "Private util for tests, etc."
(defn- get-marker "Private util for tests, etc."
^org.slf4j.Marker [n] (org.slf4j.MarkerFactory/getMarker n))
(defn est-marker!
(defn- est-marker!
"Private util for tests, etc.
Globally establishes (compound) `org.slf4j.Marker` with name `n` and mutates it
(all occurences!) to have exactly the given references. Returns the (compound) marker."
@ -55,7 +59,7 @@
(comment [(est-marker! "a1" "a2") (get-marker "a1") (= (get-marker "a1") (get-marker "a1"))])
(def marker-names
(def ^:private marker-names
"Returns #{<MarkerName>}. Cached => assumes markers NOT modified after creation."
;; We use `BasicMarkerFactory` so:
;; 1. Our markers are just labels (no other content besides their name).
@ -95,7 +99,7 @@
;;;; Intake fns (called by `TelemereLogger`)
(defn allowed?
(defn- allowed?
"Private, don't use.
Called by `com.taoensso.telemere.slf4j.TelemereLogger`."
[^org.slf4j.event.Level level]
@ -134,7 +138,7 @@
:slf4j/kvs kvs)})
nil)
(defn log!
(defn- log!
"Private, don't use.
Called by `com.taoensso.telemere.slf4j.TelemereLogger`."
@ -172,15 +176,27 @@
(org.slf4j.MDC/getCopyOfContextMap)
(org.slf4j.MDC/clear)))
(impl/add-intake-check! :slf4j
(fn []
(let [^org.slf4j.Logger sl
(org.slf4j.LoggerFactory/getLogger "IntakeTestTelemereLogger")
sending? (instance? com.taoensso.telemere.slf4j.TelemereLogger sl)
receiving?
(and sending?
(impl/test-intake! "SLF4J -> Telemere" #(.info sl %)))]
;;;;
{:present? true
:sending->telemere? sending?
:telemere-receiving? receiving?})))
(defn check-intake
"Returns {:keys [present? sending->telemere? telemere-receiving?]}."
[]
(let [^org.slf4j.Logger sl
(org.slf4j.LoggerFactory/getLogger "IntakeTestTelemereLogger")
sending? (instance? com.taoensso.telemere.slf4j.TelemereLogger sl)
receiving?
(and sending?
(impl/test-intake! "SLF4J -> Telemere" #(.info sl %)))]
{:present? true
:sending->telemere? sending?
:telemere-receiving? receiving?}))
(impl/add-intake-check! :slf4j check-intake)
(impl/on-init
(impl/signal!
{:kind :event
:level :info
:id :taoensso.telemere/slf4j->telemere!
:msg "Enabling intake: SLF4J -> Telemere"}))

View file

@ -375,25 +375,6 @@
streams/streams->telemere!
streams/streams->reset!))
#?(:clj
(enc/compile-when
(do (require '[taoensso.telemere.tools-logging :as ttl]) true)
(enc/defalias ttl/tools-logging->telemere!) ; Incl. `get-env` docs
(when (enc/get-env {:as :bool} :clojure.tools.logging-to-telemere?)
(ttl/tools-logging->telemere!))))
#?(:clj
(enc/compile-when
(and org.slf4j.Logger com.taoensso.telemere.slf4j.TelemereLogger)
(impl/signal!
{:kind :event
:level :info
:id :taoensso.telemere/slf4j->telemere!
:msg "Enabling intake: SLF4J -> Telemere"})
(require '[taoensso.telemere.slf4j :as slf4j])))
(comment (check-intakes))
;;;; Handlers
@ -403,20 +384,6 @@
#?(:cljs handlers:console/handler:console-raw)
#?(:clj handlers:file/handler:file))
#?(:clj
(enc/compile-when
(do (require '[taoensso.telemere.handlers.open-telemetry :as handlers:open-tel]) true)
(enc/defalias handlers:open-tel/handler:open-telemetry-logger)))
(defonce ^:no-doc __add-default-handlers
(do
(add-handler! :default/console (handler:console))
#?(:clj
(enc/compile-when handler:open-telemetry-logger
(when-let [handler (enc/catching (handler:open-telemetry-logger))]
(add-handler! :default/open-telemetry-logger handler))))
nil))
;;;; Flow benchmarks
(comment
@ -450,6 +417,16 @@
;;;;
(impl/on-init
(when impl/auto-handlers?
(add-handler! :default/console (handler:console)))
#?(:clj (enc/catching (require '[taoensso.telemere.tools-logging])))
#?(:clj (enc/catching (require '[taoensso.telemere.slf4j])))
#?(:clj (enc/catching (require '[taoensso.telemere.handlers.open-telemetry]))))
;;;;
(comment
(with-handler :hid1 (handlers/console-handler) {} (log! "Message"))
@ -464,5 +441,3 @@
(do (let [hf (handlers/file-handler)] (hf sig) (hf)))
(do (let [hf (handlers/console-handler)] (hf sig) (hf)))
#?(:cljs (let [hf (handlers/raw-console-handler)] (hf sig) (hf)))))
;;;;

View file

@ -1,6 +1,6 @@
(ns ^:no-doc taoensso.telemere.handlers.open-telemetry
"Private ns, implementation detail.
Core OpenTelemetry handlers.
(ns taoensso.telemere.handlers.open-telemetry
"Core OpenTelemetry handler and utils.
Telemere will attempt to load this ns automatically when possible.
Needs `OpenTelemetry Java`,
Ref. <https://github.com/open-telemetry/opentelemetry-java>."
@ -8,7 +8,9 @@
(:require
[clojure.string :as str]
[taoensso.encore :as enc :refer [have have?]]
[taoensso.telemere.utils :as utils])
[taoensso.telemere.utils :as utils]
[taoensso.telemere.impl :as impl]
[taoensso.telemere :as tel])
(:import
[io.opentelemetry.api.logs LoggerProvider Severity]
@ -21,7 +23,7 @@
;;;; Implementation
(defn level->severity
(defn- level->severity
^Severity [level]
(case level
:trace Severity/TRACE
@ -33,7 +35,7 @@
:report Severity/INFO4
Severity/UNDEFINED_SEVERITY_NUMBER))
(def ^String attr-name
(def ^:private ^String attr-name
"Returns cached OpenTelemetry-style name: `:foo/bar-baz` -> \"foo_bar_baz\", etc.
Ref. <https://opentelemetry.io/docs/specs/semconv/general/attribute-naming/>."
(enc/fmemoize
@ -49,7 +51,7 @@
(comment (enc/qb 1e6 (attr-name :x1.x2/x3-x4 :Foo/Bar-BAZ))) ; 63.6
;; AttributeTypes: String, Long, Double, Boolean, and arrays
(defprotocol IAttr+ (attr+ [_aval akey builder]))
(defprotocol IAttr+ (^:private attr+ [_aval akey builder]))
(extend-protocol IAttr+
nil (attr+ [v k ^AttributesBuilder b] (.put b (attr-name k) "nil")) ; Like pr-edn*
Boolean (attr+ [v k ^AttributesBuilder b] (.put b (attr-name k) v))
@ -78,7 +80,7 @@
Object (attr+ [v k ^AttributesBuilder b] (.put b (attr-name k) (enc/pr-edn* v))))
(defn as-attrs
(defn- as-attrs
"Returns `io.opentelemetry.api.common.Attributes` for given map."
^Attributes [m]
(if (empty? m)
@ -89,7 +91,7 @@
(comment (str (as-attrs {:s "s", :kw :foo/bar, :long 5, :double 5.0, :longs [5 5 5] :nil nil})))
(defn merge-prefix-map
(defn- merge-prefix-map
"Merges prefixed `from` into `to`."
[to prefix from]
(enc/cond
@ -103,7 +105,7 @@
(comment (merge-prefix-map {} "data" {:a/b1 "v1" :a/b2 "v2" :nil nil}))
(defn signal->attrs-map
(defn- signal->attrs-map
"Returns attributes map for given signal,
Ref. <https://opentelemetry.io/docs/specs/otel/logs/data-model/>."
[attrs-key signal]
@ -181,7 +183,7 @@
;;;; Handler
(defn ^:public handler:open-telemetry-logger
(defn handler:open-telemetry-logger
"Experimental, subject to change!! Feedback very welcome!
Returns a (fn handler [signal]) that:
@ -216,3 +218,10 @@
(.setSeverity severity)
(.setBody msg)
(.setAllAttributes attrs)))))))))
;;;;
(impl/on-init
(when impl/auto-handlers?
(when-let [handler (enc/catching (handler:open-telemetry-logger))]
(tel/add-handler! :default/open-telemetry-logger handler))))

View file

@ -21,8 +21,18 @@
;;;; Utils
(def auto-handlers? (enc/get-env {:as :bool, :default true} :taoensso.telemere/auto-handlers))
#?(:clj (defmacro threaded [& body] `(let [t# (Thread. (fn [] ~@body))] (.start t#) t#)))
#?(:clj
(defmacro on-init [& body]
(let [sym (with-meta '__on-init {:private true})
compiling? (if (:ns &env) false `*compile-files*)]
`(defonce ~sym (when-not ~compiling? ~@body nil)))))
(comment (macroexpand-1 '(on-init (println "foo"))))
;;;; Config
#?(:clj

View file

@ -1,23 +1,26 @@
(ns ^:no-doc taoensso.telemere.streams
"Private ns, implementation detail.
Intake support: standard stream/s -> Telemere."
(ns taoensso.telemere.streams
"Intake support for standard stream/s -> Telemere."
(:refer-clojure :exclude [binding])
(:require
[taoensso.encore :as enc :refer [binding have have?]]
[taoensso.telemere.impl :as impl]))
(enc/defonce orig-*out* "Original `*out*` on ns load" *out*)
(enc/defonce orig-*err* "Original `*err*` on ns load" *err*)
(enc/defonce ^:dynamic prev-*out* "Previous `*out*` (prior to any Telemere binds)" nil)
(enc/defonce ^:dynamic prev-*err* "Previous `*err*` (prior to any Telemere binds)" nil)
(enc/defonce ^:private orig-*out* "Original `*out*` on ns load" *out*)
(enc/defonce ^:private orig-*err* "Original `*err*` on ns load" *err*)
(enc/defonce ^:no-doc ^:dynamic prev-*out* "Previous `*out*` (prior to any Telemere binds)" nil)
(enc/defonce ^:no-doc ^:dynamic prev-*err* "Previous `*err*` (prior to any Telemere binds)" nil)
(def ^:private ^:const default-out-opts {:kind :system/out, :level :info})
(def ^:private ^:const default-err-opts {:kind :system/err, :level :error})
(defn osw ^java.io.OutputStreamWriter [x] (java.io.OutputStreamWriter. x))
(defn ^:no-doc osw
"Private, don't use."
^java.io.OutputStreamWriter [x]
(java.io.OutputStreamWriter. x))
(defn telemere-print-stream
"Returns a `java.io.PrintStream` that will flush to Telemere signals with given opts."
(defn ^:no-doc telemere-print-stream
"Private, don't use.
Returns a `java.io.PrintStream` that will flush to Telemere signals with given opts."
^java.io.PrintStream [{:as sig-opts :keys [kind level id]}]
(let [baos
(proxy [java.io.ByteArrayOutputStream] []
@ -42,6 +45,8 @@
(java.io.PrintStream. baos true ; Auto flush
java.nio.charset.StandardCharsets/UTF_8)))
;;;;
(defmacro ^:public with-out->telemere
"Executes form with `*out*` bound to flush to Telemere signals with given opts."
([ form] `(with-out->telemere nil ~form))
@ -143,14 +148,21 @@
(streams->telemere! {})
(streams->reset!))
(impl/add-intake-check! :system/out
(fn []
(let [sending? (boolean @orig-out_)
receiving? (and sending? (impl/test-intake! "`System/out` -> Telemere" #(.println System/out %)))]
{:sending->telemere? sending?, :telemere-receiving? receiving?})))
;;;;
(impl/add-intake-check! :system/err
(fn []
(let [sending? (boolean @orig-err_)
receiving? (and sending? (impl/test-intake! "`System/err` -> Telemere" #(.println System/err %)))]
{:sending->telemere? sending?, :telemere-receiving? receiving?})))
(defn check-out-intake
"Returns {:keys [sending->telemere? telemere-receiving?]}."
[]
(let [sending? (boolean @orig-out_)
receiving? (and sending? (impl/test-intake! "`System/out` -> Telemere" #(.println System/out %)))]
{:sending->telemere? sending?, :telemere-receiving? receiving?}))
(defn check-err-intake
"Returns {:keys [sending->telemere? telemere-receiving?]}."
[]
(let [sending? (boolean @orig-err_)
receiving? (and sending? (impl/test-intake! "`System/err` -> Telemere" #(.println System/err %)))]
{:sending->telemere? sending?, :telemere-receiving? receiving?}))
(impl/add-intake-check! :system/out check-out-intake)
(impl/add-intake-check! :system/err check-err-intake)

View file

@ -1,6 +1,6 @@
(ns ^:no-doc taoensso.telemere.tools-logging
"Private ns, implementation detail.
Intake support: `clojure.tools.logging` -> Telemere."
(ns taoensso.telemere.tools-logging
"Intake support for `clojure.tools.logging` -> Telemere.
Telemere will attempt to load this ns automatically when possible."
(:require
[taoensso.encore :as enc :refer [have have?]]
[taoensso.telemere.impl :as impl]
@ -36,7 +36,7 @@
(name [_ ] "taoensso.telemere")
(get-logger [_ logger-ns] (TelemereLogger. (str logger-ns))))
(defn ^:public tools-logging->telemere!
(defn tools-logging->telemere!
"Configures `clojure.tools.logging` to use Telemere as its logging implementation.
Called automatically if the following is true:
@ -53,19 +53,30 @@
(alter-var-root #'clojure.tools.logging/*logger-factory*
(fn [_] (TelemereLoggerFactory.))))
(defn tools-logging-factory [] (TelemereLoggerFactory.))
(defn tools-logging->telemere? []
(defn tools-logging->telemere?
"Returns true iff `clojure.tools.logging` is configured to use Telemere
as its logging implementation."
[]
(when-let [lf clojure.tools.logging/*logger-factory*]
(instance? TelemereLoggerFactory lf)))
(impl/add-intake-check! :tools-logging
(fn []
(let [sending? (tools-logging->telemere?)
receiving?
(and sending?
(impl/test-intake! "`clojure.tools.logging` -> Telemere"
#(clojure.tools.logging/info %)))]
;;;;
{:present? true
:sending->telemere? sending?
:telemere-receiving? receiving?})))
(defn check-intake
"Returns {:keys [present? sending->telemere? telemere-receiving?]}."
[]
(let [sending? (tools-logging->telemere?)
receiving?
(and sending?
(impl/test-intake! "`clojure.tools.logging` -> Telemere"
#(clojure.tools.logging/info %)))]
{:present? true
:sending->telemere? sending?
:telemere-receiving? receiving?}))
(impl/add-intake-check! :tools-logging check-intake)
(impl/on-init
(when (enc/get-env {:as :bool} :clojure.tools.logging-to-telemere?)
(tools-logging->telemere!)))

View file

@ -8,10 +8,12 @@
:refer [signal! with-signal with-signals]
:rename {signal! sig!, with-signal with-sig, with-signals with-sigs}]
[taoensso.telemere.utils :as utils]
[taoensso.telemere.timbre :as timbre]
#?(:clj [taoensso.telemere.slf4j :as slf4j])
#?(:clj [clojure.tools.logging :as ctl])
[taoensso.telemere.utils :as utils]
[taoensso.telemere.timbre :as timbre]
#_[taoensso.telemere.tools-logging :as tools-logging]
#_[taoensso.telemere.streams :as streams]
#?(:clj [taoensso.telemere.slf4j :as slf4j])
#?(:clj [clojure.tools.logging :as ctl])
#?(:default [taoensso.telemere.handlers.console :as handlers:console])
#?(:clj [taoensso.telemere.handlers.file :as handlers:file])
@ -577,9 +579,9 @@
(is (sm? (with-sig (-> (.atInfo sl) (.addKeyValue "k1" "v1") (.addKeyValue "k2" "v2") (.log))) {:data {:slf4j/kvs {"k1" "v1", "k2" "v2"}}}) "Fluent API: kvs")
(testing "Markers"
(let [m1 (slf4j/est-marker! "M1")
m2 (slf4j/est-marker! "M2")
cm (slf4j/est-marker! "Compound" "M1" "M2")]
(let [m1 (#'slf4j/est-marker! "M1")
m2 (#'slf4j/est-marker! "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 (-> (.atInfo sl) (.addMarker m1) (.addMarker cm) (.log))) {:data #:slf4j{:marker-names #{"Compound" "M1" "M2"}}}) "Fluent API: markers")]))
@ -813,26 +815,26 @@
#?(:clj
(deftest _open-telemetry
[(testing "attr-name"
[(is (= (handlers:otel/attr-name :foo) "foo"))
(is (= (handlers:otel/attr-name :foo-bar-baz) "foo_bar_baz"))
(is (= (handlers:otel/attr-name :foo/bar-baz) "foo.bar_baz"))
(is (= (handlers:otel/attr-name :Foo/Bar-BAZ) "foo.bar_baz"))
(is (= (handlers:otel/attr-name "Foo Bar-Baz") "foo_bar_baz"))
(is (= (handlers:otel/attr-name :x1.x2/x3-x4 :foo/bar-baz)
[(is (= (#'handlers:otel/attr-name :foo) "foo"))
(is (= (#'handlers:otel/attr-name :foo-bar-baz) "foo_bar_baz"))
(is (= (#'handlers:otel/attr-name :foo/bar-baz) "foo.bar_baz"))
(is (= (#'handlers:otel/attr-name :Foo/Bar-BAZ) "foo.bar_baz"))
(is (= (#'handlers:otel/attr-name "Foo Bar-Baz") "foo_bar_baz"))
(is (= (#'handlers:otel/attr-name :x1.x2/x3-x4 :foo/bar-baz)
"x1.x2.x3_x4.foo.bar_baz"))])
(testing "merge-prefix-map"
[(is (= (handlers:otel/merge-prefix-map nil "pf" nil) nil))
(is (= (handlers:otel/merge-prefix-map nil "pf" {}) nil))
(is (= (handlers:otel/merge-prefix-map {"a" "A"} "pf" {:a :A}) {"a" "A", "pf.a" :A}))
(is (= (handlers:otel/merge-prefix-map {} "pf"
[(is (= (#'handlers:otel/merge-prefix-map nil "pf" nil) nil))
(is (= (#'handlers:otel/merge-prefix-map nil "pf" {}) nil))
(is (= (#'handlers:otel/merge-prefix-map {"a" "A"} "pf" {:a :A}) {"a" "A", "pf.a" :A}))
(is (= (#'handlers:otel/merge-prefix-map {} "pf"
{:a/b1 "v1" :a/b2 "v2" :nil nil, :map {:k1 "v1"}})
{"pf.a.b1" "v1", "pf.a.b2" "v2", "pf.nil" nil, "pf.map" {:k1 "v1"}}))])
(testing "as-attrs"
(is (= (str
(handlers:otel/as-attrs
(#'handlers:otel/as-attrs
{:string "s", :keyword :foo/bar, :long 5, :double 5.0, :nil nil,
:longs [5 5.0 5.0],
:doubles [5.0 5 5],
@ -844,7 +846,7 @@
"{bools=[true, false, false], double=5.0, doubles=[5.0, 5.0, 5.0], keyword=\":foo/bar\", long=5, longs=[5, 5, 5], map=[[:k1 \"v1\"]], mixed=[5, \"5\", nil], nil=\"nil\", string=\"s\", strings=[\"a\", \"b\", \"c\"]}")))
(testing "signal->attrs-map"
(let [attrs-map handlers:otel/signal->attrs-map]
(let [attrs-map #'handlers:otel/signal->attrs-map]
[(is (= (attrs-map nil { }) {"error" false}))
(is (= (attrs-map :attrs {:attrs {:a1 :A1}}) {"error" false, :a1 :A1}))
(is

View file

@ -95,21 +95,21 @@ See section [3-Config](./3-Config) for customization.
> Signal handlers process created signals to *do something with them* (analyse them, write them to console/file/queue/db, etc.)
| Platform | Condition | Handler |
| -------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Clj | Always | [Console handler](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:console) that prints signals to `*out*` or `*err*`. |
| Cljs | Always | [Console handler](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:console) that prints signals to the **browser console**. |
| Clj | [OpenTelemetry API](https://mvnrepository.com/artifact/io.opentelemetry/opentelemetry-api) present | [OpenTelemetry handler](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:open-telemetry-logger) that emits signals as log records to a configured [`LoggerProvider`](https://opentelemetry.io/docs/specs/otel/logs/sdk/#loggerprovider). |
| Platform | Condition | Handler |
| -------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Clj | Always | [Console handler](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:console) that prints signals to `*out*` or `*err*`. |
| Cljs | Always | [Console handler](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:console) that prints signals to the **browser console**. |
| Clj      | [OpenTelemetry API](https://mvnrepository.com/artifact/io.opentelemetry/opentelemetry-api) present | [OpenTelemetry handler](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:open-telemetry-logger) that emits signals as log records to a configured [`LoggerProvider`](https://opentelemetry.io/docs/specs/otel/logs/sdk/#loggerprovider). |
**Default signal intakes**:
> Telemere can create signals from relevant **external API calls**, etc.
| Platform | Condition | Signals from |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- |
| Clj | [SLF4J API](https://mvnrepository.com/artifact/org.slf4j/slf4j-api) and [Telemere SLF4J backend](https://clojars.org/com.taoensso/slf4j-telemere) present | [SLF4J](https://www.slf4j.org/) logging calls. |
| Clj | [clojure.tools.logging](https://mvnrepository.com/artifact/org.clojure/tools.logging) present and [`tools-logging->telemere!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#tools-logging-%3Etelemere!) called | [clojure.tools.logging](https://github.com/clojure/tools.logging) logging calls. |
| Clj | [`streams->telemere!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#streams-%3Etelemere!) called | Output to `System/out` and `System/err` streams. |
| Platform | Condition | Signals from |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| Clj | [SLF4J API](https://mvnrepository.com/artifact/org.slf4j/slf4j-api) and [Telemere SLF4J backend](https://clojars.org/com.taoensso/slf4j-telemere) present | [SLF4J](https://www.slf4j.org/) logging calls. |
| Clj | [clojure.tools.logging](https://mvnrepository.com/artifact/org.clojure/tools.logging) present and [`tools-logging->telemere!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.tools-logging#tools-logging-%3Etelemere!) called | [clojure.tools.logging](https://github.com/clojure/tools.logging) logging calls. |
| Clj | [`streams->telemere!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#streams-%3Etelemere!) called | Output to `System/out` and `System/err` streams. |
Run [`check-intakes`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#check-intakes) to help verify/debug:

View file

@ -20,12 +20,12 @@ This flow is described by [`help:signal-flow`](https://cljdoc.org/d/com.taoensso
For more info see:
| Var | Help with |
| :-- | :-- |
| [`help:signal-creators`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-creators) | List of signal creators
| [`help:signal-options`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-options) | Options for signal creators
| [`help:signal-content`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-content) | Signal map content
| [`help:signal-flow`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-flow) | Ordered flow from signal creation to handling
| [`help:signal-filters`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-filters) | API for configuring signal filters
| [`help:signal-handlers`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-handlers) | API for configuring signal handlers
| [`help:signal-formatters`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-formatters) | Signal formatters for use by handlers
| Var | Help with |
| :-------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------- |
| [`help:signal-creators`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-creators) | List of signal creators |
| [`help:signal-options`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-options) | Options for signal creators |
| [`help:signal-content`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-content) | Signal map content |
| [`help:signal-flow`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-flow) | Ordered flow from signal creation to handling |
| [`help:signal-filters`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-filters) | API for configuring signal filters |
| [`help:signal-handlers`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-handlers) | API for configuring signal handlers |
| [`help:signal-formatters`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#help:signal-formatters) | Signal formatters for use by handlers |

View file

@ -29,9 +29,7 @@ See section [4-Handlers](./4-Handlers).
To do this:
1. Ensure that you have the `clojure.tools.logging` dependency, and
2. Call [`tools-logging->telemere!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#tools-logging-%3Etelemere!), or set the relevant system config as described in its docstring.
Note that the `tools-logging->telemere!` var will be present **only if** the `clojure.tools.logging` dependency is present.
2. Call [`tools-logging->telemere!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.tools-logging#tools-logging-%3Etelemere!), or set the relevant system config as described in its docstring.
Verify successful intake with [`check-intakes`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#check-intakes):
@ -90,9 +88,7 @@ Telemere can send signals as [`LogRecords`](https://opentelemetry.io/docs/specs/
To do this:
1. Ensure that you have the [OpenTelemetry Java](https://github.com/open-telemetry/opentelemetry-java) dependency.
2. Use [`handler:open-telemetry-logger`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:open-telemetry-logger) to create an appropriately configured handler, and register it with [`add-handler!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#add-handler!).
Note that the `handler:open-telemetry-logger` var will be present **only if** the OpenTelemetry Java dependency is present.
2. Use [`handler:open-telemetry-logger`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.open-telemetry#handler:open-telemetry-logger) to create an appropriately configured handler, and register it with [`add-handler!`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#add-handler!).
## Tufte

View file

@ -4,13 +4,13 @@ Signal handlers process created signals to *do something with them* (analyse the
The following handlers are included out-the-box:
| Name | Platform | Writes signals to | Writes signals as |
| :---------------------------------------------------------------------------------------------------------------------------------------- | :------- | :------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------- |
| [`handler:console`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:console) | Clj | `*out*` or `*err*` | String ([edn](https://github.com/edn-format/edn), JSON, formatted, etc.) |
| [`handler:console`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:console) | Cljs | Browser console | String ([edn](https://github.com/edn-format/edn), JSON, formatted, etc.) |
| [`handler:console-raw`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:console-raw) | Cljs | Browser console | Raw data (for [cljs-devtools](https://github.com/binaryage/cljs-devtools), etc.) |
| [`handler:file`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:file) | Clj | File/s on disk | String ([edn](https://github.com/edn-format/edn), JSON, formatted, etc.) |
| [`handler:open-telemetry-logger`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:open-telemetry-logger) | Clj | [OpenTelemetry](https://opentelemetry.io/) [Java client](https://github.com/open-telemetry/opentelemetry-java) | [LogRecord](https://opentelemetry.io/docs/specs/otel/logs/data-model/) |
| Name | Platform | Writes signals to | Writes signals as |
| :------------------------------------------------------------------------------------------------------------------------------------------------------- | :------- | :------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------- |
| [`handler:console`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:console) | Clj | `*out*` or `*err*` | String ([edn](https://github.com/edn-format/edn), JSON, formatted, etc.) |
| [`handler:console`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:console) | Cljs | Browser console | String ([edn](https://github.com/edn-format/edn), JSON, formatted, etc.) |
| [`handler:console-raw`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:console-raw) | Cljs | Browser console | Raw data (for [cljs-devtools](https://github.com/binaryage/cljs-devtools), etc.) |
| [`handler:file`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere#handler:file) | Clj | File/s on disk | String ([edn](https://github.com/edn-format/edn), JSON, formatted, etc.) |
| [`handler:open-telemetry-logger`](https://cljdoc.org/d/com.taoensso/telemere/CURRENT/api/taoensso.telemere.open-telemetry#handler:open-telemetry-logger) | Clj | [OpenTelemetry](https://opentelemetry.io/) [Java client](https://github.com/open-telemetry/opentelemetry-java) | [LogRecord](https://opentelemetry.io/docs/specs/otel/logs/data-model/) |
- See relevant docstrings (links above) for more info.
- See section [8-Community](8-Community.md) for additional handlers.