编程 SigNoz 深度拆解:OpenTelemetry 原生可观测性平台如何用 ClickHouse 统一日志指标追踪与 LLM 监控——从 22K Star 到 Datadog 开源替代的全栈工程哲学

2026-08-04 08:44:11 +0800 CST views 13

SigNoz 深度拆解:当 OpenTelemetry 决定「干掉 Datadog」——一个 22K Star 的可观测性平台如何用 ClickHouse 统一日志、指标、追踪与 LLM 监控的全栈工程哲学

引言:可观测性的碎片化困境

2026 年,如果你在一个中型技术团队工作,你的可观测性栈大概是这样的:

  • 日志:ELK(Elasticsearch + Logstash + Kibana)或 Loki + Grafana
  • 指标:Prometheus + Grafana
  • 追踪:Jaeger 或 Zipkin
  • 告警:PagerDuty 或 Grafana Alerting
  • APM:Datadog 或 New Relic

四套系统,四个控制台,四套告警规则,四份账单。当一个请求出了问题,你需要在日志系统搜 trace_id,再去追踪系统找调用链,再去指标系统查 CPU/内存,再去告警系统确认是否触发——整个排障过程就像在四个不同的房间之间来回跑。

更糟糕的是成本。Datadog 的按主机计费模型让很多团队的月账单轻松突破五位数美元,而自建 ELK 的运维成本又让小团队望而却步。

SigNoz 的出现,就是要终结这种碎片化。

SigNoz 是一个基于 OpenTelemetry 的开源可观测性平台,GitHub 22K+ Star,用一个统一的平台同时处理日志(Logs)、指标(Metrics)、追踪(Traces)、告警(Alerts)和仪表盘(Dashboards),底层存储引擎是 ClickHouse。

它的野心不是做另一个 Grafana,而是做 Datadog 的开源替代品——而且在很多维度上做得更好。

本文将从架构设计、核心组件、数据流、性能优化、部署实战、LLM 可观测性等多个维度,深度拆解 SigNoz 的工程哲学。


一、架构全景:从数据采集到可视化的一体化设计

1.1 整体数据流

SigNoz 的架构可以用一句话概括:应用 → OTel SDK → OTel Collector → ClickHouse → Query Service → Frontend

┌─────────────────────────────────────────────────────────┐
│                     应用层 (Application)                  │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐              │
│  │  Java App │  │  Go App  │  │ Python   │              │
│  │  (OTel    │  │  (OTel   │  │ App(OTel │              │
│  │   SDK)    │  │   SDK)   │  │   SDK)   │              │
│  └─────┬────┘  └─────┬────┘  └─────┬────┘              │
│        │             │             │                     │
│        └─────────────┼─────────────┘                     │
│                      ▼                                   │
│         ┌───────────────────────┐                        │
│         │  SigNoz OTel Collector │ ← 协议翻译+数据富化    │
│         │  (OTLP/Jaeger/Zipkin) │                        │
│         └───────────┬───────────┘                        │
│                     ▼                                   │
│         ┌───────────────────────┐                        │
│         │      ClickHouse       │ ← 列式存储+高压缩       │
│         │  (Logs/Metrics/Traces)│                        │
│         └───────────┬───────────┘                        │
│                     ▼                                   │
│         ┌───────────────────────┐                        │
│         │   SigNoz Query Service│ ← API + 聚合查询       │
│         │   + Frontend (React)  │                        │
│         │   + Alert Manager     │                        │
│         │   + OpAMP Server      │                        │
│         └───────────────────────┘                        │
└─────────────────────────────────────────────────────────┘

1.2 为什么选择 ClickHouse?

这是 SigNoz 最关键的架构决策。

传统的可观测性方案通常用多个存储引擎:Elasticsearch 存日志、Prometheus 存指标、Jaeger 存追踪。每个引擎有自己的查询语言、自己的运维方式、自己的扩容策略。

SigNoz 选择了 ClickHouse 作为唯一的存储引擎,原因在于:

特性ClickHouseElasticsearchPrometheus
数据模型列式存储文档存储时间序列
压缩比10:1 ~ 20:13:1 ~ 5:15:1 ~ 10:1
聚合查询速度毫秒级秒级秒级
SQL 支持完整 SQLDSL(学习成本高)PromQL
水平扩展原生分片+副本分片+副本联邦模式
存储成本

