Install any skill in seconds. Free to start, no credit card required.
Get Started Free →How to do backend logging
.claude/skills/elie222-logging/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | -36% | 0% |
| case-03 | ✗→✓ | ▲ Improved | -25% | 0% |
| case-04 | ✗→✓ | ▲ Improved | -36% | 0% |
| case-05 | ✗→✓ | ▲ Improved | -30% | 0% |
| case-06 | ✗→✓ | ▲ Improved | -44% | 0% |
We use a centralized, request-scoped logging pattern where loggers are created by middleware and passed through the request/function chain.
Use middleware wrappers that automatically create loggers with request context:
typescriptimport { 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 routesAdd additional context within your route handler:
typescriptexport 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 functions called from routes should receive the logger as a parameter instead of creating their own:
typescriptimport 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:
typescriptexport const POST = withEmailAccount("process", async (request) => { await processEmail(body.emailId, request.logger); });
Server actions using actionClient receive the logger through context, similar to route middleware:
typescriptimport { 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 typeUse createScopedLogger only for code that doesn't run within a middleware chain (route or action):
typescriptimport { 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.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-22 | pass→pass | 12,375 | 10,005 | -19% | 1 | 1 | 0% | 2,349 | 2,744 | +17% | 0 | 0 | — |
case-01 | fail→pass | 15,458 | 5,933 | -62% | 1 | 1 | 0% | 3,297 | 2,117 | -36% | 0 | 0 | — |
case-02 | fail→fail | 14,993 | 6,730 | -55% | 1 | 1 | 0% | 2,913 | 2,305 | -21% | 0 | 0 | — |
case-03 | fail→pass | 16,282 | 7,705 | -53% | 1 | 1 | 0% | 3,353 | 2,498 | -25% | 0 | 0 | — |
case-04 | fail→pass | 15,425 | 5,922 | -62% | 1 | 1 | 0% | 3,012 | 1,923 | -36% | 0 | 0 | — |
case-05 | fail→pass | 14,781 | 6,154 | -58% | 1 | 1 | 0% | 2,782 | 1,950 | -30% | 0 | 0 | — |
case-06 | fail→pass | 18,208 | 4,594 | -75% | 1 | 1 | 0% | 3,155 | 1,769 | -44% | 0 | 0 | — |
case-07 | fail→pass | 7,624 | 4,013 | -47% | 1 | 1 | 0% | 1,469 | 1,580 | +8% | 0 | 0 | — |
case-08 | fail→pass | 8,806 | 2,888 | -67% | 1 | 1 | 0% | 1,577 | 1,390 | -12% | 0 | 0 | — |
case-09 | fail→fail | 11,491 | 5,028 | -56% | 1 | 1 | 0% | 2,081 | 1,741 | -16% | 0 | 0 | — |
case-10 | fail→pass | 15,042 | 6,361 | -58% | 1 | 1 | 0% | 2,707 | 1,997 | -26% | 0 | 0 | — |
case-11 | fail→pass | 10,168 | 7,840 | -23% | 1 | 1 | 0% | 1,642 | 2,246 | +37% | 0 | 0 | — |
case-12 | pass→pass | 13,006 | 6,006 | -54% | 1 | 1 | 0% | 2,393 | 1,983 | -17% | 0 | 0 | — |
case-13 | pass→pass | 18,480 | 3,806 | -79% | 1 | 1 | 0% | 2,713 | 1,618 | -40% | 0 | 0 | — |
case-14 | fail→pass | 13,356 | 5,438 | -59% | 1 | 1 | 0% | 2,412 | 1,851 | -23% | 0 | 0 | — |
case-15 | fail→pass | 13,156 | 3,207 | -76% | 1 | 1 | 0% | 2,314 | 1,387 | -40% | 0 | 0 | — |
case-16 | fail→pass | 15,961 | 7,558 | -53% | 1 | 1 | 0% | 2,389 | 2,290 | -4% | 0 | 0 | — |
case-17 | fail→pass | 13,566 | 4,513 | -67% | 1 | 1 | 0% | 2,097 | 1,670 | -20% | 0 | 0 | — |
case-18 | pass→pass | 10,548 | 4,187 | -60% | 1 | 1 | 0% | 1,416 | 1,669 | +18% | 0 | 0 | — |
case-19 | fail→pass | 8,236 | 2,467 | -70% | 1 | 1 | 0% | 1,440 | 1,301 | -10% | 0 | 0 | — |
case-20 | pass→pass | 13,038 | 6,124 | -53% | 1 | 1 | 0% | 2,457 | 1,826 | -26% | 0 | 0 | — |
case-21 | pass→pass | 14,494 | 10,481 | -28% | 1 | 1 | 0% | 2,620 | 2,819 | +8% | 0 | 0 | — |
case-23 | fail→pass | 10,938 | 4,190 | -62% | 1 | 1 | 0% | 1,986 | 1,671 | -16% | 0 | 0 | — |
case-24 | pass→pass | 8,973 | 1,735 | -81% | 1 | 1 | 0% | 1,425 | 1,103 | -23% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 24 cases were attempted. The headline lift of +63 percentage points is the difference between those two pass rates over the 24 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.