本文へ移動
cccskills
無料GitHub で公開

observability

实现日志 / 指标 / 追踪三大支柱时使用。覆盖 OpenTelemetry / Prometheus / Grafana / Jaeger / Datadog / Sentry 集成。融合 Google SRE 三支柱 + 结构化日志 + Trace Context 传播。

インストール方法を見る

含まれるファイル(2)

  • SKILL.md13.1 KB
  • templates/observability-checklist.md4.6 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

可观测性(Observability)

参考来源:Google《Site Reliability Engineering》、《Distributed Systems Observability》(Cindy Sridharan)、OpenTelemetry 官方文档、Honeycomb 高基数追踪、Stripe 可观测性实践。

适用场景

  • 接入日志 / 指标 / 追踪(三大支柱)
  • 排查线上问题
  • 性能监控
  • SLO / SLI 度量
  • 错误跟踪(Sentry / Datadog Errors)
  • 业务指标埋点

核心原则

1. 三大支柱缺一不可
   日志(Logs):发生了什么
   指标(Metrics):当前状态
   追踪(Traces):请求路径

2. 结构化日志
   不用纯文本,用 JSON / key-value

3. Trace ID 贯穿全链路
   从入口生成,所有日志 / 调用都带

4. 关键业务指标必埋
   订单数 / 支付成功率 / 注册量

5. 用 OpenTelemetry 标准
   厂商无关,可切换后端

6. 不日志输出 PII / Token
   用 [REDACTED] 占位

7. 采样率合理
   全采样压垮系统,1% 又看不清

8. 告警 ≠ 监控
   监控全采集,告警仅必报

三大支柱

1. 日志(Logs)

作用:发生了什么的离散事件
适合:排查具体问题

特点:
  - 结构化(JSON)
  - 含 trace_id
  - 不输出敏感信息
  - 分级(DEBUG/INFO/WARN/ERROR)

2. 指标(Metrics)

作用:当前状态的数值聚合
适合:监控告警

类型:
  - Counter(累加,如请求数)
  - Gauge(瞬时,如内存使用)
  - Histogram(分布,如响应时间 P99)
  - Summary(quantile)

四个黄金信号(Google SRE):
  Latency(延迟)
  Traffic(流量)
  Errors(错误率)
  Saturation(饱和度)

3. 追踪(Traces)

作用:请求经过哪些服务
适合:分布式系统排查、性能定位

概念:
  Trace:一次完整请求
  Span:trace 中的一段(一个服务一个 span)
  Parent / Child:调用关系

结构化日志

Spring Boot

// logback-spring.xml 配置 JSON 格式
<configuration>
  <appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="net.logstash.logback.encoder.LogstashEncoder">
      <includeMdcKeyName>traceId</includeMdcKeyName>
      <includeMdcKeyName>spanId</includeMdcKeyName>
      <includeMdcKeyName>userId</includeMdcKeyName>
    </encoder>
  </appender>
</configuration>
// 用法
@Slf4j
@RestController
public class OrderController {
  @PostMapping("/orders")
  public Order create(@RequestBody CreateOrderRequest req) {
    MDC.put("userId", String.valueOf(req.getUserId()));
    
    log.info("Creating order, items={}", req.getItems().size());
    
    try {
      Order order = orderService.create(req);
      log.info("Order created, orderId={}, amount={}", order.getId(), order.getTotal());
      return order;
    } catch (Exception e) {
      log.error("Failed to create order", e);
      throw e;
    } finally {
      MDC.clear();
    }
  }
}

NestJS

// 用 nestjs-pino 或 winston
import { Logger as PinoLogger } from 'nestjs-pino';

@Injectable()
export class OrderService {
  constructor(private logger: PinoLogger) {}
  
  async create(dto: CreateOrderDto, user: User) {
    this.logger.info({ userId: user.id, items: dto.items.length }, 'Creating order');
    
    try {
      const order = await this.repo.create(dto);
      this.logger.info({ orderId: order.id, amount: order.total }, 'Order created');
      return order;
    } catch (err) {
      this.logger.error({ err, userId: user.id }, 'Failed to create order');
      throw err;
    }
  }
}

Python

import structlog

logger = structlog.get_logger()

def create_order(dto, user):
    log = logger.bind(user_id=user.id, items=len(dto.items))
    log.info('creating_order')
    
    try:
        order = repo.create(dto)
        log.info('order_created', order_id=order.id, amount=order.total)
        return order
    except Exception as e:
        log.error('order_creation_failed', error=str(e))
        raise

Go

import "go.uber.org/zap"

logger, _ := zap.NewProduction()

func CreateOrder(ctx context.Context, dto CreateOrderDto, user User) (*Order, error) {
    log := logger.With(
        zap.Int64("user_id", user.ID),
        zap.Int("items", len(dto.Items)),
        zap.String("trace_id", trace.GetTraceID(ctx)),
    )
    log.Info("creating_order")
    
    order, err := repo.Create(dto)
    if err != nil {
        log.Error("order_creation_failed", zap.Error(err))
        return nil, err
    }
    log.Info("order_created", zap.Int64("order_id", order.ID))
    return order, nil
}

