feat: configurable log levels

This commit is contained in:
adamdottv
2025-07-09 10:00:03 -05:00
parent ca8ce88354
commit 53f8e7850e
13 changed files with 129 additions and 20 deletions

View File

@@ -299,6 +299,16 @@
"type": "string",
"description": "Model to use in the format of provider/model, eg anthropic/claude-2"
},
"log_level": {
"type": "string",
"enum": [
"DEBUG",
"INFO",
"WARN",
"ERROR"
],
"description": "Minimum log level to write to log files"
},
"provider": {
"type": "object",
"additionalProperties": {

View File

@@ -108,6 +108,7 @@ export namespace Config {
autoupdate: z.boolean().optional().describe("Automatically update to the latest version"),
disabled_providers: z.array(z.string()).optional().describe("Disable providers that are loaded automatically"),
model: z.string().describe("Model to use in the format of provider/model, eg anthropic/claude-2").optional(),
log_level: Log.Level.optional().describe("Minimum log level to write to log files"),
provider: z
.record(
ModelsDev.Provider.partial().extend({

View File

@@ -40,6 +40,24 @@ const cli = yargs(hideBin(process.argv))
})
.middleware(async () => {
await Log.init({ print: process.argv.includes("--print-logs") })
try {
const { Config } = await import("./config/config")
const { App } = await import("./app/app")
App.provide({ cwd: process.cwd() }, async () => {
const cfg = await Config.get()
if (cfg.log_level) {
Log.setLevel(cfg.log_level as Log.Level)
} else {
const defaultLevel = Installation.isDev() ? "DEBUG" : "INFO"
Log.setLevel(defaultLevel)
}
})
} catch (e) {
Log.Default.error("failed to load config", { error: e })
}
Log.Default.info("opencode", {
version: Installation.VERSION,
args: process.argv.slice(2),

View File

@@ -651,7 +651,7 @@ export namespace Server {
"json",
z.object({
service: z.string().openapi({ description: "Service name for the log entry" }),
level: z.enum(["info", "error", "warn"]).openapi({ description: "Log level" }),
level: z.enum(["debug", "info", "error", "warn"]).openapi({ description: "Log level" }),
message: z.string().openapi({ description: "Log message" }),
extra: z
.record(z.string(), z.any())
@@ -664,6 +664,9 @@ export namespace Server {
const logger = Log.create({ service })
switch (level) {
case "debug":
logger.debug(message, extra)
break
case "info":
logger.info(message, extra)
break

View File

@@ -1,8 +1,35 @@
import path from "path"
import fs from "fs/promises"
import { Global } from "../global"
import z from "zod"
export namespace Log {
export const Level = z.enum(["DEBUG", "INFO", "WARN", "ERROR"]).openapi({ ref: "LogLevel", description: "Log level" })
export type Level = z.infer<typeof Level>
const levelPriority: Record<Level, number> = {
DEBUG: 0,
INFO: 1,
WARN: 2,
ERROR: 3,
}
let currentLevel: Level = "INFO"
export function setLevel(level: Level) {
currentLevel = level
}
export function getLevel(): Level {
return currentLevel
}
function shouldLog(level: Level): boolean {
return levelPriority[level] >= levelPriority[currentLevel]
}
export type Logger = {
debug(message?: any, extra?: Record<string, any>): void
info(message?: any, extra?: Record<string, any>): void
error(message?: any, extra?: Record<string, any>): void
warn(message?: any, extra?: Record<string, any>): void
@@ -23,6 +50,7 @@ export namespace Log {
export interface Options {
print: boolean
level?: Level
}
let logpath = ""
@@ -85,14 +113,25 @@ export namespace Log {
return [next.toISOString().split(".")[0], "+" + diff + "ms", prefix, message].filter(Boolean).join(" ") + "\n"
}
const result: Logger = {
debug(message?: any, extra?: Record<string, any>) {
if (shouldLog("DEBUG")) {
process.stderr.write("DEBUG " + build(message, extra))
}
},
info(message?: any, extra?: Record<string, any>) {
process.stderr.write("INFO " + build(message, extra))
if (shouldLog("INFO")) {
process.stderr.write("INFO " + build(message, extra))
}
},
error(message?: any, extra?: Record<string, any>) {
process.stderr.write("ERROR " + build(message, extra))
if (shouldLog("ERROR")) {
process.stderr.write("ERROR " + build(message, extra))
}
},
warn(message?: any, extra?: Record<string, any>) {
process.stderr.write("WARN " + build(message, extra))
if (shouldLog("WARN")) {
process.stderr.write("WARN " + build(message, extra))
}
},
tag(key: string, value: string) {
if (tags) tags[key] = value