Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 65 additions & 34 deletions src/main/kotlin/com/coder/toolbox/diagnostics/CoderLogger.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@file:Suppress("NOTHING_TO_INLINE", "OVERRIDE_BY_INLINE")

package com.coder.toolbox.diagnostics

import com.coder.toolbox.session.SessionId
Expand All @@ -11,94 +13,121 @@ import kotlinx.coroutines.launch

private const val CLIENT_SESSION_ID_LOG_KEY = "client_session_id"

private fun withSessionId(sessionId: SessionId?, message: String): String =
@PublishedApi
internal fun withSessionId(sessionId: SessionId?, message: String): String =
sessionId?.let { "$CLIENT_SESSION_ID_LOG_KEY=$it $message" } ?: message

/**
* The plugin's single logging entry point.
*
* A null [SessionId] leaves the message unchanged. A non-null ID adds the correlation field, while
* a set of IDs emits one log for each session (or one unchanged log when the set is empty).
* Logging methods are inline so the Toolbox logger identifies the business call site rather than
* this wrapper as the source of each message.
*/
class CoderLogger(
private val delegate: Logger,
@PublishedApi internal val delegate: Logger,
private val ui: ToolboxUi,
private val cs: CoroutineScope,
private val i18n: LocalizableStringFactory,
) : Logger by delegate {
fun error(sessionId: SessionId?, exception: Throwable, message: String) {
override inline fun error(message: String) {
delegate.error(message)
}

override inline fun error(exception: Throwable, message: String) {
delegate.error(exception, message)
}

inline fun error(sessionId: SessionId?, exception: Throwable, message: String) {
delegate.error(exception, withSessionId(sessionId, message))
}

fun error(sessionIds: Set<SessionId>, exception: Throwable, message: String) {
sessionIds.onceOrForEach { error(it, exception, message) }
inline fun error(sessionIds: Set<SessionId>, exception: Throwable, message: String) {
sessionIds.onceOrForEach { delegate.error(exception, withSessionId(it, message)) }
}

override inline fun warn(message: String) {
delegate.warn(message)
}

fun warn(sessionId: SessionId?, message: String) {
override inline fun warn(exception: Throwable, message: String) {
delegate.warn(exception, message)
}

inline fun warn(sessionId: SessionId?, message: String) {
delegate.warn(withSessionId(sessionId, message))
}

fun warn(sessionIds: Set<SessionId>, exception: Throwable, message: String) {
inline fun warn(sessionIds: Set<SessionId>, exception: Throwable, message: String) {
sessionIds.onceOrForEach { delegate.warn(exception, withSessionId(it, message)) }
}

fun debug(sessionId: SessionId?, message: String) {
override inline fun debug(message: String) {
delegate.debug(message)
}

inline fun debug(sessionId: SessionId?, message: String) {
delegate.debug(withSessionId(sessionId, message))
}

fun debug(sessionIds: Set<SessionId>, message: String) {
sessionIds.onceOrForEach { debug(it, message) }
inline fun debug(sessionIds: Set<SessionId>, message: String) {
sessionIds.onceOrForEach { delegate.debug(withSessionId(it, message)) }
}

override inline fun info(message: String) {
delegate.info(message)
}

fun info(sessionId: SessionId?, message: String) {
inline fun info(sessionId: SessionId?, message: String) {
delegate.info(withSessionId(sessionId, message))
}

fun info(sessionIds: Set<SessionId>, message: String) {
sessionIds.onceOrForEach { info(it, message) }
inline fun info(sessionIds: Set<SessionId>, message: String) {
sessionIds.onceOrForEach { delegate.info(withSessionId(it, message)) }
}

fun logAndShowError(title: String, error: String) {
error(error)
inline fun logAndShowError(title: String, error: String) {
delegate.error(error)
showInfoPopup(title, error)
}

fun logAndShowError(sessionId: SessionId?, title: String, error: String) {
inline fun logAndShowError(sessionId: SessionId?, title: String, error: String) {
delegate.error(withSessionId(sessionId, error))
showInfoPopup(title, error)
}

fun logAndShowError(title: String, error: String, exception: Throwable) {
error(exception, error)
inline fun logAndShowError(title: String, error: String, exception: Throwable) {
delegate.error(exception, error)
showInfoPopup(title, error)
}

fun logAndShowError(sessionId: SessionId?, title: String, error: String, exception: Throwable) {
error(sessionId, exception, error)
inline fun logAndShowError(sessionId: SessionId?, title: String, error: String, exception: Throwable) {
delegate.error(exception, withSessionId(sessionId, error))
showInfoPopup(title, error)
}

fun logAndShowError(
inline fun logAndShowError(
sessionIds: Set<SessionId>,
title: String,
error: String,
exception: Throwable,
) {
sessionIds.onceOrForEach { this.error(it, exception, error) }
sessionIds.onceOrForEach { delegate.error(exception, withSessionId(it, error)) }
showInfoPopup(title, error)
}

fun logAndShowWarning(title: String, warning: String) {
warn(warning)
inline fun logAndShowWarning(title: String, warning: String) {
delegate.warn(warning)
showInfoPopup(title, warning)
}

fun logAndShowWarning(sessionId: SessionId?, title: String, warning: String) {
warn(sessionId, warning)
inline fun logAndShowWarning(sessionId: SessionId?, title: String, warning: String) {
delegate.warn(withSessionId(sessionId, warning))
showInfoPopup(title, warning)
}

fun logAndShowWarning(
inline fun logAndShowWarning(
sessionIds: Set<SessionId>,
title: String,
warning: String,
Expand All @@ -108,13 +137,13 @@ class CoderLogger(
showInfoPopup(title, warning)
}

fun logAndShowWarning(title: String, warning: String, exception: Throwable) {
warn(exception, warning)
inline fun logAndShowWarning(title: String, warning: String, exception: Throwable) {
delegate.warn(exception, warning)
showInfoPopup(title, warning)
}

fun logAndShowInfo(title: String, info: String) {
info(info)
inline fun logAndShowInfo(title: String, info: String) {
delegate.info(info)
showInfoPopup(title, info)
}

Expand All @@ -130,7 +159,8 @@ class CoderLogger(
* It is launched fire-and-forget so the caller is not suspended until the user closes the
* popup. The caller can run any follow-up work immediately.
*/
private fun showInfoPopup(title: String, text: String) {
@PublishedApi
internal fun showInfoPopup(title: String, text: String) {
cs.launch(CoroutineName("popup")) {
try {
ui.showInfoPopup(
Expand All @@ -141,12 +171,13 @@ class CoderLogger(
} catch (_: CancellationException) {
// Expected when the plugin scope shuts down while the popup is open.
} catch (ex: Exception) {
error(ex, "Failed to display popup with title '$title'")
delegate.error(ex, "Failed to display popup with title '$title'")
}
}
}

private inline fun Set<SessionId>.onceOrForEach(action: (SessionId?) -> Unit) {
@PublishedApi
internal inline fun Set<SessionId>.onceOrForEach(action: (SessionId?) -> Unit) {
if (isEmpty()) {
action(null)
} else {
Expand Down
Loading
Loading