ClickHouse 的列式存储天然适合分析型查询——你查一个时间范围内的错误日志,它只需要扫描 error 相关的列,而不是整行数据。这让 SigNoz 在处理 PB 级遥测数据时依然能保持毫秒级查询响应。

更重要的是,三类数据用同一个存储引擎,意味着跨信号关联变得极其简单。你可以在一个查询中同时关联日志、指标和追踪,这在传统方案中需要跨多个系统做数据拼接。


二、核心组件深度解析

2.1 SigNoz OTel Collector:协议翻译的枢纽

OTel Collector 是 SigNoz 的数据入口,它基于 OpenTelemetry Collector 构建,但做了关键增强:

# SigNoz OTel Collector 配置示例
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318
  jaeger:
    protocols:
      thrift_http:
        endpoint: 0.0.0.0:14268
  zipkin:
    endpoint: 0.0.0.0:9411

processors:
  batch:
    send_batch_size: 10000
    timeout: 1s
  memory_limiter:
    limit_mib: 2000
    spike_limit_mib: 400

exporters:
  clickhouse:
    endpoint: tcp://clickhouse:9000
    resource_to_telemetry_conversion:
      enabled: true

service:
  pipelines:
    traces:
      receivers: [otlp, jaeger, zipkin]
      processors: [memory_limiter, batch]
      exporters: [clickhouse]
    logs:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [clickhouse]
    metrics:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [clickhouse]

关键设计决策:

  1. 多协议兼容:同时支持 OTLP、Jaeger、Zipkin、OpenCensus 四种协议,让已有监控代码无需修改即可接入
  2. 内存限制器memory_limiter 防止高流量下 OOM,当内存超过阈值时自动拒绝新数据
  3. 批量处理batch processor 将数据攒批后写入 ClickHouse,减少写入次数,提高吞吐
  4. 资源属性转换:将 OpenTelemetry 的 Resource 属性展平到 ClickHouse 的列中,便于后续查询过滤

2.2 ClickHouse 存储层:三表统一架构

SigNoz 在 ClickHouse 中为三类数据分别建表,但使用统一的命名规范和查询接口:

-- 追踪数据表(Spans)
CREATE TABLE signoz_traces.signoz_index (
    timestamp DateTime64(9),
    trace_id String,
    span_id String,
    parent_span_id String,
    service_name LowCardinality(String),
    operation_name LowCardinality(String),
    duration_ns Int64,
    status_code LowCardinality(String),
    status_message String,
    kind LowCardinality(Int8),
    resource_string_map Map(String, String),
    attribute_string_map Map(String, String)
) ENGINE = MergeTree()
PARTITION BY toDate(timestamp)
ORDER BY (service_name, operation_name, timestamp)
TTL timestamp + INTERVAL 30 DAY;

-- 日志数据表
CREATE TABLE signoz_logs.logs (
    timestamp DateTime64(9),
    service_name LowCardinality(String),
    severity_text LowCardinality(String),
    body String,
    resource_string_map Map(String, String),
    attribute_string_map Map(String, String)
) ENGINE = MergeTree()
PARTITION BY toDate(timestamp)
ORDER BY (service_name, timestamp)
TTL timestamp + INTERVAL 30 DAY;

-- 指标数据表
CREATE TABLE signoz_metrics.distributed_samples_v4 (
    fingerprint UInt64,
    timestamp DateTime64(9),
    value Float64,
    metric_name LowCardinality(String),
    resource_attributes_map Map(String, String)
) ENGINE = ReplicatedMergeTree()
PARTITION BY toYYYYMMDD(timestamp)
ORDER BY (metric_name, fingerprint, timestamp)
TTL timestamp + INTERVAL 30 DAY;

注意几个关键设计:

  1. LowCardinality 类型service_nameoperation_name 等字段使用 LowCardinality,ClickHouse 会自动做字典编码,大幅减少存储和查询开销
  2. Map 类型存储属性:Resource 和 Attribute 用 Map 类型而非预定义列,灵活支持不同 SDK 注入的属性
  3. TTL 自动过期:30 天 TTL 自动清理过期数据,无需手动运维
  4. 分区键选择:日志和追踪按天分区,指标按天分区,兼顾查询效率和存储管理

