11package works .bosk .logback ;
22
33import ch .qos .logback .classic .Level ;
4- import ch .qos .logback .classic .spi . ILoggingEvent ;
5- import ch .qos .logback .core . filter . Filter ;
4+ import ch .qos .logback .classic .Logger ;
5+ import ch .qos .logback .classic . turbo . TurboFilter ;
66import ch .qos .logback .core .spi .FilterReply ;
77import java .util .Map ;
88import java .util .concurrent .ConcurrentHashMap ;
99import java .util .stream .Stream ;
10- import org .slf4j .Logger ;
1110import org .slf4j .LoggerFactory ;
1211import org .slf4j .MDC ;
12+ import org .slf4j .Marker ;
1313import works .bosk .Bosk ;
1414import works .bosk .DriverFactory ;
1515import works .bosk .StateTreeNode ;
2020import static java .util .stream .Collectors .toMap ;
2121import static works .bosk .logging .MdcKeys .BOSK_INSTANCE_ID ;
2222
23- public class BoskLogFilter extends Filter <ILoggingEvent > {
23+ /**
24+ * A Logback {@link TurboFilter} that provides per-bosk logging control.
25+ * Intended to suppress expected warnings and errors during testing.
26+ * <p>
27+ * A {@link Bosk} configured whose driver stack includes {@link #withController}
28+ * will be able to set log levels using {@link LogController#setLogging}
29+ * without affecting other logs.
30+ * <p>
31+ * This class infers that a log message is associated with a particular bosk
32+ * by checking the MDC for the key {@link MdcKeys#BOSK_INSTANCE_ID},
33+ * which you can set using {@link works.bosk.logging.MappedDiagnosticContext#setupMDC setupMDC}.
34+ * <p>
35+ * <em>Note</em>: most bosk drivers don't set this MDC key. TODO: Improve this.
36+ * <p>
37+ * Log levels are determined using the following precedence:
38+ * <ol>
39+ * <li>
40+ * If the specific logger is configured with some level,
41+ * that level is used;
42+ * </li>
43+ * <li>
44+ * otherwise, if the logger is associated with a bosk whose driver
45+ * was configured with {@link #withController} and that controller
46+ * has an override for that specific logger, that override is used;
47+ * </li>
48+ * <li>
49+ * otherwise, the usual Logback rules apply, which means
50+ * that the logger inherits the level from its ancestors.
51+ * </li>
52+ * </ol>
53+ *
54+ */
55+ public class BoskLogFilter extends TurboFilter {
2456 private static final ConcurrentHashMap <String , LogController > controllersByBoskID = new ConcurrentHashMap <>();
2557
2658 public static final class LogController {
@@ -57,7 +89,11 @@ public static <R extends StateTreeNode> DriverFactory<R> withController(LogContr
5789 }
5890
5991 @ Override
60- public FilterReply decide (ILoggingEvent event ) {
92+ public FilterReply decide (Marker marker , Logger logger , Level messageLevel , String format , Object [] params , Throwable t ) {
93+ if (logger .getLevel () != null ) {
94+ // Respect user-supplied log levels
95+ return NEUTRAL ;
96+ }
6197 String boskID = MDC .get (BOSK_INSTANCE_ID );
6298 if (boskID == null ) {
6399 return NEUTRAL ;
@@ -66,16 +102,18 @@ public FilterReply decide(ILoggingEvent event) {
66102 if (controller == null ) {
67103 return NEUTRAL ;
68104 }
69- Level level = controller .overrides .get (event . getLoggerName ());
70- if (level == null ) {
105+ Level overrideLevel = controller .overrides .get (logger . getName ());
106+ if (overrideLevel == null ) {
71107 return NEUTRAL ;
72108 }
73- if ( event . getLevel (). isGreaterOrEqual ( level )) {
74- return NEUTRAL ;
75- } else {
109+
110+ // There is an override. Deny if the message's level is too low.
111+ if ( overrideLevel . isGreaterOrEqual ( messageLevel )) {
76112 return DENY ;
113+ } else {
114+ return NEUTRAL ;
77115 }
78116 }
79117
80- private static final Logger LOGGER = LoggerFactory .getLogger (BoskLogFilter .class );
118+ private static final org . slf4j . Logger LOGGER = LoggerFactory .getLogger (BoskLogFilter .class );
81119}
0 commit comments