Logging

SkillMonitoring & ops

Once added, your AI can write backend logs where every entry is tied to the specific request it came from. The skill teaches your AI how backend logging should be done, using the loggers your app's middleware already provides. This keeps log records organized and easy to follow.

Available today. Use it from your connected AI after setup.

Add the skill, then ask your AI to add logging to the backend work it is doing. It will write log entries tied to each request using the loggers your app already provides.

Then ask your AI: use the Logging skill

What your AI can do with it

  • Write logs for backend requests
  • Tie each log entry to the request that produced it
  • Use the loggers your app's middleware provides
  • Apply consistent logging practices to backend code

What this skill tells your AI

The instructions your AI receives, as published by elie222/inbox-zero in .claude/skills/logging/SKILL.md and read by ahel’s review.

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 tracing
  • url - Request URL
  • userId - For authenticated routes
  • emailAccountId, 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 context
  • emailAccountId - Current email account
  • provider - 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.

Signals

GitHub stars
12k
Forks
2k
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
logging-elie222
Source
github.com/elie222/inbox-zero