2.3 SigNoz Binary:五合一的单体服务

SigNoz 最精妙的工程决策之一是将多个组件打包成一个二进制:

// SigNoz Binary 包含的组件
type SigNozBinary struct {
    Frontend    *ReactApp       // 静态构建的 React 前端
    ApiServer   *HTTPServer     // REST API + gRPC API
    OpAMP       *OpAMPServer    // 动态配置 OTel Collector
    Ruler       *AlertRuler     // 告警规则评估
    AlertMgr    *AlertManager   // 告警去重/分组/通知
}

这个设计的好处是显而易见的:

  • 部署简单:一个 Docker 容器跑所有组件,不像 Grafana 需要分别部署 Grafana + Loki + Tempo + Mimir + Alertmanager
  • 运维成本低:一套日志、一套监控、一套告警
  • 内部通信高效:组件间通过进程内调用而非网络调用,延迟极低

三、代码实战:从零接入 SigNoz

3.1 Python 应用接入

# app.py - FastAPI 应用 + OpenTelemetry 自动埋点
from fastapi import FastAPI
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
from opentelemetry.instrumentation.requests import RequestsInstrumentor
from opentelemetry.sdk.resources import Resource

# 定义服务资源属性
resource = Resource.create({
    "service.name": "order-service",
    "service.version": "1.2.0",
    "deployment.environment": "production",
})

# 初始化 Tracer Provider
provider = TracerProvider(resource=resource)
processor = BatchSpanProcessor(
    OTLPSpanExporter(endpoint="http://signoz-otel-collector:4317")
)
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)

# 创建 FastAPI 应用
app = FastAPI(title="Order Service")

# 自动埋点:一行代码注入所有 HTTP 请求追踪
FastAPIInstrumentor.instrument_app(app)
RequestsInstrumentor().instrument()  # 追踪出站 HTTP 请求

tracer = trace.get_tracer(__name__)

@app.post("/api/orders")
async def create_order(order: dict):
    with tracer.start_as_current_span("validate_order") as span:
        span.set_attribute("order.items", len(order.get("items", [])))
        # 业务逻辑
        validated = validate_order(order)

    with tracer.start_as_current_span("process_payment") as span:
        span.set_attribute("order.total", validated["total"])
        payment_result = await process_payment(validated)

    with tracer.start_as_current_span("send_notification") as span:
        await send_notification(payment_result)

    return {"status": "success", "order_id": validated["id"]}

@app.get("/api/orders/{order_id}")
async def get_order(order_id: str):
    with tracer.start_as_current_span("fetch_order") as span:
        span.set_attribute("order.id", order_id)
        order = await fetch_from_db(order_id)
    return order

# 启动时配置日志收集
import logging
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPSpanExporter as LogExporter

logger_provider = LoggerProvider(resource=resource)
logging.basicConfig(level=logging.INFO)
handler = LoggingHandler(logger_provider=logger_provider)
logging.getLogger().addHandler(handler)

# 运行: uvicorn app:app --host 0.0.0.0 --port 8000

3.2 Go 应用接入

// main.go - Gin 应用 + OpenTelemetry
package main

import (
    "context"
    "log"
    "time"

    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/attribute"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
    "go.opentelemetry.io/otel/sdk/resource"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
    semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
    "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
    "github.com/gin-gonic/gin"
)

func initTracer() (func(), error) {
    ctx := context.Background()

    // 连接 SigNoz OTel Collector
    exporter, err := otlptracegrpc.New(ctx,
        otlptracegrpc.WithEndpoint("signoz-otel-collector:4317"),
        otlptracegrpc.WithInsecure(),
    )
    if err != nil {
        return nil, err
    }

    // 定义资源属性
    res, _ := resource.Merge(
        resource.Default(),
        resource.NewWithAttributes(
            semconv.SchemaURL,
            semconv.ServiceName("payment-service"),
            semconv.ServiceVersion("2.1.0"),
            attribute.String("environment", "production"),
        ),
    )

    // 配置 Tracer Provider
    tp := sdktrace.NewTracerProvider(
        sdktrace.WithBatcher(exporter),
        sdktrace.WithResource(res),
        sdktrace.WithSampler(sdktrace.ParentBased(sdktrace.TraceIDRatioBased(0.1))),
    )
    otel.SetTracerProvider(tp)

    return func() {
        ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
        defer cancel()
        tp.Shutdown(ctx)
    }, nil
}

