-
Notifications
You must be signed in to change notification settings - Fork 23
/
adapter.clj
154 lines (137 loc) · 7.75 KB
/
adapter.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
(ns slf4j-timbre.adapter
(:gen-class :name com.github.fzakaria.slf4j.timbre.TimbreLoggerAdapter
:implements [org.slf4j.spi.LocationAwareLogger]
:state state
:init init
:constructors {[String] []})
(:require clojure.string
[taoensso.timbre :as timbre])
(:import com.github.fzakaria.slf4j.timbre.TimbreLoggerAdapter
org.slf4j.Marker
org.slf4j.helpers.MessageFormatter
org.slf4j.spi.LocationAwareLogger))
(defn -init
[logger-name]
[[] logger-name])
(defn -getName
[^TimbreLoggerAdapter this]
(.state this))
(defn- identify-caller ^StackTraceElement
[fqcn stack]
(letfn [(matches [^StackTraceElement el] (= 0 (clojure.string/index-of (str (.getClassName el) "$") (str fqcn "$"))))]
(->> stack
(drop-while (comp not matches))
(drop-while matches)
(first))))
(def override-level
"If non-nil, logging calls we make will be wrapped with `timbre/with-level` using the level keyword set here.
We use this to raise the minimum logging level to a sensibly high value until the timbre end user is able to set their own *config*."
(atom nil))
(defmacro wrap-override-level
[& body]
`(if @override-level
(binding [timbre/*config* (assoc timbre/*config* :min-level @override-level)]
~@body)
(do ~@body)))
(defmacro define-methods
"Defines the various overloads for a given logging method (e.g., -info).
Several have the same arity so we use an undocumented Clojure feature [1] to specify their type signatures.
This macro expands into a (do ...) sexpr containing a defn for each of the ten variants declared in the Logger interface.
[1] https://groups.google.com/d/embed/msg/clojure/KmNbLo8xTSs/d1Rs3Cs6DbAJ"
[method-name level]
`(do
~@(for [signature ["-String" "-String-Object" "-String-Object-Object" "-String-Object<>" "-String-Throwable"]
with-marker? [false true]]
(let [func-sym (symbol (str method-name (when with-marker? "-Marker") signature))
args-sym (gensym "args")
ns-str-sym (gensym "ns-str")
file-sym (gensym "file")
line-sym (gensym "line")]
`(defn ~func-sym [^TimbreLoggerAdapter this# & ~args-sym]
(when (timbre/may-log? ~level (.getName this#))
(let [context# {:marker ~(when with-marker? `(when-let [^Marker marker# (first ~args-sym)] (.getName marker#)))}
; we check for a null Marker above to work around a bug in log4j-over-slf4j
; see https://jira.qos.ch/projects/SLF4J/issues/SLF4J-432
~args-sym ~(if with-marker? `(rest ~args-sym) args-sym)
stack# (.getStackTrace (Thread/currentThread))
caller# (identify-caller (.getName (.getClass this#)) stack#)
~ns-str-sym (.getName this#)
~file-sym (.getFileName caller#)
~line-sym (.getLineNumber caller#)]
(wrap-override-level
(timbre/with-context+ context#
~(case signature
"-String"
`(let [[msg#] ~args-sym]
(timbre/log! ~level :p [msg#] {:?ns-str ~ns-str-sym :?file ~file-sym :?line ~line-sym}))
"-String-Object"
`(let [[fmt# o#] ~args-sym
ft# (MessageFormatter/format fmt# o#)]
(if-let [t# (.getThrowable ft#)]
(timbre/log! ~level :p [t# (.getMessage ft#)] {:?ns-str ~ns-str-sym :?file ~file-sym :?line ~line-sym})
(timbre/log! ~level :p [(.getMessage ft#)] {:?ns-str ~ns-str-sym :?file ~file-sym :?line ~line-sym})))
"-String-Object-Object"
`(let [[fmt# o1# o2#] ~args-sym
ft# (MessageFormatter/format fmt# o1# o2#)]
(if-let [t# (.getThrowable ft#)]
(timbre/log! ~level :p [t# (.getMessage ft#)] {:?ns-str ~ns-str-sym :?file ~file-sym :?line ~line-sym})
(timbre/log! ~level :p [(.getMessage ft#)] {:?ns-str ~ns-str-sym :?file ~file-sym :?line ~line-sym})))
"-String-Object<>"
`(let [[fmt# os#] ~args-sym
ft# (MessageFormatter/arrayFormat fmt# os#)]
(if-let [t# (.getThrowable ft#)]
(timbre/log! ~level :p [t# (.getMessage ft#)] {:?ns-str ~ns-str-sym :?file ~file-sym :?line ~line-sym})
(timbre/log! ~level :p [(.getMessage ft#)] {:?ns-str ~ns-str-sym :?file ~file-sym :?line ~line-sym})))
"-String-Throwable"
`(let [[msg# t#] ~args-sym]
(timbre/log! ~level :p [t# msg#] {:?ns-str ~ns-str-sym :?file ~file-sym :?line ~line-sym}))))))))))))
(define-methods "-error" :error)
(define-methods "-warn" :warn)
(define-methods "-info" :info)
(define-methods "-debug" :debug)
(define-methods "-trace" :trace)
(defn -log
"-log method of LocationAwareLogger interface used by log4j-over-slf4j etc"
[^TimbreLoggerAdapter this ^Marker marker fqcn level-const fmt arg-array t]
(let [levels {LocationAwareLogger/ERROR_INT :error
LocationAwareLogger/WARN_INT :warn
LocationAwareLogger/INFO_INT :info
LocationAwareLogger/DEBUG_INT :debug
LocationAwareLogger/TRACE_INT :trace}
level-keyword (levels level-const)]
(when (timbre/may-log? level-keyword (.getName this))
(let [stack (.getStackTrace (Thread/currentThread))
caller (identify-caller fqcn stack)
message (.getMessage (MessageFormatter/arrayFormat fmt arg-array))]
(wrap-override-level
(timbre/with-context+ {:marker (when marker (.getName marker))}
(if caller ; nil when fqcn provided is incorrect (not present in call stack)
(if t
(timbre/log! level-keyword :p
[t message]
{:?ns-str (.getName this)
:?file (.getFileName caller)
:?line (.getLineNumber caller)})
(timbre/log! level-keyword :p
[message]
{:?ns-str (.getName this)
:?file (.getFileName caller)
:?line (.getLineNumber caller)}))
(if t
(timbre/log! level-keyword :p [t message] {:?ns-str (.getName this)})
(timbre/log! level-keyword :p [message] {:?ns-str (.getName this)})))))))))
(defn -isErrorEnabled
([^TimbreLoggerAdapter this] (boolean (timbre/may-log? :error (.getName this))))
([^TimbreLoggerAdapter this _] (boolean (timbre/may-log? :error (.getName this)))))
(defn -isWarnEnabled
([^TimbreLoggerAdapter this] (boolean (timbre/may-log? :warn (.getName this))))
([^TimbreLoggerAdapter this _] (boolean (timbre/may-log? :warn (.getName this)))))
(defn -isInfoEnabled
([^TimbreLoggerAdapter this] (boolean (timbre/may-log? :info (.getName this))))
([^TimbreLoggerAdapter this _] (boolean (timbre/may-log? :info (.getName this)))))
(defn -isDebugEnabled
([^TimbreLoggerAdapter this] (boolean (timbre/may-log? :debug (.getName this))))
([^TimbreLoggerAdapter this _] (boolean (timbre/may-log? :debug (.getName this)))))
(defn -isTraceEnabled
([^TimbreLoggerAdapter this] (boolean (timbre/may-log? :trace (.getName this))))
([^TimbreLoggerAdapter this _] (boolean (timbre/may-log? :trace (.getName this)))))