Skip to content

Commit 7ff8aaa

Browse files
Tweak Log4j appender; add util fns
- Correct logic to always dissoc event.name from attributes when it is captured - Move common logging config to new namespace - Add common util fns
1 parent 43df298 commit 7ff8aaa

File tree

3 files changed

+136
-84
lines changed
  • clj-otel-adapter-log4j/src/steffan_westcott/clj_otel/adapter
  • clj-otel-api/src/steffan_westcott/clj_otel

3 files changed

+136
-84
lines changed

clj-otel-adapter-log4j/src/steffan_westcott/clj_otel/adapter/log4j.clj

Lines changed: 59 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
[steffan-westcott.clj-otel.api.logs.log-record :as log-record]
55
[steffan-westcott.clj-otel.api.trace.span :as span]
66
[steffan-westcott.clj-otel.context :as context]
7-
[steffan-westcott.clj-otel.instrumentation.api.config :as inst-config])
7+
[steffan-westcott.clj-otel.instrumentation.api.config :as inst-config]
8+
[steffan-westcott.clj-otel.instrumentation.api.config.logging :as config-logging]
9+
[steffan-westcott.clj-otel.util :as util])
810
(:import (io.opentelemetry.api.baggage BaggageEntry)
911
(io.opentelemetry.api.logs Severity)
1012
(io.opentelemetry.api.trace Span SpanContext TraceFlags TraceState)
1113
(io.opentelemetry.semconv.incubating ThreadIncubatingAttributes)
1214
(java.util.concurrent TimeUnit)
13-
(org.apache.logging.log4j Level)
1415
(org.apache.logging.log4j.core LogEvent)
1516
(org.apache.logging.log4j.core.time Instant)
1617
(org.apache.logging.log4j.message MapMessage Message)
@@ -24,60 +25,37 @@
2425
StandardLevel/INFO Severity/INFO
2526
StandardLevel/WARN Severity/WARN
2627
StandardLevel/ERROR Severity/ERROR
27-
StandardLevel/FATAL Severity/FATAL
28-
StandardLevel/OFF Severity/UNDEFINED_SEVERITY_NUMBER})
29-
30-
(defn- trace-id-key
31-
[]
32-
(inst-config/get-string "otel.instrumentation.common.logging.trace-id" "trace_id"))
33-
34-
(defn- span-id-key
35-
[]
36-
(inst-config/get-string "otel.instrumentation.common.logging.span-id" "span_id"))
37-
38-
(defn- trace-flags-key
39-
[]
40-
(inst-config/get-string "otel.instrumentation.common.logging.trace-flags" "trace_flags"))
28+
StandardLevel/FATAL Severity/FATAL})
4129

4230
(defn- add-baggage?
4331
[]
4432
(inst-config/get-boolean "otel.instrumentation.log4j-context-data.add-baggage" false))
4533

46-
(defn- assoc-all!
47-
[m kvs]
48-
(reduce (fn [m [k v]]
49-
(assoc! m k v))
50-
m
51-
kvs))
52-
5334
(defn context-data
5435
"Returns a string map to add to Log4j context data. This includes trace id,
5536
span id, trace flags and baggage (if enabled). The data is taken from the
5637
span in the bound or current context."
5738
[]
5839
(let [span-context (span/get-span-context)]
59-
(if (.isValid span-context)
60-
(let [cdata (transient {(trace-id-key) (.getTraceId span-context)
61-
(span-id-key) (.getSpanId span-context)
62-
(trace-flags-key) (.asHex (.getTraceFlags span-context))})
63-
cdata (reduce (fn [m [k ^BaggageEntry v]]
64-
(assoc! m
65-
(str "baggage." k)
66-
(.getValue v)))
67-
cdata
68-
(when (add-baggage?)
69-
(.asMap (baggage/get-baggage))))]
70-
(persistent! cdata))
71-
{})))
40+
(persistent! (cond-> (transient {})
41+
(add-baggage?) (util/into! (map (fn [[k v]] [(str "baggage." k)
42+
(.getValue ^BaggageEntry v)]))
43+
(.asMap (baggage/get-baggage)))
44+
(.isValid span-context) (assoc! (config-logging/trace-id-key)
45+
(.getTraceId span-context)
46+
(config-logging/span-id-key)
47+
(.getSpanId span-context)
48+
(config-logging/trace-flags-key)
49+
(.asHex (.getTraceFlags span-context)))))))
7250