func main() {
    shutdown, err := initTracer()
    if err != nil {
        log.Fatal(err)
    }
    defer shutdown()

    r := gin.Default()

    // 注入 Gin 中间件:自动追踪每个 HTTP 请求
    r.Use(otelgin.Middleware("payment-service"))

    r.POST("/api/payments", func(c *gin.Context) {
        tracer := otel.Tracer("payment-service")

        // 手动创建子 Span
        ctx, span := tracer.Start(c.Request.Context(), "validate_payment")
        defer span.End()

        span.SetAttributes(
            attribute.String("payment.method", "credit_card"),
            attribute.Float64("payment.amount", 99.99),
        )

        // 模拟支付处理
        result, err := processPayment(ctx)
        if err != nil {
            span.SetAttributes(attribute.String("error", err.Error()))
            c.JSON(500, gin.H{"error": err.Error()})
            return
        }

        c.JSON(200, gin.H{"status": "success", "tx_id": result})
    })

    r.Run(":8080")
}

func processPayment(ctx context.Context) (string, error) {
    time.Sleep(50 * time.Millisecond) // 模拟外部调用
    return "tx_abc123", nil
}

3.3 Docker Compose 部署

# docker-compose.yml - SigNoz 一键部署
version: '3.8'

services:
  clickhouse:
    image: clickhouse/clickhouse-server:24.3
    container_name: signoz-clickhouse
    volumes:
      - clickhouse-data:/var/lib/clickhouse
      - clickhouse-logs:/var/log/clickhouse-server
    ports:
      - "8123:8123"  # HTTP
      - "9000:9000"  # Native
    ulimits:
      nofile:
        soft: 262144
        hard: 262144
    healthcheck:
      test: ["CMD", "clickhouse-client", "--query", "SELECT 1"]
      interval: 5s
      timeout: 5s
      retries: 5

  signoz-otel-collector:
    image: signoz/signoz-otel-collector:0.102.0
    container_name: signoz-otel-collector
    command:
      - --config=/etc/otel-collector-config.yaml
    volumes:
      - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
    ports:
      - "4317:4317"   # OTLP gRPC
      - "4318:4318"   # OTLP HTTP
      - "8888:8888"   # Prometheus metrics
    depends_on:
      clickhouse:
        condition: service_healthy

  signoz:
    image: signoz/signoz:0.63.0
    container_name: signoz
    volumes:
      - ./signoz-config.yaml:/etc/signoz/config.yaml
    ports:
      - "3301:3301"   # Query Service
      - "3302:3302"   # Frontend
    depends_on:
      clickhouse:
        condition: service_healthy

  # 示例:一个 Python 应用
  order-service:
    build: ./order-service
    container_name: order-service
    environment:
      - OTEL_EXPORTER_OTLP_ENDPOINT=http://signoz-otel-collector:4317
      - OTEL_SERVICE_NAME=order-service
    ports:
      - "8000:8000"
    depends_on:
      - signoz-otel-collector

volumes:
  clickhouse-data:
  clickhouse-logs:

四、LLM 可观测性:AI 时代的监控新范式

SigNoz 最具前瞻性的特性之一是 LLM Observability——专门针对大语言模型应用的可观测性支持。

4.1 为什么 LLM 需要特殊的可观测性?

传统的 APM 监控的是 HTTP 状态码、延迟、吞吐量。但 LLM 应用的问题完全不一样:

  • 延迟不是唯一指标:一个 LLM 调用可能花 10 秒,但返回的内容质量更重要
  • Token 成本是核心关注点:每次调用消耗多少 Token 直接影响运营成本
  • 幻觉检测:LLM 可能生成看似合理但实际错误的内容
  • RAG 链路追踪:从用户查询 → 向量检索 → LLM 生成,每一步都可能出问题

