skills/node-observability/SKILL.md
Node 生产可观测性——结构化日志(pino/winston)、指标(prom-client)、OpenTelemetry 追踪、健康检查、告警、关联 ID。
npx skillsauth add rockcookies/skills node-observabilityInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Persona: 你是 Node 可观测性工程师。未 instrument 的生产系统在你眼中是负债——主动埋点、关联信号、功能未完成前不算交付。
模式:
requestId / traceId 注入每条记录import pino from 'pino'
const logger = pino({
level: process.env.LOG_LEVEL ?? 'info',
// 生产 JSON;开发可用 pino-pretty
})
// ✓ Good — 结构化字段 + 关联 ID
function logRequest(req: Request, requestId: string) {
logger.info({
requestId,
method: req.method,
path: req.url,
userId: req.user?.id,
}, 'request received')
}
// ✓ Good — 错误只记录一次(遵循 node-error-handling 单一处理规则)
logger.error({ err, orderId }, 'order creation failed')
// ✗ Bad — 字符串拼接,无法检索
console.log(`User ${userId} did ${action} at ${new Date()}`)
import { Counter, Histogram, register } from 'prom-client'
// ✓ Good — 在指标声明上方写 PromQL 注释,便于发现
// rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m])
const httpRequests = new Counter({
name: 'http_requests_total',
help: 'Total HTTP requests',
labelNames: ['method', 'route', 'status'] as const,
})
const httpDuration = new Histogram({
name: 'http_request_duration_seconds',
help: 'HTTP request latency',
labelNames: ['method', 'route'] as const,
buckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5],
})
// ✓ Good — 有界标签:路由模式,非原始 path
httpRequests.inc({ method: 'GET', route: '/users/:id', status: '200' })
// ✗ Bad — 用户 ID 作为标签,基数爆炸
httpRequests.inc({ method: 'GET', route: req.url, status: '200', userId })
import { trace, SpanStatusCode } from '@opentelemetry/api'
const tracer = trace.getTracer('my-service')
// ✓ Good — 关键操作包裹 span,错误记入 span
async function createOrder(order: Order): Promise<void> {
return tracer.startActiveSpan('createOrder', async (span) => {
span.setAttribute('order.id', order.id)
try {
await db.orders.insert(order)
span.setStatus({ code: SpanStatusCode.OK })
}
catch (err) {
span.recordException(err as Error)
span.setStatus({ code: SpanStatusCode.ERROR })
throw err
}
finally {
span.end()
}
})
}
// ✓ Good — HTTP 中间件自动传播 trace context(@opentelemetry/instrumentation-http)
import { randomUUID } from 'node:crypto'
// ✓ Good — 中间件注入 requestId,写入 AsyncLocalStorage
app.use((req, res, next) => {
const requestId = req.headers['x-request-id'] ?? randomUUID()
res.setHeader('x-request-id', requestId)
asyncLocalStorage.run({ requestId }, next)
})
// ✓ Good — 健康检查区分 liveness 与 readiness
app.get('/health/live', (_req, res) => res.json({ status: 'ok' }))
app.get('/health/ready', async (_req, res) => {
const dbOk = await db.ping()
res.status(dbOk ? 200 : 503).json({ status: dbOk ? 'ok' : 'degraded', db: dbOk })
})
// ✓ Good — 从 active span 提取 traceId 写入日志
import { trace } from '@opentelemetry/api'
function enrichLog(fields: object) {
const span = trace.getActiveSpan()
const spanCtx = span?.spanContext()
return {
...fields,
traceId: spanCtx?.traceId,
spanId: spanCtx?.spanId,
}
}
logger.info(enrichLog({ orderId }), 'order created')
requestId / traceIdrecordException 记录错误node-security)| 反模式 | 解决方案 |
|--------|----------|
| log-and-throw 重复记录 | 单一处理规则(node-error-handling) |
| 高基数 Prometheus 标签 | 路由模式、状态码等有限集合 |
| span 未 end() | try/finally 或 startActiveSpan 自动管理 |
| console.log 上生产 | pino / winston JSON |
| 无 readiness 探针 | 检查 DB、队列、缓存连通性 |
交叉引用:错误单一处理见 node-error-handling,性能瓶颈优化见 node-performance,安全日志见 node-security。
tools
Turn the current conversation into a spec and publish it to the project issue tracker — no interview, just synthesis of what you've already discussed.
tools
Teach the user a new skill or concept, within this workspace.
development
Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.
documentation
Configure this repo for the engineering skills — set up its issue tracker, triage label vocabulary, and domain doc layout. Run once before first use of the other engineering skills.