← 返回资讯
林远舟
技术编辑
已审核

可观测性实战:从日志、指标到链路追踪的完整体系

title: "可观测性实战:从日志、指标到链路追踪的完整体系"

可观测性实战:从日志、指标到链路追踪的完整体系

title: "可观测性实战:从日志、指标到链路追踪的完整体系"

date: "2026-07-10"

tags: ["可观测性", "监控", "日志", "链路追踪"]


可观测性实战:从日志、指标到链路追踪的完整体系

可观测性(Observability)是理解系统运行状态的核心能力。它由三个支柱组成:日志(Logs)、指标(Metrics)和链路追踪(Traces)。

三大支柱

日志(Logs)

记录离散的事件信息。

PYTHON
import logging
import structlog

# 结构化日志
structlog.configure(
    processors=[
        structlog.processors.TimeStamper(fmt="iso"),
        structlog.processors.add_log_level,
        structlog.processors.JSONRenderer()
    ]
)

logger = structlog.get_logger()

def process_order(order_id: str, user_id: str):
    logger.info(
        "order_processing_started",
        order_id=order_id,
        user_id=user_id,
        service="order-service"
    )
    
    try:
        # 业务逻辑
        result = validate_order(order_id)
        
        logger.info(
            "order_validated",
            order_id=order_id,
            validation_result=result
        )
        
    except ValidationError as e:
        logger.error(
            "order_validation_failed",
            order_id=order_id,
            error=str(e),
            error_type=type(e).__name__
        )
        raise

指标(Metrics)

记录可聚合的数值型数据。

PYTHON
from prometheus_client import Counter, Histogram, Gauge, start_http_server

# 计数器
request_counter = Counter(
    'http_requests_total',
    'Total HTTP requests',
    ['method', 'endpoint', 'status']
)

# 直方图
request_duration = Histogram(
    'http_request_duration_seconds',
    'HTTP request duration',
    ['method', 'endpoint'],
    buckets=[0.01, 0.05, 0.1, 0.5, 1.0, 5.0]
)

# 仪表盘
active_connections = Gauge(
    'active_connections',
    'Number of active connections'
)

# 使用
from fastapi import FastAPI, Request
import time

app = FastAPI()

@app.middleware("http")
async def metrics_middleware(request: Request, call_next):
    start_time = time.time()
    active_connections.inc()
    
    try:
        response = await call_next(request)
        status = response.status_code
    except Exception:
        status = 500
        raise
    finally:
        duration = time.time() - start_time
        active_connections.dec()
        
        request_counter.labels(
            method=request.method,
            endpoint=request.url.path,
            status=status
        ).inc()
        
        request_duration.labels(
            method=request.method,
            endpoint=request.url.path
        ).observe(duration)
    
    return response

# 启动指标服务器
start_http_server(8000)

链路追踪(Traces)

记录跨服务的请求流转。

PYTHON
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor

# 配置追踪
provider = TracerProvider()
exporter = OTLPSpanExporter(endpoint="http://jaeger:4317")
provider.add_span_processor(BatchSpanProcessor(exporter))
trace.set_tracer_provider(provider)

tracer = trace.get_tracer(__name__)

# FastAPI 自动追踪
FastAPIInstrumentor.instrument_app(app)

# 手动追踪
@app.get("/api/orders/{order_id}")
async def get_order(order_id: str):
    with tracer.start_as_current_span("get_order") as span:
        span.set_attribute("order_id", order_id)
        
        # 调用其他服务
        with tracer.start_as_current_span("fetch_user_data"):
            user_data = await user_service.get_user(order_id)
        
        with tracer.start_as_current_span("fetch_order_items"):
            items = await inventory_service.get_items(order_id)
        
        span.set_attribute("item_count", len(items))
        
        return {"order": order_id, "user": user_data, "items": items}

ELK 日志栈

YAML
# docker-compose.yml
version: '3'
services:
  elasticsearch:
    image: elasticsearch:8.11.0
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
    volumes:
      - es_data:/usr/share/elasticsearch/data
    ports:
      - "9200:9200"

  logstash:
    image: logstash:8.11.0
    volumes:
      - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
    ports:
      - "5044:5044"

  kibana:
    image: kibana:8.11.0
    ports:
      - "5601:5601"
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200

volumes:
  es_data:
RUBY
# logstash.conf
input {
  beats {
    port => 5044
  }
}

filter {
  json {
    source => "message"
  }
  
  if [service] {
    mutate {
      add_field => { "app_name" => "%{service}" }
    }
  }
  
  date {
    match => ["timestamp", "ISO8601"]
    target => "@timestamp"
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "app-logs-%{+YYYY.MM.dd}"
  }
}

Prometheus + Grafana

YAML
# prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'api-service'
    static_configs:
      - targets: ['api:8000']
    metrics_path: '/metrics'
  
  - job_name: 'order-service'
    static_configs:
      - targets: ['order:8000']
YAML
# docker-compose.yml
services:
  prometheus:
    image: prom/prometheus
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"

  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana_data:/var/lib/grafana

volumes:
  grafana_data:

告警规则

YAML
# alert_rules.yml
groups:
  - name: api_alerts
    rules:
      - alert: HighErrorRate
        expr: rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "High error rate detected"
          description: "Error rate is {{ $value | humanizePercentage }} for {{ $labels.endpoint }}"

      - alert: HighLatency
        expr: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) > 1
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High latency detected"
          description: "P99 latency is {{ $value }}s for {{ $labels.endpoint }}"

      - alert: ServiceDown
        expr: up == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Service {{ $labels.job }} is down"

统一可观测性

PYTHON
from opentelemetry import trace, metrics
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.metrics import MeterProvider

# 统一资源标识
resource = Resource.create({
    "service.name": "order-service",
    "service.version": "1.0.0",
    "deployment.environment": "production"
})

# 追踪
tracer_provider = TracerProvider(resource=resource)
trace.set_tracer_provider(tracer_provider)

# 指标
meter_provider = MeterProvider(resource=resource)
metrics.set_meter_provider(meter_provider)

# 日志(通过 OpenTelemetry)
from opentelemetry._logs import set_logger_provider
from opentelemetry.sdk._logs import LoggerProvider

logger_provider = LoggerProvider(resource=resource)
set_logger_provider(logger_provider)

可观测性不是"出了问题再看",而是"随时能理解系统在做什么"。建立完整的可观测性体系,是保障系统稳定性的基础投资。

341
11369 阅读
3 评论
分享
链接已复制
编辑说明

本文由 MakeSense 编辑团队撰写并审核。文中引用的数据和观点均经过交叉验证,如有疏漏欢迎在评论区指正。最后更新:2026年07月11日 08:54

林远舟

技术编辑

全栈工程师出身,做过 5 年技术社区运营。对 AI 编程工具、开发者生态有深入研究,喜欢用实测数据说话。

读者评论 3

运营小陈 2天前
转发到团队群了,大家都觉得有参考价值。
回复 点赞 (4)
数据分析师 5天前
数据引用很扎实,建议补充一下近三个月的最新数据。
回复 点赞 (9)
产品经理阿杰 1周前
从产品角度看,这个方向确实有机会,但商业化路径还需要验证。
回复 点赞 (15)