Audit Logging
Validate that all logging in Tarsk uses the standardized shared logger.
The Standard
Every log statement in Tarsk must use createLogger from @tarsk/shared.
import { createLogger } from "@tarsk/shared";
const log = createLogger("domain.action");
log.debug("Detailed diagnostic info", { key: "value" });
log.info("Normal operation event", { key: "value" });
log.warn("Degraded but recoverable", { key: "value" });
log.error("Operation failed", error);
No raw console.log, console.error, console.warn, or console.debug calls outside of the logger implementation itself.
Log Event Naming
Event names use lowercase dot-notation. The first segment is the domain; subsequent segments narrow the scope.
Format: domain.action or domain.subdomain.action
Examples:
agent.executor-- AI agent executionagent.subagent-- Sub-agent spawningchat.post-- Chat message postingchat.stop-- Chat generation stopchat.media-- Media loading in chatgit.push-- Git push operationsgit.create-pr-- Pull request creationgit.sync-branch-- Branch synchronizationgitops.timing-- Git operations timinggitops.subprocess-- Subprocess timinggitops.ttl-cache-- TTL cache operationsgitops.api-- Frontend git API callsmcp.connection-- MCP server connectionsmcp.tools-- MCP tool loadingdb.migration-- Database migrationsroutes-- HTTP route error logginglogs.api-- Log forwarding endpointvoice.stt-- Speech-to-textvoice.tts-- Text-to-speechvoice.vosk-cache-- Vosk model cachingmodels.catalog-- Model catalog operationsmodels.info-- Model info fetchingprojects.open-- Opening projectsagents.manager-- Agent file managementimage.generate-- Image generationimage.save-- Image savingskills.frontmatter-- Skill file parsingstore-- Frontend state managementbrowser.tool-- Browser tool operations
Rules:
- All lowercase, no spaces.
- Segments separated by dots.
- No bracket prefixes (
[tag]), no emoji, no decorative characters. - Keep segment names short but descriptive (2-4 words max per segment).
- Reuse existing domain names when adding logging to an existing subsystem.
Log Levels
| Level | When to use | Survives production? |
|---|---|---|
DEBUG |
Timing data, cache hits/misses, request/response payloads, internal state transitions. Disabled in production. | No (set to WARN in production) |
INFO |
Successful operations, state changes, user actions, configuration loaded. | No (set to WARN in production) |
WARN |
Degraded but recoverable conditions, fallback paths taken, deprecated API usage, validation failures. | Yes |
ERROR |
Operation failures, uncaught exceptions, data corruption, external service failures. | Yes |
Production behavior: The CLI entrypoint sets LogLevel.WARN unless --debug is passed. This means DEBUG and INFO calls are silent in production. Only WARN and ERROR produce output.
Rules:
- Never use
ERRORfor expected conditions (e.g., validation failures that return 400). - Never use
WARNfor normal operational messages -- useINFOinstead. - Use
DEBUGfor anything that would be noisy in development (timing, cache state, event payloads). - Include structured data as the second argument rather than interpolating into the message string.
Message Format
Good:
log.info("Agent prompt completed", { model, provider, duration: ms });
log.error("Failed to save conversation", error);
log.debug("Cache hit", { key, ms });
Bad:
console.log(`[ai] Agent prompt completed successfully for ${model} from ${provider} in ${ms}ms`);
console.error("[chat-media] Failed to load media", details);
console.warn("[MCP] Connected to " + serverName);
Rules:
- Message is a short, human-readable sentence. No prefix tags, no emoji.
- Structured data goes in the second argument as an object.
- Error objects pass directly as the data argument -- the logger serializes them safely.
- Do not stringify data into the message string.
Import Path
CLI (backend):
import { createLogger } from "@tarsk/shared";
App (frontend):
import { createLogger } from "@tarsk/shared";
Re-export from cli/src/core/logger.ts exists for migration convenience but new code should import from @tarsk/shared directly.
Validation Checklist
When reviewing code changes that add or modify logging:
Required (blocks merge)
- All log calls use
createLoggerfrom@tarsk/shared-- no rawconsole.*calls. - Logger is created at module scope:
const log = createLogger("domain.action"); - Event name follows dot-notation format, all lowercase, no brackets.
- Correct log level for the severity (DEBUG/INFO/WARN/ERROR).
- Message is a short sentence without embedded data.
- Structured data passed as the second argument, not interpolated into the message.
- Error objects passed directly, not wrapped in string template.
- No circular reference hazards in logged data (the logger handles this, but avoid logging DOM nodes or React fiber trees).
Recommended (should fix)
- Reuses existing domain names when adding to an existing subsystem.
- DEBUG level for timing/cache/payload data that would be noisy.
- No logging inside tight loops or render paths without a guard.
- No sensitive data in logs (API keys, tokens, passwords, PII).
Migration Guide
When converting a file from ad-hoc logging:
- Add
import { createLogger } from "@tarsk/shared";(CLI) or same from@tarsk/shared(app). - Add
const log = createLogger("domain.action");after imports. - Replace
console.log("[prefix] message")withlog.info("message"). - Replace
console.error("[prefix] message")withlog.error("message"). - Replace
console.warn("[prefix] message")withlog.warn("message"). - Move interpolated values into the data object.
- Remove the
[prefix]from the message string.
Skill Execution
To audit an entire subsystem or file:
- Use
grepto find allconsole.(log|error|warn|debug)calls in the target files. - For each call, verify it uses
createLoggeror is inside the logger implementation. - Check the event name, level, message format, and data argument.
- Report violations grouped by severity (blocks/should-fix).
- Offer to fix violations if the user requests it.
Architecture Notes
- The shared logger lives in
shared/src/logger.ts. - It works in both Node.js and browser environments.
- The CLI entrypoint (
cli/src/index.ts) sets the global log level and optionally adds a file transport for debug mode. - The frontend can register transports via
addLogTransport()for server-side log forwarding. - The old
cli/src/core/logger.tsLogger class is dead code -- it re-exports from shared now. - The old
app/lib/voice/utils/Logger.tshas been removed -- replaced by the shared logger.