diff --git a/bosk-core/src/main/java/works/bosk/logging/MdcKeys.java b/bosk-core/src/main/java/works/bosk/logging/MdcKeys.java
new file mode 100644
index 00000000..a579d4f0
--- /dev/null
+++ b/bosk-core/src/main/java/works/bosk/logging/MdcKeys.java
@@ -0,0 +1,14 @@
+package works.bosk.logging;
+
+/**
+ * Keys to use for SLF4J's Mapped Diagnostic Context.
+ *
+ * Evolution note: we're going to want to get organized in how we generate MDC.
+ * For now, only bosk-mongo uses these.
+ */
+public final class MdcKeys {
+ public static final String BOSK_NAME = "bosk.name";
+ public static final String BOSK_INSTANCE_ID = "bosk.instanceID";
+ public static final String EVENT = "bosk.MongoDriver.event";
+ public static final String TRANSACTION = "bosk.MongoDriver.transaction";
+}
diff --git a/bosk-logback/README.md b/bosk-logback/README.md
new file mode 100644
index 00000000..51bd9e1d
--- /dev/null
+++ b/bosk-logback/README.md
@@ -0,0 +1,4 @@
+## bosk-logback
+
+This is the subproject for the published `bosk-logback` library,
+containing extra support for the Logback implementation of SLF4J.
diff --git a/bosk-logback/build.gradle b/bosk-logback/build.gradle
new file mode 100644
index 00000000..0575a132
--- /dev/null
+++ b/bosk-logback/build.gradle
@@ -0,0 +1,20 @@
+
+plugins {
+ id 'bosk.development'
+ id 'bosk.maven-publish'
+ id 'com.github.spotbugs' version '5.1.5'
+}
+
+java {
+ sourceCompatibility = '17'
+ targetCompatibility = '17'
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ api project(":bosk-core")
+ implementation "ch.qos.logback:logback-classic:1.4.14"
+}
diff --git a/bosk-mongo/src/test/java/works/bosk/drivers/mongo/BoskLogFilter.java b/bosk-logback/src/main/java/works/bosk/logback/BoskLogFilter.java
similarity index 94%
rename from bosk-mongo/src/test/java/works/bosk/drivers/mongo/BoskLogFilter.java
rename to bosk-logback/src/main/java/works/bosk/logback/BoskLogFilter.java
index c60565c9..713e982f 100644
--- a/bosk-mongo/src/test/java/works/bosk/drivers/mongo/BoskLogFilter.java
+++ b/bosk-logback/src/main/java/works/bosk/logback/BoskLogFilter.java
@@ -1,4 +1,4 @@
-package works.bosk.drivers.mongo;
+package works.bosk.logback;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.spi.ILoggingEvent;
@@ -13,10 +13,12 @@
import works.bosk.Bosk;
import works.bosk.DriverFactory;
import works.bosk.StateTreeNode;
+import works.bosk.logging.MdcKeys;
import static ch.qos.logback.core.spi.FilterReply.DENY;
import static ch.qos.logback.core.spi.FilterReply.NEUTRAL;
import static java.util.stream.Collectors.toMap;
+import static works.bosk.logging.MdcKeys.BOSK_INSTANCE_ID;
public class BoskLogFilter extends Filter {
private static final ConcurrentHashMap controllersByBoskID = new ConcurrentHashMap<>();
@@ -62,7 +64,7 @@ public static DriverFactory withController(LogContr
@Override
public FilterReply decide(ILoggingEvent event) {
- String boskID = MDC.get(MdcKeys.BOSK_INSTANCE_ID);
+ String boskID = MDC.get(BOSK_INSTANCE_ID);
if (boskID == null) {
return NEUTRAL;
}
diff --git a/bosk-mongo/build.gradle b/bosk-mongo/build.gradle
index 0d3b5fb1..e36e3daf 100644
--- a/bosk-mongo/build.gradle
+++ b/bosk-mongo/build.gradle
@@ -22,6 +22,7 @@ dependencies {
// Allows us to annotate status objects so they're handy to serialize with jackson
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.16.1'
+ testImplementation project(":bosk-logback")
testImplementation project(":bosk-testing")
testImplementation project(":lib-testing")
}
diff --git a/bosk-mongo/src/main/java/works/bosk/drivers/mongo/ChangeReceiver.java b/bosk-mongo/src/main/java/works/bosk/drivers/mongo/ChangeReceiver.java
index 823a2ef1..5026dc0d 100644
--- a/bosk-mongo/src/main/java/works/bosk/drivers/mongo/ChangeReceiver.java
+++ b/bosk-mongo/src/main/java/works/bosk/drivers/mongo/ChangeReceiver.java
@@ -16,6 +16,7 @@
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import works.bosk.Identifier;
+import works.bosk.logging.MdcKeys;
import static java.lang.Thread.currentThread;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
diff --git a/bosk-mongo/src/main/java/works/bosk/drivers/mongo/MappedDiagnosticContext.java b/bosk-mongo/src/main/java/works/bosk/drivers/mongo/MappedDiagnosticContext.java
index 36c55a2b..f1e62a58 100644
--- a/bosk-mongo/src/main/java/works/bosk/drivers/mongo/MappedDiagnosticContext.java
+++ b/bosk-mongo/src/main/java/works/bosk/drivers/mongo/MappedDiagnosticContext.java
@@ -2,6 +2,7 @@
import org.slf4j.MDC;
import works.bosk.Identifier;
+import works.bosk.logging.MdcKeys;
final class MappedDiagnosticContext {
diff --git a/bosk-mongo/src/main/java/works/bosk/drivers/mongo/MdcKeys.java b/bosk-mongo/src/main/java/works/bosk/drivers/mongo/MdcKeys.java
deleted file mode 100644
index 9b341012..00000000
--- a/bosk-mongo/src/main/java/works/bosk/drivers/mongo/MdcKeys.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package works.bosk.drivers.mongo;
-
-/**
- * Evolution note: we're going to want to get organized in how we generate MDC.
- * For now, it's all in bosk-mongo, so we can put these keys here.
- */
-final class MdcKeys {
- static final String BOSK_NAME = "bosk.name";
- static final String BOSK_INSTANCE_ID = "bosk.instanceID";
- static final String EVENT = "bosk.MongoDriver.event";
- static final String TRANSACTION = "bosk.MongoDriver.transaction";
-}
diff --git a/bosk-mongo/src/main/java/works/bosk/drivers/mongo/TransactionalCollection.java b/bosk-mongo/src/main/java/works/bosk/drivers/mongo/TransactionalCollection.java
index 740ee50f..b2052674 100644
--- a/bosk-mongo/src/main/java/works/bosk/drivers/mongo/TransactionalCollection.java
+++ b/bosk-mongo/src/main/java/works/bosk/drivers/mongo/TransactionalCollection.java
@@ -47,6 +47,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
+import works.bosk.logging.MdcKeys;
/**
* A wrapper for {@link MongoCollection} that manages a thread-local
diff --git a/bosk-mongo/src/test/java/works/bosk/drivers/mongo/AbstractMongoDriverTest.java b/bosk-mongo/src/test/java/works/bosk/drivers/mongo/AbstractMongoDriverTest.java
index 6c0a580d..b84021a5 100644
--- a/bosk-mongo/src/test/java/works/bosk/drivers/mongo/AbstractMongoDriverTest.java
+++ b/bosk-mongo/src/test/java/works/bosk/drivers/mongo/AbstractMongoDriverTest.java
@@ -30,6 +30,7 @@
import works.bosk.drivers.state.TestEntity;
import works.bosk.drivers.state.TestValues;
import works.bosk.exceptions.InvalidTypeException;
+import works.bosk.logback.BoskLogFilter;
import static java.util.concurrent.TimeUnit.SECONDS;
diff --git a/lib-testing/build.gradle b/lib-testing/build.gradle
index 97c33b88..f8856c5d 100644
--- a/lib-testing/build.gradle
+++ b/lib-testing/build.gradle
@@ -14,6 +14,7 @@ dependencies {
// It's a mild kind of circular dependency we can probably live with for now.
implementation project(":bosk-core")
implementation project(":bosk-jackson")
+ implementation project(":bosk-logback")
implementation project(":bosk-mongo")
// The bosk.development plugin brings these in as test dependencies,
diff --git a/lib-testing/src/main/resources/logback.xml b/lib-testing/src/main/resources/logback.xml
index 892315a9..62d9f69e 100644
--- a/lib-testing/src/main/resources/logback.xml
+++ b/lib-testing/src/main/resources/logback.xml
@@ -1,6 +1,6 @@
-
+
%d %-5level [%thread] [%X{bosk.name}]%X{bosk.MongoDriver.transaction}%X{bosk.MongoDriver.event} %logger{25}: %msg%n
@@ -19,7 +19,7 @@
-
+
-->
diff --git a/settings.gradle b/settings.gradle
index 6c23ae9a..2f60ba18 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,3 +1,3 @@
rootProject.name = 'bosk'
-include 'bosk-annotations', 'bosk-core', 'bosk-jackson', 'bosk-mongo', 'bosk-spring-boot-3', 'bosk-testing', 'lib-testing', 'example-hello'
+include 'bosk-annotations', 'bosk-core', 'bosk-jackson', 'bosk-logback', 'bosk-mongo', 'bosk-spring-boot-3', 'bosk-testing', 'lib-testing', 'example-hello'