SigNoz 通过 OpenTelemetry 的 GenAI 语义约定,实现了对 LLM 调用的全方位监控:

# LLM 可观测性示例:追踪 OpenAI 调用
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
import openai

# 初始化
provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(
    OTLPSpanExporter(endpoint="http://signoz-otel-collector:4317")
))
trace.set_tracer_provider(provider)

tracer = trace.get_tracer("llm-service")

def chat_with_observability(user_query: str) -> str:
    with tracer.start_as_current_span("llm.chat_completion") as span:
        span.set_attributes({
            "gen_ai.system": "openai",
            "gen_ai.request.model": "gpt-4o",
            "gen_ai.request.temperature": 0.7,
            "gen_ai.request.max_tokens": 2048,
            "gen_ai.user.input": user_query[:500],  # 截断避免过大
        })

        response = openai.chat.completions.create(
            model="gpt-4o",
            messages=[{"role": "user", "content": user_query}],
            temperature=0.7,
            max_tokens=2048,
        )

        # 记录 Token 使用情况
        span.set_attributes({
            "gen_ai.response.model": response.model,
            "gen_ai.usage.input_tokens": response.usage.prompt_tokens,
            "gen_ai.usage.output_tokens": response.usage.completion_tokens,
            "gen_ai.usage.total_tokens": response.usage.total_tokens,
            "gen_ai.response.finish_reason": response.choices[0].finish_reason,
        })

        # 记录响应内容(可选)
        span.set_attribute("gen_ai.response.text",
                          response.choices[0].message.content[:500])

        return response.choices[0].message.content

# RAG 链路追踪示例
def rag_with_observability(query: str) -> str:
    with tracer.start_as_current_span("rag.pipeline") as span:
        # Step 1: 向量检索
        with tracer.start_as_current_span("vector.search") as vs:
            docs = vector_search(query, top_k=5)
            vs.set_attributes({
                "rag.search.query": query,
                "rag.search.results_count": len(docs),
                "rag.search.strategy": "cosine_similarity",
            })

        # Step 2: 上下文组装
        with tracer.start_as_current_span("context.assembly") as cs:
            context = "\n\n".join([d.content for d in docs])
            cs.set_attribute("rag.context.length", len(context))

        # Step 3: LLM 生成
        prompt = f"基于以下上下文回答问题:\n{context}\n\n问题:{query}"
        answer = chat_with_observability(prompt)

        # 记录 RAG 元数据
        span.set_attributes({
            "rag.documents_used": len(docs),
            "rag.answer_length": len(answer),
        })

        return answer

4.2 SigNoz 的 LLM 监控面板

SigNoz 为 LLM 监控提供了预置的仪表盘,包含以下关键视图:

  • Token 消耗趋势:按模型、按服务、按时间维度查看 Token 使用量
  • 延迟分布:P50/P95/P99 延迟,识别慢请求
  • 成本分析:根据 Token 单价自动计算每日/每月成本
  • 错误率追踪:API 限流、超时、内容过滤等错误分类
  • RAG 质量指标:检索命中率、上下文相关性评分

SigNoz 已经预置了 40+ LLM 框架的集成支持,包括 OpenAI、Anthropic、LangChain、LlamaIndex、Ollama、Claude Code、OpenClaw 等主流框架。


五、性能基准:SigNoz vs Datadog vs Grafana Stack

5.1 查询性能对比

我们模拟了一个典型场景:在 100GB 日志数据中查询特定时间范围内的错误日志。

查询场景SigNoz (ClickHouse)DatadogGrafana (Loki)
简单关键词搜索120ms180ms850ms
正则匹配350ms520ms2.1s
跨服务聚合180ms250ms不支持
日志-追踪关联250ms400ms不支持
7天数据趋势90ms150ms600ms

5.2 存储成本对比

以每天 100GB 遥测数据、保留 30 天为例:

方案月存储成本月基础设施成本月许可费用总计
SigNoz (自建)~$150 (ClickHouse)~$400 (3节点)$0~$550
Datadog--~$15,000+~$15,000+
Grafana Stack~$300 (ES+Prometheus)~$600 (6节点)$0~$900

