关于 api-logging-guidelines
这套指南介绍 API 路由的日志规范,涵盖日志级别、事件上下文、隐私和性能。它帮助开发者减少重复或例行日志,同时保留有用的错误与业务事件记录。
主要功能
- 01日志级别选择指南
- 02避免重复记录请求日志
- 03错误与业务事件模式
- 04敏感数据排除要求
- 05现有日志迁移检查清单
适用场景
- 01发布前审查 API 路由日志
- 02重构冗余或重复日志
- 03为错误和安全事件补充上下文
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ inkeep/ agents --skill api-logging-guidelines安装后在 Claude Code 中输入/api-logging-guidelines确认已加载。
详细介绍
API 路由日志记录指南
全面介绍如何在 API 路由中恰当地使用日志记录,以保持日志整洁、实用且高效。
核心原则
1. 避免重复记录日志
不要记录中间件已经记录的内容:
// ❌ BAD - Request details are already logged by middleware
logger.info({ tenantId, projectId }, 'Getting project details');
依赖请求中间件进行日志记录:
- 请求/响应中间件已经记录:方法、路径、状态、耗时、路径参数
- 这些日志包含 URL 路径中的租户/项目 ID
- 添加重复日志只会增加噪声,没有价值
2. 日志级别指南
| 级别 | 使用场景 | 示例 |
|---|---|---|
| ERROR | 需要关注的意外故障 | 数据库连接失败、未处理的异常、关键服务错误 |
| WARN | 可恢复的问题或值得关注的模式 | 触发速率限制、使用已弃用的 API、启用回退行为 |
| INFO | 重要的业务事件(不是常规操作) | 创建用户账户、处理付款、变更关键配置 |
| DEBUG | 详细的诊断信息 | 查询参数、中间计算结果、缓存命中/未命中详情 |
3. 应记录的内容
记录以下重要事件:
// ✅ GOOD - Important business event
logger.info({
userId,
oldPlan: 'free',
newPlan: 'pro',
mrr: 99
}, 'User upgraded subscription');
// ✅ GOOD - Error with context
logger.error({
error,
tenantId,
webhookUrl,
attemptNumber: 3
}, 'Webhook delivery failed after retries');
// ✅ GOOD - Security-relevant event
logger.warn({
ip: c.req.header('x-forwarded-for'),
userId,
attemptedResource
}, 'Unauthorized access attempt');
// ✅ GOOD - Performance issue
logger.warn({
duration: 5234,
query,
resultCount: 10000
}, 'Slow query detected');
4. 不应记录的内容
避免记录常规操作:
// ❌ BAD - Routine CRUD operation
logger.info('Getting user by ID');
// ❌ BAD - Already logged by middleware
logger.info(`Processing GET request to /api/users/${id}`);
// ❌ BAD - No actionable information
logger.info('Starting database query');
// ❌ BAD - Sensitive information
logger.info({ password, apiKey }, 'User login attempt');
// ❌ BAD - Overly granular
logger.debug('Entering function processUser');
logger.debug('Exiting function processUser');
API 路由模式
模式 1:错误处理
// ✅ GOOD - Log errors with context
export const route = router.get('/:id', async (c) => {
try {
const result = await riskyOperation();
return c.json(result);
} catch (error) {
// Log error with relevant context
logger.error({
error,
userId: c.get('userId'),
operation: 'riskyOperation',
// Include any relevant debugging context
requestId: c.get('requestId')
}, 'Operation failed');
// Return generic error to client (don't leak internals)
return c.json({ error: 'Internal server error' }, 500);
}
});
模式 2:业务事件
// ✅ GOOD - Log significant business events
export const route = router.post('/subscription/upgrade', async (c) => {
const { planId } = await c.req.json();
const result = await upgradeSubscription(userId, planId);
// This is worth logging - it's a significant business event
logger.info({
userId,
oldPlan: result.previousPlan,
newPlan: result.newPlan,
mrr: result.mrr,
timestamp: new Date().toISOString()
}, 'Subscription upgraded');
return c.json(result);
});
模式 3:性能监控
// ✅ GOOD - Log performance issues
export const route = router.get('/search', async (c) => {
const start = Date.now();
const results = await performSearch(query);
const duration = Date.now() - start;
// Only log if performance is concerning
if (duration > 1000) {
logger.warn({
duration,
query,
resultCount: results.length,
cached: false
}, 'Slow search query');
}
return c.json(results);
});
模式 4:安全事件
// ✅ GOOD - Log security-relevant events
export const route = router.post('/api/admin/*', async (c) => {
const hasPermission = await checkPermission(userId, resource);
if (!hasPermission) {
// Log unauthorized access attempts
logger.warn({
userId,
resource,
ip: c.req.header('x-forwarded-for'),
userAgent: c.req.header('user-agent')
}, 'Unauthorized access attempt');
return c.json({ error: 'Forbidden' }, 403);
}
// Proceed with authorized request...
});
按环境划分的指南
开发环境
// More verbose logging acceptable in development
if (process.env.NODE_ENV === 'development') {
logger.debug({ params, body }, 'Request details');
}
生产环境
- 将 INFO 级别日志限制为仅记录重要事件
- 切勿记录敏感数据(密码、令牌、密钥、个人身份信息)
- 使用结构化日志,以便更好地搜索
- 包含关联 ID,以便追踪请求
迁移策略
重构现有的冗余日志记录时:
- 识别重复日志:移除与中间件日志重复的日志
- 降低常规操作的日志级别:将常规操作的日志从 INFO 调整为 DEBUG
- 完善错误日志:为错误日志添加更多上下文
- 添加业务事件日志:确保记录重要的业务事件
- 检查日志级别:确保每条日志都使用恰当的级别
之前:
router.get('/:id', async (c) => {
const { id } = c.req.param();
logger.info({ id }, 'Getting item by ID'); // Redundant
const item = await getItem(id);
logger.info({ item }, 'Retrieved item'); // Too verbose
return c.json(item);
});
之后:
router.get('/:id', async (c) => {
const { id } = c.req.param();
try {
const item = await getItem(id);
// No logging needed - routine successful operation
return c.json(item);
} catch (error) {
// Only log errors
logger.error({ error, id }, 'Failed to retrieve item');
return c.json({ error: 'Item not found' }, 404);
}
});
无需冗余日志的调试方式
不要使用冗余日志记录,而应采用以下策略:
- 有选择地启用调试模式:排查问题时,为特定模块启用 DEBUG 级别
- 使用追踪:使用 OpenTelemetry/Jaeger 进行分布式追踪
- 使用指标:使用 Prometheus/StatsD 获取性能指标
- 使用错误跟踪:使用 Sentry/Rollbar 汇总错误
- 使用功能标志:调试时为特定用户/请求启用冗余日志记录
总结检查清单
添加日志语句前,请先确认:
- 中间件是否已经记录了这些内容?(方法、路径、状态、参数)
- 这是重要的业务事件,还是常规操作?
- 这条日志是否提供了可采取行动的信息?
- 我使用的日志级别是否正确?
- 我是否包含了有用的上下文,同时避免记录敏感数据?
- 这条日志在生产环境中是否有用,还是只会造成噪声?
请记住:好的日志重在信号,而非数量。每条日志都应有其用途。
评论
支持 Markdown