diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index f924ffa60340a1e698e657a3fb5da535b95b03c3..52e52ffac5f6ec850a16e2673b05709c8bce785c 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -5,12 +5,12 @@ java = "17"
 kotlin = "2.0.21"
 ksp = "2.0.21-1.0.28"
 
-ktor = "3.0.1"
+ktor = "3.0.2"
 kotlinx-serialization = "1.7.3"
 cache4k = "0.13.0"
 statelyConcurrentCollections = "2.1.0"
 slf4j = "2.0.16"
-
+kotlinx-io = "0.6.0"
 
 ktorfit = "2.2.0"
 coroutines = "1.9.0"
@@ -80,6 +80,8 @@ ktor-test = { group = "io.ktor", name = "ktor-client-mock", version.ref = "ktor"
 cache4k = { group = "io.github.reactivecircus.cache4k", name = "cache4k", version.ref = "cache4k" }
 statelyConcurrentCollections = { group = "co.touchlab", name = "stately-concurrent-collections", version.ref = "statelyConcurrentCollections" }
 slf4j = { group = "org.slf4j", name = "slf4j-simple", version.ref = "slf4j" }
+slf4j-api = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j" }
+
 
 kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
 kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
@@ -87,6 +89,7 @@ kotlinx-coroutines-debug = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-
 kotlinx-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "coroutines" }
 kotlinx-coroutines-slf4j = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-slf4j", version.ref = "coroutines" }
 kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" }
+kotlinx-io = { module = "org.jetbrains.kotlinx:kotlinx-io", version.ref = "kotlinx-io" }
 
 ktorfit-lib = { module = "de.jensklingenberg.ktorfit:ktorfit-lib-light", version.ref = "ktorfit" }
 
diff --git a/temerity/NOTICE b/temerity/NOTICE
index 63f8d0fa9d96698bafee25b7a2a884655b75c898..9ad9767805d081fcc63003a80956b53cf79bbef6 100644
--- a/temerity/NOTICE
+++ b/temerity/NOTICE
@@ -1,7 +1,34 @@
-This product includes software developed by Álvaro Salcedo García
+This library includes portions of software developed by John O'Reilly et. al.: https://github.com/joreilly/FantasyPremierLeague/
+
+Function(s): buildHttpClient()
+File(s): commonMain/Temerity.kt
+Description: A method which builds and returns a Ktor HttpClient given a request engine, engine config, and logger
+License: Apache License, Version 2.0
+
+--------------------------------------------
+
+This library includes portions of software developed by Chris Krueger et. al: https://github.com/ChrisKruegerDev/tmdb-kotlin
+
+Function(s): buildHttpClient()
+File(s): commonMain/Temerity.kt
+Description: A method which builds and returns a Ktor HttpClient given a request engine, engine config, and logger
+License: Apache License, Version 2.0
+
+--------------------------------------------
+
+This library includes software developed by Álvaro Salcedo García
 
 File(s): jvmTest/Util.kt
 Description: A utility function for configuring Koin DI context in Kotest tests.
 License: Apache License, Version 2.0
 
