feat(ui): add runtime logger and replace console usage
This commit is contained in:
128
packages/ui/src/lib/logger.ts
Normal file
128
packages/ui/src/lib/logger.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import debug from "debug"
|
||||
|
||||
export type LoggerNamespace = "sse" | "api" | "session" | "actions"
|
||||
|
||||
interface Logger {
|
||||
log: (...args: unknown[]) => void
|
||||
info: (...args: unknown[]) => void
|
||||
warn: (...args: unknown[]) => void
|
||||
error: (...args: unknown[]) => void
|
||||
}
|
||||
|
||||
interface NamespaceState {
|
||||
name: LoggerNamespace
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
const KNOWN_NAMESPACES: LoggerNamespace[] = ["sse", "api", "session", "actions"]
|
||||
const STORAGE_KEY = "opencode:logger:namespaces"
|
||||
|
||||
const namespaceLoggers = new Map<LoggerNamespace, Logger>()
|
||||
const enabledNamespaces = new Set<LoggerNamespace>()
|
||||
const rawConsole = typeof globalThis !== "undefined" ? globalThis.console : undefined
|
||||
|
||||
function applyEnabledNamespaces(): void {
|
||||
if (enabledNamespaces.size === 0) {
|
||||
debug.disable()
|
||||
} else {
|
||||
debug.enable(Array.from(enabledNamespaces).join(","))
|
||||
}
|
||||
}
|
||||
|
||||
function persistEnabledNamespaces(): void {
|
||||
if (typeof window === "undefined" || !window?.localStorage) return
|
||||
try {
|
||||
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(Array.from(enabledNamespaces)))
|
||||
} catch (error) {
|
||||
rawConsole?.warn?.("Failed to persist logger namespaces", error)
|
||||
}
|
||||
}
|
||||
|
||||
function hydrateNamespacesFromStorage(): void {
|
||||
if (typeof window === "undefined" || !window?.localStorage) return
|
||||
try {
|
||||
const stored = window.localStorage.getItem(STORAGE_KEY)
|
||||
if (!stored) return
|
||||
const parsed: unknown = JSON.parse(stored)
|
||||
if (!Array.isArray(parsed)) return
|
||||
for (const name of parsed) {
|
||||
if (KNOWN_NAMESPACES.includes(name as LoggerNamespace)) {
|
||||
enabledNamespaces.add(name as LoggerNamespace)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
rawConsole?.warn?.("Failed to hydrate logger namespaces", error)
|
||||
}
|
||||
}
|
||||
|
||||
hydrateNamespacesFromStorage()
|
||||
applyEnabledNamespaces()
|
||||
|
||||
function buildLogger(namespace: LoggerNamespace): Logger {
|
||||
const base = debug(namespace)
|
||||
const baseLogger: (...args: any[]) => void = base
|
||||
const formatAndLog = (level: string, args: any[]) => {
|
||||
baseLogger(level, ...args)
|
||||
}
|
||||
return {
|
||||
log: (...args: any[]) => baseLogger(...args),
|
||||
info: (...args: any[]) => baseLogger(...args),
|
||||
warn: (...args: any[]) => formatAndLog("[warn]", args),
|
||||
error: (...args: any[]) => formatAndLog("[error]", args),
|
||||
}
|
||||
}
|
||||
|
||||
function getLogger(namespace: LoggerNamespace): Logger {
|
||||
if (!KNOWN_NAMESPACES.includes(namespace)) {
|
||||
throw new Error(`Unknown logger namespace: ${namespace}`)
|
||||
}
|
||||
if (!namespaceLoggers.has(namespace)) {
|
||||
namespaceLoggers.set(namespace, buildLogger(namespace))
|
||||
}
|
||||
return namespaceLoggers.get(namespace)!
|
||||
}
|
||||
|
||||
function listLoggerNamespaces(): NamespaceState[] {
|
||||
return KNOWN_NAMESPACES.map((name) => ({ name, enabled: enabledNamespaces.has(name) }))
|
||||
}
|
||||
|
||||
function enableLogger(namespace: LoggerNamespace): void {
|
||||
if (!KNOWN_NAMESPACES.includes(namespace)) {
|
||||
throw new Error(`Unknown logger namespace: ${namespace}`)
|
||||
}
|
||||
if (enabledNamespaces.has(namespace)) return
|
||||
enabledNamespaces.add(namespace)
|
||||
persistEnabledNamespaces()
|
||||
applyEnabledNamespaces()
|
||||
}
|
||||
|
||||
function disableLogger(namespace: LoggerNamespace): void {
|
||||
if (!KNOWN_NAMESPACES.includes(namespace)) {
|
||||
throw new Error(`Unknown logger namespace: ${namespace}`)
|
||||
}
|
||||
if (!enabledNamespaces.has(namespace)) return
|
||||
enabledNamespaces.delete(namespace)
|
||||
persistEnabledNamespaces()
|
||||
applyEnabledNamespaces()
|
||||
}
|
||||
|
||||
function disableAllLoggers(): void {
|
||||
enabledNamespaces.clear()
|
||||
persistEnabledNamespaces()
|
||||
applyEnabledNamespaces()
|
||||
}
|
||||
|
||||
function enableAllLoggers(): void {
|
||||
KNOWN_NAMESPACES.forEach((namespace) => enabledNamespaces.add(namespace))
|
||||
persistEnabledNamespaces()
|
||||
applyEnabledNamespaces()
|
||||
}
|
||||
|
||||
export {
|
||||
getLogger,
|
||||
listLoggerNamespaces,
|
||||
enableLogger,
|
||||
disableLogger,
|
||||
enableAllLoggers,
|
||||
disableAllLoggers,
|
||||
}
|
||||
Reference in New Issue
Block a user