Grafana观测日志/指标

在.NET Core Web API中集成Grafana生态系统的监控工具链,实现全栈可观测性(日志、指标、追踪)

本文在此使用下面组件:

  • Grafana:用于监控和观测数据
  • Prometheus:指标(Metrics)收集
  • Loki:日志收集
  • Alloy:统一观测数据收集器

Grafana 是可视化层,用于展示 Prometheus(指标)、Loki(日志) 和 Tempo(追踪) 的数据。

Alloy 是数据收集层,统一采集指标、日志、追踪并转发到对应后端(Prometheus/Loki/Tempo)。

它们共同构成完整的 观测性栈(Observability Stack):Alloy(收集) → Prometheus/Loki/Tempo(存储) → Grafana(可视化)

部署Grafana服务

使用docker compose编排grafana相关服务:

yaml 复制代码
services:
  grafana-alloy:
    image: grafana/alloy:latest
    container_name: grafana-alloy
    restart: unless-stopped
    volumes:
      - /etc/grafana-config/alloy/config.alloy:/etc/alloy/config.alloy
      - /etc/grafana-config/alloy/data:/var/lib/alloy
    ports:
      - "12345:12345"  # 用于 Alloy 的 HTTP 服务器
      - "4317:4317"    # OpenTelemetry gRPC 接收器
      - "4318:4318"    # OpenTelemetry HTTP 接收器
      - "8888:8888"    # 用于 Prometheus 远程写入
    command: ["run", "--server.http.listen-addr=0.0.0.0:12345", "/etc/alloy/config.alloy"]
  loki:
     image: grafana/loki:3.0.0
     ports:
       - "3100:3100"
     command: -config.file=/etc/loki/local-config.yaml
  prometheus:
     image: prom/prometheus:v2.47.0
     command:
       - --web.enable-remote-write-receiver
       - --config.file=/etc/prometheus/prometheus.yml
     ports:
       - "9090:9090"
  grafana:
     environment:
       - GF_PATHS_PROVISIONING=/etc/grafana/provisioning
       - GF_AUTH_ANONYMOUS_ENABLED=true
       - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
     entrypoint:
       - sh
       - -euc
       - |
         mkdir -p /etc/grafana/provisioning/datasources
         cat <<EOF > /etc/grafana/provisioning/datasources/ds.yaml
         apiVersion: 1
         datasources:
         - name: Loki
           type: loki
           access: proxy
           orgId: 1
           url: http://loki:3100
           basicAuth: false
           isDefault: false
           version: 1
           editable: false
         - name: Prometheus
           type: prometheus
           orgId: 1
           url: http://prometheus:9090
           basicAuth: false
           isDefault: true
           version: 1
           editable: false
         EOF
         /run.sh
     image: grafana/grafana:latest
     ports:
       - "13000:3000"
volumes:
  grafana-storage: {}

构建容器

通过ip:13000(端口根据自己的配置调整)来访问grafana

grafana已经根据我们的配置预先设置好了Loki和Prometheus。

查看Loki:

查看Prometheus:

配置Alloy

找到alloy配置文件config.alloy,如本文配置文件挂载在/etc/grafana-config/alloy/config.alloy,打开并修改:

yaml 复制代码
//配置收集/var/log/ 目录下的所有.log文件
local.file_match "local_files" {
     path_targets = [{"__path__" = "/var/log/*.log"}]
     sync_period = "5s"
}
loki.source.file "log_scrape" {
    targets    = local.file_match.local_files.targets
    forward_to = [loki.process.filter_logs.receiver]
    tail_from_end = true
}
loki.process "filter_logs" {
    stage.drop {
        source = ""
        expression  = ".*Connection closed by authenticating user root"
        drop_counter_reason = "noisy"
      }
    forward_to = [loki.write.grafana_loki.receiver]
}
//配置收集/tmp/alloy-logs/ 目录下的所有.log文件
local.file_match "tmplogs" {
    path_targets = [{"__path__" = "/tmp/alloy-logs/*.log"}]
}

loki.source.file "local_files" {
    targets    = local.file_match.tmplogs.targets
    forward_to = [loki.write.grafana_loki.receiver]
}
//将收集的日志写入loki
loki.write "grafana_loki" {
    endpoint {
      url = "http://loki:3100/loki/api/v1/push"
      // basic_auth {
      //  username = "admin"
      //  password = "admin"
      // }
    }
}

//配置本地系统指标导出器
prometheus.exporter.unix "local_system" { }

//配置发送源与时间间隔
prometheus.scrape "scrape_metrics" {
  targets         = prometheus.exporter.unix.local_system.targets
  forward_to      = [prometheus.relabel.filter_metrics.receiver]
  scrape_interval = "10s"
}
prometheus.relabel "filter_metrics" {
  rule {
    action        = "drop"
    source_labels = ["env"]
    regex         = "dev"
  }

  forward_to = [prometheus.remote_write.metrics_service.receiver]
}
//将收集的指标写入prometheus
prometheus.remote_write "metrics_service" {
    endpoint {
        url = "http://prometheus:9090/api/v1/write"
        // basic_auth {
        //   username = "admin"
        //   password = "admin"
        // }
    }
}

