feat(logging): add a trace tier across all plugins, unlocked by DEBUG#56
Conversation
Adds a most-verbose `trace` level (priority 5, below `debug`) to every
plugin's logger, plus ~85 fine-grained trace events at the points you
actually debug:
- oauth2: config-hook steps, providers considered/registered/skipped,
token cache load + ensureAccessToken decision branches, refresh and
model-discovery start/finish, per-request bearer injection, scheduler ticks.
- models-info: per-provider meta parse, cache key/lookup, fetch start,
per-model match + field-merge (upstream-wins) decisions, counts.
- ratelimit: per-provider opt-in, wrapped-fetch invocation + scope bucket,
parsed x-ratelimit triple, throttle/tier selection, pre-emptive wait,
429 backoff/retry, cooldown set/cleared.
- browser: handshake steps, every frame routed (agent->executor),
command dispatch/result/timeout, group ownership (assign/deny/adopt/
orphan), executor/agent connect/disconnect, host/guest election.
The tier is deliberately high-volume, so it's not its own host knob:
OpenCode's `DEBUG` now maps to `trace` (fromOpenCodeLogLevel("DEBUG") ->
"trace"), so `--log-level DEBUG` unlocks the whole trace stream. Since
OpenCode's client.app.log enum has no `trace`, trace records forward to
the host labelled `debug` (gating already happened locally); the JSON
console fallback keeps the true `trace` label. Secret redaction applies
to trace like every other level — no raw tokens, only booleans/counts/
fingerprints.
Infra (LogLevel union, priority, Logger.trace, console logger, OpenCode
wrapper, oauth2 validateLogLevel, browser-mcp logger literal) + per-plugin
logging tests updated. Docs: architecture.md "Logging" gains a trace
section.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Code Review
This pull request introduces a new, highly detailed trace log tier across all workspace packages, allowing fine-grained tracking of internal operations when the host is run with --log-level DEBUG. The review feedback focuses on optimizing the performance of the newly added trace logging logic in packages/opencode-models-info/src/plugin.ts by eliminating redundant Map lookups and avoiding double-filtering of array fields.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| const matchById = byId.has(modelId); | ||
| const match = byId.get(modelId) ?? (declaredId ? byId.get(declaredId) : undefined); | ||
| if (!match) { | ||
| deps.logger.trace("models_info_model_unmatched", { providerId, modelId, declaredId }); | ||
| continue; | ||
| } | ||
| deps.logger.trace("models_info_model_matched", { | ||
| providerId, | ||
| modelId, | ||
| matchedBy: matchById ? "id" : "declaredId" |
There was a problem hiding this comment.
We can optimize the Map lookups here by retrieving the value directly with byId.get(modelId) instead of calling byId.has(modelId) first. This avoids an extra Map lookup on every iteration of the loop.
| const matchById = byId.has(modelId); | |
| const match = byId.get(modelId) ?? (declaredId ? byId.get(declaredId) : undefined); | |
| if (!match) { | |
| deps.logger.trace("models_info_model_unmatched", { providerId, modelId, declaredId }); | |
| continue; | |
| } | |
| deps.logger.trace("models_info_model_matched", { | |
| providerId, | |
| modelId, | |
| matchedBy: matchById ? "id" : "declaredId" | |
| const matchFromId = byId.get(modelId); | |
| const match = matchFromId ?? (declaredId ? byId.get(declaredId) : undefined); | |
| if (!match) { | |
| deps.logger.trace("models_info_model_unmatched", { providerId, modelId, declaredId }); | |
| continue; | |
| } | |
| deps.logger.trace("models_info_model_matched", { | |
| providerId, | |
| modelId, | |
| matchedBy: matchFromId ? "id" : "declaredId" | |
| }); |
| const derivedFields = Object.keys(derived); | ||
| const appliedFields = derivedFields.filter( | ||
| (f) => modelConfig[f] === undefined || overwrite?.has(f) | ||
| ); | ||
| const skippedFields = derivedFields.filter((f) => !appliedFields.includes(f)); |
There was a problem hiding this comment.
Evaluating derivedFields.filter twice (including an .includes check inside the second filter) on every model iteration creates unnecessary array allocations and overhead, even when trace logging is disabled. We can optimize this by partitioning the fields in a single pass.
const derivedFields = Object.keys(derived);
const appliedFields: string[] = [];
const skippedFields: string[] = [];
for (const f of derivedFields) {
if (modelConfig[f] === undefined || overwrite?.has(f)) {
appliedFields.push(f);
} else {
skippedFields.push(f);
}
}
What
Adds a most-verbose
tracelog level (priority 5, belowdebug) to all four plugins, plus ~85 fine-grained trace events at the points worth watching when debugging. Answers "can we get A LOT more logs?" — yes, and they all surface at--log-level DEBUG.How it's gated
The tier is high-volume, so it isn't a separate host knob: OpenCode's
DEBUGnow maps totrace(fromOpenCodeLogLevel("DEBUG") → "trace"). Running the host at--log-level DEBUGunlocks the full trace stream on top of the existingdebuglifecycle events. A clean run at the defaultinfostays quiet.OpenCode's
client.app.log()enum has notrace, so a trace record is forwarded to the host labelleddebug(the finer gating already happened locally); the JSON-console fallback keeps the truetracelabel. All four plugins converged on this independently.Events by plugin
ensureAccessTokenbranches (reuse/refresh/interactive), refresh + model-discovery start/finish, per-request bearer injection, scheduler ticks.x-ratelimittriple, throttle/tier selection, pre-emptive wait, 429 backoff/retry, cooldown set/cleared.No secrets: redaction applies to
tracelike every level; events carry booleans/counts/ids, never raw tokens.Infra + tests + docs
LogLevelunion,LOG_LEVEL_PRIORITY,Logger.trace, console logger, OpenCode wrapper. Plus oauth2validateLogLevelaccepts"trace", and the browser-mcp logger literal gainedtrace.logging.test.tscoversDEBUG → trace, trace-emits-at-trace / suppressed-at-debug, and the workingtracemethod.docs/architecture.md→ Logging gains a "trace tier" section.Full pre-push gate green:
pnpm -r build && pnpm -r typecheck && pnpm coverage && pnpm lint && pnpm format:check(every per-package coverage threshold still met; 449 tests pass).🤖 Generated with Claude Code