analyze performance upgrade
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
// Browser-side logger with levels, timestamps and a ring buffer.
|
||||
//
|
||||
// Level: NEXT_PUBLIC_LOG_LEVEL at build time, overridable per browser with
|
||||
// localStorage.setItem("iotp_log_level", "debug"). The last 300 entries are
|
||||
// kept in window.__iotpLogs and can be copied with window.__iotpDumpLogs().
|
||||
|
||||
export type LogLevel = "debug" | "info" | "warn" | "error"
|
||||
|
||||
const ORDER: Record<LogLevel, number> = { debug: 10, info: 20, warn: 30, error: 40 }
|
||||
const BUFFER_SIZE = 300
|
||||
|
||||
interface LogEntry {
|
||||
ts: string
|
||||
level: LogLevel
|
||||
scope: string
|
||||
message: string
|
||||
data?: unknown
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
__iotpLogs?: LogEntry[]
|
||||
__iotpDumpLogs?: () => string
|
||||
}
|
||||
}
|
||||
|
||||
function configuredLevel(): LogLevel {
|
||||
let level = (process.env.NEXT_PUBLIC_LOG_LEVEL || (process.env.NODE_ENV === "production" ? "info" : "debug")) as LogLevel
|
||||
if (typeof window !== "undefined") {
|
||||
try {
|
||||
const override = window.localStorage.getItem("iotp_log_level") as LogLevel | null
|
||||
if (override && override in ORDER) level = override
|
||||
} catch { /* storage may be unavailable */ }
|
||||
}
|
||||
return level in ORDER ? level : "info"
|
||||
}
|
||||
|
||||
function buffer(): LogEntry[] {
|
||||
if (typeof window === "undefined") return []
|
||||
if (!window.__iotpLogs) {
|
||||
window.__iotpLogs = []
|
||||
window.__iotpDumpLogs = () =>
|
||||
(window.__iotpLogs || [])
|
||||
.map((e) => `${e.ts} ${e.level.toUpperCase().padEnd(5)} [${e.scope}] ${e.message}${e.data !== undefined ? " " + safeJson(e.data) : ""}`)
|
||||
.join("\n")
|
||||
}
|
||||
return window.__iotpLogs
|
||||
}
|
||||
|
||||
function safeJson(v: unknown): string {
|
||||
try { return JSON.stringify(v) } catch { return String(v) }
|
||||
}
|
||||
|
||||
function emit(level: LogLevel, scope: string, message: string, data?: unknown) {
|
||||
if (ORDER[level] < ORDER[configuredLevel()]) return
|
||||
const ts = new Date().toISOString()
|
||||
const buf = buffer()
|
||||
buf.push({ ts, level, scope, message, data })
|
||||
if (buf.length > BUFFER_SIZE) buf.splice(0, buf.length - BUFFER_SIZE)
|
||||
const line = `[iotp ${ts.slice(11, 23)}] [${scope}] ${message}`
|
||||
const fn = level === "error" ? console.error : level === "warn" ? console.warn : level === "info" ? console.info : console.debug
|
||||
if (data !== undefined) fn(line, data)
|
||||
else fn(line)
|
||||
}
|
||||
|
||||
export function getLogger(scope: string) {
|
||||
return {
|
||||
debug: (message: string, data?: unknown) => emit("debug", scope, message, data),
|
||||
info: (message: string, data?: unknown) => emit("info", scope, message, data),
|
||||
warn: (message: string, data?: unknown) => emit("warn", scope, message, data),
|
||||
error: (message: string, data?: unknown) => emit("error", scope, message, data),
|
||||
}
|
||||
}
|
||||
|
||||
// Describe any thrown value for logs and user messages.
|
||||
export function describeError(err: unknown): string {
|
||||
if (err instanceof Error) return `${err.name}: ${err.message}`
|
||||
if (typeof err === "string") return err
|
||||
return safeJson(err)
|
||||
}
|
||||
|
||||
// Global handlers so uncaught browser errors land in the buffer too.
|
||||
export function installGlobalErrorLogging() {
|
||||
if (typeof window === "undefined") return
|
||||
const w = window as Window & { __iotpGlobalInstalled?: boolean }
|
||||
if (w.__iotpGlobalInstalled) return
|
||||
w.__iotpGlobalInstalled = true
|
||||
const log = getLogger("window")
|
||||
window.addEventListener("error", (ev) => log.error(`Uncaught: ${ev.message}`, { file: ev.filename, line: ev.lineno }))
|
||||
window.addEventListener("unhandledrejection", (ev) => log.error(`Unhandled rejection: ${describeError(ev.reason)}`))
|
||||
log.debug(`Logging active at level "${configuredLevel()}"; call window.__iotpDumpLogs() to copy the buffer`)
|
||||
}
|
||||
Reference in New Issue
Block a user