+--------------------------------------------
+
+This library includes software developed by Paul Hawke et. al: https://github.com/psh/KermitExt
+
+File(s): /extensions/log/*.kt
+Description: A set of extension functions for Kermit, a Kotlin multiplatform logging library.
+These allow for custom log file writers, builders, and framework binders.
+License: Apache License, Version 2.0
+
 --------------------------------------------
\ No newline at end of file
diff --git a/temerity/TemerityDemo.ipynb b/temerity/TemerityDemo.ipynb
index 5c539d3c3d1f8ee0e4f5087dca22942865f77de7..48c19dbce2243aad4fab3b1df87bb2ab2e34525e 100644
--- a/temerity/TemerityDemo.ipynb
+++ b/temerity/TemerityDemo.ipynb
@@ -5,8 +5,8 @@
    "metadata": {
     "collapsed": true,
     "ExecuteTime": {
-     "end_time": "2024-11-26T08:55:53.124544800Z",
-     "start_time": "2024-11-26T08:55:52.761207200Z"
+     "end_time": "2024-12-04T17:26:08.492177800Z",
+     "start_time": "2024-12-04T17:26:08.025486300Z"
     }
    },
    "source": [
@@ -45,10 +45,13 @@
    "outputs": [
     {
      "ename": "org.jetbrains.kotlinx.jupyter.exceptions.ReplCompilerException",
-     "evalue": "at Cell In[1], line 11, column 3: Unresolved reference: supportKtxNotebook",
+     "evalue": "at Cell In[1], line 4, column 18: Unresolved reference: cdimascio\nat Cell In[1], line 6, column 12: Unresolved reference: dotenv\nat Cell In[1], line 8, column 61: Unresolved reference: Dotenv\nat Cell In[1], line 23, column 35: Unresolved reference: dotenvVault",
      "output_type": "error",
      "traceback": [
-      "org.jetbrains.kotlinx.jupyter.exceptions.ReplCompilerException: at Cell In[1], line 11, column 3: Unresolved reference: supportKtxNotebook",
+      "org.jetbrains.kotlinx.jupyter.exceptions.ReplCompilerException: at Cell In[1], line 4, column 18: Unresolved reference: cdimascio",
+      "at Cell In[1], line 6, column 12: Unresolved reference: dotenv",
+      "at Cell In[1], line 8, column 61: Unresolved reference: Dotenv",
+      "at Cell In[1], line 23, column 35: Unresolved reference: dotenvVault",
       "\tat org.jetbrains.kotlinx.jupyter.repl.impl.JupyterCompilerImpl.compileSync(JupyterCompilerImpl.kt:208)",
       "\tat org.jetbrains.kotlinx.jupyter.repl.impl.InternalEvaluatorImpl.eval(InternalEvaluatorImpl.kt:126)",
       "\tat org.jetbrains.kotlinx.jupyter.repl.impl.CellExecutorImpl$execute$1$result$1.invoke(CellExecutorImpl.kt:80)",
diff --git a/temerity/build.gradle.kts b/temerity/build.gradle.kts
index 4c08df2c332ec0323f42134d95020f8ede82371c..399af7dc7a0ac163b8357e687b3a69feb61f6a8d 100644
--- a/temerity/build.gradle.kts
+++ b/temerity/build.gradle.kts
@@ -88,6 +88,7 @@ kotlin {
                 // TODO: Re-add cache4k when updated for Kotlin 2.0.21
                 implementation(libs.statelyConcurrentCollections)
                 implementation(libs.kmpIo)
+                implementation(libs.kotlinx.io)
             }
         }
         val commonTest by getting {
@@ -102,7 +103,7 @@ kotlin {
         val jvmMain by getting {
             dependencies {
                 implementation(libs.ktor.client.java)
-                implementation(libs.slf4j)
+                implementation(libs.slf4j.api)
             }
         }
         val androidMain by getting {
diff --git a/temerity/src/androidMain/kotlin/edu/ucsc/its/temerity/extensions/log/FilesystemActual.kt b/temerity/src/androidMain/kotlin/edu/ucsc/its/temerity/extensions/log/FilesystemActual.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4cba6149f39b8faeefff8e9c0c915242c8fc07a6
--- /dev/null
+++ b/temerity/src/androidMain/kotlin/edu/ucsc/its/temerity/extensions/log/FilesystemActual.kt
@@ -0,0 +1,6 @@
+package edu.ucsc.its.temerity.extensions.log
+
+import kotlinx.io.files.FileSystem
+import kotlinx.io.files.SystemFileSystem
+
+internal actual fun fileSystem() : FileSystem = SystemFileSystem
diff --git a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/TemClientConfig.kt b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/TemClientConfig.kt
index 7f43c7a455cffb2dc97d7a5afa605f9056c3a290..d3caf43b6b3599adffeae0e23684371df94ff89e 100644
--- a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/TemClientConfig.kt
+++ b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/TemClientConfig.kt
@@ -44,6 +44,7 @@ public class TemClientConfig {
 
   public var serviceUrl: String? = null
   public var serviceToken: String? = null
+  public var optLoggingEnabled: Boolean = true
   public var optDebugEnabled: Boolean = false
   public var supportKtxNotebook: Boolean = false
 
@@ -53,6 +54,7 @@ public class TemClientConfig {
   @Suppress("MemberVisibilityCanBePrivate")
   public var webTimeout: Duration? = null
 
+  @Suppress("MemberVisibilityCanBePrivate")
   // Default cache timeout is set to 15 minutes if this is left unspecified; @see [edu.ucsc.its.temerity.core.Temerity] init block
   public var cacheTimeout: Duration? = null
 
diff --git a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/core/DispatcherFactory.kt b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/core/DispatcherFactory.kt
index ba2536d32eff5e99d84038f993c2a8474901385d..576562b997e701fb8ed6c78cde804b9cd9102812 100644
--- a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/core/DispatcherFactory.kt
+++ b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/core/DispatcherFactory.kt
@@ -25,7 +25,7 @@ import kotlinx.coroutines.Dispatchers
 
 internal object DispatcherFactory {
 
-  internal fun createDispatcher(dispatcherThreadPool: CoroutineDispatcher = Dispatchers.IO, threadCount: Int, dispatcherName: String): CoroutineDispatcher = dispatcherThreadPool.limitedParallelism(threadCount, dispatcherName)
+  internal fun createDispatcher(dispatcherThreadPool: CoroutineDispatcher, threadCount: Int, dispatcherName: String): CoroutineDispatcher = dispatcherThreadPool.limitedParallelism(threadCount, dispatcherName)
 
   internal fun createLibraryScope(dispatcher: CoroutineDispatcher): CoroutineScope = createJobScope(dispatcher, allowIndependentFailure = true)
 
diff --git a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/core/LoggerFactory.kt b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/core/LoggerFactory.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1a3902befd259c65dcc2f55ec4d3ed22007c55bc
--- /dev/null
+++ b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/core/LoggerFactory.kt
@@ -0,0 +1,46 @@
+package edu.ucsc.its.temerity.core
+
+import co.touchlab.kermit.CommonWriter
+import co.touchlab.kermit.Logger
+import co.touchlab.kermit.NoTagFormatter
+import co.touchlab.kermit.Severity
+import co.touchlab.kermit.loggerConfigInit
+import co.touchlab.kermit.platformLogWriter
+import edu.ucsc.its.temerity.TemClientConfig
+
+internal object LoggerFactory {
+  internal fun createLogger(tag: String?, config: TemClientConfig? = null, supportKtxNotebook: Boolean = false): Logger =
+    when (config){
+      null -> {
+        Logger(
+          config = loggerConfigInit(
+            if (supportKtxNotebook) CommonWriter(NoTagFormatter) else platformLogWriter(NoTagFormatter),
+            minSeverity = Severity.Debug,
+          ),
+          tag = tag ?: "TemerityLib",
+        )
+      }
+      else -> {
+        when (config.optDebugEnabled) {
+          // If debug logging is enabled, create a logger which color prints to the console
+          true -> {
+            Logger(
+              config = loggerConfigInit(
+                if (supportKtxNotebook) CommonWriter(NoTagFormatter) else platformLogWriter(NoTagFormatter),
+                minSeverity = Severity.Debug,
+              ),
+              tag = tag ?: "TemerityLib",
+            )
+          }
+          // If debug logging is disabled, create a logger which prints to a log file
+          false -> {
+            Logger(
+              config = loggerConfigInit(
+              
+              )
+            )
+          }
+        }
+      }
+    }
+}
diff --git a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/core/Temerity.kt b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/core/Temerity.kt
index 935c401cf9db835cb735fff1b18b06362f38531e..0139a0a052296ce7e756278e7330a3584d72d95d 100644
--- a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/core/Temerity.kt
+++ b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/core/Temerity.kt
@@ -17,11 +17,6 @@
  */
 package edu.ucsc.its.temerity.core
 
