一键导入
nextjs-logging
This skill should be used when implementing logging, Pino, or structured logging patterns. Guides Pino structured logging setup.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
This skill should be used when implementing logging, Pino, or structured logging patterns. Guides Pino structured logging setup.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
This skill should be used when implementing code, creating files, or writing new features. Provides complete file structure, naming patterns, and import rules.
Decompose large requirements into phased task files that fit AI context windows using Gherkin + State Machine approach.
This skill should be used when working with database, Drizzle ORM, entities, schemas, migrations, and seeds. It provides Rails-style DB conventions, local sqlite/libSQL file defaults, and Effect-friendly data patterns.
Use this skill when generating enterprise admin, back-office, or operations module UI. Activates on: new module screen, admin panel, dashboard, operations list, approval queue, entity detail, multi-step form wizard, RBAC console, audit log, analytics cockpit, support console, settings page, or any layout described as "enterprise", "compact", or "admin-heavy". Provides recipe selection, page anatomy contract, density mode, state checklist, and AI prompt template.
This skill should be used when performing browser testing, verifying in browser, or runtime checks. Guides browser verification patterns.
This skill should be used when working with Chakra UI, UI components, or the design system. Guides Chakra UI v3 and Ark UI patterns.
| name | nextjs-logging |
| description | This skill should be used when implementing logging, Pino, or structured logging patterns. Guides Pino structured logging setup. |
Use this skill when implementing structured logging with Pino.
// src/shared/lib/logger.ts
import pino from 'pino';
const isServer = typeof window === 'undefined';
const isDev = process.env.NODE_ENV === 'development';
export const logger = pino({
level: process.env.LOG_LEVEL || (isDev ? 'debug' : 'info'),
...(isDev && {
transport: {
target: 'pino-pretty',
options: {
colorize: true,
ignore: 'pid,hostname',
translateTime: 'SYS:standard',
},
},
}),
base: {
env: process.env.NODE_ENV,
...(isServer && { pid: process.pid }),
},
redact: {
paths: ['password', 'token', 'authorization', '*.password', '*.token'],
censor: '[REDACTED]',
},
});
// Create child loggers for modules
export function createLogger(module: string) {
return logger.child({ module });
}
// src/shared/lib/browser-logger.ts
const LOG_LEVELS = ['debug', 'info', 'warn', 'error'] as const;
type LogLevel = (typeof LOG_LEVELS)[number];
const currentLevel = (process.env.NEXT_PUBLIC_LOG_LEVEL || 'info') as LogLevel;
const levelIndex = LOG_LEVELS.indexOf(currentLevel);
function shouldLog(level: LogLevel): boolean {
return LOG_LEVELS.indexOf(level) >= levelIndex;
}
export const browserLogger = {
debug: (msg: string, data?: object) => {
if (shouldLog('debug')) console.debug(msg, data);
},
info: (msg: string, data?: object) => {
if (shouldLog('info')) console.info(msg, data);
},
warn: (msg: string, data?: object) => {
if (shouldLog('warn')) console.warn(msg, data);
},
error: (msg: string, data?: object) => {
if (shouldLog('error')) console.error(msg, data);
},
};
import { logger } from '@/shared/lib/logger';
// Simple message
logger.info('User logged in');
// With structured data
logger.info({ userId: '123', action: 'login' }, 'User logged in');
// Error logging
logger.error({ err: error, userId: '123' }, 'Failed to process payment');
// src/modules/users/lib/logger.ts
import { createLogger } from '@/shared/lib/logger';
export const usersLogger = createLogger('users');
// Usage
usersLogger.info({ userId }, 'User profile updated');
// src/modules/users/actions/createUser.ts
'use server';
import { createLogger } from '@/shared/lib/logger';
const logger = createLogger('users:actions');
export const createUser = actionClient
.schema(createUserSchema)
.action(async ({ parsedInput }) => {
logger.info({ email: parsedInput.email }, 'Creating user');
try {
const user = await db.user.create({ data: parsedInput });
logger.info({ userId: user.id }, 'User created successfully');
return { success: true, user };
} catch (error) {
logger.error({ err: error, email: parsedInput.email }, 'Failed to create user');
throw error;
}
});
// src/app/api/users/route.ts
import { createLogger } from '@/shared/lib/logger';
import { NextResponse } from 'next/server';
const logger = createLogger('api:users');
export async function GET(request: Request) {
const start = Date.now();
logger.debug({ url: request.url }, 'Incoming request');
try {
const users = await getUsers();
logger.info(
{ duration: Date.now() - start, count: users.length },
'Users fetched successfully'
);
return NextResponse.json(users);
} catch (error) {
logger.error(
{ err: error, duration: Date.now() - start },
'Failed to fetch users'
);
return NextResponse.json({ error: 'Internal error' }, { status: 500 });
}
}
// src/shared/lib/request-context.ts
import { AsyncLocalStorage } from 'node:async_hooks';
import { createLogger } from './logger';
interface RequestContext {
requestId: string;
userId?: string;
path: string;
}
export const requestContextStorage = new AsyncLocalStorage<RequestContext>();
export function getRequestLogger() {
const context = requestContextStorage.getStore();
const baseLogger = createLogger('request');
if (context) {
return baseLogger.child({
requestId: context.requestId,
userId: context.userId,
path: context.path,
});
}
return baseLogger;
}
// src/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { createLogger } from '@/shared/lib/logger';
const logger = createLogger('middleware');
export function middleware(request: NextRequest) {
const start = Date.now();
const requestId = crypto.randomUUID();
logger.info({
requestId,
method: request.method,
path: request.nextUrl.pathname,
userAgent: request.headers.get('user-agent'),
}, 'Incoming request');
const response = NextResponse.next();
response.headers.set('x-request-id', requestId);
// Log on response (Note: middleware runs before response is complete)
logger.info({
requestId,
duration: Date.now() - start,
}, 'Request processed');
return response;
}
| Level | When to Use |
|---|---|
debug | Detailed debugging info, disabled in production |
info | Normal operations, user actions, business events |
warn | Unexpected but recoverable situations |
error | Errors that need attention |
// Good: Structured data with message
logger.info({ userId, action: 'purchase', amount }, 'Purchase completed');
// Good: Error with context
logger.error({ err: error, userId, orderId }, 'Payment failed');
// Good: Performance metrics
logger.info({ duration, query, resultCount }, 'Database query executed');
// Bad: String interpolation loses structure
logger.info(`User ${userId} made a purchase of ${amount}`);
// Bad: Logging sensitive data
logger.info({ password, creditCard }, 'User data');
// Bad: Too verbose in production
logger.debug({ fullRequestBody }, 'Request received');
# .env.local
LOG_LEVEL=debug # Server-side log level
NEXT_PUBLIC_LOG_LEVEL=warn # Client-side log level