访问Alloy UI查看配置间的关系图:

测试收集数据

直接向loki端点发送数据:

plain 复制代码
NOW=$(date +%s%N)
curl -v -XPOST "http://localhost:3100/loki/api/v1/push" \
  -H "Content-Type: application/json" \
  -d '{
    "streams": [{
      "stream": { "job": "test" },
      "values": [ [ "'"$NOW"'", "test log" ] ]
    }]
  }'

在Grafana中查看:

向本地文件写入log日志echo "New log line" >> /tmp/alloy-logs/log.log

可以查到收集的日志信息:

在Net Core应用程序中收集指标与日志

  1. 添加相应的包
plain 复制代码
dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Instrumentation.AspNetCore
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol
dotnet add package OpenTelemetry.Instrumentation.Http
dotnet add package OpenTelemetry.Instrumentation.SqlClient
dotnet add package OpenTelemetry.Instrumentation.Runtime
//使用 Serilog 将日志发送到 Loki
dotnet add package Serilog.AspNetCore
dotnet add package Serilog.Sinks.Grafana.Loki
  1. 编辑启动程序代码:
plain 复制代码
// 1. 配置 OpenTelemetry 资源(服务名、版本等)
var resourceBuilder = ResourceBuilder
    .CreateDefault()
    .AddService(serviceName: "my-webapi", serviceVersion: "1.0.0");
// 2. 配置 Tracing(分布式追踪)
builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        tracing
            .SetResourceBuilder(resourceBuilder)
            .AddAspNetCoreInstrumentation() // 监控 ASP.NET Core 请求
            .AddHttpClientInstrumentation()   // 监控 HttpClient 请求
            .AddSqlClientInstrumentation()    // 监控 SQL 查询
            .AddOtlpExporter(options =>       // 发送到 Alloy 的 OTLP 接收端
            {
                options.Endpoint = new Uri("http://grafana-alloy:4317"); // Docker 内部网络
            });
    });
// 3. 配置 Metrics(指标)
builder.Services.AddOpenTelemetry()
    .WithMetrics(metrics =>
    {
        metrics
            .SetResourceBuilder(resourceBuilder)
            .AddAspNetCoreInstrumentation()
            .AddHttpClientInstrumentation()
            .AddRuntimeInstrumentation()   // 监控 .NET 运行时指标
            .AddOtlpExporter(options =>       // 发送到 Alloy
            {
                options.Endpoint = new Uri("http://grafana-alloy:4317");
            });
    });
// 4. 配置 Logging(日志)
builder.Logging.AddOpenTelemetry(logging =>
{
    logging.SetResourceBuilder(resourceBuilder)
        .AddOtlpExporter(options =>
        {
            options.Endpoint = new Uri("http://grafana-alloy:4317");
        });
});
builder.Host.UseSerilog(); // 启用 Serilog
  1. 在alloy.config中添加下面配置
plain 复制代码
otelcol.receiver.otlp "default" {
  grpc { endpoint = "0.0.0.0:4317" }
  http  { endpoint = "0.0.0.0:4318" }

  output {
    metrics = [otelcol.exporter.prometheus.default.input]
    logs    = [otelcol.exporter.loki.default.input]
    traces  = []
  }
}
otelcol.exporter.loki "default" {
  forward_to = [loki.write.grafana_loki.receiver]  // 转发到 Loki Write
}
otelcol.exporter.prometheus "default" {
  forward_to = [prometheus.remote_write.metrics_service.receiver]  // 转发到 Prometheus
}
  1. 选择一个仪表盘模板展示数据
  1. 查看仪表盘
相关推荐
Piper蛋窝4 分钟前
Go 1.7 相比 Go 1.6 有哪些值得注意的改动?
后端·go
张哈大5 分钟前
《苍穹外卖Day2:大一菜鸟的代码升空纪实》
后端
一介输生5 分钟前
Spring Cloud实现权限管理(网关+jwt版)
java·后端
AI_Infra智塔6 分钟前
ZStack文档DevOps平台建设实践
后端
雪糕222 分钟前
@EnableAutoConfiguration注解解析过程
后端
shark_chili27 分钟前
mini-redis复刻Redis的INCR指令
后端
友恒写实27 分钟前
Python面试官:你来解释一下协程的实现原理
后端·python
vocal28 分钟前
MCP:LLM与知识库之间的通信协议—(1)初认知
后端
noodb软件工作室29 分钟前
juc之ReentrantLock
后端
无吟唱_指尖魔术师32 分钟前
Streamlit - python 快速生成UI框架
后端