name: workers-observability
description: Cloudflare Workers 可观测性,包括日志记录、Analytics Engine、Tail Workers、指标和警报。用于监控、调试、追踪,或解决日志解析、指标聚合、警报配置错误。
Cloudflare Workers 可观测性
生产级 Cloudflare Workers 可观测性:日志记录、指标、追踪和警报。
快速开始
// 结构化日志记录与上下文
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const requestId = crypto.randomUUID();
const logger = createLogger(requestId, env);
try {
logger.info('请求已接收', { method: request.method, url: request.url });
const result = await handleRequest(request, env);
logger.info('请求已完成', { status: result.status });
return result;
} catch (error) {
logger.error('请求失败', { error: error.message, stack: error.stack });
throw error;
}
}
};
// 简单的日志记录器工厂函数
function createLogger(requestId: string, env: Env) {
return {
info: (msg: string, data?: object) => console.log(JSON.stringify({ level: 'info', requestId, msg, ...data, timestamp: Date.now() })),
error: (msg: string, data?: object) => console.error(JSON.stringify({ level: 'error', requestId, msg, ...data, timestamp: Date.now() })),
warn: (msg: string, data?: object) => console.warn(JSON.stringify({ level: 'warn', requestId, msg, ...data, timestamp: Date.now() })),
};
}
关键规则
- 始终使用结构化 JSON 日志记录 - 纯文本日志难以解析和聚合
- 包含请求上下文 - 在每个日志条目中添加请求 ID、方法、路径
- 切勿记录敏感数据 - 从日志中脱敏令牌、密码、个人身份信息
- 使用适当的日志级别 - ERROR 用于失败,WARN 用于可恢复问题,INFO 用于操作
- 对高容量日志进行采样 - 在生产环境中对请求日志使用 1-10% 采样
可观测性组件
| 组件 |
目的 |
何时使用 |
console.log |
基本日志记录 |
开发、调试 |
| Tail Workers |
实时日志流 |
生产日志聚合 |
| Analytics Engine |
自定义指标/分析 |
业务指标、性能跟踪 |
| Logpush |
日志导出到外部服务 |
长期存储、合规性 |
| Workers Trace Events |
分布式追踪 |
请求流调试 |
前 8 个预防错误
| 错误 |
症状 |
预防 |
| 日志不显示 |
仪表板无输出 |
在 wrangler.jsonc 中启用 “Standard” 日志记录 |
| 日志截断 |
消息在 128KB 处截断 |
分块大负载,使用采样 |
| Tail Worker 未接收 |
无事件处理 |
检查绑定名称是否与 wrangler.jsonc 匹配 |
| Analytics Engine 写入失败 |
数据未记录 |
验证 AE 绑定,检查 blobs 格式 |
| 日志中包含 PII |
安全/合规违规 |
实现脱敏中间件 |
| 缺少请求上下文 |
无法关联日志 |
在所有日志条目中添加 requestId |
| 日志量爆炸 |
高成本、噪音 |
对高频事件实施采样 |
| 警报缺失 |
事件未检测到 |
为错误率阈值配置监视器 |
日志记录配置
wrangler.jsonc:
{
"name": "my-worker",
"observability": {
"enabled": true,
"head_sampling_rate": 1 // 0-1,1 = 100% 的请求
},
"tail_consumers": [
{
"service": "log-aggregator", // Tail Worker 名称
"environment": "production"
}
],
"analytics_engine_datasets": [
{
"binding": "ANALYTICS",
"dataset": "my_worker_metrics"
}
]
}
结构化日志记录模式
interface LogEntry {
level: 'debug' | 'info' | 'warn' | 'error';
message: string;
requestId: string;
timestamp: number;
// 上下文数据
method?: string;
path?: string;
status?: number;
duration?: number;
// 错误详情
error?: {
name: string;
message: string;
stack?: string;
};
// 自定义字段
[key: string]: unknown;
}
class Logger {
constructor(private requestId: string, private baseContext: object = {}) {}
private log(level: LogEntry['level'], message: string, data?: object) {
const entry: LogEntry = {
level,
message,
requestId: this.requestId,
timestamp: Date.now(),
...this.baseContext,
...data,
};
// 脱敏敏感字段
const sanitized = this.redact(entry);
const output = JSON.stringify(sanitized);
level === 'error' ? console.error(output) : console.log(output);
}
private redact(entry: LogEntry): LogEntry {
const sensitiveKeys = ['password', 'token', 'secret', 'authorization', 'cookie'];
const redacted = { ...entry };
for (const key of Object.keys(redacted)) {
if (sensitiveKeys.some(s => key.toLowerCase().includes(s))) {
redacted[key] = '[REDACTED]';
}
}
return redacted;
}
info(message: string, data?: object) { this.log('info', message, data); }
warn(message: string, data?: object) { this.log('warn', message, data); }
error(message: string, data?: object) { this.log('error', message, data); }
debug(message: string, data?: object) { this.log('debug', message, data); }
}
Analytics Engine 使用
interface Env {
ANALYTICS: AnalyticsEngineDataset;
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const start = Date.now();
const url = new URL(request.url);
try {
const response = await handleRequest(request, env);
// 写入成功指标
env.ANALYTICS.writeDataPoint({
blobs: [request.method, url.pathname, String(response.status)],
doubles: [Date.now() - start], // 响应时间(毫秒)
indexes: [url.pathname.split('/')[1] || 'root'], // 索引用于快速查询
});
return response;
} catch (error) {
// 写入错误指标
env.ANALYTICS.writeDataPoint({
blobs: [request.method, url.pathname, 'error', error.message],
doubles: [Date.now() - start],
indexes: ['error'],
});
throw error;
}
}
};
Tail Worker 模式
// tail-worker.ts - 接收来自其他 Workers 的日志
interface TailEvent {
scriptName: string;
event: {
request?: { method: string; url: string };
response?: { status: number };
};
logs: Array<{
level: string;
message: unknown[];
timestamp: number;
}>;
exceptions: Array<{
name: string;
message: string;
timestamp: number;
}>;
outcome: 'ok' | 'exception' | 'exceededCpu' | 'exceededMemory' | 'canceled';
eventTimestamp: number;
}
export default {
async tail(events: TailEvent[], env: Env): Promise<void> {
for (const event of events) {
// 过滤并转发日志
const errorLogs = event.logs.filter(l => l.level === 'error');
const exceptions = event.exceptions;
if (errorLogs.length > 0 || exceptions.length > 0) {
// 发送到外部日志服务
await fetch(env.LOGGING_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
scriptName: event.scriptName,
timestamp: event.eventTimestamp,
errors: errorLogs,
exceptions,
outcome: event.outcome,
}),
});
}
}
}
};
何时加载参考
根据任务加载特定参考:
- 设置日志记录? → 加载
references/logging.md 以获取结构化日志记录模式、日志级别、脱敏
- 构建自定义指标? → 加载
references/analytics-engine.md 以获取 Analytics Engine SQL 查询、数据建模
- 实现日志聚合? → 加载
references/tail-workers.md 以获取 Tail Worker 模式、外部服务集成
- 创建仪表板/跟踪? → 加载
references/custom-metrics.md 以获取业务指标、性能跟踪
- 设置警报? → 加载
references/alerting.md 以获取错误率监控、PagerDuty/Slack 集成
模板
| 模板 |
目的 |
何时使用 |
templates/logging-setup.ts |
生产日志记录类 |
设置新 Worker 并配置日志记录 |
templates/analytics-worker.ts |
Analytics Engine 集成 |
添加自定义指标 |
templates/tail-worker.ts |
完整 Tail Worker |
构建日志聚合管道 |
脚本
| 脚本 |
目的 |
命令 |
scripts/setup-logging.sh |
配置日志记录设置 |
./setup-logging.sh |
scripts/analyze-logs.sh |
查询和分析日志 |
./analyze-logs.sh --errors --last 1h |
资源