7351
(defn- context-from-cdata
7452
"Returns a context containing a span with span context data stored in cdata.
7553
This is used in cases where `emit` may be evaluated in a different context
7654
to where the log record occurred i.e. asynchronously."
7755
[cdata]
78-
(let [trace-id (get cdata (trace-id-key))
79-
span-id (get cdata (span-id-key))
80-
trace-flags (get cdata (trace-flags-key))]
56+
(let [trace-id (get cdata (config-logging/trace-id-key))
57+
span-id (get cdata (config-logging/span-id-key))
58+
trace-flags (get cdata (config-logging/trace-flags-key))]
8159
(and trace-id
8260
span-id
8361
trace-flags
@@ -94,6 +72,13 @@
9472
logger-name
9573
"ROOT")))
9674

75+
(defn- get-timestamp
76+
[^LogEvent event]
77+
(when-some [^Instant instant (.getInstant event)]
78+
(let [nanos (+ (.toNanos TimeUnit/MILLISECONDS (.getEpochMillisecond instant))
79+
(.getNanoOfMillisecond instant))]
80+
[nanos TimeUnit/NANOSECONDS])))
81+
9782
(defn- get-source
9883
[^LogEvent event]
9984
(when-some [elem (.getSource event)]
@@ -103,21 +88,14 @@
10388
:line (when (> line 0)
10489
line)})))
10590

106-
(defn- get-timestamp
107-
[^LogEvent event]
108-
(when-some [^Instant instant (.getInstant event)]
109-
(let [nanos (+ (.toNanos TimeUnit/MILLISECONDS (.getEpochMillisecond instant))
110-
(.getNanoOfMillisecond instant))]
111-
[nanos TimeUnit/NANOSECONDS])))
112-
11391
(defn- ->log-record
11492
[^CljOtelAppender appender ^LogEvent event]
115-
(let [logger-name (get-logger-name event)
116-
cdata (.toMap (.getContextData event))
93+
(let [cdata (.toMap (.getContextData event))
11794
context (context/dyn)
11895
context (if (identical? (context/root) context)
11996
(context-from-cdata cdata) ; cover asynchronous case
12097
context)
98+
level (.getLevel event)
12199
^Message message (.getMessage event)
122100
map-message? (and message (instance? MapMessage message))
123101
body (when message
@@ -128,43 +106,48 @@
128106
body (if check-msg-attr?
129107
(.get ^MapMessage message "message")
130108
body)
131-
^Level level (.getLevel event)
132109
attributes (persistent!
133-
(cond-> (transient {})
134-
(.-experimentalAttrs appender) (assoc!
135-
ThreadIncubatingAttributes/THREAD_NAME
136-
(.getThreadName event)
137-
ThreadIncubatingAttributes/THREAD_ID
138-
(.getThreadId event))
139-
(and map-message? (.-mapMessageAttrs appender))
140-
(assoc-all! (->> (.getData ^MapMessage message)
141-
(remove (fn [[k _]]
142-
(and check-msg-attr? (= k "message"))))
143-
(map (fn [[k v]] [(str "log4j.map_message." k)
144-
(str v)]))))
145-
146-
(.-markerAttr appender) (assoc! "log4j.marker"
147-
(.getName (.getMarker event)))
148-
:always (assoc-all! (filter
149-
(fn [[k _]]
150-
(and (not (and (.-eventName appender)
151-
(= k "event.name")))
152-
(or (.-allCdataAttrs appender)
153-
(contains? (.-cdataAttrs appender) k))))
154-
cdata))))]
155-
{:logger-name logger-name
110+
(cond-> (util/into!
111+
(transient {})
112+
(filter
113+
(fn [[k _]]
114+
(or (.-captureAllContextDataAttributes appender)
115+
(contains? (.-captureContextDataAttributes appender) k))))
116+
cdata)
117+
(.-captureExperimentalAttributes appender)
118+
(assoc! ThreadIncubatingAttributes/THREAD_NAME
119+
(.getThreadName event)
120+
ThreadIncubatingAttributes/THREAD_ID
121+
(.getThreadId event))
122+
123+
(and map-message? (.-captureMapMessageAttributes appender))
124+
(util/into! (comp (remove (fn [[k _]]
125+
(and check-msg-attr? (= k "message"))))
126+
(map (fn [[k v]] [(str "log4j.map_message." k)
127+
(str v)])))
128+
(.getData ^MapMessage message))
129+
130+
(.-captureMarkerAttribute appender)
131+
(assoc! "log4j.marker" (.getName (.getMarker event)))))
132+
event-name (when (.-captureEventName appender)
133+
(get attributes "event.name"))
134+
attributes (cond-> attributes
135+
event-name (dissoc "event.name"))]
136+
{:logger-name (get-logger-name event)
156137
:context context
157-
:severity (and level (StandardLevel->Severity (.getStandardLevel level)))
138+
:severity (and level
139+
(get StandardLevel->Severity
140+
(.getStandardLevel level)
141+
Severity/UNDEFINED_SEVERITY_NUMBER))
158142
:severity-text (and level (.name level))
159143
:body body
160144
:attributes attributes
161145
:exception (.getThrown event)
162146
:thread nil ; thread added as attributes instead
163147
:timestamp (get-timestamp event)
164-
:source (when (.-codeAttrs appender)
148+
:source (when (.-captureCodeAttributes appender)
165149
(get-source event))
166-
:event-name (when (.-eventName appender)
167-
(get cdata "event.name"))}))
150+
:event-name event-name}))
168151

