Guide structured logging using @repo/logger package with Winston. Use when adding logs, debugging, or understanding log patterns.
This skill provides guidance for structured logging using the @repo/logger package in the YouTube Channel Analyzer project.
The @repo/logger package provides Winston-based structured logging with request correlation and context propagation.
import { Logger } from "@repo/logger";
const logger = new Logger({ context: "feature-name" });
| Level | Use Case |
|---|---|
error | Unrecoverable failures, exceptions, critical issues |
warn | Recoverable issues, rate limits, deprecations |
info | Important business events, state changes, startup |
http | Request/response logging (used by middleware) |
debug | Development details, variable values, diagnostics |
Each service file should create a logger with a descriptive context:
import { Logger } from "@repo/logger";
const logger = new Logger({ context: "meta-bot" });
// Error with exception
logger.error("Failed to process message", error, { messageId, userId });
// Warning for recoverable issues
logger.warn("Rate limit approaching", { currentRate, limit });
// Info for important business events
logger.info("User completed onboarding", { userId, organizationId });
// Debug for development details
logger.debug("Processing webhook payload", { payload });
logger.error(message: string, errorOrMeta?: unknown, meta?: Record<string, unknown>): void
logger.warn(message: string, errorOrMeta?: unknown, meta?: Record<string, unknown>): void
logger.info(message: string, errorOrMeta?: unknown, meta?: Record<string, unknown>): void
logger.http(message: string, errorOrMeta?: unknown, meta?: Record<string, unknown>): void
logger.debug(message: string, errorOrMeta?: unknown, meta?: Record<string, unknown>): void
Create child loggers for sub-contexts:
const logger = new Logger({ context: "meta-bot" });
const aiLogger = logger.child("ai"); // Context: "meta-bot:ai"
const flowLogger = logger.child("flow"); // Context: "meta-bot:flow"
aiLogger.info("Generating response"); // [meta-bot:ai] Generating response
Request context (requestId, userId, organizationId) is automatically included via AsyncLocalStorage:
// In middleware - context is set automatically
// In service code - context is included in logs automatically
logger.info("Processing request");
// Output includes: { requestId: "abc-123", userId: "user_1", ... }
import { updateContext } from "@repo/logger";
// Add user context after authentication
updateContext({
userId: user.id,
organizationId: session.activeOrganizationId,
});
try {
await processMessage(message);
} catch (error) {
logger.error("Failed to process message", error, {
messageId: message.id,
conversationId,
});
throw error;
}
if (!user) {
logger.error("User not found", undefined, { userId });
throw new TRPCError({ code: "NOT_FOUND" });
}
Errors with .cause are automatically serialized with the full chain.
import { timeAsync, PerformanceMarker } from "@repo/logger";
// Simple timing
const result = await timeAsync(
"ai-generation",
async () => generateResponse(prompt),
logger
);
// Multi-step timing
const marker = new PerformanceMarker("flow-execution");
marker.mark("start");
await executeNode(node);
marker.mark("node-executed");
await sendMessage(response);
marker.mark("message-sent");
logger.info("Flow completed", { timing: marker.getSummary() });