OpenTelemetry 集成(统一标准)

Spring Boot

# application.yml
otel:
  service:
    name: order-service
  exporter:
    otlp:
      endpoint: http://otel-collector:4317
  traces:
    sampler: parentbased_traceidratio
    sampler-arg: 0.1  # 10% 采样
// 自动追踪(用 micrometer-tracing)
@RestController
public class OrderController {
  @Autowired
  private Tracer tracer;
  
  @PostMapping("/orders")
  public Order create(@RequestBody CreateOrderRequest req) {
    Span span = tracer.spanBuilder("createOrder")
      .setAttribute("user.id", req.getUserId())
      .startSpan();
    try (Scope scope = span.makeCurrent()) {
      return orderService.create(req);
    } finally {
      span.end();
    }
  }
}

NestJS

// 用 @opentelemetry/sdk-node
import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';

const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({
    url: 'http://otel-collector:4318/v1/traces',
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start();

Python

from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
trace.set_tracer_provider(provider)

tracer = trace.get_tracer(__name__)

@app.post('/orders')
async def create_order(dto: CreateOrderDto, user: User = Depends(get_current_user)):
    with tracer.start_as_current_span('createOrder') as span:
        span.set_attribute('user.id', user.id)
        span.set_attribute('items.count', len(dto.items))
        return await order_service.create(dto, user)

关键指标(必埋)

通用指标

HTTP 层:
  http_requests_total{method, endpoint, status}        # Counter
  http_request_duration_seconds{method, endpoint}      # Histogram
  http_requests_in_flight                              # Gauge

DB 层:
  db_queries_total{operation, table}
  db_query_duration_seconds{operation, table}
  db_connections_active

Cache 层:
  cache_operations_total{operation, status}
  cache_hit_rate

Queue 层:
  queue_depth{queue_name}
  queue_processing_duration_seconds{queue_name}
  queue_failed_total{queue_name}

业务指标

orders_created_total                       # 订单数
payments_succeeded_total                   # 支付成功
payments_failed_total{reason}              # 支付失败(按原因)
user_signups_total{source}                 # 注册(按来源)
revenue_total{currency}                    # 收入

Spring Boot + Micrometer

@RestController
public class OrderController {
  @Autowired private MeterRegistry meterRegistry;
  
  @PostMapping("/orders")
  public Order create(@RequestBody CreateOrderRequest req) {
    Timer.Sample sample = Timer.start(meterRegistry);
    try {
      Order order = orderService.create(req);
      meterRegistry.counter("orders.created.total", 
        "user_type", req.getUserType()).increment();
      return order;
    } finally {
      sample.stop(meterRegistry.timer("orders.create.duration"));
    }
  }
}

NestJS + prom-client

import { Counter, Histogram } from 'prom-client';

const orderCreatedCounter = new Counter({
  name: 'orders_created_total',
  help: 'Total orders created',
  labelNames: ['user_type'],
});

const orderDuration = new Histogram({
  name: 'order_create_duration_seconds',
  help: 'Order creation duration',
  buckets: [0.1, 0.5, 1, 2, 5],
});

@Injectable()
export class OrderService {
  async create(dto: CreateOrderDto, user: User) {
    const end = orderDuration.startTimer();
    try {
      const order = await this.repo.create(dto);
      orderCreatedCounter.labels(user.type).inc();
      return order;
    } finally {
      end();
    }
  }
}

// 暴露 /metrics 端点(Prometheus 抓取)
@Controller()
export class MetricsController {
  @Get('metrics')
  async metrics(@Res() res: Response) {
    res.set('Content-Type', register.contentType);
    res.send(await register.metrics());
  }
}

Trace ID 传播

入口(API Gateway / Controller):
  生成 trace_id(如未有)
  注入 MDC / AsyncLocalStorage / context

下游调用:
  HTTP:传播 X-Request-Id / traceparent header
  消息队列:放进消息 metadata
  数据库:可选

日志:
  每行日志带 trace_id

监控:
  追踪图自动串联
// NestJS 中间件
@Injectable()
export class TraceMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    const traceId = req.headers['x-request-id'] || uuidv4();
    AsyncLocalStorage.run({ traceId }, () => {
      res.setHeader('X-Request-Id', traceId);
      next();
    });
  }
}

错误跟踪(Sentry)

import * as Sentry from '@sentry/node';

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  environment: process.env.NODE_ENV,
  tracesSampleRate: 0.1,  // 10% 性能追踪
  beforeSend(event) {
    // 过滤敏感信息
    if (event.request?.headers) {
      delete event.request.headers['authorization'];
      delete event.request.headers['cookie'];
    }
    return event;
  },
});

// 用法
try {
  await orderService.create(...);
} catch (err) {
  Sentry.captureException(err, {
    user: { id: user.id },
    extra: { orderId: order.id },
  });
  throw err;
}

健康检查端点

@Controller()
export class HealthController {
  @Get('health')
  async health() {
    return { status: 'ok', timestamp: Date.now() };
  }
  
