监控与可观测性 monitoring-observability

监控与可观测性技能专注于使用OpenTelemetry、Prometheus、Grafana等工具来实现系统的监控、追踪、日志记录和告警,以提高系统的可靠性、可维护性和可观测性。关键词包括监控、可观测性、OpenTelemetry、Prometheus、Grafana、日志管理、告警规则、DevOps、云原生。

DevOps 0 次安装 0 次浏览 更新于 3/8/2026

名称:监控与可观测性 描述:使用OpenTelemetry、Prometheus、Grafana仪表板和结构化日志的监控与可观测性

监控与可观测性

OpenTelemetry 设置

import { NodeSDK } from "@opentelemetry/sdk-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
import { PgInstrumentation } from "@opentelemetry/instrumentation-pg";
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";

const sdk = new NodeSDK({
  serviceName: "order-service",
  traceExporter: new OTLPTraceExporter({
    url: "http://otel-collector:4318/v1/traces",
  }),
  metricReader: new PeriodicExportingMetricReader({
    exporter: new OTLPMetricExporter({
      url: "http://otel-collector:4318/v1/metrics",
    }),
    exportIntervalMillis: 15000,
  }),
  instrumentations: [
    new HttpInstrumentation(),
    new PgInstrumentation(),
  ],
});

sdk.start();
process.on("SIGTERM", () => sdk.shutdown());

自定义跨度与指标

import { trace, metrics, SpanStatusCode } from "@opentelemetry/api";

const tracer = trace.getTracer("order-service");
const meter = metrics.getMeter("order-service");

const orderCounter = meter.createCounter("orders.created", {
  description: "创建的订单数量",
});

const orderDuration = meter.createHistogram("orders.processing_duration_ms", {
  description: "订单处理持续时间(毫秒)",
  unit: "ms",
});

async function createOrder(input: CreateOrderInput) {
  return tracer.startActiveSpan("createOrder", async (span) => {
    try {
      span.setAttributes({
        "order.customer_id": input.customerId,
        "order.item_count": input.items.length,
      });

      const start = performance.now();
      const order = await db.order.create({ data: input });

      orderCounter.add(1, { status: "success" });
      orderDuration.record(performance.now() - start);

      span.setStatus({ code: SpanStatusCode.OK });
      return order;
    } catch (error) {
      span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
      orderCounter.add(1, { status: "error" });
      throw error;
    } finally {
      span.end();
    }
  });
}

Prometheus 指标

# prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: "api-servers"
    static_configs:
      - targets: ["api-1:9090", "api-2:9090"]
    metrics_path: /metrics

  - job_name: "node-exporter"
    static_configs:
      - targets: ["node-exporter:9100"]
import { collectDefaultMetrics, Counter, Histogram, Registry } from "prom-client";

const registry = new Registry();
collectDefaultMetrics({ register: registry });

const httpRequestDuration = new Histogram({
  name: "http_request_duration_seconds",
  help: "HTTP请求持续时间(秒)",
  labelNames: ["method", "route", "status"],
  buckets: [0.01, 0.05, 0.1, 0.5, 1, 5],
  registers: [registry],
});

app.use((req, res, next) => {
  const end = httpRequestDuration.startTimer();
  res.on("finish", () => {
    end({ method: req.method, route: req.route?.path ?? req.path, status: res.statusCode });
  });
  next();
});

app.get("/metrics", async (req, res) => {
  res.set("Content-Type", registry.contentType);
  res.end(await registry.metrics());
});

结构化日志

import pino from "pino";

const logger = pino({
  level: process.env.LOG_LEVEL ?? "info",
  formatters: {
    level: (label) => ({ level: label }),
  },
  redact: ["req.headers.authorization", "password", "token"],
});

function requestLogger(req, res, next) {
  const start = Date.now();
  res.on("finish", () => {
    logger.info({
      method: req.method,
      url: req.url,
      status: res.statusCode,
      duration_ms: Date.now() - start,
      trace_id: req.headers["x-trace-id"],
    });
  });
  next();
}

告警规则

groups:
  - name: api-alerts
    rules:
      - alert: HighErrorRate
        expr: rate(http_request_duration_seconds_count{status=~"5.."}[5m]) / rate(http_request_duration_seconds_count[5m]) > 0.05
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "错误率超过5%于 {{ $labels.route }}"

      - alert: HighLatency
        expr: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) > 2
        for: 10m
        labels:
          severity: warning

反模式

  • 记录敏感数据(密码、令牌、个人身份信息)而不进行脱敏
  • 在日志消息中使用字符串插值而不是结构化字段
  • 在指标标签中创建无限基数(例如,用户ID作为标签)
  • 不通过共享追踪ID关联日志和追踪
  • 在未理解根本原因的情况下对症状(如高CPU)发出告警
  • 在构建仪表板前缺少服务级别目标定义

检查清单

  • [ ] OpenTelemetry SDK 已初始化,包含HTTP、数据库和消息的自动检测
  • [ ] 为业务关键操作添加自定义跨度
  • [ ] 指标使用有限标签基数
  • [ ] 结构化日志输出为JSON并脱敏敏感信息
  • [ ] 追踪上下文在服务边界之间传播
  • [ ] 告警规则基于服务级别目标(错误率、延迟百分位数)
  • [ ] 仪表板显示每个服务的RED指标(速率、错误、持续时间)
  • [ ] 配置日志保留和轮转策略