Vitest Testing
提供 Vitest 单元测试与集成测试的模式与最佳实践,涵盖断言、异步测试与模拟方法。
提供结构化日志、分布式追踪与指标收集的最佳实践,适用于系统可观测性建设。
openclaw skills install @wpank/logging-observability命令、参数、文件名以原文为准
适用于日志、指标和追踪三大支柱的系统可观测性设计模式。
| 支柱 | 目的 | 解答的问题 | 示例 |
|---|---|---|---|
| 日志 | 发生了什么 | 此请求为何失败? | {"level":"error","msg":"payment declined","user_id":"u_82"} |
| 指标 | 多少 / 多快 | 延迟是否在增加? | http_request_duration_seconds{route="/api/orders"} 0.342 |
| 追踪 | 请求流程 | 性能瓶颈在哪里? | Span: api-gateway → auth → order-service → db |
每个支柱在相互关联时效果最佳。在每条日志中嵌入 trace_id,以便从日志条目跳转到完整的分布式追踪。
始终以结构化的 JSON 格式输出日志 —— 不要使用自由文本字符串。
| 字段 | 用途 | 是否必需 |
|---|---|---|
timestamp | ISO-8601 格式(含毫秒) | 是 |
level | 严重程度(DEBUG … FATAL) | 是 |
service | 日志来源服务名称 | 是 |
message | 可读的人类描述 | 是 |
trace_id | 分布式追踪关联标识 | 是 |
span_id | 追踪中的当前跨度 | 是 |
correlation_id | 业务级关联标识(如订单 ID) | 适用时 |
error | 结构化错误对象 | 出错时 |
context | 请求相关的元数据 | 推荐 |
在中间件层级附加上下文,使下游日志自动继承:
app.use((req, res, next) => {
const ctx = {
trace_id: req.headers['x-trace-id'] || crypto.randomUUID(),
request_id: crypto.randomUUID(),
user_id: req.user?.id,
method: req.method,
path: req.path,
};
asyncLocalStorage.run(ctx, () => next());
});| 库 | 语言 | 优势 | 性能 |
|---|---|---|---|
| Pino | Node.js | Node.js 中最快的日志库,开销极低 | 优秀 |
| structlog | Python | 可组合处理器,支持上下文绑定 | 良好 |
| zerolog | Go | 零分配 JSON 日志 | 优秀 |
| zap | Go | 高性能,支持类型字段 | 优秀 |
| tracing | Rust | 支持跨度与事件,异步感知 | 优秀 |
选择原生输出结构化 JSON 的日志库。避免需要后期处理的日志工具。
| 级别 | 使用时机 | 示例 |
|---|---|---|
| FATAL | 应用无法继续运行,进程将退出 | 数据库连接池耗尽 |
| ERROR | 操作失败,需关注 | 支付扣款失败:CARD_DECLINED |
| WARN | 意外但可恢复 | 上游超时重试第 2/3 次 |
| INFO | 正常业务事件 | 订单 ORD-1234 成功创建 |
| DEBUG | 开发者排查问题 | 缓存未命中,键为 user:82:preferences |
| TRACE | 极细粒度(生产环境极少使用) | 进入 validateAddress,载荷为 ... |
规则: 生产环境默认日志级别为 INFO 及以上。若记录 ERROR,应有人响应处理。每个 FATAL 都应触发告警。
优先使用 OpenTelemetry 而非厂商专用 SDK:
import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
const sdk = new NodeSDK({
serviceName: 'order-service',
traceExporter: new OTLPTraceExporter({
url: 'http://otel-collector:4318/v1/traces',
}),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();const tracer = trace.getTracer('order-service');
async function processOrder(order: Order) {
return tracer.startActiveSpan('processOrder', async (span) => {
try {
span.setAttribute('order.id', order.id);
span.setAttribute('order.total_cents', order.totalCents);
await validateInventory(order);
await chargePayment(order);
span.setStatus({ code: SpanStatusCode.OK });
} catch (err) {
span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });
span.recordException(err);
throw err;
} finally {
span.end();
}
});
}traceparent 头部) —— OTel 默认方式traceparent 序列化至任务负载中| 策略 | 使用场景 |
|---|---|
| 始终开启 | 低流量服务、调试 |
| 概率采样 (N%) | 一般生产环境 |
| 速率限制 (N/sec) | 高吞吐服务 |
| 尾部采样 | 需要捕获所有错误追踪时 |
无论采用何种策略,始终对 100% 的错误追踪进行采样。
监控每个服务端点的以下三项:
| 指标 | 衡量内容 | Prometheus 示例 |
|---|---|---|
| 速率 | 每秒请求量 | rate(http_requests_total[5m]) |
| 错误 | 失败请求比例 | rate(http_requests_total{status=~"5.."}[5m]) |
| 持续时间 | 响应时间 | histogram_quantile(0.99, http_request_duration_seconds) |
用于基础设施组件(CPU、内存、磁盘、网络):
| 指标 | 衡量内容 | 示例 |
|---|---|---|
| 利用率 | 资源繁忙百分比 | CPU 使用率 78% |
| 饱和度 | 队列或等待的工作量 | 线程池中有 12 个请求排队 |
| 错误 | 资源上的错误事件 | 最近一分钟内发生 3 次磁盘 I/O 错误 |
markdown
name: Logging Observability
version: 0.1.0
description: 用于实现可观测性的日志与监控最佳实践
summary: 一套完整的日志、指标、链路追踪和告警设计指南,适用于微服务架构
category: observability
tags:
- logging
- metrics
- tracing
- alerting
- dashboard
- best practices
| 工具 | 类别 | 适用场景 |
|---|---|---|
| Prometheus | 指标 | 基于拉取的指标采集,告警规则定义 |
| Grafana | 可视化 | 指标、日志、链路追踪的仪表盘展示 |
| Jaeger | 链路追踪 | 分布式调用链的可视化分析 |
| Loki | 日志 | 日志聚合(常与 Grafana 配合使用) |
| OpenTelemetry | 数据采集 | 无厂商锁定的遥测数据采集框架 |
推荐方案: 优先采用 OTel Collector → Prometheus + Grafana + Loki + Jaeger 组合。仅当运维成本显著高于收益时,再考虑迁移到 SaaS 平台。
| 严重性 | 响应时限 | 示例 |
|---|---|---|
| P1 | 立即响应 | 服务完全不可用,数据丢失 |
| P2 | 小于 30 分钟 | 错误率 > 5%,延迟 p99 > 5 秒 |
| P3 | 工作时间内响应 | 磁盘使用率 > 80%,证书将在 7 天内过期 |
| P4 | 尽力处理 | 非关键功能弃用警告 |
每个服务必须具备:
/healthz 和 /readyz)| 反模式 | 问题 | 解决方案 |
|---|---|---|
| 记录 PII | 隐私或合规风险 | 隐藏或剔除 PII;使用令牌引用 |
| 过度日志输出 | 存储成本激增,有效信息被淹没 | 仅记录业务事件,而非数据流转过程 |
| 未结构化日志 | 无法查询或基于字段告警 | 使用结构化 JSON 并保持一致 schema |
| 字符串拼接日志 | 破坏结构化字段,存在注入风险 | 将字段作为元数据传递,而非嵌入消息 |
| 缺少关联 ID | 无法跨服务追踪请求 | 在所有请求中生成并传播 trace_id |
| 告警风暴 | 当班人员疲劳,真实问题被掩盖 | 使用分组、抑制、去重机制 |
| 指标高基数标签 | Prometheus 内存溢出,仪表盘超时 | 绝对不要将用户 ID 或请求 ID 作为标签 |
已收录 6 个 Skill