169152
(defn- emit
170153
[record]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
(ns steffan-westcott.clj-otel.instrumentation.api.config.logging
2+
"Common configuration properties for logging."
3+
(:require [steffan-westcott.clj-otel.instrumentation.api.config :as config]))
4+
5+
(defn trace-id-key
6+
"Returns logging context data key for trace id."
7+
[]
8+
(config/get-string "otel.instrumentation.common.logging.trace-id" "trace_id"))
9+
10+
(defn span-id-key
11+
"Returns logging context data key for span id."
12+
[]
13+
(config/get-string "otel.instrumentation.common.logging.span-id" "span_id"))
14+
15+
(defn trace-flags-key
16+
"Returns logging context data key for trace flags."
17+
[]
18+
(config/get-string "otel.instrumentation.common.logging.trace-flags" "trace_flags"))

clj-otel-api/src/steffan_westcott/clj_otel/util.clj

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,18 +61,63 @@
6161
.findFirst
6262
(.orElse nil)))))
6363

64-
(defmacro ^:private compile-if
64+
(defmacro compile-if
65+
"Evals `test` at macro expansion time and expands to `true-expr`
66+
or `false-expr` based on the result."
6567
[test true-expr false-expr]
66-
(if (eval test)
67-
`~true-expr
68-
`~false-expr))
69-
70-
(defn ^:private class-exists?
71-
[class-name]
68+
(if (try
69+
(eval test)
70+
(catch Throwable _
71+
false))
72+
`(do
73+
~true-expr)
74+
`(do
75+
~false-expr)))
76+
77+
(defmacro compile-when
78+
"Evals `test` at macro expansion time and expands to `true-expr`
79+
or nil based on the result."
80+
[test true-expr]
81+
`(compile-if ~test ~true-expr nil))
82+
83+
(def ^{:arglists '([^String class-name])} resolve-class
84+
"Returns the class with the given fully qualified name, or nil if not
85+
available on the current class loader."
86+
(memoize (fn [^String class-name]
87+
(try
88+
(Class/forName class-name)
89+
(catch Exception _)))))
90+
91+
(defn class-exists?
92+
"Returns true if the class with the given fully qualified name is available
93+
on the current class loader."
94+
[^String class-name]
95+
(boolean (resolve-class class-name)))
96+
97+
(defn has-method?
98+
"Returns true if the method with the given name and parameter types is
99+
available on the class."
100+
[^Class class ^String method-name & param-types]
72101
(boolean (try
73-
(Class/forName class-name)
102+
(.getMethod class method-name param-types)
74103
(catch Exception _))))
75104

105+
(defn resolve-instance?
106+
"Returns true if `x` is an instance of class with given name. This fn is safe
107+
to use with names of classes that may not be available on the class path."
108+
[^String class-name x]
109+
(boolean (some-> (resolve-class class-name)
110+
(instance? x))))
111+
112+
(def ^{:arglists '([^Class class ^String field-name])} accessible-field
113+
"Returns `java.lang.reflect.Field` with accessibility flag set to true for
114+
class field with given name, or nil if field not found."
115+
(memoize (fn [^Class class ^String field-name]
116+
(try
117+
(doto (.getDeclaredField class field-name)
118+
(.setAccessible true))
119+
(catch Exception _)))))
120+
76121
(defn fn-name
77122
"Returns the name of the currently executing function, using the StackWalker
78123
API first available in Java 9. If `StackWalker` is not available, returns
@@ -133,3 +178,9 @@
133178
(if (or (instance? ExecutionException e) (instance? CompletionException e))
134179
(or (ex-cause e) e)
135180
e))
181+
182+
(defn into!
183+
"Same as `into`, but for transient `to` collection."
184+
([to from] (reduce conj! to from))
185+
([to xf from] (transduce xf conj! to from)))
186+

0 commit comments

Comments
 (0)