-import co.touchlab.kermit.CommonWriter
-import co.touchlab.kermit.NoTagFormatter
-import co.touchlab.kermit.Severity
-import co.touchlab.kermit.loggerConfigInit
-import co.touchlab.kermit.platformLogWriter
 import co.touchlab.stately.collections.ConcurrentMutableMap
 import com.skydoves.sandwich.ApiResponse
 import com.skydoves.sandwich.StatusCode
@@ -37,13 +32,12 @@ import edu.ucsc.its.temerity.TemClientConfig
 import edu.ucsc.its.temerity.TemerityApi
 import edu.ucsc.its.temerity.api.PlatformApi
 import edu.ucsc.its.temerity.api.createPlatformApi
-import edu.ucsc.its.temerity.core.DispatcherFactory.createDispatcher
 import edu.ucsc.its.temerity.core.DispatcherFactory.createLibraryScope
 import edu.ucsc.its.temerity.core.JsonFactory.buildJson
 import edu.ucsc.its.temerity.core.Temerity.Companion.DEFAULT_WEB_TIMEOUT
 import edu.ucsc.its.temerity.createJobScope
-import edu.ucsc.its.temerity.extensions.applyAuditLogFormat
-import edu.ucsc.its.temerity.extensions.applyScheduledSessionDateFormat
+import edu.ucsc.its.temerity.extensions.time.applyAuditLogFormat
+import edu.ucsc.its.temerity.extensions.time.applyScheduledSessionDateFormat
 import edu.ucsc.its.temerity.model.AuditLogEntry
 import edu.ucsc.its.temerity.model.Course
 import edu.ucsc.its.temerity.model.Device