  @Get('health/ready')  // K8s readiness
  async ready() {
    const checks = await Promise.all([
      this.checkDatabase(),
      this.checkRedis(),
    ]);
    if (checks.every(c => c.ok)) {
      return { status: 'ready', checks };
    }
    throw new ServiceUnavailableException({ status: 'not_ready', checks });
  }
  
  @Get('health/live')  // K8s liveness
  async live() {
    return { status: 'alive' };
  }
}

工作流程

1. 接入 OpenTelemetry SDK
   - 自动追踪 HTTP / DB / Redis / 消息队列

2. 配置结构化日志
   - JSON 格式
   - 含 trace_id / user_id

3. 埋点关键指标
   - HTTP 黄金信号
   - 业务指标
   - 资源使用

4. Trace ID 传播
   - 入口生成
   - 下游传递

5. 错误跟踪接入(Sentry)
   - 过滤敏感信息

6. 配置健康检查
   - liveness / readiness

7. 接入 APM 平台
   - Datadog / Prometheus + Grafana / 自建

8. 设计告警
   - SLO 偏离
   - 错误率突增
   - 延迟退化

配套模板

  • templates/observability-checklist.md — 三大支柱 + 关键指标 + Trace ID + Sentry + 健康检查 + 告警

质量自检

□ 日志结构化(JSON)
□ 日志含 trace_id
□ 日志不输出 PII / Token
□ 关键业务指标埋点
□ HTTP 黄金信号采集
□ Trace 串联全链路
□ OpenTelemetry 标准
□ 错误跟踪(Sentry)
□ 健康检查(liveness + readiness)
□ 采样率合理(不全采)
□ 监控覆盖率 > 80%
□ 告警基于 SLO 不基于 alert fatigue

常见坑

  1. 纯文本日志——无法搜索 / 聚合
  2. 不带 trace_id——分布式排错困难
  3. 日志输出密码 / Token——合规问题
  4. 指标 cardinality 爆炸——userId 当 label
  5. 全采样追踪——性能 / 存储压垮
  6. 不区分 INFO / DEBUG——日志爆炸
  7. 告警过多——alert fatigue,重要的被忽略
  8. 不监控业务指标——只盯技术指标,业务退化无感
  9. 采样率太低——关键链路追不到
  10. 不集成 OpenTelemetry——锁定厂商
  11. 错误日志不带堆栈——排查困难
  12. 指标命名不统一——查询难
  13. 不用结构化错误——Sentry 看不出聚合
  14. 健康检查 = 假死检查——liveness 检查 DB 拖死自己

与其他 skill 的协作

上游:
  api-implementation → 中间件埋点
  data-access → SQL 监控
  caching-strategy → 缓存监控
  async-jobs → 队列监控
  error-handling-resilience → 错误监控

下游:
  sre-ops 工作流 → 告警 / SLO

相关参考

  • 项目根 references/backend-tech-stack-guide.md — 可观测性方案

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

A/B 测试设计与分析时使用。适用于产品实验、功能验证、转化率优化。融合假设驱动 + 样本量计算 + 置信度判断。

日本語の概要は準備中です。原文の説明を表示しています。

zhaoxuya520/AI-Fullstack-Delivery-Workflow532026年5月19日 更新

上线前业务方/产品方验收时使用。适用于功能交付确认、UAT、发布会签。融合 BDD Given-When-Then、Specification by Example、ATDD。

日本語の概要は準備中です。原文の説明を表示しています。

zhaoxuya520/AI-Fullstack-Delivery-Workflow532026年5月19日 更新

设计无障碍可访问的界面时使用。适用于满足 WCAG 标准、键盘导航、屏幕阅读器支持。优先使用 WCAG 2.2 AA 级标准 + 键盘 + 焦点 + 对比度 + ARIA。

日本語の概要は準備中です。原文の説明を表示しています。

zhaoxuya520/AI-Fullstack-Delivery-Workflow532026年5月19日 更新

实现可访问性(无障碍)时使用。覆盖 WCAG 2.2 AA / ARIA / 键盘导航 / 屏幕阅读器 / 颜色对比 / 焦点管理。融合 React Aria / Radix UI / Headless UI 实践 + axe-core 自动化测试。

日本語の概要は準備中です。原文の説明を表示しています。

zhaoxuya520/AI-Fullstack-Delivery-Workflow532026年5月19日 更新

AI 模型部署时使用。适用于推理服务 / GPU / TensorRT / 监控 / 扩缩容。

日本語の概要は準備中です。原文の説明を表示しています。

zhaoxuya520/AI-Fullstack-Delivery-Workflow532026年5月19日 更新

数据分析报告输出时使用。适用于业务分析报告、周报、专项分析、决策支持。融合洞察 + 建议 + 行动项。

日本語の概要は準備中です。原文の説明を表示しています。

zhaoxuya520/AI-Fullstack-Delivery-Workflow532026年5月19日 更新

zhaoxuya520 のスキルをすべて見る

このスキルの問題を報告する