commonMain.com.giancarlobuenaflor.kflogger.KMetadataKey.kt Maven / Gradle / Ivy
package com.giancarlobuenaflor.kflogger
/**
* Callback interface to handle additional contextual `Metadata` in log statements. This interface
* is only intended to be implemented by logger backend classes as part of handling metadata, and
* should not be used in any general application code, other than to implement the
* [KMetadataKey.emit] method in this class.
*/
public expect interface KMetadataKeyKeyValueHandler {
/** Handle a single key/value pair of contextual metadata for a log statement. */
public fun handle(key: String?, value: Any?)
}
/**
* Key for logging semi-structured metadata values.
*
* Metadata keys can be used to provide log statements with strongly typed values which can be read
* and interpreted by logging backends or other logs related tools. This mechanism is intended for
* values with specific semantics and should not be seen as a replacement for logging arguments as
* part of a formatted log message.
*
* Examples of where using `MetadataKey` is suitable are:
* * Logging a value with special semantics (e.g. values that are handled specially by the logger
* backend).
* * Passing configuration to a specific logger backend to modify behaviour for individual log
* statements or all log statements in a `ScopedLoggingContext`.
* * Logging a structured value in many places with consistent formatting (e.g. so it can later be
* re-parsed by logs related tools).
*
* If you just want to log an general "key value pair" in a small number of log statements, it is
* still better to just do something like `log("key=%s", value)`.
*
* Metadata keys are expected to be singleton constants, and should never be allocated at the log
* site itself. Even though they are expected to be singletons, comparing keys should be done via
* `equals()` (rather than '==') since this will be safe in cases where non-singleton keys exist,
* and is just as fast if the keys are singletons.
*
* It is strongly recommended that any public [MetadataKey] instances are defined as `public static
* final` fields in a top-level or nested class which does no logging. Ideally a separate class
* would be defined to hold only the keys, since this allows keys to be loaded very early in the
* logging [Platform] lifecycle without risking any static initialization issues.
*
* Custom subclasses of `MetadataKey` which override either of the protected [.emit] methods should
* take care to avoid calling any code which might trigger logging since this could lead to
* unexpected recusrion, especially if the key is being logged as part of a `ScopedLoggingContext`.
* While there is protection against unbounded reentrant logging in Flogger, it is still best
* practice to avoid it where possible.
*
* Metadata keys are passed to a log statement via the `with()` method, so it can aid readability to
* choose a name for the constant field which reads "fluently" as part of the log statement. For
* example:
* `// Prefer this...
* logger.atInfo().with(FILE_LOGGING_FOR, user).log("User specific log message...");
* // to...
* logger.atInfo().with(SET_LOGGING_TO_USER_FILE, user).log("User specific log message...");
* `
*
*
* Logger backends can act upon metadata present in log statements to modify behaviour. Any metadata
* entries that are not handled by a backend explicitly are, by default, rendered as part of the log
* statement in a default format.
*
* Note that some metadata entries are handled prior to being processed by the backend (e.g. rate
* limiting), but a metadata entry remains present to record the fact that rate limiting was
* enabled.
*/
public expect open class KMetadataKey {
/* TODO
/**
* Constructor for custom key subclasses. Most use-cases will not require the use of custom keys,
* but occasionally it can be useful to create a specific subtype to control the formatting of
* values or to have a family of related keys with a common parent type.
*/
protected constructor(label: String, clazz: java.lang.Class, canRepeat: Boolean) : this(
label,
clazz,
canRepeat,
true
)*/
/** Cast an arbitrary value to the type of this key. */
public fun cast(value: Any?): T
/** Whether this key can be used to set more than one value in the metadata. */
public fun canRepeat(): Boolean
/**
* Emits one or more key/value pairs for the given metadata value. Call this method in preference
* to using [.emitRepeated] directly to protect against unbounded reentrant logging.
*/
public fun safeEmit(value: T, kvh: KMetadataKeyKeyValueHandler)
/**
* Emits one or more key/value pairs for a sequence of repeated metadata values. Call this method
* in preference to using [.emitRepeated] directly to protect against unbounded reentrant logging.
*/
public fun safeEmitRepeated(values: Iterator, kvh: KMetadataKeyKeyValueHandler)
/**
* Override this method to provide custom logic for emitting one or more key/value pairs for a
* given metadata value (call [.safeEmit] from logging code to actually emit values).
*
* By default this method simply emits the given value with this key's label, but it can be
* customized key/value pairs if necessary.
*
* Note that if multiple key/value pairs are emitted, the following best-practice should be
* followed:
* * Key names should be of the form `"
© 2015 - 2025 Weber Informatics LLC | Privacy Policy