@@ -67,11 +61,9 @@ import io.ktor.client.plugins.logging.LogLevel
 import io.ktor.client.plugins.logging.Logging
 import io.ktor.client.request.header
 import io.ktor.client.statement.HttpResponse
-import io.ktor.client.utils.clientDispatcher
 import io.ktor.http.URLProtocol
 import io.ktor.util.cio.toByteArray
 import io.ktor.utils.io.ByteReadChannel
-import java.io.FileWriter
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.async
 import kotlinx.coroutines.awaitAll
@@ -100,7 +92,7 @@ import kotlin.time.Duration
 import kotlin.time.Duration.Companion.minutes
 import kotlin.time.DurationUnit
 import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.Dispatchers
+import org.koin.core.logger.Logger
 import co.touchlab.kermit.Logger as KermitLogger
 import io.ktor.client.plugins.logging.Logger as KtorLogger
 
@@ -122,15 +114,6 @@ public class Temerity internal constructor(
     // TODO: Use this as cache4k expiration time
     public val DEFAULT_CACHE_EXPIRATION: Duration = 15.minutes
     internal const val DEFAULT_MINIMUM_THREAD_COUNT: Int = 2
-
-    internal fun createLogger(tag: String?, supportKtxNotebook: Boolean = false): co.touchlab.kermit.Logger =
-      co.touchlab.kermit.Logger(
-        config = loggerConfigInit(
-          if (supportKtxNotebook) CommonWriter(NoTagFormatter) else platformLogWriter(NoTagFormatter),
-          minSeverity = Severity.Debug,
-        ),
-        tag = tag ?: "TemerityLib",
-      )
   }
 
   /**
@@ -146,7 +129,7 @@ public class Temerity internal constructor(
       )
     }
     factory { (config: TemClientConfig) ->
-      val client: HttpClient = get { parametersOf(config, createLogger("Temerity Library Web Request engine")) }
+      val client: HttpClient = get { parametersOf(config, createLogger(tag = "Temerity Library Web Request engine", config = config)) }
       val ktorfit = ktorfit {
         config.serviceUrl?.let { baseUrl(it) }
         httpClient(client)
@@ -165,9 +148,9 @@ public class Temerity internal constructor(
    * via factories defined as part of the lib Module.
    */
   private fun createKoinApp() = object : TemerityKoinContext() {
-    val logger = createLogger("TemerityLib Koin DI Context")
+    override val logger = createLogger(tag = "TemerityLib Koin DI Context")
     override val koinApp = koinApplication {
-      logger(object : org.koin.core.logger.Logger() {
+      logger(object : Logger() {
         override fun display(level: Level, msg: MESSAGE) {
           when (level) {
             DEBUG -> logger.d(msg)
@@ -181,7 +164,7 @@ public class Temerity internal constructor(
       modules(
         libModule,
         platformModule()
-        )
+      )
     }
     override val koin: Koin = koinApp.koin
   }
@@ -189,6 +172,7 @@ public class Temerity internal constructor(
   internal abstract class TemerityKoinContext {
     abstract val koinApp: KoinApplication
     abstract val koin: Koin
+    abstract val logger: KermitLogger
   }
 
   @Suppress("PropertyName")
@@ -556,7 +540,7 @@ public class Temerity internal constructor(
 internal fun buildHttpClient(
   httpClientEngine: HttpClientEngine,
   config: TemClientConfig,
-  logger: co.touchlab.kermit.Logger,
+  logger: KermitLogger,
 ): HttpClient {
   val defaultConfig: HttpClientConfig<*>.() -> Unit = {
     // Can't use install(ContentNegotiation){ json() } here because YuJa's API returns a 406 error if the Accept header is set to application/json
diff --git a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/log/FilesystemLogWriter.kt b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/log/FilesystemLogWriter.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9c4f5388dded3be5f714a86c4635ab43c3e48a80
--- /dev/null
+++ b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/log/FilesystemLogWriter.kt
@@ -0,0 +1,80 @@
+package edu.ucsc.its.temerity.extensions.log
+
+import co.touchlab.kermit.DefaultFormatter
+import co.touchlab.kermit.LogWriter
+import co.touchlab.kermit.Message
+import co.touchlab.kermit.MessageStringFormatter
+import co.touchlab.kermit.Severity
+import co.touchlab.kermit.Tag
+import kotlinx.io.buffered
+import kotlinx.io.files.FileSystem
+import kotlinx.io.files.Path
+import kotlinx.io.writeString
+
+internal expect fun fileSystem(): FileSystem
+
+internal class FilesystemLogWriter internal constructor(
+    private val logPath: String,
+    private val logRoller: LogRoller? = null,
+    private val formatter: MessageStringFormatter = DefaultFormatter
+) : LogWriter() {
+
+    // Not called since we are "Context Aware"
+    override fun log(severity: Severity, message: String, tag: String, throwable: Throwable?) {
+        val fileSystem = fileSystem()
+        val kotlinxIoPath = Path(logPath)
+
+        logRoller?.rollLogs(kotlinxIoPath, fileSystem)
+
+        val sink = fileSystem.sink(kotlinxIoPath, append = true).buffered()
+
+        with(sink) {
+            writeString(formatter.formatMessage(severity, Tag(tag), Message(message)))
+            writeString("\n")
+            throwable?.let {
+                writeString(it.stackTraceToString())
+            }
+            flush()
+        }
+    }
+
+    companion object {
+        operator fun invoke(block: Builder.() -> Unit) =
+            with(FilesystemLogWriterBuilder()) {
+                block(this)
+                build()
+            }
+    }
+
+    interface Builder {
+        fun rollLogAtSize(size: Long): Builder
+        fun logPath(path: String): Builder
+        fun build(): FilesystemLogWriter
+    }
+
+    class FilesystemLogWriterBuilder() : Builder {
+        private var maxFileSize: Long? = null
+        private var logPath: String? = null
+
+        override fun rollLogAtSize(size: Long): Builder {
+            maxFileSize = size
+            return this
+        }
+
+        override fun logPath(path: String): Builder {
+            logPath = path
+            return this
+        }
+
+        override fun build(): FilesystemLogWriter {
+            if (logPath == null) throw NullPointerException("Invalid / missing log path")
+
+            // Can you resist the urge to Rick-roll the logs?
+            val rick = maxFileSize?.let {
+                FileSizeLogRoller(logPath!!, maxFileSize!!)
+            }
+
+            return FilesystemLogWriter(logPath!!, rick)
+        }
+    }
+}
diff --git a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/log/LogRoller.kt b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/log/LogRoller.kt
new file mode 100644
index 0000000000000000000000000000000000000000..68b050cc5c46c2f2df730508a6e3265158bc2202
--- /dev/null
+++ b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/log/LogRoller.kt
@@ -0,0 +1,29 @@
+package edu.ucsc.its.temerity.extensions.log
+
+import kotlinx.io.files.FileSystem
+import kotlinx.io.files.Path
+
+internal interface LogRoller {
+    fun rollLogs(kotlinxIoPath: Path, fileSystem: FileSystem)
+}
+
+internal class FileSizeLogRoller(
+    private val logPath: String,
+    private val maxFileSize: Long
+) : LogRoller {
+    override fun rollLogs(kotlinxIoPath: Path, fileSystem: FileSystem) {
+        val metadata = fileSystem.metadataOrNull(kotlinxIoPath)
+        metadata?.let {
+            it.size.let { size ->
+                if (size >= maxFileSize) {
+                    val filename = kotlinxIoPath.name
+                    val count = fileSystem.list(kotlinxIoPath.parent!!).count { p: Path ->
+                        p.name.contains(filename)
+                    }
+                    val to = Path("$logPath.$count")
+                    fileSystem.atomicMove(kotlinxIoPath, to)
+                }
+            }
+        }
+    }
+}
diff --git a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/DateTimeExtensions.kt b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/time/DateTimeExtensions.kt
similarity index 97%
rename from temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/DateTimeExtensions.kt
rename to temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/time/DateTimeExtensions.kt
index bfb164a31af64f1e313fb4c280d0e6161b6ff0ba..7ed64322b5d44f0fd76206566d0b49c49e09f028 100644
--- a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/DateTimeExtensions.kt
+++ b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/time/DateTimeExtensions.kt
@@ -15,7 +15,7 @@
  *     License along with this library; if not, write to the Free Software
  *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  */
-package edu.ucsc.its.temerity.extensions
+package edu.ucsc.its.temerity.extensions.time
 
 import edu.ucsc.its.temerity.AUDIT_LOG_REQUEST_DATE_FORMAT
 import edu.ucsc.its.temerity.SCHEDULED_SESSION_DATE_FORMAT
diff --git a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/LocalDateTimeSerializer.kt b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/time/LocalDateTimeSerializer.kt
similarity index 97%
rename from temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/LocalDateTimeSerializer.kt
rename to temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/time/LocalDateTimeSerializer.kt
index b33286c63652fd4129abecd17b1e13b064aea7af..96695252d3ce72acaa684e88cb9f2a96b62533bb 100644
--- a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/LocalDateTimeSerializer.kt
+++ b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/time/LocalDateTimeSerializer.kt
@@ -15,7 +15,7 @@
  *     License along with this library; if not, write to the Free Software
  *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  */
-package edu.ucsc.its.temerity.extensions
+package edu.ucsc.its.temerity.extensions.time
 
 import edu.ucsc.its.temerity.AUDIT_LOG_TIMESTAMP_FORMAT
 import edu.ucsc.its.temerity.model.AuditLogEntry
diff --git a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/model/AuditLogs.kt b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/model/AuditLogs.kt
index 538b45508ef65fd405ae3a10fbc0583fec87b7a5..ed163d057075a7e1c37d9060733833a6952ace73 100644
--- a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/model/AuditLogs.kt
+++ b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/model/AuditLogs.kt
@@ -17,7 +17,7 @@
  */
 package edu.ucsc.its.temerity.model
 
-import edu.ucsc.its.temerity.extensions.LocalDateTimeSerializer
+import edu.ucsc.its.temerity.extensions.time.LocalDateTimeSerializer
 import kotlinx.datetime.LocalDateTime
 import kotlinx.serialization.Serializable
 
diff --git a/temerity/src/jvmMain/kotlin/edu/ucsc/its/temerity/extensions/log/KermitServiceProvider.kt b/temerity/src/jvmMain/kotlin/edu/ucsc/its/temerity/extensions/log/KermitServiceProvider.kt
new file mode 100644
index 0000000000000000000000000000000000000000..0010197ea7ae91755baa602f0d1feb199a88f1ce
--- /dev/null
+++ b/temerity/src/jvmMain/kotlin/edu/ucsc/its/temerity/extensions/log/KermitServiceProvider.kt
@@ -0,0 +1,55 @@
+@file:Suppress("unused")
+
+package edu.ucsc.its.temerity.extensions.log
+
+import co.touchlab.kermit.CommonWriter
+import co.touchlab.kermit.LogWriter
+import co.touchlab.kermit.Severity
+import co.touchlab.kermit.StaticConfig
+import kotlinx.io.files.FileSystem
+import kotlinx.io.files.SystemFileSystem
+import org.slf4j.ILoggerFactory
+import org.slf4j.helpers.BasicMarkerFactory
+import org.slf4j.helpers.NOPMDCAdapter
+
+internal actual fun fileSystem() : FileSystem = SystemFileSystem
+
+internal class KermitServiceProvider : org.slf4j.spi.SLF4JServiceProvider {
+    private val markerFactory = BasicMarkerFactory()
+    private val mdcAdapter = NOPMDCAdapter()
+    override fun getLoggerFactory() = ILoggerFactory {
+        Slf4jKermitLogger(it, config)
+    }
+
+    override fun getMarkerFactory() = markerFactory
+
+    override fun getMDCAdapter() = mdcAdapter
+
+    override fun getRequestedApiVersion() = "2.0.99"
+
+    override fun initialize() = Unit
+
+    companion object {
+        private val writers = mutableListOf<LogWriter>().apply {
+            add(CommonWriter())
+        }
+        var config: StaticConfig = StaticConfig(logWriterList = writers)
+
+        var minSeverity: Severity
+            get() = config.minSeverity
+            set(value) {
+                config = config.copy(minSeverity = value)
+            }
+
+        fun addWriter(writer: LogWriter) {
+            writers.add(writer)
+            config = config.copy(logWriterList = writers)
+        }
+
+        fun setWriters(vararg writer: LogWriter) {
+            writers.clear()
+            writers.addAll(writer)
+            config = config.copy(logWriterList = writers)
+        }
+    }
+}
diff --git a/temerity/src/jvmMain/kotlin/edu/ucsc/its/temerity/extensions/log/Slf4jKermitLogger.kt b/temerity/src/jvmMain/kotlin/edu/ucsc/its/temerity/extensions/log/Slf4jKermitLogger.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4ffbc2053e5345de72ee129959198bb3d6c884cd
--- /dev/null
+++ b/temerity/src/jvmMain/kotlin/edu/ucsc/its/temerity/extensions/log/Slf4jKermitLogger.kt
@@ -0,0 +1,60 @@
+@file:Suppress("unused")
+
+package edu.ucsc.its.temerity.extensions.log
+
+import co.touchlab.kermit.BaseLogger
+import co.touchlab.kermit.LoggerConfig
+import co.touchlab.kermit.Severity
+import org.slf4j.Marker
+import org.slf4j.event.Level
+import org.slf4j.event.Level.*
+import org.slf4j.helpers.AbstractLogger
+
+internal class Slf4jKermitLogger(private val name: String, config: LoggerConfig) : AbstractLogger() {
+    private val logger = BaseLogger(config)
+    override fun getName(): String = "slf4j-over-kermit"
+
+    //region Is Logging enabled at various levels
+    override fun isTraceEnabled() = logger.config.minSeverity <= Severity.Verbose
+    override fun isTraceEnabled(marker: Marker?) = logger.config.minSeverity <= Severity.Verbose
+    override fun isDebugEnabled() = logger.config.minSeverity <= Severity.Debug
+    override fun isDebugEnabled(marker: Marker?) = logger.config.minSeverity <= Severity.Debug
+    override fun isInfoEnabled() = logger.config.minSeverity <= Severity.Info
+    override fun isInfoEnabled(marker: Marker?) = logger.config.minSeverity <= Severity.Info
+    override fun isWarnEnabled() = logger.config.minSeverity <= Severity.Warn
+    override fun isWarnEnabled(marker: Marker?) = logger.config.minSeverity <= Severity.Warn
+    override fun isErrorEnabled() = logger.config.minSeverity <= Severity.Error
+    override fun isErrorEnabled(marker: Marker?) = logger.config.minSeverity <= Severity.Error
+    //endregion
+
+    override fun getFullyQualifiedCallerName(): String? = null
+
+    override fun handleNormalizedLoggingCall(
+        level: Level?,
+        marker: Marker?,
+        messagePattern: String?,
+        arguments: Array<out Any>?,
+        throwable: Throwable?
+    ) {
+        val severity = when (level) {
+            ERROR -> Severity.Error
+            WARN -> Severity.Warn
+            INFO -> Severity.Info
+            DEBUG -> Severity.Debug
+            else -> Severity.Verbose
+        }
+
+        val formatted = if (messagePattern != null && arguments != null) {
+            String.format(messagePattern, *(arguments.toList().toTypedArray()))
+        } else null
+
+        messagePattern.let {
+            logger.log(
+                severity,
+                marker?.toString() ?: name,
+                throwable,
+                formatted ?: (messagePattern ?: "")
+            )
+        }
+    }
+}