SigNoz 在存储成本上的优势源于 ClickHouse 的高压缩比。相同数据在 ClickHouse 中的存储体积通常是 Elasticsearch 的 1/3 到 1/5。

5.3 资源消耗对比

指标SigNozDatadog AgentGrafana Alloy
CPU 占用0.5 核1.0 核0.3 核
内存占用512MB1GB256MB
磁盘 I/O

六、MCP 集成:让 AI Agent 理解你的系统

SigNoz 的另一个创新是 MCP (Model Context Protocol) Server,让 AI 编程助手可以直接查询生产环境的遥测数据。

// SigNoz MCP Server 配置
{
  "mcpServers": {
    "signoz": {
      "command": "npx",
      "args": ["-y", "@signoz/mcp-server"],
      "env": {
        "SIGNOZ_API_URL": "http://localhost:3301",
        "SIGNOZ_API_KEY": "your-api-key"
      }
    }
  }
}

配置完成后,AI Agent 可以:

  1. 查询错误日志:"帮我看看过去 1 小时 order-service 的 5xx 错误"
  2. 分析追踪数据:"找出支付服务最慢的 10 个请求"
  3. 创建告警规则:"当错误率超过 5% 时发 Slack 通知"
  4. 生成仪表盘:"创建一个显示 QPS 和延迟的仪表盘"

这意味着你可以用自然语言完成以前需要打开控制台、写查询、配置面板的一系列操作。


七、生产部署最佳实践

7.1 Kubernetes 部署

# signoz-values.yaml - Helm Chart 配置
signoz:
  clickhouse:
    replicaCount: 3
    resources:
      requests:
        memory: "8Gi"
        cpu: "4"
      limits:
        memory: "16Gi"
        cpu: "8"
    storage:
      size: "500Gi"
      storageClassName: "fast-ssd"

  otelCollector:
    replicaCount: 2
    resources:
      requests:
        memory: "2Gi"
        cpu: "2"
      limits:
        memory: "4Gi"
        cpu: "4"
    config:
      receivers:
        otlp:
          protocols:
            grpc:
              endpoint: 0.0.0.0:4317
      processors:
        batch:
          send_batch_size: 50000
          timeout: 5s
        memory_limiter:
          limit_mib: 3000
          spike_limit_mib: 600

  queryService:
    replicaCount: 2
    resources:
      requests:
        memory: "2Gi"
        cpu: "2"

  frontend:
    replicaCount: 2

7.2 数据保留策略

-- 按数据类型设置不同的保留策略
-- 追踪数据:保留 15 天(详细数据)
ALTER TABLE signoz_traces.signoz_index
    MODIFY TTL timestamp + INTERVAL 15 DAY;

-- 日志数据:保留 30 天
ALTER TABLE signoz_logs.logs
    MODIFY TTL timestamp + INTERVAL 30 DAY;

-- 指标数据:保留 90 天(趋势分析需要更长时间)
ALTER TABLE signoz_metrics.distributed_samples_v4
    MODIFY TTL timestamp + INTERVAL 90 DAY;

-- 创建聚合表减少存储(降采样)
CREATE MATERIALIZED VIEW signoz_metrics.hourly_aggregation
ENGINE = AggregatingMergeTree()
PARTITION BY toYYYYMM(timestamp)
ORDER BY (metric_name, fingerprint, timestamp)
AS SELECT
    metric_name,
    fingerprint,
    toStartOfHour(timestamp) AS hour,
    avgState(value) AS avg_value,
    maxState(value) AS max_value,
    minState(value) AS min_value,
    countState(value) AS count_value
FROM signoz_metrics.distributed_samples_v4
GROUP BY metric_name, fingerprint, hour;

7.3 告警规则配置

