Skip to content

Commit d0f0169

Browse files
Add example logging fns for SLF4J
This is a simple Clojure wrapper for a subset of SLF4J, for use in the examples.
1 parent 9064a1f commit d0f0169

File tree

3 files changed

+113
-0
lines changed

3 files changed

+113
-0
lines changed

build.clj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ clojure -A:deps -T:build help/doc"
5151
"examples/common/async"
5252
"examples/common/load-gen"
5353
"examples/common/log4j2.utils"
54+
"examples/common/slf4j.utils"
5455
"examples/common/system"
5556
"examples/countries-service"
5657
"examples/cube-app"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
;!zprint {:style [:respect-nl] :width 140}
2+
3+
{:paths ["src"]
4+
5+
:deps {com.github.steffan-westcott/clj-otel-api {:local/root "../../../clj-otel-api"}
6+
org.slf4j/slf4j-api {:mvn/version "2.0.17"}}}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
(ns example.common.slf4j.utils
2+
"Macros for SLF4J logging with bound, current or explicit context."
3+
(:require [steffan-westcott.clj-otel.context :as context]
4+
[steffan-westcott.clj-otel.util :as util])
5+
(:import (io.opentelemetry.context Context)
6+
(org.slf4j Logger LoggerFactory Marker)
7+
(org.slf4j.event Level)
8+
(org.slf4j.spi LoggingEventBuilder)))
9+
10+
(def ^:private levels
11+
{:trace Level/TRACE
12+
:debug Level/DEBUG
13+
:info Level/INFO
14+
:warn Level/WARN
15+
:error Level/ERROR})
16+
17+
(defn log*
18+
"Internal fn to use `logger` to log a `level` message specified by `more`.
19+
`more` is `context? marker* throwable? kvs? (message arg*)?` where `kvs` is a
20+
map with string keys and vals that are string or fn that returns a string,
21+
`message` is a string or fn that returns a string and each arg may be an
22+
object or fn that returns an object."
23+
[^Logger logger ^Level level & more]
24+
(let [builder (.makeLoggingEventBuilder logger level)
25+
[context more] (if (instance? Context (first more))
26+
[(first more) (rest more)]
27+
[nil more])
28+
[^LoggingEventBuilder builder more] (loop [^LoggingEventBuilder builder builder
29+
more more]
30+
(if (instance? Marker (first more))
31+
(recur (.addMarker builder (first more))
32+
(rest more))
33+
[builder more]))
34+
[^LoggingEventBuilder builder more] (if (instance? Throwable (first more))
35+
[(.setCause builder (first more)) (rest more)]
36+
[builder more])
37+
[^LoggingEventBuilder builder more] (if (map? (first more))
38+
[(reduce-kv
39+
(fn [^LoggingEventBuilder builder ^String k v]
40+
(if (fn? v)
41+
(.addKeyValue builder k (util/supplier v))
42+
(.addKeyValue builder k v)))
43+
builder
44+
(first more)) (rest more)]
45+
[builder more])
46+
[message & args] more
47+
^LoggingEventBuilder builder (if message
48+
(if (fn? message)
49+
(.setMessage builder (util/supplier message))
50+
(.setMessage builder (str message)))
51+
builder)
52+
^LoggingEventBuilder builder (reduce (fn [^LoggingEventBuilder builder arg]
53+
(if (fn? arg)
54+
(.addArgument builder (util/supplier arg))
55+
(.addArgument builder arg)))
56+
builder
57+
args)]
58+
(if context
59+
(context/with-context! context
60+
(.log builder))
61+
(.log builder))))
62+
63+
(defmacro log'
64+
"Internal macro to log a message."
65+
[^Level level & args]
66+
`(let [^Logger logger# (LoggerFactory/getLogger (str ~*ns*))]
67+
(when (.isEnabledForLevel logger# ~level)
68+
(log* logger# ~level ~@args))))
69+
70+
(defmacro log
71+
"Logs a `level` message specified by `more`. `more` is `marker* throwable?
72+
kvs? (message arg*)?` where `kvs` is a map with string keys and vals that
73+
are string or fn that returns a string, `message` is a string or fn that
74+
returns a string and each arg may be an object or fn that returns an object."
75+
[level & args]
76+
`(log' (get levels ~level Level/ERROR) ~@args))
77+
78+
(defmacro error
79+
"Write an ERROR message to the log. If first arg is a context, use as
80+
explicit context for the message. Otherwise, use bound or current context."
81+
[& args]
82+
`(log' Level/ERROR ~@args))
83+
84+
(defmacro warn
85+
"Write a WARN message to the log. If first arg is a context, use as
86+
explicit context for the message. Otherwise, use bound or current context."
87+
[& args]
88+
`(log' Level/WARN ~@args))
89+
90+
(defmacro info
91+
"Write an INFO message to the log. If first arg is a context, use as
92+
explicit context for the message. Otherwise, use bound or current context."
93+
[& args]
94+
`(log' Level/INFO ~@args))
95+
96+
(defmacro debug
97+
"Write a DEBUG message to the log. If first arg is a context, use as
98+
explicit context for the message. Otherwise, use bound or current context."
99+
[& args]
100+
`(log' Level/DEBUG ~@args))
101+
102+
(defmacro trace
103+
"Write a TRACE message to the log. If first arg is a context, use as
104+
explicit context for the message. Otherwise, use bound or current context."
105+
[& args]
106+
`(log' Level/TRACE ~@args))

0 commit comments

Comments
 (0)