1616
1717package org .typelevel .log4cats .slf4j .internal
1818
19- import org .typelevel .log4cats .*
20- import cats .syntax .all .*
2119import cats .effect .*
22- import org .slf4j .Logger as JLogger
23- import org .slf4j .MDC
24-
25- import scala .annotation .nowarn
20+ import org .slf4j .{Logger as JLogger , MDC }
21+ import org .typelevel .log4cats .*
2622
2723private [slf4j] object Slf4jLoggerInternal {
2824
@@ -36,106 +32,127 @@ private[slf4j] object Slf4jLoggerInternal {
3632 def apply (t : Throwable )(msg : => String ): F [Unit ]
3733 }
3834
39- // Need this to make sure MDC is correctly cleared before logging
40- private [this ] def noContextLog [F [_]](isEnabled : F [Boolean ], logging : () => Unit )(implicit
41- F : Sync [F ]
42- ): F [Unit ] =
43- contextLog[F ](isEnabled, Map .empty, logging)
44-
45- private [this ] def contextLog [F [_]](
46- isEnabled : F [Boolean ],
47- ctx : Map [String , String ],
48- logging : () => Unit
49- )(implicit F : Sync [F ]): F [Unit ] = {
50-
51- val ifEnabled = F .delay {
52- val backup =
53- try MDC .getCopyOfContextMap()
54- catch {
55- case e : IllegalStateException =>
56- // MDCAdapter is missing, no point in doing anything with
57- // the MDC, so just hope the logging backend can salvage
58- // something.
59- logging()
60- throw e
61- }
62-
63- try {
64- // Once 2.12 is no longer supported, change this to MDC.setContextMap(ctx.asJava)
65- MDC .clear()
66- ctx.foreach { case (k, v) => MDC .put(k, v) }
67- logging()
68- } finally
69- if (backup eq null ) MDC .clear()
70- else MDC .setContextMap(backup)
71- }
72-
73- isEnabled.ifM(
74- ifEnabled,
75- F .unit
76- )
77- }
78-
79- @ nowarn(" msg=used" )
80- final class Slf4jLogger [F [_]](val logger : JLogger , sync : Sync .Type = Sync .Type .Delay )(implicit
35+ final class Slf4jLogger [F [_]](
36+ val logger : JLogger ,
37+ sync : Sync .Type ,
38+ defaultCtx : Map [String , String ]
39+ )(implicit
8140 F : Sync [F ]
8241 ) extends SelfAwareStructuredLogger [F ] {
8342
43+ def this (logger : JLogger , sync : Sync .Type = Sync .Type .Delay )(F : Sync [F ]) =
44+ this (logger, sync, Map .empty)(F )
45+
8446 @ deprecated(" Use constructor with sync" , " 2.6.0" )
8547 def this (logger : JLogger )(
8648 F : Sync [F ]
8749 ) =
8850 this (logger, Sync .Type .Delay )(F )
8951
90- override def isTraceEnabled : F [Boolean ] = F .delay(logger.isTraceEnabled)
91- override def isDebugEnabled : F [Boolean ] = F .delay(logger.isDebugEnabled)
92- override def isInfoEnabled : F [Boolean ] = F .delay(logger.isInfoEnabled)
93- override def isWarnEnabled : F [Boolean ] = F .delay(logger.isWarnEnabled)
94- override def isErrorEnabled : F [Boolean ] = F .delay(logger.isErrorEnabled)
52+ private val isTraceEnabledUnsafe = () => logger.isTraceEnabled
53+ private val isDebugEnabledUnsafe = () => logger.isDebugEnabled
54+ private val isInfoEnabledUnsafe = () => logger.isInfoEnabled
55+ private val isWarnEnabledUnsafe = () => logger.isWarnEnabled
56+ private val isErrorEnabledUnsafe = () => logger.isErrorEnabled
57+
58+ override def addContext (ctx : Map [String , String ]): SelfAwareStructuredLogger [F ] =
59+ new Slf4jLogger [F ](logger, sync, defaultCtx ++ ctx)
60+
61+ override def isTraceEnabled : F [Boolean ] =
62+ F .delay(withPreparedMDCUnsafe(Map .empty, isTraceEnabledUnsafe))
63+ override def isDebugEnabled : F [Boolean ] =
64+ F .delay(withPreparedMDCUnsafe(Map .empty, isDebugEnabledUnsafe))
65+ override def isInfoEnabled : F [Boolean ] =
66+ F .delay(withPreparedMDCUnsafe(Map .empty, isInfoEnabledUnsafe))
67+ override def isWarnEnabled : F [Boolean ] =
68+ F .delay(withPreparedMDCUnsafe(Map .empty, isWarnEnabledUnsafe))
69+ override def isErrorEnabled : F [Boolean ] =
70+ F .delay(withPreparedMDCUnsafe(Map .empty, isErrorEnabledUnsafe))
9571
9672 override def trace (t : Throwable )(msg : => String ): F [Unit ] =
97- noContextLog(isTraceEnabled , () => logger.trace(msg, t))
73+ noContextLog(isTraceEnabledUnsafe , () => logger.trace(msg, t))
9874 override def trace (msg : => String ): F [Unit ] =
99- noContextLog(isTraceEnabled , () => logger.trace(msg))
75+ noContextLog(isTraceEnabledUnsafe , () => logger.trace(msg))
10076 override def trace (ctx : Map [String , String ])(msg : => String ): F [Unit ] =
101- contextLog(isTraceEnabled , ctx, () => logger.trace(msg))
77+ contextLog(isTraceEnabledUnsafe , ctx, () => logger.trace(msg))
10278 override def trace (ctx : Map [String , String ], t : Throwable )(msg : => String ): F [Unit ] =
103- contextLog(isTraceEnabled , ctx, () => logger.trace(msg, t))
79+ contextLog(isTraceEnabledUnsafe , ctx, () => logger.trace(msg, t))
10480
10581 override def debug (t : Throwable )(msg : => String ): F [Unit ] =
106- noContextLog(isDebugEnabled , () => logger.debug(msg, t))
82+ noContextLog(isDebugEnabledUnsafe , () => logger.debug(msg, t))
10783 override def debug (msg : => String ): F [Unit ] =
108- noContextLog(isDebugEnabled , () => logger.debug(msg))
84+ noContextLog(isDebugEnabledUnsafe , () => logger.debug(msg))
10985 override def debug (ctx : Map [String , String ])(msg : => String ): F [Unit ] =
110- contextLog(isDebugEnabled , ctx, () => logger.debug(msg))
86+ contextLog(isDebugEnabledUnsafe , ctx, () => logger.debug(msg))
11187 override def debug (ctx : Map [String , String ], t : Throwable )(msg : => String ): F [Unit ] =
112- contextLog(isDebugEnabled , ctx, () => logger.debug(msg, t))
88+ contextLog(isDebugEnabledUnsafe , ctx, () => logger.debug(msg, t))
11389
11490 override def info (t : Throwable )(msg : => String ): F [Unit ] =
115- noContextLog(isInfoEnabled , () => logger.info(msg, t))
91+ noContextLog(isInfoEnabledUnsafe , () => logger.info(msg, t))
11692 override def info (msg : => String ): F [Unit ] =
117- noContextLog(isInfoEnabled , () => logger.info(msg))
93+ noContextLog(isInfoEnabledUnsafe , () => logger.info(msg))
11894 override def info (ctx : Map [String , String ])(msg : => String ): F [Unit ] =
119- contextLog(isInfoEnabled , ctx, () => logger.info(msg))
95+ contextLog(isInfoEnabledUnsafe , ctx, () => logger.info(msg))
12096 override def info (ctx : Map [String , String ], t : Throwable )(msg : => String ): F [Unit ] =
121- contextLog(isInfoEnabled , ctx, () => logger.info(msg, t))
97+ contextLog(isInfoEnabledUnsafe , ctx, () => logger.info(msg, t))
12298
12399 override def warn (t : Throwable )(msg : => String ): F [Unit ] =
124- noContextLog(isWarnEnabled , () => logger.warn(msg, t))
100+ noContextLog(isWarnEnabledUnsafe , () => logger.warn(msg, t))
125101 override def warn (msg : => String ): F [Unit ] =
126- noContextLog(isWarnEnabled , () => logger.warn(msg))
102+ noContextLog(isWarnEnabledUnsafe , () => logger.warn(msg))
127103 override def warn (ctx : Map [String , String ])(msg : => String ): F [Unit ] =
128- contextLog(isWarnEnabled , ctx, () => logger.warn(msg))
104+ contextLog(isWarnEnabledUnsafe , ctx, () => logger.warn(msg))
129105 override def warn (ctx : Map [String , String ], t : Throwable )(msg : => String ): F [Unit ] =
130- contextLog(isWarnEnabled , ctx, () => logger.warn(msg, t))
106+ contextLog(isWarnEnabledUnsafe , ctx, () => logger.warn(msg, t))
131107
132108 override def error (t : Throwable )(msg : => String ): F [Unit ] =
133- noContextLog(isErrorEnabled , () => logger.error(msg, t))
109+ noContextLog(isErrorEnabledUnsafe , () => logger.error(msg, t))
134110 override def error (msg : => String ): F [Unit ] =
135- noContextLog(isErrorEnabled , () => logger.error(msg))
111+ noContextLog(isErrorEnabledUnsafe , () => logger.error(msg))
136112 override def error (ctx : Map [String , String ])(msg : => String ): F [Unit ] =
137- contextLog(isErrorEnabled , ctx, () => logger.error(msg))
113+ contextLog(isErrorEnabledUnsafe , ctx, () => logger.error(msg))
138114 override def error (ctx : Map [String , String ], t : Throwable )(msg : => String ): F [Unit ] =
139- contextLog(isErrorEnabled, ctx, () => logger.error(msg, t))
115+ contextLog(isErrorEnabledUnsafe, ctx, () => logger.error(msg, t))
116+
117+ private def withPreparedMDCUnsafe [A ](extraCtx : Map [String , String ], body : () => A ): A = {
118+ val ctx = defaultCtx ++ extraCtx
119+ val backup =
120+ try MDC .getCopyOfContextMap()
121+ catch {
122+ case e : IllegalStateException =>
123+ // MDCAdapter is missing, no point in doing anything with
124+ // the MDC, so just hope the logging backend can salvage
125+ // something.
126+ body()
127+ throw e
128+ }
129+
130+ try {
131+ // Once 2.12 is no longer supported, change this to MDC.setContextMap(ctx.asJava)
132+ MDC .clear()
133+ ctx.foreach { case (k, v) => MDC .put(k, v) }
134+ body()
135+ } finally
136+ if (backup eq null ) MDC .clear()
137+ else MDC .setContextMap(backup)
138+ }
139+
140+ private def noContextLog (isEnabledUnsafe : () => Boolean , logging : () => Unit ): F [Unit ] =
141+ contextLog(isEnabledUnsafe, Map .empty, logging)
142+
143+ private def contextLog (
144+ isEnabledUnsafe : () => Boolean ,
145+ ctx : Map [String , String ],
146+ logging : () => Unit
147+ ): F [Unit ] =
148+ F .delay(
149+ withPreparedMDCUnsafe(
150+ ctx,
151+ () =>
152+ if (isEnabledUnsafe()) {
153+ logging()
154+ }
155+ )
156+ )
140157 }
141158}
0 commit comments