From ffea1a30ed07c314a0e0a399deef0dbae31d14bd Mon Sep 17 00:00:00 2001 From: Peter Taoussanis Date: Tue, 23 Apr 2024 11:25:15 +0200 Subject: [PATCH] [fix] Fix broken AOT support, add AOT tests Thanks to @AdamFrey for reporting this issue! Ref. 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. --- project.clj | 2 + slf4j/src/taoensso/telemere/slf4j.clj | 56 ++++++++++++------- src/taoensso/telemere.cljc | 45 ++++----------- .../telemere/handlers/open_telemetry.clj | 31 ++++++---- src/taoensso/telemere/impl.cljc | 10 ++++ src/taoensso/telemere/streams.clj | 52 ++++++++++------- src/taoensso/telemere/tools_logging.clj | 43 ++++++++------ test/taoensso/telemere_tests.cljc | 40 ++++++------- wiki/1-Getting-started.md | 20 +++---- wiki/2-Architecture.md | 18 +++--- wiki/3-Config.md | 8 +-- wiki/4-Handlers.md | 14 ++--- 12 files changed, 186 insertions(+), 153 deletions(-) diff --git a/project.clj b/project.clj index 82623e8..26a52c0 100644 --- a/project.clj +++ b/project.clj @@ -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 diff --git a/slf4j/src/taoensso/telemere/slf4j.clj b/slf4j/src/taoensso/telemere/slf4j.clj index 4aff376..b69a69f 100644 --- a/slf4j/src/taoensso/telemere/slf4j.clj +++ b/slf4j/src/taoensso/telemere/slf4j.clj @@ -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 #{}. 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"})) diff --git a/src/taoensso/telemere.cljc b/src/taoensso/telemere.cljc index 6f3cff4..94fe2cc 100644 --- a/src/taoensso/telemere.cljc +++ b/src/taoensso/telemere.cljc @@ -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))))) - -;;;; diff --git a/src/taoensso/telemere/handlers/open_telemetry.clj b/src/taoensso/telemere/handlers/open_telemetry.clj index 72d6c7d..4c00da0 100644 --- a/src/taoensso/telemere/handlers/open_telemetry.clj +++ b/src/taoensso/telemere/handlers/open_telemetry.clj @@ -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. ." @@ -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. ." (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. ." [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)))) diff --git a/src/taoensso/telemere/impl.cljc b/src/taoensso/telemere/impl.cljc index 0b59e24..aa0b074 100644 --- a/src/taoensso/telemere/impl.cljc +++ b/src/taoensso/telemere/impl.cljc @@ -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 diff --git a/src/taoensso/telemere/streams.clj b/src/taoensso/telemere/streams.clj index 192d447..c654ff9 100644 --- a/src/taoensso/telemere/streams.clj +++ b/src/taoensso/telemere/streams.clj @@ -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) diff --git a/src/taoensso/telemere/tools_logging.clj b/src/taoensso/telemere/tools_logging.clj index 9608f86..81c39c5 100644 --- a/src/taoensso/telemere/tools_logging.clj +++ b/src/taoensso/telemere/tools_logging.clj @@ -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!))) diff --git a/test/taoensso/telemere_tests.cljc b/test/taoensso/telemere_tests.cljc index 3af1c9d..7e1b3f7 100644 --- a/test/taoensso/telemere_tests.cljc +++ b/test/taoensso/telemere_tests.cljc @@ -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 diff --git a/wiki/1-Getting-started.md b/wiki/1-Getting-started.md index cdebdf6..6467888 100644 --- a/wiki/1-Getting-started.md +++ b/wiki/1-Getting-started.md @@ -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: diff --git a/wiki/2-Architecture.md b/wiki/2-Architecture.md index 0981e69..2b71207 100644 --- a/wiki/2-Architecture.md +++ b/wiki/2-Architecture.md @@ -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 \ No newline at end of file +| 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 | diff --git a/wiki/3-Config.md b/wiki/3-Config.md index ec45e9d..609372b 100644 --- a/wiki/3-Config.md +++ b/wiki/3-Config.md @@ -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 diff --git a/wiki/4-Handlers.md b/wiki/4-Handlers.md index 4daa0a7..0a0dfc5 100644 --- a/wiki/4-Handlers.md +++ b/wiki/4-Handlers.md @@ -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.