### Install Console Logger Source: https://github.com/juullabs/khronicle/blob/main/README.md Initialize logging by installing the ConsoleLogger. If no logger is installed, log blocks will not be executed at runtime. ```kotlin Log.dispatcher.install(ConsoleLogger) ``` -------------------------------- ### Install Apple System Logger Source: https://github.com/juullabs/khronicle/blob/main/README.md Log messages to the Apple System Log by installing the AppleSystemLogger. This is specific to Apple platforms. ```kotlin Log.dispatcher.install(AppleSystemLogger) ``` -------------------------------- ### Initialize ConsoleLogger with Optional Minimum Log Level Source: https://context7.com/juullabs/khronicle/llms.txt Install `ConsoleLogger` for platform-aware console output. Use `withMinimumLogLevel` to filter logs below a certain severity. ```kotlin import com.juul.khronicle.ConsoleLogger import com.juul.khronicle.Log import com.juul.khronicle.LogLevel // Typical application initialization fun initLogging(debug: Boolean) { Log.dispatcher.install( if (debug) ConsoleLogger else ConsoleLogger.withMinimumLogLevel(LogLevel.Warn) ) } ``` -------------------------------- ### Install Logger with Content Filter Source: https://github.com/juullabs/khronicle/blob/main/README.md Installs a logger with a content filter that checks metadata for sensitivity. Logs are only printed if the metadata indicates 'NotSensitive'. ```kotlin Log.dispatcher.install( ConsoleLogger .withFilter { tag, message, metadata, throwable -> metadata[Sensitivity] == Sensitivity.NotSensitive } ) Log.debug("Tag") { "This block is evaluated, but does not get printed to the console." } Log.warn("Tag") { metadata -> metadata[Sensitivity] = Sensitivity.NotSensitive "This is also evaluated, and does print to the console." } ``` -------------------------------- ### Install Console Logger with Minimum Log Level Source: https://github.com/juullabs/khronicle/blob/main/README.md Filter logs by installing a logger with a minimum log level. Logs below this level will not be processed, offering a performance optimization. ```kotlin Log.dispatcher.install( ConsoleLogger .withMinimumLogLevel(LogLevel.Warn) ) Log.debug("Tag") { "This is not called." } Log.warn("Tag") { "This still gets called." } ``` -------------------------------- ### Install and Use PrintlnLogger Source: https://context7.com/juullabs/khronicle/llms.txt Use `PrintlnLogger` for simple console output without platform-specific APIs. It formats logs as `[L/Tag] Message` or includes a stack trace for throwables. ```kotlin import com.juul.khronicle.Log import com.juul.khronicle.PrintlnLogger Log.dispatcher.install(PrintlnLogger) Log.info("Server") { "Listening on port 8080" } // Prints: [I/Server] Listening on port 8080 try { error("boom") } catch (e: IllegalStateException) { Log.error("Server", throwable = e) { "Unexpected failure" } // Prints: [E/Server] Unexpected failure: java.lang.IllegalStateException: boom // at ... } ``` -------------------------------- ### Initialize AppleSystemLogger for Apple Platforms Source: https://context7.com/juullabs/khronicle/llms.txt Install `AppleSystemLogger` on Apple targets (iOS, macOS) to route logs through `NSLog`, making them visible in Xcode and the unified logging system. ```kotlin // Apple targets only (iOS, macOS, tvOS, watchOS) import com.juul.khronicle.AppleSystemLogger import com.juul.khronicle.Log fun initLogging() { Log.dispatcher.install(AppleSystemLogger) } Log.debug("BLE") { "Scanning for peripherals" } // NSLog output: D/BLE: Scanning for peripherals ``` -------------------------------- ### Implement Custom Logger with Metadata Check Source: https://github.com/juullabs/khronicle/blob/main/README.md Example of a custom logger that checks for specific metadata. Sensitive data is handled conditionally based on metadata presence. ```kotlin class SampleLogger : Logger { override fun verbose(tag: String, message: String, metadata: ReadMetadata, throwable: Throwable?) { if (metadata[Sensitivity] != Sensitivity.Sensitive) { // report to a destination that cannot include sensitive data } } // ... } ``` -------------------------------- ### Install ConsoleLogger for Console Output Source: https://context7.com/juullabs/khronicle/llms.txt Register the built-in ConsoleLogger with the global dispatcher to print log messages to the console. This logger adapts to the platform (Android, Apple, JS, JVM). ```kotlin import com.juul.khronicle.ConsoleLogger import com.juul.khronicle.Log // Install the built-in ConsoleLogger so log messages are printed to the console. // On Android, ConsoleLogger uses android.util.Log; on Apple platforms it uses NSLog; // on JS it maps to console.log/warn/error; on JVM it writes to stdout/stderr. fun initLogging() { Log.dispatcher.install(ConsoleLogger) } // Output example for Log.debug("App") { "Hello" }: // [D/App] Hello ``` -------------------------------- ### Integrate Khronicle with Ktor HttpClient Source: https://github.com/juullabs/khronicle/blob/main/README.md Configures a Ktor HttpClient to use Khronicle for logging by setting KhronicleKtorClientLogger as the logger. This routes HttpClient logs to installed Khronicle dispatchers. ```kotlin HttpClient { install(Logging) { logger = KhronicleKtorClientLogger() } } ``` -------------------------------- ### Emit Logs at Different Severity Levels Source: https://context7.com/juullabs/khronicle/llms.txt Use the top-level log functions (`verbose`, `debug`, `info`, `warn`, `error`, `assert`) to emit messages. The log message lambda is only evaluated if consumers are installed and the log level is accepted. ```kotlin import com.juul.khronicle.Log // Basic message — lambda is NOT called if no consumers are installed or level is filtered out. Log.verbose("NetworkService") { "Connecting to host" } Log.debug("NetworkService") { "Socket opened" } Log.info("NetworkService") { "Request sent: GET /api/users" } Log.warn("NetworkService") { "Slow response: 3200ms" } // With a Throwable try { performRequest() } catch (e: IOException) { Log.error("NetworkService", throwable = e) { "Request failed" } } // With metadata written inside the lambda Log.info("AuthService") { metadata -> metadata[Sensitivity] = Sensitivity.Sensitive "User token refreshed for uid=42" } ``` -------------------------------- ### Clear All Registered Loggers Source: https://context7.com/juullabs/khronicle/llms.txt Use `Log.dispatcher.clear()` to remove all installed log consumers. This is particularly useful in tests to reset the logging state between test runs. ```kotlin import com.juul.khronicle.Log fun tearDown() { Log.dispatcher.clear() // After clear(), all log lambdas become no-ops (never evaluated). } ``` -------------------------------- ### Ensure Thread Safety with Logger.synchronized Source: https://context7.com/juullabs/khronicle/llms.txt Wraps a logger with a reentrant lock to prevent log message interleaving from concurrent threads. Install this decorator when multiple threads or coroutines might log simultaneously. ```kotlin import com.juul.khronicle.Log import com.juul.khronicle.PrintlnLogger Log.dispatcher.install( PrintlnLogger.synchronized() ) // Safe to call Log.* from multiple coroutines / threads simultaneously. ``` -------------------------------- ### Build Metadata for Tests with buildMetadata Source: https://context7.com/juullabs/khronicle/llms.txt Use `buildMetadata` from `khronicle-test` to construct `ReadMetadata` instances directly in tests. This is useful for simulating metadata without going through the `Log` dispatcher. ```kotlin import com.juul.khronicle.LogLevel import com.juul.khronicle.Sensitivity import com.juul.khronicle.test.buildMetadata import kotlin.test.Test import kotlin.test.assertEquals class SensitivityFilterTest { @Test fun `filter blocks sensitive metadata`() { val logger = RemoteLogger("https://logs.example.com") val sensitiveMetadata = buildMetadata { it[Sensitivity] = Sensitivity.Sensitive } val publicMetadata = buildMetadata { it[Sensitivity] = Sensitivity.NotSensitive } logger.info("Auth", "token=abc123", sensitiveMetadata, null) // should be blocked logger.info("Metrics", "req_count=500", publicMetadata, null) // should pass through } } ``` -------------------------------- ### Integrate Ktor HTTP Client with Khronicle Source: https://context7.com/juullabs/khronicle/llms.txt Use `KhronicleKtorClientLogger` to forward Ktor HTTP client logs to Khronicle's dispatcher. Configure the log level and optionally add custom metadata to each log entry. ```kotlin import com.juul.khronicle.ConsoleLogger import com.juul.khronicle.KhronicleKtorClientLogger import com.juul.khronicle.Log import com.juul.khronicle.LogLevel import com.juul.khronicle.Sensitivity import io.ktor.client.HttpClient import io.ktor.client.plugins.logging.Logging import io.ktor.client.plugins.logging.LogLevel as KtorLogLevel // Initialize Khronicle first Log.dispatcher.install(ConsoleLogger) // Create an HttpClient that routes its logs through Khronicle val client = HttpClient { install(Logging) { level = KtorLogLevel.ALL logger = KhronicleKtorClientLogger( level = LogLevel.Debug, writeMetadata = { metadata -> // All Ktor HTTP logs are marked as not sensitive metadata[Sensitivity] = Sensitivity.NotSensitive } ) } } // All Ktor request/response logs now flow through Log.debug("KtorClient") { ... } ``` -------------------------------- ### Add Khronicle Core Dependency Source: https://github.com/juullabs/khronicle/blob/main/README.md Include the core Khronicle library in your Gradle build file. Replace '$version' with the desired Khronicle version. ```kotlin implementation("com.juul.khronicle:khronicle-core:$version") ``` -------------------------------- ### Add Khronicle Dependencies to Gradle Source: https://context7.com/juullabs/khronicle/llms.txt Include the core Khronicle library and any optional modules like Ktor integration, test logger, or Android Lint rule in your Gradle build file. ```kotlin implementation("com.juul.khronicle:khronicle-core:$version") // Ktor HTTP client integration (optional) implementation("com.juul.khronicle:khronicle-ktor-client:$version") // In-memory logger for tests (optional) testImplementation("com.juul.khronicle:khronicle-test:$version") // Android Lint rule to enforce Khronicle over android.util.Log (optional) lintChecks("com.juul.khronicle:khronicle-android-lint:$version") ``` -------------------------------- ### Khronicle LogLevel Enum Source: https://context7.com/juullabs/khronicle/llms.txt Demonstrates the ordered nature of LogLevel and how to compare levels for filtering. ```kotlin import com.juul.khronicle.LogLevel // Ordered from lowest to highest: // LogLevel.Verbose < Debug < Info < Warn < Error < Assert val current: LogLevel = LogLevel.Info println(current >= LogLevel.Warn) // false println(current >= LogLevel.Debug) // true ``` -------------------------------- ### Use Companion Object as Metadata Key Source: https://github.com/juullabs/khronicle/blob/main/README.md A common pattern is to use the companion object of an enum or class as the metadata key, simplifying key definition. ```kotlin enum class Sample { A, B, C; companion object : Key } ``` -------------------------------- ### Implement Custom SimpleLogger for File Logging Source: https://context7.com/juullabs/khronicle/llms.txt Extend SimpleLogger to create a logger that writes to a file. This is useful when the underlying system does not have per-level methods. ```kotlin import com.juul.khronicle.Log import com.juul.khronicle.LogLevel import com.juul.khronicle.ReadMetadata import com.juul.khronicle.SimpleLogger class FileLogger(private val path: String) : SimpleLogger() { override fun log( level: LogLevel, tag: String, message: String, metadata: ReadMetadata, throwable: Throwable?, ) { val line = buildString { append("["${level.name.first()}"/"$tag"] "$message") if (throwable != null) append("\n" +"${throwable.stackTraceToString()}") } // appendLine(path, line) — write to file println("→ FILE: "$line) } } Log.dispatcher.install(FileLogger("/var/log/app.log")) Log.info("Startup") { "Server started on :8080" } // → FILE: [I/Startup] Server started on :8080 ``` -------------------------------- ### Define and Use Typed Metadata with Key Source: https://context7.com/juullabs/khronicle/llms.txt Defines custom typed metadata keys for structured logging. Use `object` or `companion object` for key singletons. Metadata is written in the log lambda and read in custom logger implementations. ```kotlin import com.juul.khronicle.Key import com.juul.khronicle.Log import com.juul.khronicle.Logger import com.juul.khronicle.ReadMetadata import com.juul.khronicle.SimpleLogger // Define custom metadata keys object RequestId : Key object UserId : Key enum class Environment { Production, Staging, Development; companion object : Key } // Write metadata in the log call Log.info("OrderService") { metadata -> metadata[RequestId] = "req-abc-123" metadata[UserId] = 42L metadata[Environment] = Environment.Production "Order #9876 created" } // Read metadata in a custom logger class StructuredLogger : SimpleLogger() { override fun log(level: LogLevel, tag: String, message: String, metadata: ReadMetadata, throwable: Throwable?) { val requestId = metadata[RequestId] ?: "unknown" val userId = metadata[UserId] ?: -1L val env = metadata[Environment] ?: Environment.Development println("[$level] [$env] req=$requestId uid=$userId [$tag] $message") // → [Info] [Production] req=req-abc-123 uid=42 [OrderService] Order #9876 created } } Log.dispatcher.install(StructuredLogger()) ``` -------------------------------- ### Define Custom Metadata Key Source: https://github.com/juullabs/khronicle/blob/main/README.md Create a custom metadata key by defining an object that implements the Key interface. The generic type specifies the value type. ```kotlin object YourMetadata : Key ``` -------------------------------- ### Query All Values of a Key Type with ReadMetadata.getAll Source: https://context7.com/juullabs/khronicle/llms.txt Retrieves all metadata entries whose keys are instances of a specified class. Useful for grouping or processing multiple related metadata fields, such as different types of labels. ```kotlin import com.juul.khronicle.Key import com.juul.khronicle.Log import com.juul.khronicle.ReadMetadata import com.juul.khronicle.SimpleLogger import kotlin.reflect.KClass interface Label : Key object Team : Label object Feature : Label Log.debug("Analytics") { metadata -> metadata[Team] = "backend" metadata[Feature] = "checkout" "Checkout flow initiated" } class TaggingLogger : SimpleLogger() { override fun log(level: LogLevel, tag: String, message: String, metadata: ReadMetadata, throwable: Throwable?) { val labels: Map = metadata.getAll(Label::class) println("labels=$labels message=$message") // → labels={Team=backend, Feature=checkout} message=Checkout flow initiated } } ``` -------------------------------- ### Key & Metadata Source: https://context7.com/juullabs/khronicle/llms.txt Defines typed metadata for log entries using `Key` markers. Custom keys can be created as singletons or companion objects, allowing for structured data to be attached to logs and read by custom loggers. ```APIDOC ## Key & Metadata ### Description `Key` is a marker interface for metadata map keys. Create `object` singletons or `companion object` instances on data classes/enums to define typed metadata fields. Metadata is written via `WriteMetadata` in the log lambda and read via `ReadMetadata` inside `Logger` implementations. ### Usage ```kotlin import com.juul.khronicle.Key import com.juul.khronicle.Log import com.juul.khronicle.Logger import com.juul.khronicle.ReadMetadata import com.juul.khronicle.SimpleLogger // Define custom metadata keys object RequestId : Key object UserId : Key enum class Environment { Production, Staging, Development; companion object : Key } // Write metadata in the log call Log.info("OrderService") { metadata -> metadata[RequestId] = "req-abc-123" metadata[UserId] = 42L metadata[Environment] = Environment.Production "Order #9876 created" } // Read metadata in a custom logger class StructuredLogger : SimpleLogger() { override fun log(level: LogLevel, tag: String, message: String, metadata: ReadMetadata, throwable: Throwable?) { val requestId = metadata[RequestId] ?: "unknown" val userId = metadata[UserId] ?: -1L val env = metadata[Environment] ?: Environment.Development println("[$level] [$env] req=$requestId uid=$userId [$tag] $message") // → [Info] [Production] req=req-abc-123 uid=42 [OrderService] Order #9876 created } } Log.dispatcher.install(StructuredLogger()) ``` ``` -------------------------------- ### Log with Metadata Source: https://github.com/juullabs/khronicle/blob/main/README.md Associate arbitrary metadata with a log message. The metadata can be accessed within a custom Logger implementation. ```kotlin Log.verbose("Tag") { metadata -> metadata[Sensitivity] = Sensitivity.Sensitive "My social security number is 123 45 6789" } ``` -------------------------------- ### Test Ktor Client Logs with CallListLogger Source: https://context7.com/juullabs/khronicle/llms.txt Use `CallListLogger` to capture all log calls in memory for testing purposes. It provides typed accessors for different log levels and captures metadata. ```kotlin import com.juul.khronicle.Log import com.juul.khronicle.LogLevel import com.juul.khronicle.Sensitivity import com.juul.khronicle.test.CallListLogger import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue class PaymentServiceTest { @Test fun `sensitive logs are tagged correctly`() { val logger = CallListLogger() Log.dispatcher.install(logger) try { Log.warn("Payment") { metadata -> metadata[Sensitivity] = Sensitivity.Sensitive "Card number: 4111111111111111" } Log.info("Payment") { "Transaction approved" } assertEquals(2, logger.allCalls.size) val sensitiveCall = logger.warnCalls.single() assertEquals("Payment", sensitiveCall.tag) assertEquals(Sensitivity.Sensitive, sensitiveCall.metadata[Sensitivity]) assertTrue(sensitiveCall.message.contains("4111111111111111")) val infoCall = logger.infoCalls.single() assertEquals("Transaction approved", infoCall.message) } finally { Log.dispatcher.clear() } } } ``` -------------------------------- ### Use Sensitivity Enum for PII Metadata Source: https://context7.com/juullabs/khronicle/llms.txt Mark logs with `Sensitivity.Sensitive` or `Sensitivity.NotSensitive` for PII handling. The `ComplianceLogger` demonstrates routing logs based on this metadata. ```kotlin import com.juul.khronicle.Log import com.juul.khronicle.Logger import com.juul.khronicle.ReadMetadata import com.juul.khronicle.Sensitivity import com.juul.khronicle.SimpleLogger // Writer side Log.warn("UserProfile") { metadata -> metadata[Sensitivity] = Sensitivity.Sensitive "User changed email to bob@example.com" } Log.info("Metrics") { metadata -> metadata[Sensitivity] = Sensitivity.NotSensitive "Page views today: 1024" } // Reader side — route sensitive logs to a secure sink only class ComplianceLogger : SimpleLogger() { override fun log(level: LogLevel, tag: String, message: String, metadata: ReadMetadata, throwable: Throwable?) { when (metadata[Sensitivity]) { Sensitivity.Sensitive -> secureSink(level, tag, message) Sensitivity.NotSensitive -> publicSink(level, tag, message) null -> publicSink(level, tag, message) // default: treat as non-sensitive } } private fun secureSink(l: LogLevel, tag: String, msg: String) = println("[SECURE] [$l/$tag] $msg") private fun publicSink(l: LogLevel, tag: String, msg: String) = println("[PUBLIC] [$l/$tag] $msg") } ``` -------------------------------- ### Add Khronicle Android Lint Artifact Source: https://context7.com/juullabs/khronicle/llms.txt Integrate the Khronicle Android Lint check into your project by adding the lint artifact to your `build.gradle.kts` file. ```kotlin // build.gradle.kts lintChecks("com.juul.khronicle:khronicle-android-lint:$version") ``` -------------------------------- ### Log a Verbose Message Source: https://github.com/juullabs/khronicle/blob/main/README.md Log a message with the 'verbose' level. A tag is required, and an optional throwable can be provided. The message is provided as a lambda. ```kotlin Log.verbose("Tag") { "Example" } ``` -------------------------------- ### Implement Custom RemoteLogger Source: https://context7.com/juullabs/khronicle/llms.txt Implement the Logger interface to send logs to a remote endpoint. Override minimumLogLevel to filter messages before they are processed. ```kotlin import com.juul.khronicle.Log import com.juul.khronicle.LogLevel import com.juul.khronicle.Logger import com.juul.khronicle.ReadMetadata class RemoteLogger(private val endpoint: String) : Logger { // Only handle Warn and above; Verbose/Debug/Info never reach this logger. override val minimumLogLevel: LogLevel = LogLevel.Warn override fun verbose(tag: String, message: String, metadata: ReadMetadata, throwable: Throwable?) = Unit override fun debug(tag: String, message: String, metadata: ReadMetadata, throwable: Throwable?) = Unit override fun info(tag: String, message: String, metadata: ReadMetadata, throwable: Throwable?) = Unit override fun warn(tag: String, message: String, metadata: ReadMetadata, throwable: Throwable?) = send("WARN", tag, message, throwable) override fun error(tag: String, message: String, metadata: ReadMetadata, throwable: Throwable?) = send("ERROR", tag, message, throwable) override fun assert(tag: String, message: String, metadata: ReadMetadata, throwable: Throwable?) = send("ASSERT", tag, message, throwable) private fun send(level: String, tag: String, message: String, throwable: Throwable?) { // e.g. HTTP POST to a remote logging endpoint println("POST $endpoint → ["$level"/"$tag"] "$message" "${throwable?.message.orEmpty()}") } } // Usage: Log.dispatcher.install(RemoteLogger("https://logs.example.com/ingest")) Log.warn("Payment") { "Retry attempt 3/3" } // → POST https://logs.example.com/ingest → [WARN/Payment] Retry attempt 3/3 ``` -------------------------------- ### ReadMetadata.getAll Source: https://context7.com/juullabs/khronicle/llms.txt Retrieves all metadata entries whose keys are instances of a specified class. This is useful for querying multiple metadata values that share a common base type. ```APIDOC ## ReadMetadata.getAll ### Description `ReadMetadata.getAll(clazz: KClass): Map` returns all entries whose key is an instance of the given class. Useful when multiple keys share the same base type. ### Usage ```kotlin import com.juul.khronicle.Key import com.juul.khronicle.Log import com.juul.khronicle.ReadMetadata import com.juul.khronicle.SimpleLogger import kotlin.reflect.KClass interface Label : Key object Team : Label object Feature : Label Log.debug("Analytics") { metadata -> metadata[Team] = "backend" metadata[Feature] = "checkout" "Checkout flow initiated" } class TaggingLogger : SimpleLogger() { override fun log(level: LogLevel, tag: String, message: String, metadata: ReadMetadata, throwable: Throwable?) { val labels: Map = metadata.getAll(Label::class) println("labels=$labels message=$message") // → labels={Team=backend, Feature=checkout} message=Checkout flow initiated } } ``` ``` -------------------------------- ### Log Dynamically Based on Runtime Level Source: https://context7.com/juullabs/khronicle/llms.txt Use `Log.dynamic` when the log severity level is determined programmatically at runtime. This function dispatches to the appropriate severity level function. ```kotlin import com.juul.khronicle.Log import com.juul.khronicle.LogLevel fun logHttpStatus(status: Int, body: String) { val level = when { status >= 500 -> LogLevel.Error status >= 400 -> LogLevel.Warn else -> LogLevel.Info } Log.dynamic(level, "HttpClient") { "Response $status: $body" } } logHttpStatus(200, "OK") // → Info logHttpStatus(404, "Not Found") // → Warn logHttpStatus(503, "Unavailable") // → Error ``` -------------------------------- ### Copy Metadata Snapshot in AsyncLogger Source: https://context7.com/juullabs/khronicle/llms.txt Use `metadata.copy()` to retain metadata asynchronously. The original metadata object is recycled after the logger call returns. ```kotlin import com.juul.khronicle.Log import com.juul.khronicle.Logger import com.juul.khronicle.ReadMetadata import com.juul.khronicle.SimpleLogger import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch class AsyncLogger(private val scope: CoroutineScope) : SimpleLogger() { override fun log(level: LogLevel, tag: String, message: String, metadata: ReadMetadata, throwable: Throwable?) { // MUST copy before the function returns — original is recycled by the pool. val snapshot = metadata.copy() scope.launch(Dispatchers.IO) { sendToRemote(level, tag, message, snapshot, throwable) } } private suspend fun sendToRemote(level: LogLevel, tag: String, message: String, metadata: ReadMetadata, throwable: Throwable?) { println("Async send: [$level/$tag] $message | metadata=$metadata") } } ``` -------------------------------- ### Filter Logs with Logger.withFilter Source: https://context7.com/juullabs/khronicle/llms.txt Wraps a logger to apply a content-based filter. Log lambdas are evaluated before the filter. Use this to conditionally suppress logs based on message content or metadata. ```kotlin import com.juul.khronicle.ConsoleLogger import com.juul.khronicle.Log import com.juul.khronicle.Sensitivity // Only forward logs that are explicitly marked NotSensitive. Log.dispatcher.install( ConsoleLogger.withFilter { tag, message, metadata, throwable -> metadata[Sensitivity] != Sensitivity.Sensitive } ) Log.info("Profile") { metadata -> metadata[Sensitivity] = Sensitivity.Sensitive "Email: user@example.com" // ← filtered out, not printed } Log.info("Profile") { metadata -> metadata[Sensitivity] = Sensitivity.NotSensitive "Profile page loaded" // ← printed ✓ } ``` -------------------------------- ### Logger.withFilter Source: https://context7.com/juullabs/khronicle/llms.txt Wraps a Logger with a content-based filter. Log messages are evaluated by a predicate before being forwarded, allowing for selective logging based on message content, tags, metadata, or throwables. ```APIDOC ## Logger.withFilter ### Description Wraps a `Logger` so that each log is passed through a `LogFilter.canLog(tag, message, metadata, throwable): Boolean` predicate before forwarding. Unlike `withMinimumLogLevel`, the log lambda IS evaluated before the filter runs. ### Usage ```kotlin import com.juul.khronicle.ConsoleLogger import com.juul.khronicle.Log import com.juul.khronicle.Sensitivity // Only forward logs that are explicitly marked NotSensitive. Log.dispatcher.install( ConsoleLogger.withFilter { tag, message, metadata, throwable -> metadata[Sensitivity] != Sensitivity.Sensitive } ) Log.info("Profile") { metadata -> metadata[Sensitivity] = Sensitivity.Sensitive "Email: user@example.com" // ← filtered out, not printed } Log.info("Profile") { metadata -> metadata[Sensitivity] = Sensitivity.NotSensitive "Profile page loaded" // ← printed ✓ } ``` ``` -------------------------------- ### Inspect Captured Log Entries with Call Source: https://context7.com/juullabs/khronicle/llms.txt The `Call` data class, produced by `CallListLogger`, represents a single log invocation. Access its properties like `level`, `tag`, `message`, and `throwable` to inspect log details. ```kotlin import com.juul.khronicle.LogLevel import com.juul.khronicle.test.Call import com.juul.khronicle.test.CallListLogger val logger = CallListLogger() // ... emit some logs ... val call: Call = logger.errorCalls.first() println(call.level) // LogLevel.Error println(call.tag) // "MyService" println(call.message) // "Something went wrong" println(call.throwable) // kotlin.IllegalStateException: boom (or null) ``` -------------------------------- ### Android Lint Rule: LogNotKhronicle Source: https://context7.com/juullabs/khronicle/llms.txt Flags calls to `android.util.Log` and provides quick-fix replacements to `com.juul.khronicle.Log`. Use this to enforce consistent logging across your Android project. ```kotlin // BEFORE (triggers lint warning "Using 'android.util.Log' instead of 'com.juul.khronicle.Log'") android.util.Log.d("MyActivity", "onCreate called") android.util.Log.e("MyActivity", "Error occurred", exception) // AFTER quick-fix option 1 (explicit tag parameter) com.juul.khronicle.Log.debug(tag = "MyActivity") { "onCreate called" } com.juul.khronicle.Log.error(tag = "MyActivity", throwable = exception) { "Error occurred" } // AFTER quick-fix option 2 (no explicit tag parameter — uses receiver) com.juul.khronicle.Log.debug { "onCreate called" } com.juul.khronicle.Log.error(throwable = exception) { "Error occurred" } ``` -------------------------------- ### Filter Logs with Logger.withMinimumLogLevel Source: https://context7.com/juullabs/khronicle/llms.txt Use Logger.withMinimumLogLevel to wrap an existing logger, discarding messages below a specified level. This decorator improves efficiency by preventing the log lambda from being invoked for filtered messages. ```kotlin import com.juul.khronicle.ConsoleLogger import com.juul.khronicle.Log import com.juul.khronicle.LogLevel // Only Warn, Error, and Assert messages reach the console. Log.dispatcher.install( ConsoleLogger.withMinimumLogLevel(LogLevel.Warn) ) Log.debug("DB") { "Query executed in 5ms" } // lambda never called Log.info("DB") { "Connection pool size: 10" } // lambda never called Log.warn("DB") { "Slow query: 800ms" } // printed ✓ Log.error("DB", throwable = e) { "Connection lost" } // printed ✓ ``` -------------------------------- ### Logger.synchronized Source: https://context7.com/juullabs/khronicle/llms.txt Wraps any Logger in a reentrant lock, ensuring that concurrent log calls from multiple threads do not interleave, thus guaranteeing thread safety. ```APIDOC ## Logger.synchronized ### Description Wraps any `Logger` in a reentrant lock, guaranteeing that concurrent log calls from multiple threads do not interleave. ### Usage ```kotlin import com.juul.khronicle.Log import com.juul.khronicle.PrintlnLogger Log.dispatcher.install( PrintlnLogger.synchronized() ) // Safe to call Log.* from multiple coroutines / threads simultaneously. ``` ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.