commonMain.com.caesarealabs.loggy.lib.Loggy.kt Maven / Gradle / Ivy
The newest version!
package com.caesarealabs.loggy.lib
import com.caesarealabs.logging.LogSeverity
import com.caesarealabs.logging.Logging
import com.caesarealabs.logging.LoggingFactory
import com.caesarealabs.loggy.shared.*
import com.caesarealabs.rpc4k.generated.LoggyServiceEventInvoker
import com.caesarealabs.rpc4k.generated.rpc4k
import kotlinx.datetime.Clock
public enum class LogPrintMode {
/**
* Logging will print out a line to std as soon as a log function is called
*/
Immediate,
/**
* Loggy will gather up all logs done in an invocation, and when it ends it will print it out
* as a nicely formatted text.
*/
AfterInvocation,
/**
* Nothing will be printed to standard output
*/
None
}
/**
* The entry point to using Loggy.
* First construct a static instance of this, and then use [wrapCall] to wrap your endpoint handlers
*/
public class Loggy(
private val database: LogSink,
private val serviceEventInvoker: LoggyServiceEventInvoker?,
private val printMode: LogPrintMode = LogPrintMode.AfterInvocation
) : LoggingFactory {
init {
if (printMode == LogPrintMode.Immediate) error("LogPrintMode.Immediate is not implemented yet")
}
/**
* Use this to wrap code responding to a single [tag].
* The code will have access to a [LoggyLogging] which will allow logging in a way associated
* to this specific call (of this [tag])..
*/
override suspend fun wrapCall(tag: String, code: suspend Logging.() -> T): T {
val startTime = Clock.System.now()
val logs = LoggyLogging()
val result = code(logs)
val invocation = Invocation(
endpoint = tag,
startTime = startTime,
endTime = Clock.System.now(),
logs = logs.logs,
metadata = logs.metadata,
id = InvocationId.generate()
)
if (printMode == LogPrintMode.AfterInvocation) println(invocation.renderToString())
database.add(invocation)
serviceEventInvoker?.invokeOnInvocationAdded(invocation)
return result
}
}
/**
* Logs using Loggy
*/
public class LoggyLogging: Logging {
@PublishedApi
internal val metadata: MutableList = mutableListOf()
@PublishedApi
internal val logs: MutableList = mutableListOf()
// All functions are inline with a lambda message so that logging can be turned off without evaluating the strings
public override fun log(
severity: LogSeverity,
exception: Throwable?,
message: () -> String
) {
logs.add(
Log(
message = message(),
time = Clock.System.now(),
exception = exception?.toSerializable(),
severity = severity
)
)
}
public override fun logVerbose(message: () -> String) {
log(LogSeverity.Verbose, null, message)
}
public override fun logDebug(message: () -> String) {
log(LogSeverity.Debug, null, message)
}
public override fun logInfo(message: () -> String) {
log(LogSeverity.Info, null, message)
}
override fun logWarn(exception: Throwable?, message: () -> String) {
log(LogSeverity.Warn, exception, message)
}
public override fun logError(exception: Throwable, message: () -> String) {
log(LogSeverity.Error, exception, message)
}
/**
* Will mark the [key] with the [value]'s string representation
*/
public override fun logData(key: String, value: () -> Any?) {
metadata.add(InvocationMetadata(key, value().toString()))
}
}
/**
* Defines how to store logs
* The 'endpoint' field should be indexed for maximal performance.
*/
public fun interface LogSink {
public suspend fun add(invocation: Invocation)
/**
* Will not store any log information. Only suitable for tests.
*/
public object None : LogSink {
override suspend fun add(invocation: Invocation) {
}
}
public class InMemory(private val items: MutableList) : LogSink {
override suspend fun add(invocation: Invocation) {
items.add(invocation)
}
}
}