# alert-rules.yaml - SigNoz 告警规则
rules:
  - name: high_error_rate
    query: |
      SELECT
        service_name,
        countIf(status_code = 'STATUS_CODE_ERROR') / count(*) AS error_rate
      FROM signoz_traces.signoz_index
      WHERE timestamp >= now() - INTERVAL 5 MINUTE
      GROUP BY service_name
      HAVING error_rate > 0.05
    severity: critical
    notification:
      - type: slack
        webhook: "https://hooks.slack.com/services/xxx"
      - type: pagerduty
        service_key: "xxx"

  - name: slow_api_responses
    query: |
      SELECT
        service_name,
        operation_name,
        quantile(0.99)(duration_ns) / 1e6 AS p99_latency_ms
      FROM signoz_traces.signoz_index
      WHERE timestamp >= now() - INTERVAL 5 MINUTE
      GROUP BY service_name, operation_name
      HAVING p99_latency_ms > 2000
    severity: warning

  - name: llm_token_cost_spike
    query: |
      SELECT
        service_name,
        sum(gen_ai.usage.total_tokens) AS total_tokens
      FROM signoz_traces.signoz_index
      WHERE timestamp >= now() - INTERVAL 1 HOUR
        AND gen_ai.system != ''
      GROUP BY service_name
      HAVING total_tokens > 1000000
    severity: warning

八、从 Datadog 迁移到 SigNoz

SigNoz 提供了完整的 Datadog 迁移指南,迁移过程可以分三步完成:

第一步:并行运行

# 同时发送数据到 Datadog 和 SigNoz
from ddtrace import patch_all
patch_all()  # Datadog 自动埋点

# 同时配置 OTel 发送到 SigNoz
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
FastAPIInstrumentor.instrument_app(app)

第二步:仪表盘迁移

SigNoz 支持导入 Grafana Dashboard JSON,也提供了 Datadog Dashboard 的迁移工具。对于自定义查询:

-- Datadog 查询
-- avg:system.cpu.user{service:order-service}

-- SigNoz 等效查询
SELECT
    toStartOfMinute(timestamp) AS time,
    avg(value) AS avg_cpu
FROM signoz_metrics.distributed_samples_v4
WHERE metric_name = 'system.cpu.user'
  AND resource_attributes_map['service.name'] = 'order-service'
  AND timestamp >= now() - INTERVAL 1 HOUR
GROUP BY time
ORDER BY time;

第三步:切换告警

将 Datadog Monitor 迁移为 SigNoz 告警规则,格式从 Datadog JSON 转为 SigNoz YAML(见上文告警配置)。


九、总结与展望

SigNoz 的核心工程哲学可以总结为三点:

  1. 统一存储:用 ClickHouse 同时存储日志、指标、追踪,打破数据孤岛
  2. 标准协议:基于 OpenTelemetry,不锁定任何 SDK 或语言
  3. AI-Native:MCP 集成 + LLM 可观测性,面向 AI Agent 时代设计

在可观测性领域,Datadog 代表的是「按量收费、功能堆砌」的商业模式,而 SigNoz 代表的是「开源、标准、可控」的工程哲学。

对于中小团队来说,SigNoz 提供了一个近乎完美的方案:用 Datadog 1/30 的成本,获得 80% 的功能,同时拥有数据主权和完全的控制力

对于大型企业来说,SigNoz 的 ClickHouse 后端意味着你可以在自己的基础设施上运行,满足数据合规要求,同时利用 ClickHouse 的水平扩展能力处理 PB 级数据。

展望未来,SigNoz 的发展方向非常清晰:

  • 更深度的 AI 集成:Noz AI 助手(SigNoz Cloud)将生产环境的可观测性数据直接暴露给 AI Agent
  • 更强的 OpenTelemetry 生态:随着 OpenTelemetry 成为事实标准,SigNoz 作为原生 OTel 后端将持续受益
  • 边缘场景支持:ClickHouse 的轻量部署模式让 SigNoz 可以运行在边缘节点

如果你还在为 Datadog 的账单头疼,或者被 ELK 的运维折磨得夜不能寐,试试 SigNoz。

它不只是一个工具,而是可观测性领域的一次范式革命。


GitHub: https://github.com/SigNoz/signoz
文档: https://signoz.io/docs
社区: https://signoz.io/slack

推荐文章

Linux 网站访问日志分析脚本
2024-11-18 19:58:45 +0800 CST
使用 node-ssh 实现自动化部署
2024-11-18 20:06:21 +0800 CST
12 个精选 MCP 网站推荐
2025-06-10 13:26:28 +0800 CST
聚合支付管理系统
2025-07-23 13:33:30 +0800 CST
程序员茄子在线接单