Agent skill
logging
How to do backend logging
Install this agent skill to your Project
npx add-skill https://github.com/elie222/inbox-zero/tree/main/.claude/skills/logging
SKILL.md
Logging
We use a centralized, request-scoped logging pattern where loggers are created by middleware and passed through the request/function chain.
API Route Logging (Primary Pattern)
Use middleware wrappers that automatically create loggers with request context:
import { withError, withAuth, withEmailAccount, withEmailProvider } from "@/utils/middleware";
// Basic route with error handling and logging
export const POST = withError("my-route", async (request) => {
const logger = request.logger;
logger.info("Processing request");
// ...
});
// Authenticated route - logger includes userId
export const GET = withAuth("my-route", async (request) => {
request.logger.info("User action"); // Already has userId context
// ...
});
// Email account route - logger includes emailAccountId, email
export const POST = withEmailAccount("my-route", async (request) => {
request.logger.info("Email action"); // Has userId, emailAccountId, email
// ...
});
// Email provider route - same as email account, plus provides emailProvider
export const GET = withEmailProvider("my-route", async (request) => {
request.logger.info("Provider action");
const emails = await request.emailProvider.getMessages();
// ...
});
The middleware automatically adds:
requestId- Unique ID for request tracingurl- Request URLuserId- For authenticated routesemailAccountId,email- For email account routes
Enriching Logger Context
Add additional context within your route handler:
export const POST = withEmailAccount("digest", async (request) => {
let logger = request.logger;
const body = await request.json();
logger = logger.with({ messageId: body.messageId });
logger.info("Processing message");
// ...
});
Helper Function Logging
Helper functions called from routes should receive the logger as a parameter instead of creating their own:
import type { Logger } from "@/utils/logger";
export async function processEmail(
emailId: string,
logger: Logger,
) {
logger = logger.with({ emailId });
logger.info("Processing email");
// ...
}
Then call from your route:
export const POST = withEmailAccount("process", async (request) => {
await processEmail(body.emailId, request.logger);
});
Server Action Logging
Server actions using actionClient receive the logger through context, similar to route middleware:
import { actionClient } from "@/utils/actions/safe-action";
export const createRuleAction = actionClient
.metadata({ name: "createRule" })
.inputSchema(createRuleBody)
.action(
async ({
ctx: { emailAccountId, logger, provider },
parsedInput: { name, actions },
}) => {
logger.info("Creating rule", { name });
// ...
},
);
The actionClient context provides:
logger- Scoped logger with request contextemailAccountId- Current email accountprovider- Email provider type
When to Use createScopedLogger
Use createScopedLogger only for code that doesn't run within a middleware chain (route or action):
import { createScopedLogger } from "@/utils/logger";
// Standalone scripts
const logger = createScopedLogger("script/migrate");
// Tests
const logger = createScopedLogger("test");
Don't use .with() for a global/file-level logger. Only use within a specific function.
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
qa-new-flow
Create a new browser QA flow file from the template
llm
Guidelines for implementing LLM (Language Model) functionality in the application
fullstack-workflow
Complete fullstack workflow combining GET API routes, server actions, SWR data fetching, and form handling. Use when building features that need both data fetching and mutations from API to UI.
write-tests
Write focused unit tests for backend and utility logic
e2e
Run and debug E2E flow tests. Use when triggering E2E tests, checking test status, debugging failures with Axiom logs, or setting up local E2E testing.
prisma
How to use Prisma
Didn't find tool you were looking for?