Skip to content

logger.kt

src/jvmMain/kotlin/afield/app/diagnostics/logging/logger.kt · 101 lines · cited by Logging: channels, sinks and the console

The lprintln / lprint / lblock logging calls, which send a message to the server log on a named channel such as "info,startup".

Each call goes to LogDispatcher with the current thread's indent. lblock logs a begin line, runs its block with the indent deepened, and logs a matching end line even if the block throws, so nested blocks read as a tree. The single-argument forms are deprecated and log on info,legacy. formatMillis formats a nanosecond duration with its unit for stat lines.

package afield.app.diagnostics.logging

import afield.ai.diagnostics.BlockBoundary
import afield.ai.diagnostics.LogDispatcher
import org.jetbrains.kotlin.utils.threadLocal
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.Uuid

fun lprintln(message: Any?)

@Deprecated("Use channelled lprintln with explicit tags", ReplaceWith("lprintln(\"info,legacy\", message)"))
fun lprintln(message: Any?) = lprintln("info,legacy", message)

fun lprint(message: Any?)

@Deprecated("Use channelled lprint with explicit tags", ReplaceWith("lprint(\"info,legacy\", message)"))
fun lprint(message: Any?) = lprint("info,legacy", message)

fun lprintln(channel: String, message: Any?)

fun lprintln(channel: String, message: Any?) {
    val msg = message?.toString() ?: "null"
    LogDispatcher.log(channel = channel, message = msg, indent = indent)
    isNL = true
}

fun lprint(channel: String, message: Any?)

fun lprint(channel: String, message: Any?) {
    val msg = message?.toString() ?: "null"
    LogDispatcher.log(channel = channel, message = msg, indent = indent)
    isNL = false
}

fun formatMillis(nanos: Long): String

Format a nanosecond duration as a compact, human-readable value with its unit for pinned-console stat lines: more significant digits for sub-millisecond values, plain integers once past 100ms, and a rollover to decimal seconds past 1s (so a slow compile=1.63s reads cleanly rather than compile=1629ms). The trailing unit (ms/s) is part of the returned string and satisfies the afield.app.diagnostics.logging heatmap token grammar (key=value unit), so callers emit "compile=${formatMillis(ns)}" with no unit of their own.

fun formatMillis(nanos: Long): String {
    val ms = nanos / 1_000_000.0
    return when {
        ms >= 10_000.0 -> "%.1fs".format(ms / 1000.0)
        ms >= 1_000.0 -> "%.2fs".format(ms / 1000.0)
        ms >= 100.0 -> "%.0fms".format(ms)
        ms >= 10.0 -> "%.1fms".format(ms)
        ms >= 1.0 -> "%.2fms".format(ms)
        else -> "%.3fms".format(ms)
    }
}

var indent by threadLocal

var indent by threadLocal {
    ""
}

var isNL = true

inline fun lblock(channel: String, message: Any?, block: () -> Unit) {

@OptIn(ExperimentalContracts::class, ExperimentalUuidApi::class)
inline fun lblock(channel: String, message: Any?, block: () -> Unit) {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    val u = Uuid.random().toString()
    LogDispatcher.log(
        channel = channel,
        message = "${message ?: ""} - '$u'",
        indent = indent,
        blockId = u,
        boundary = BlockBoundary.BEGIN
    )
    val added = "${u}\t"
    indent += added
    try {
        block()
    } finally {
        if (indent.length > added.length) {
            indent = indent.substring(added.length)
        } else {
            indent = ""
        }
        LogDispatcher.log(
            channel = channel,
            message = "- '$u'",
            indent = indent,
            blockId = u,
            boundary = BlockBoundary.END
        )
    }
}

inline fun lblock(message: Any?, block: () -> Unit) = lblock("info,legacy", message, block)

@Deprecated("Use channelled lblock with explicit tags", ReplaceWith("lblock(\"info,legacy\", message, block)"))
inline fun lblock(message: Any?, block: () -> Unit) = lblock("info,legacy", message, block)