- Use English only - All log messages must be in English
- No emojis - Do not use emojis in log messages
- Structured logging - Use data objects instead of string concatenation
- Avoid verbose logging - Keep log statements concise and meaningful, avoid excessive logging in normal operation paths
| Level | Value | Usage |
|---|---|---|
| TRACE | 0 | Verbose diagnostic info, performance-sensitive paths |
| DEBUG | 1 | Development debugging, internal state |
| INFO | 2 | General operational info (default in dev) |
| WARN | 3 | Potential issues, degraded functionality (default in prod) |
| ERROR | 4 | Failures, exceptions, requires attention |
import { createLogger, logger } from '@/shared/utils/logger';
// Create scoped logger (recommended)
const log = createLogger('ModuleName');
log.info('Operation completed');
log.info('Loaded items', { count: 10 });
// Logging errors with context data
log.error('Failed to load config', { configPath, error });
// Or use global logger with context string
logger.info('Message', 'Context', { data });[context] message data
context: Component or module name in bracketsmessage: Descriptive text, no trailing punctuationdata: Optional structured data (objects auto-serialized)
Error objects in data are automatically extracted and appended separately from JSON:
// Input
log.error('Failed to load', { location: 'initializeApp', error });
// Output format
[App] Failed to load {"location":"initializeApp"}, Error: Something went wrong
at initializeApp (file.ts:143:13)- Regular data is serialized as JSON
- Error stack traces are appended after the JSON, separated by comma
- Only first-level Error objects in data are processed
Examples:
[WorkspaceManager] Loading workspace configuration
[LspService] Failed to start server {"code":"ENOENT"}, Error: spawn node ENOENT
at ChildProcess._handle.onexit (...)
- Use
createLogger('ModuleName')at file top, matching component/service name - Prefer structured data over string interpolation:
log.info('Loaded items', { count }) - Include Error objects in data object:
log.error('Request failed', { requestId, error }) - Avoid logging sensitive data (tokens, passwords, PII)
- Avoid excessive logging in hot paths (loops, frequent callbacks)
- Use TRACE for expensive computations that may impact performance
Use src/web-ui/src/shared/utils/timing.ts as the single timing helper for frontend diagnostics.
import { createLogger } from '@/shared/utils/logger';
import { measureAsyncAndLog, sendDebugProbe } from '@/shared/utils';
const log = createLogger('ModuleName');
await measureAsyncAndLog(log, 'Workspace loaded', () => loadWorkspace(), {
data: { workspacePath },
});
sendDebugProbe('ModuleName.ts:42', 'Workspace refresh completed', { workspacePath }, {
startedAt,
});Rules:
- Prefer
measureSync,measureAsync,measureSyncAndLog,measureAsyncAndLog,logDuration, andlogElapsedover handwrittenperformance.now()/Date.now()timing logs - Use
durationMsfor frontend diagnostic log fields - Treat
sendDebugProbeas a thin wrapper over the shared logger/timing helpers, not as a separate logging system - Do not replace protocol or persisted fields such as
duration_mswhen they are part of API payloads, events, or stored data - Do not migrate animation, polling, or deadline logic that depends on raw clock semantics into the logging helper layer