Skip to content

Commit

Permalink
[new] [#374] Add OpenTelemetry Protocol (OTLP) community appender (@d…
Browse files Browse the repository at this point in the history
…evurandom)

Once <steffan-westcott/clj-otel#8> is implemented,
the actual log emission should be replaced with clj-otel's API.
  • Loading branch information
devurandom authored and ptaoussanis committed Feb 23, 2024
1 parent e00f8ec commit 7b4356a
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 1 deletion.
2 changes: 1 addition & 1 deletion project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
[server-socket "1.0.0"]
[org.zeromq/cljzmq "0.1.4"]
[cljs-node-io "1.1.2"] ; Node spit appender
]}
[com.github.steffan-westcott/clj-otel-api "0.2.6"]]}

:dev [:dev+ :community #_:deploy]
:dev+
Expand Down
104 changes: 104 additions & 0 deletions src/taoensso/timbre/appenders/community/otlp.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
(ns taoensso.timbre.appenders.community.otlp
"OpenTelemetry Protocol (OTLP) appender.
Requires <https://github.com/steffan-westcott/clj-otel>.
== For use WITH OpenTelemetry Java Agent ==
Setup a Java Agent appender, e.g.:
(otlp/appender {:logger-provider (.getLogsBridge (GlobalOpenTelemetry/get))})
For agent v1.x: enable the logs exporter with `OTEL_LOGS_EXPORTER=otlp`.
For agent v2.x: the logs exporter should be enabled by default [1].
== For use WITHOUT OpenTelemetry Java Agent ==
Setup an \"autoconfiguration\" appender, e.g.:
(otlp/appender
{:logger-provider
(.getSdkLoggerProvider
(.getOpenTelemetrySdk
(.build (AutoConfiguredOpenTelemetrySdk/builder))))})
You'll need the following on your classpath:
`io.opentelemetry/opentelemetry-sdk-extension-autoconfigure`,
`io.opentelemetry/opentelemetry-exporter-otlp`.
If you already have an instance of `GlobalOpenTelemetry` (e.g. created
by agent), you'll need to prevent setting the newly-created SDK as the
global default:
(.build
(doto (AutoConfiguredOpenTelemetrySdk/builder)
(.setResultAsGlobal false)))
[1] Ref. <https://shorturl.at/fgoP3>, <https://shorturl.at/pruB5>"
{:author "Dennis Schridde (@devurandom)"}
(:require
[steffan-westcott.clj-otel.api.attributes :as attr]
[taoensso.encore :as enc])

(:import
[io.opentelemetry.api.logs LoggerProvider Severity]))

(comment (set! *warn-on-reflection* true))

(def ^:private default-severity Severity/INFO)
(def ^:private timbre->otlp-levels
{:trace Severity/TRACE
:debug Severity/DEBUG
:info Severity/INFO
:warn Severity/WARN
:error Severity/ERROR
:fatal Severity/FATAL
:report default-severity})

(defn- single-map [xs]
(let [[x & r] xs]
(when (and (map? x) (not r))
x)))

(defn- assoc-some-nx
([m k v ] (if (contains? m k) m (enc/assoc-some m k v)))
([m k v & kvs] (enc/reduce-kvs assoc-some-nx (enc/assoc-some m k v) kvs))
([m kvs]
(reduce-kv
(fn [m k v] (if (contains? m k) m (enc/assoc-some m k v)))
(if (nil? m) {} m)
kvs)))

(defn appender
"Returns a `com.github.steffan-westcott/clj-otel-api` appender."
[{:keys [^LoggerProvider logger-provider]}]
{:enabled? true
:async? true
:min-level nil
:rate-limit nil
:output-fn :inherit
:fn
(fn [{:keys [^java.util.Date instant level ^String ?ns-str
?file ?line ?err vargs msg_ context]}]

(let [timestamp (.toInstant instant)
severity (get timbre->otlp-levels level default-severity)
arg (single-map vargs)
message (if-let [msg (:msg arg)] msg (force msg_))
?ex-data (ex-data ?err)
extra
(assoc-some-nx context
:file ?file
:line ?line
:ex-data ?ex-data)

event (merge (dissoc arg :msg) extra)
attributes (attr/->attributes event)

;; TODO Use clj-otel once it supports the logs API,
;; Ref. <https://github.com/steffan-westcott/clj-otel/issues/8>
logger (.get logger-provider ?ns-str)]

(.emit
(doto (.logRecordBuilder logger)
(.setAllAttributes attributes)
(.setTimestamp timestamp)
(.setBody message)
(.setSeverity severity)
(.setSeverityText (.toString severity))))))})

0 comments on commit 7b4356a

Please sign in to comment.