All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.com.caesarealabs.loggy.lib.Loggy.kt Maven / Gradle / Ivy

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)
        }

    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy