APM-基于Grafana生态以及OTLP协议的Java轻量级日志监控系统

发布于:2025-03-29 ⋅ 阅读:(24) ⋅ 点赞:(0)

APM-基于Grafana生态以及OTLP协议的Java轻量级日志监控系统

读写分离模式部署

初始化文件夹和一些配置文件

初始化文件夹
# minio存储
mkdir -p /data/apm/minio
# java日志存储
mkdir -p /opt/app
创建nginx相关配置文件
nginx.conf
vim /data/apm/nginx/nginx.conf
user nginx;
worker_processes 5; # worker线程数

events {
    worker_connections 1000; # 单个worker连接数
}

http {
    # 使用Docker内置DNS解析服务名, DNS缓存有效期10秒
    resolver 127.0.0.11 valid=10s;
    # 开启访问日志, 生产中建议关闭
    access_log on;
    
    # 定义上游Loki writer服务器组
    upstream loki_writers {
        server write:3100;
        # 保持长连接池
        keepalive 32;
    }

    # 定义上游Loki reader服务器组
    upstream loki_readers {
        server read:3100;
        keepalive 32;
    }
    
    # 定义上游alloy服务器组
    upstream alloys {
        server alloy:12345;
    }

    # Grafana UI
    server {
        listen 3000;
        
        location / {
            proxy_pass http://grafana:3000;
            
            # 代理设置请求头, 否则Grafana会提示一直登录
            # 并且要设置WebSocket, 否则无法运行实时跟踪
            # 见https://blog.csdn.net/weixin_41287260/article/details/134630447
            # https://www.cnblogs.com/hahaha111122222/p/16407564.html
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host $http_host;
        }
    }

    
    # Loki相关日志推送, 存储等配置
    server {
        # 监听容器内 3100 端口(通过 ports 映射到宿主机 3100)启用端口复用提升性能
        listen 3100 reuseport;

         # 定义写入类请求的通用配置块
        location ~ ^/(api/prom/push|loki/api/v1/push) {
            proxy_pass http://loki_writers$request_uri;
            proxy_http_version 1.1;
            proxy_set_header Connection "";
        }

        # 定义实时日志流式传输(tail)请求的通用配置块
        location ~ ^/(api/prom/tail|loki/api/v1/tail) {
            proxy_pass http://loki_readers$request_uri;
            proxy_read_timeout 3600s;
            # 这里必须要配置WebSocket, 否则无法运行实时跟踪
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }

        # 定义所有 Prometheus 格式和 Loki 原生格式查询请求的通用配置块
        location ~ ^/(api/prom/.*|loki/api/.*) {
            proxy_pass http://loki_readers$request_uri;
            proxy_http_version 1.1;
            proxy_set_header Connection "";
            # 缓存查询结果10秒
            proxy_cache_valid 200 10s;
        }
    }

    # read 端点, 生产中应该禁外界访问(这里做演示, 所以开放), 容器内部通信即可
    server {
        listen 3101;
        
        location / {
            proxy_pass http://loki_readers/ready;
        }
    }
    
    # write 端点, 生产中禁外界访问(这里做演示, 所以开放), 容器内部通信即可
    server {
        listen 3102;
        
        location / {
            proxy_pass http://loki_writers/ready;
        }
    }

    # Minio UI
    server {
        listen 9001;
        
        location / {
            proxy_pass http://minio:9001;
            
            # 添加websocket支持, 否则Minio会卡主, 页面一直loading
            # 见https://blog.csdn.net/qq_25231683/article/details/128734555
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host $http_host;
        }
    }
    
    # Alloy UI
    server {
        listen 12345;
        
        location / {
            proxy_pass http://alloys;
        }
    }    
}
创建alloy相关配置文件
alloy-local-config.yaml
vim /data/apm/alloy/alloy-local-config.yaml

基础架构的日志(例如MySQL, Redis等中间件): 这些组件以类似于 Prometheus 指标的方式标记您的日志。 这使得关联组件收集的基础设施指标变得容易 以及组件收集的日志。组件通常有以下特点

  • loki.*: 日志收集

  • prometheus.*: 指标收集

应用程序的日志(比如Java, Python产生的日志): 以 OpenTelemetry 原生方式收集应用程序日志, 从而更轻松地 将日志与来自应用程序的 OpenTelemetry 指标和跟踪相关联。 所有应用程序遥测都必须遵循 OpenTelemetry 语义约定, 从而简化此关联。组件通常有以下特点

  • otelcol.receiver.*
// ========================
// 实时调试
// ========================
livedebugging {
  enabled = true
}

// ========================
// Docker容器日志发现和采集
// ========================
// Docker 容器发现配置
discovery.docker "flog_scrape" {
    // 连接 Docker daemon 的地址(Unix 套接字)
    host             = "unix:///var/run/docker.sock"  
    // 每5s抓取Docker信息日志
    refresh_interval = "5s"  
}

// 主要用于服务发现阶段对发现的目标(如容器、节点等)的元数据标签进行预处理。当通过服务发现机制(如基于 Docker、Kubernetes 等)发现一系列目标时,这些目标会带有各种元数据标签,这里可以对这些原始标签进行修改、添加、删除等操作,使得标签更加符
discovery.relabel "flog_scrape" {
    // 初始空目标列表(自动从上游发现discovery.docker "flog_scrape"填充)
    targets = []  

    // 提取容器名称
    rule {
        // 原始元数据标签(来自 Docker 的属性)
        source_labels = ["__meta_docker_container_name"]  
        // 正则提取容器名称(去除路径前缀)
        regex         = "/(.*)"  
        // 生成新标签 container 存储处理结果
        target_label  = "container"
    }
    
    // 提取项目名
    rule {
        //
        source_labels = ["__meta_docker_container_label_com_docker_compose_project"]
        regex         = "(.*)"  
        target_label  = "project"     
    }       
}

// Loki Docker 日志采集配置
loki.source.docker "flog_scrape" {
    // Docker 连接配置(需与发现模块一致)
    host             = "unix:///var/run/docker.sock"  
    // 要从中读取日志的容器列表, 关联发现模块获取的目标列表
    targets          = discovery.docker.flog_scrape.targets  
    // 日志转发目的地(指向写入模块)
    forward_to       = [loki.write.default.receiver]  
    // 应用标签重写规则
    relabel_rules    = discovery.relabel.flog_scrape.rules  
    // 目标同步频率(与发现模块同步)
    refresh_interval = "5s"  
}

// ========================
// *.log文件匹配和采集
// ========================
// 本地*.log文件匹配
local.file_match "local_log" {
  path_targets = [
    {__path__ = "/opt/app/logs/*.log"},
  ]
}

// ========================
// 本地*.log文件采集
// ========================
loki.source.file "local_log" {
  // 关联发现模块获取的目标列表, 关联到本地机器日志匹配
  targets    = local.file_match.local_log.targets
  // 日志转发目的地(指向写入模块)
  forward_to = [loki.write.default.receiver]
}

// ========================
// *.gz文件匹配和采集
// ========================
// 本地*.gz文件匹配
local.file_match "local_log_gz" {
  path_targets = [
    {__path__ = "/opt/app/logs/*.gz"},
  ]
}

// 本地*.log文件采集
loki.source.file "local_log_gz" {
  // 关联发现模块获取的目标列表, 关联到本地机器日志匹配
  targets    = local.file_match.local_log_gz.targets
  // 日志转发目的地(指向写入模块)
  forward_to = [loki.write.default.receiver]
  // 解压缩
  decompression {
    // 是否启用解压缩
    enabled       = true
    // 开始从新的压缩文件读取之前要等待的时间
    initial_delay = "10s"
    // 使用的压缩格式Gzip
    format        = "gz"
  }
}


// ========================
// Loki 日志写入配置
// ========================
loki.write "default" {
    // 将日志发送到的位置
    endpoint {
        // 要将日志发送到的完整 URL, Loki 接收端 API 地址
        url       = "http://gateway:3100/loki/api/v1/push"
        // 发送前要累积的最大日志批次大小
        batch_size = "1MiB"
        // 发送批次前要等待的最长时间
        batch_wait = "1s"
        // 多租户标识(生产环境建议动态获取)
        tenant_id = "tenant1"
    }
    // 附加全局标签(当前为空配置)
    external_labels = {}  
}



 
// =====================Otel配置===================================
// 接收otel协议转发数据
otelcol.receiver.otlp "default" {
    http {}
	// gRPC方式接受OTEL数据
    grpc {
      endpoint = "0.0.0.0:4317"
    }
	// 输出
    output {
    	//logs 通过 loki.source.file 采集, 不通过这个方式采集
		// otel需要在应用服务启动中并且agent代理生效后,才能正常抓取otel协议日志数据,因此会有部份缺失;通过Alloy直接抓取本地日志文件内容,能保证日志数据的全生命周期流程完整性
		// logs	= [otelcol.exporter.loki.default.input]
		// 指标输出
        metrics = [otelcol.exporter.prometheus.default.input]
		// traces链路数据输出
        traces  = [
			// 输出到tempo存储
			otelcol.exporter.otlp.tempo_default.input, 
			// 输出到span日志
            otelcol.connector.spanlogs.default.input,
			// 输出到服务拓扑图
            otelcol.connector.servicegraph.default.input,
        ]
    }
}

// 案例中, 不适用otel转发日志, 日志还是存储在本地
// 从otel中输出日志loki
otelcol.exporter.loki "default" {
  forward_to = [loki.write.default.receiver]
}

// 通过gRPC发送otlp链路数据到tempo服务
otelcol.exporter.otlp "tempo_default" {
    client {
        endpoint = "tempo:4317"
        tls { 
            insecure = true        	
            insecure_skip_verify = true
        }
    }
}

// 从otel中输出指标数据到prometheus
otelcol.exporter.prometheus "default" {
  forward_to = [prometheus.remote_write.default.receiver]
}
 
// =====================Span日志记录配置===================================
otelcol.connector.spanlogs "default" {
  roots = true
  span_attributes = ["http.method", "http.target"]
  output {
    logs = [otelcol.exporter.loki.default.input]
  }
}
 
// =====================服务图配置===================================
otelcol.connector.servicegraph "default" {
  dimensions = ["http.method", "http.target"]
  output {
    metrics = [otelcol.exporter.prometheus.default.input]
  }
}
 
// =====================Prometheus配置===================================
// 抓取远程服务(Java程序)指标
prometheus.scrape "remote_default" {
	// 这里填写docker0的地址, 此时才可以实现docker容器访问宿主机
	targets = [{"__address__" = "172.17.0.1:8080"}]
	// 使用http方式拉取
	scheme = "http"
	// 指标地址
	metrics_path = "/actuator/prometheus"
	// 拉取时间间隔
	scrape_interval = "30s"
	// 超时时间
	scrape_timeout = "10s"
	// 抓发到Prometheus
	forward_to = [prometheus.remote_write.default.receiver]
}

// Alloy指标导出器
prometheus.exporter.self "alloy" { }

// Linux主机指标导出器
prometheus.exporter.unix "node" {}


// 抓取Alloy指标和Linux主机指标
prometheus.scrape "defalut" {
    targets    = array.concat(prometheus.exporter.self.alloy.targets, prometheus.exporter.unix.node.targets)
    forward_to = [prometheus.remote_write.default.receiver]
    job_name   = "scraper"
}

// 指标发送Prometheus
prometheus.remote_write "default" {
    endpoint {
        url  = "http://gateway:9090/api/v1/write"
    }
}
endpoints.json

这个是alloy的endpoints文件, 定了endpoints, 和一些认证的信息

vim /etc/apm/alloy/endpoints.json

metrics: minir服务

logs: loki服务

traces: tempo服务

profiles: pyroscope服务

{
    "metrics": {
        "url": "http://mimir:9009/api/v1/push",
        "basicAuth": {
            "username": "",
            "password": ""
        }
    },
    "logs": {
        "url": "http://gateway:3100/loki/api/v1/push",
        "basicAuth": {
            "username": "",
            "password": ""
        }
    },
    "traces": {
        "url": "http://tempo:4317",
        "basicAuthToken": "",
        "tls": {
            "insecure": true,
            "insecureSkipVerify": true
        }
    },
    "profiles": {
        "url": "http://pyroscope:4040",
        "basicAuth": {
            "username": "",
            "password": ""
        }
    }
}
创建loki相关配置文件

Loki 是一个用于长期保留日志的后端存储

loki-config.yaml
vim /data/apm/loki/loki-config.yaml
# ========================
# Loki 服务核心配置
# ========================
server:
  # 监听所有网络接口
  http_listen_address: 0.0.0.0
  # 默认 Loki 服务端口
  http_listen_port: 3100
  grpc_listen_port: 9096
  # 设置Loki的日志级别
  log_level: info

# ========================
# Loki 管理租户隔离配置
# - Loki 默认以多租户模式运行。多租户模式在配置中使用 auth_enabled: true 设置。
# - 当配置为 auth_enabled: false 时,Loki 使用单租户。Loki API 请求中不需要 X-Scope-OrgID 标头。单租户 ID 将是字符串 fake(即此时只有一个租户)
# ========================
auth_enabled: false


# ========================
# 集群节点发现与通信配置
# ========================
memberlist:
  join_members: ["read", "write", "backend"]  # 需要连接的初始节点列表(建议使用IP或DNS)
  dead_node_reclaim_time: 30s     # 节点标记为死亡后保留元数据的时间
  gossip_to_dead_nodes_time: 15s  # 停止向死亡节点发送gossip包的时间
  left_ingesters_timeout: 30s     # 离开节点清理超时时间
  bind_addr: ['0.0.0.0']          # 集群通信绑定地址
  bind_port: 7946                 # 集群通信端口
  gossip_interval: 2s             # 节点状态同步间隔

# ========================
# 存储日志模式配置
# ========================
schema_config:
  configs:
    # `from` 值标记了该 schema 的起始点。该 schema 将一直处于活动状态, 直到另一个条目定义了一个新的 schema 和一个新的 `from` 日期
    # Loki 使用定义的 schema 来确定在存储和查询数据时使用的格式, 使用 schema 允许 Loki 迭代存储层, 而无需迁移现有数据
    # 对于没有先前数据的新 Loki 安装, 这是一个包含推荐值的 schema 配置示例, 见https://grafana.org.cn/docs/loki/latest/operations/storage/schema/
    - from: 2025-03-07
      # 存储模式, tsdb 是当前唯一推荐的 store 值
      store: tsdb
      object_store: s3
      # 存储模式的版本, v13 是最新的 schema 和推荐值
      schema: v13
      # index用于配置如何创建和存储索引表
      # 索引表是允许Loki在查询日志时确定读取哪些chunk的目录
      index:
        # 所有索引表的前缀
        prefix: index_
        # 每个索引表的存储24h的数据(超过标记为过期), 必须为 24h
        period: 24h

# ========================
# 数据索引和块存储配置
# ========================
common:
  # 这个Loki实例在本地哈希环上可以到达的地址
  instance_addr: 127.0.0.1
  # 所有HTTP端点的前缀
  path_prefix: /loki
  # 数据副本数(生产环境建议 >=3)
  replication_factor: 1
  compactor_address: http://backend:3100  # 压缩组件地址

  # 基础Loki存储系统的配置
  storage:
    # S3对象存储
    s3:
      endpoint: minio:9000         # S3兼容存储地址
      insecure: true              # 禁用HTTPS(生产环境不推荐)
      bucketnames: loki-data      # 主数据存储桶
      access_key_id: whiteBrocade         # 访问密钥(建议使用环境变量)
      secret_access_key: whiteBrocade  # 密钥(存在安全隐患)
      s3forcepathstyle: true      # 强制路径访问模式
    # 本地文件存储(由于是容器形式启动loki, 这里指的是容器内系统)
    #filesystem:
    #  # 数据保存的地方
    #  chunks_directory: /tmp/loki/chunks
    #  # 规则保存的地方
    #  rules_directory: /tmp/loki/rules
  # 一致性哈希环配置
  ring:
    # 使用一个分布式环memberlist, 其它的分布式环列如Consul或etcd
    kvstore:
      store: memberlist           # 使用memberlist实现分布式哈希环

# ========================
# 告警规则配置
# ========================
ruler:
  # 告警推送接口, 需要集成alertmanager
  # alertmanager_url: http://localhost:9093
  storage:
    s3:
      # 独立存储告警规则的桶
      bucketnames: loki-ruler


# ========================
# Compactor 负责索引文件的压缩和应用日志保留
# - Grafana Loki 中的保留是通过 Compactor 实现的。默认情况下, `compactor.retention-enabled` 标志未设置, 因此发送到 Loki 的日志将永久保留。
# - 如果您在对象存储上配置了生命周期策略, 请确保其长于保留期限
# - 仅当索引周期为 24 小时时, 保留才可用。单存储 TSDB 和单存储 BoltDB 需要 24 小时索引周期。
# - 参考官方文档https://grafana.org.cn/docs/loki/latest/operations/storage/retention/
# ========================
compactor:
  # 保存标记 chunks 和临时表的目录
  working_directory: /tmp/compactor
  # 应用压缩/保留的频率。如果 Compactor 落后, 则会尽快进行压缩和/或保留,
  compaction_interval: 10m
  # 设置为 true。否则,Compactor 将仅压缩表
  # 只要 compactor.retention_enabled 设置为 true,API 端点将可用。之后, 可以通过 deletion_mode 租户覆盖为每个租户启用对删除 API 的访问
  retention_enabled: true
  # 索引文件延迟过期时间, 2h表示延迟两个小时候过期(过期不一定立马删除, 异步删除)
  # 在索引上应用保留算法时,chunks 不会被删除。它们由 sweeper 进程异步删除, 并且可以通过设置 -compactor.retention-delete-delay 来配置此延迟
  retention_delete_delay: 2h
  # 实例化以删除 chunks 的 goroutine worker 的最大数量
  retention_delete_worker_count: 150
  # 应设置为配置删除请求的存储。启用保留时, 这是必需的
  delete_request_store: s3
# ========================
# 用于设置全局默认资源限制(例如存储、请求速率等
# - 这里的配置全局保留, 该保留应用于所有租户(除非通过配置每个租户的覆盖来覆盖)
# - 有两种设置保留策略的方法
#   - retention_period,全局应用于所有日志流。如果不指定, 那么默认就是774h(30天)的保留期限
#   - retention_stream,仅应用于与选择器匹配的日志流
# - 最短保留期限为 24 小时。
# - 启用保留策略时要非常小心。强烈建议您同时在对象存储中对对象启用版本控制, 以便您可以从意外配置错误的保留设置中恢复。如果您想启用删除但不强制执行保留策略, 请将 retention_period 设置配置为 0s 值。
# ========================
limits_config:
  # 日志保留期限为7天(7*24=168)
  retention_period: 168h
  # 只能在 retention_stream 定义的 selector 字段中使用标签匹配器。不支持任意 LogQL 表达式。
  retention_stream:
  - selector: '{namespace="dev"}'
    priority: 1
    period: 24h
  # 运行配置, 可以通过Docker的volumes将这个映射进来
  per_tenant_override_config: /etc/overrides.yaml

# ========================
# 日志条目删除
# - 索引存储配置 TSDB 或 BoltDB Shipper 时, 才支持日志条目删除
# - 通过在 compactor 的配置中将 retention_enabled 设置为 true,并在运行时配置中将 deletion_mode 设置为 filter-only 或 filter-and-delete,来启用日志条目删除。启用保留策略以处理删除请求时, 还需要配置 delete_request_store,这将确定存储删除请求的存储桶
# - 日志条目删除依赖于为 compactor 定义的自定义日志保留工作流的配置。Compactor 查看已过取消期限的未处理请求, 以决定是否删除 chunk
# - 启用保留策略时要非常小心。强烈建议您同时在对象存储中对对象启用版本控制, 以便您可以从意外配置错误的保留设置中恢复。如果您想启用删除但不强制执行保留策略, 请将 retention_period 设置配置为 0s 值。
# - 使用 filter-only,当查询 Loki 时, 与删除请求中的查询匹配的日志行将被过滤掉。它们不会从存储中删除。使用 filter-and-delete,当查询 Loki 时, 与删除请求中的查询匹配的日志行将被过滤掉, 并且它们也会从存储中删除
# - 只要 compactor.retention_enabled 设置为 true,API 端点将可用。之后, 可以通过 deletion_mode 租户覆盖为每个租户启用对删除 API 的访问
# - 参考官方文档https://grafana.org.cn/docs/loki/latest/operations/storage/logs-deletion/
# - https://grafana.org.cn/docs/loki/latest/operations/multi-tenancy/
# ========================
overrides.yaml

注意事项

  • Loki 默认以多租户模式运行。多租户模式在配置中使用 auth_enabled: true 设置
  • 如果没有开启租户的话, 那么其实这个配置的基本没有用
vim /data/apm/loki/overrides.yaml
overrides:
   # 针对租户29的配置
  "29":
    # 保留期限为7天
    retention_period: 168h
    retention_stream:
    # 对于namespace标签prod的六保留保留期限为14天, 优先级2
    - selector: '{namespace="prod"}'
      # 优先级2, 数字越大, 优先级越高
      priority: 2
      period: 336h
    # 对于container标签为loki的且namespace 标签不为prod的流的保留期限为3天
    - selector: '{container="loki"}'
      priority: 1
      period: 72h
  # 具有标签 nginx 和级别 debug 的流的保留期限为 24h
  # 对于此租户中的其余流, 全局保留期限为 744h,因为没有指定retention_period
  "30":
    retention_stream:
    - selector: '{container="nginx", level="debug"}'
      priority: 1
      period: 24h
  # 除租户 29 和 30 之外的所有租户
  # 具有 namespace 标签 dev 的流的保留期限为 24h 小时
  # 除具有 namespace 标签 dev 的流之外, 其余流的保留期限为 744h
创建Tempo相关配置文件
vim /data/apm/tempo/tempo-config.yaml
# http监听端口
server:
  http_listen_address: 0.0.0.0    # 监听所有网络接口
  http_listen_port: 3200
  # 调大grpc的max_frame_size
  grpc_server_max_recv_msg_size: 104857600  # 100MB
  grpc_server_max_send_msg_size: 104857600
 
# 支持otlp协议数据的本地接收器http和grpc端口,分别默认为4318和4317
distributor:
  receivers: # This configuration will listen on all ports and protocols that tempo is capable of.
      otlp:
        protocols:
          http: # default = 0.0.0.0:4318 http protocol
            endpoint: 0.0.0.0:4318
          grpc:    # default = 0.0.0.0:4317 http protocol
            endpoint: 0.0.0.0:4317
 
# 数据压缩存储有效期48小时
compactor:
  compaction:
    block_retention: 48h   # configure total trace retention here (Default is 14 days (336h))
 
# 可观测性数据的指标存储配置
metrics_generator:
  registry:
    external_labels:
      source: tempo
      cluster: linux-microservices
  storage:
    path: /tmp/tempo
    remote_write:
    - url: http://prometheus:9090/api/v1/write
      send_exemplars: true
 
# 支持多种存储方式,如:s3、local等
storage:
  trace:
    backend: s3     # 本地存储
    # S3对象存储
    s3:
      endpoint: minio:9000         # S3兼容存储地址
      bucket: tempo-data
      forcepathstyle: true # 强制路径访问模式
      insecure: true # 禁用HTTPS(生产环境不推荐)
      access_key: whiteBrocade         # 访问密钥(建议使用环境变量)
      secret_key: whiteBrocade  # 密钥(存在安全隐患)     


# 指标类型配置
overrides:
  metrics_generator_processors: [service-graphs, span-metrics]
创建Prometheus相关配置文件

普罗米修斯是一个后台存储和服务,用于从各种来源抓取(拉取)指标数据

vim /data/apm/prometheus/prometheus.yaml
global:
  scrape_interval:     15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: [ 'prometheus:9090' ]
  - job_name: 'tempo'
    static_configs:
      - targets: [ 'tempo:3200' ]
创建Mimir相关配置文件

Mimir 是 Prometheus 数据的长期保留存储

Grafana Mimir 是一个开源软件项目,为 Prometheus 和 OpenTelemetry 指标提供水平可扩展、高可用、多租户和长期存储

创建Grafana相关配置文件

Grafana 是一个可视化工具,允许从各种数据源创建仪表盘

grafana-datasource.yaml
vim /data/apm/grafana/grafana-datasource.yaml
apiVersion: 1

datasources:
  - name: Prometheus
    uid: prometheus
    type: prometheus
    access: proxy
    orgId: 1
    url: http://gateway:9090
    basicAuth: false
    isDefault: false
    editable: false
    version: 1

  # Loki数据源
  - name: Loki
    type: loki
    uid: loki
    access: proxy # 通过Grafana服务代理访问
    orgId: 1
    url: http://gateway:3100 # 通过NG网关访问
    basicAuth: false
    isDefault: true
    editable: false
    version: 1
    jsonData:
      derivedFields:
        - datasourceUid: tempo
          # 派生字段名
          name: traceID
          # 正则匹配
          matcherRegex: "^.*?traceI[d|D]=(\\w+).*$"
          url: '$${__value.raw}'
      httpHeaderName1: "X-Scope-OrgID"  # 多租户头部名称
      maxLines: 1000
    secureJsonData:
      httpHeaderValue1: "tenant1"  # 租户ID
   
  - name: Tempo
    uid: tempo
    type: tempo
    access: proxy
    orgId: 1
    url: http://tempo:3200
    basicAuth: false
    isDefault: false
    editable: false
    version: 1
    apiVersion: 1
    jsonData:
      # trase关联log
      tracesToLogsV2:
        datasourceUid: 'loki'
        # 8.3.x 有bug;  8.4.2 开始增加了在界面上配置
        # loki日志标签
        tags: ['service.name','http.method', 'namespace']
        # 标签映射
        mappedTags: [{ key: 'service.name', value: 'service' },{key: 'http.method',value: 'method'}]
        # 是否开启tag name映射
        mapTagNamesEnabled: true
        # 这两个配置项用于 扩展从Tempo跳转至Loki查询日志的时间范围。当你在Tempo中点击某个Span跳转查看关联日志时,Grafana会基于Span的开始和结束时间,自动向前后扩展时间窗口,确保日志查询覆盖潜在的相关记录
        # 存在一下情况需要拓展时间范围
        #   - 时间不同步: 分布式系统中,Span时间戳和日志时间戳可能因服务器时钟不同步而存在偏差(如Span记录UTC时间,而日志使用本地时间
        #   - 异步任务追踪:若Span触发异步操作(如消息队列消费),日志可能远晚于Span结束时间
        #   - 跨时区系统:组件分布在多个时区时,需覆盖更大时间跨度
        # 存在一下情况需要缩小时间范围提高查询性能
        #   - 精准时间同步环境:若已确保所有组件时钟同步(如NTP),可将值缩小至 5m 甚至 1m
        #   - 高频短时Span场景:若Span持续时间短(如HTTP请求平均<1秒),但日志量极大,缩小范围可提升查询性能
        spanStartTimeShift: '-5m'
        spanEndTimeShift: '5m'
        # 启用traceID过滤
        filterByTraceID: true
        # 不启用spanID过滤
        filterBySpanID: false
      # trace关联指标
      tracesToMetrics:
        datasourceUid: 'prometheus'
        spanStartTimeShift: '-5m'
        spanEndTimeShift: '5m'
        # 跨度数据标签名
        tags: [{ key: 'service.name', value: 'service' }, { key: 'job' }, {key: 'service.instance.id', value: 'instance'}]
        # 查询与之关联的
        queries:
          # 查询每分钟CPU使用率
          - name: 'CPU usage per minute'
            query: 'rate(jvm_cpu_time_seconds_total{$__tags}[1m]) * 60'
          # 查询每分钟内存使用
          - name: 'Memory usage per minute'
            query: 'sum by(id) (increase(jvm_memory_used_bytes{$__tags}[1m]))'
          # 查询每分钟线程数
          - name: 'thread count'
            query: 'increase(jvm_thread_count{$__tags}[1m]) '
          # 查询每分钟请求数
          - name: 'http request'
            query: 'sum by(http_route) (rate(http_server_request_duration_seconds_count{$__tags}[1m]) * 60)'
      # trace关联性能文件
      # tracesToProfiles:
      #   datasourceUid: 'grafana-pyroscope-datasource'
      #   tags: ['job', 'instance', 'pod', 'namespace']
      #   profileTypeId: 'process_cpu:cpu:nanoseconds:cpu:nanoseconds'
      #   customQuery: true
      #   query: 'method="$${__span.tags.method}"'
      # 
      # 服务依赖关系图(Service Map),可视化展示微服务之间的调用关系和拓扑结构
      serviceMap:
        datasourceUid: 'prometheus'
      # 控制是否在Tempo的追踪查询界面隐藏搜索栏。设置为 false 表示保留搜索功能,用户可以直接在Tempo中根据TraceID、服务名等条件搜索追踪数据
      search:
        hide: false
      # 启用节点图, 可视化服务依赖关系
      nodeGraph:
        enabled: true
      # 在Tempo追踪详情页中集成Loki日志查询,允许用户直接从Span跳转到关联的日志(基于时间范围或标签过滤)
      lokiSearch:
        datasourceUid: 'loki'
      # trace查询
      traceQuery:
        # 启用追踪查询时间范围动态调整
        timeShiftEnabled: true
        # 查询开始时间向前扩展1小时
        spanStartTimeShift: '-5m'
        # 查询开始时间向后扩展1小时
        spanEndTimeShift: '5m'
      spanBar:
        # 使用标签值作为Span条形图的分类依据
        type: 'Tag'
        # 指定Span标签键为 `http.path`
        tag: 'http.path'
      # 启用流式搜索(增量加载追踪数据)
      streamingEnabled:
        search: true
安装grafana插件

进入Grafana 所有插件, 分别搜索grafana-lokiexplore-appgrafana-exploretraces-app, 下载zip文件
在这里插入图片描述

上传到Linux中/data/apm/grafana/plugins, 解压两个文件
在这里插入图片描述

重启Grafana

docker-compose文件

apm
  1. loki使用的是读写分离模式部署, 拆分成了read, write, backend三个组件
  2. 存储使用的Minio, 生产中建议对存储日志的桶设置过期策略, 减少存储成本
  3. 使用ng作为网关统一入口, 有些端口生产中不应该开放, 比如说loki的read和write的read访问
version: "3.8"
# ========================
# 自定义网络配置
# ========================
networks:
  apm:  # 创建专用网络确保服务隔离
    driver: bridge

services:
  # ========================
  # Loki 读取组件(查询节点)
  # ========================
  read:
    image: grafana/loki:latest
    # 容器名
    container_name: loki-read
    # 指定read模式启动
    command: "-config.file=/etc/loki/config.yaml -target=read"  # 指定角色为读取节点
    ports:
      - 3100    # 映射外部访问端口
      - 7946    # memberlist 通信端口
      - 9095    # 指标暴露端口(未映射到宿主机)
    volumes:
      - /data/apm/loki/loki-config.yaml:/etc/loki/config.yaml  # 共享配置文件
    healthcheck:  # 健康检查策略
      test: [
        "CMD-SHELL", # 使用 shell 执行命令
        "wget --no-verbose --tries=1 --spider http://localhost:3100/ready || exit 1"
        ]
      interval: 10s # 每10秒检查一次
      timeout: 5s   # 超时时间5秒
      retries: 5    # 最多重试5次
    depends_on: # 依赖Minio
      - minio
    # 定义网络锚点, 后续直接复用
    networks: &apm-dns  # 网络别名锚点
      apm:
        aliases:
          - apm  # 其他服务可通过 apm 域名访问

  # ========================
  # Loki 写入组件(接收节点)
  # ========================
  write:
    image: grafana/loki:latest
    # 容器名
    container_name: loki-write
    # 指定write模式启动
    command: "-config.file=/etc/loki/config.yaml -target=write"
    ports:
      - 3100    # 与读节点区分端口
      - 7946    # memberlist 通信端口
      - 9095    # 指标暴露端口(未映射到宿主机)
    volumes:
      - /data/apm/loki/loki-config.yaml:/etc/loki/config.yaml
    healthcheck: # 健康检查策略
      test: [
        "CMD-SHELL", # 使用 shell 执行命令
        "wget --no-verbose --tries=1 --spider http://localhost:3100/ready || exit 1"
        ]
      interval: 10s # 每10秒检查一次
      timeout: 5s   # 超时时间5秒
      retries: 5    # 最多重试5次
    depends_on: # 依赖Minio
      - minio
    networks:
      <<: *apm-dns  # 复用网络别名配置

  # ========================
  # Loki 后台处理组件
  # ========================
  backend:
    image: grafana/loki:latest
    # 容器名
    container_name: loki-backend
    command: "-config.file=/etc/loki/config.yaml -target=backend -legacy-read-mode=false"
    ports:
      - 3100    # 默认API端口(未映射到宿主机)
      - 7946    # memberlist端口
    volumes:
      - /data/apm/loki/loki-config.yaml:/etc/loki/config.yaml
    depends_on: # 依赖Minio
      - minio
    networks:
      - apm

  # ========================
  # 日志采集组件(原Grafana Agent)
  # ========================
  alloy:
    image: grafana/alloy:latest
    # 容器名
    container_name: alloy
    command: run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy
    ports:
      - 12345   # Web UI端口
      - 4317:4317   # otlp grpc
      - 4318:4318   # otlp http
    volumes:
      # 将程序产生的*.log,*.gzd的父目录映射到alloy, 这样才能探测到
      - /opt/app/logs:/opt/app/logs
      - /data/apm/alloy/alloy-local-config.yaml:/etc/alloy/config.alloy:ro  # 采集配置
      - /var/run/docker.sock:/var/run/docker.sock  # 挂载docker socket, 如果不挂载这个, 那么没法获取到容器的日志
    networks:
      - apm

  tempo:
    image: grafana/tempo:latest
    container_name: tempo
    command: "-config.file=/etc/tempo/tempo-config.yaml"
    ports:
      - 3200:3200   # tempo
    volumes: 
      - /data/apm/tempo/tempo-config.yaml:/etc/tempo/tempo-config.yaml
      - /data/apm/tempo:/tmp/tempo
    depends_on:
      - prometheus
      - minio
    networks:
      - apm

  prometheus:
    image: prom/prometheus:v3.2.1
    container_name: prometheus
    command:
      - --config.file=/etc/prometheus/prometheus.yaml
      # 必须要添加这个参数, 否则Prometheus不开启remove write功能
      - --web.enable-remote-write-receiver
      # 接受otlp观测数据
      - --web.enable-otlp-receiver
      - --enable-feature=exemplar-storage
      - --enable-feature=native-histograms
    ports:
      - 9090
    volumes:
      - /data/apm/prometheus/prometheus.yaml:/etc/prometheus/prometheus.yaml
    networks:
      - apm


  # ========================
  # 对象存储服务(S3兼容)
  # ========================
  minio:
    image: minio/minio:latest
    # 容器名
    container_name: minio
    entrypoint:  # 初始化存储目录
      - sh          
      - -euc        # 执行脚本的参数:e(报错退出) u(未定义变量报错) c(执行后续命令)
      - |           # 多行脚本开始, minio创建目录挂载日志
        mkdir -p /data/loki-data && \
        mkdir -p /data/loki-ruler && \
        mkdir -p /data/tempo-data && \
        minio server /data --console-address :9001
    environment:
      - MINIO_ROOT_USER=whiteBrocade        # 用户名(与Loki配置对应)
      - MINIO_ROOT_PASSWORD=whiteBrocade    # 密码(需加密处理)
      - MINIO_PROMETHEUS_AUTH_TYPE=public   # 开放指标
    volumes:
      - /data/apm/minio:/data  # 持久化存储路径
    ports:
      - 9000    # API端口
      - 9001    # UI端口
    networks:
      - apm

  # ========================
  # 可视化平台
  # ========================
  grafana:
    image: grafana/grafana-enterprise:latest
    # 容器名
    container_name: grafana
    # 数据持久化
    environment:
      - GF_AUTH_ANONYMOUS_ENABLED=true  # 开启匿名访问(生产环境应关闭)
      # 设置 Grafana 的管理员(admin)账户的初始密码为admin 
      - GF_SECURITY_ADMIN_PASSWORD=admin
      # 设置 Grafana 的默认用户界面主题为暗黑模式
      - GF_USERS_DEFAULT_THEME=dark
      # 实验性功能开关
      # traceqlSearch: 启用 TraceQL 搜索功能。TraceQL 是一种用于搜索和分析追踪数据的查询语言,支持复杂的追踪数据查询和过滤(集成Tempo)
      # traceToMetrics: 启用将追踪数据转换为指标的功能。该功能允许用户将追踪数据(如调用链)转换为可监控的指标,便于进行更高级的分析和监控
      # traceQLStreaming: 启用 TraceQL 流式查询功能。流式查询允许实时追踪数据的分析,适用于实时监控和快速响应
      - GF_FEATURE_TOGGLES_ENABLE=traceqlEditor, traceToMetrics, traceQLStreaming
      # 插件, 这个通过网络下载的方式可能会因为墙的原因下载不下来, 进而导致grafana无法启动, 建议手动下载解压, 然后通过plugins目录进行挂载
      # grafana-lokiexplore-app:增强 Loki 日志查询功能
      # grafana-exploretraces-app:优化 Tempo 追踪数据探索
      # - GF_INSTALL_PLUGINS=grafana-lokiexplore-app, grafana-exploretraces-app
    volumes: 
      # 可视化面板目录
      - /data/apm/grafana/dashboards:/etc/grafana/provisioning/dashboards
      # 数据源目录
      - /data/apm/grafana/grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml
      # 持久化插件目录
      - /data/apm/grafana/plugins:/var/lib/grafana/plugins
    ports:
      - 3000    # Web访问端口
    depends_on: # 依赖网关服务
      - gateway
    networks:
      - apm

  # ========================
  # API网关(流量路由)
  # ========================
  gateway:
    image: nginx:latest
    # 容器名
    container_name: nginx
    volumes:
      - /data/apm/nginx/nginx.conf:/etc/nginx/nginx.conf
    ports:
      - 3000:3000	# Grafana UI
      - 3100:3100   # Loki统一入口端口
      - 3101:3101   # read 端点
      - 3102:3102   # write 端点
      - 9001:9001   # Minio UI
      - 9090:9090   # Prometheus UI
      - 12345:12345 # Alloy UI
    healthcheck: # 健康检查策略
      test: [
        "CMD", 
        "service", 
        "nginx", 
        "status"]
      interval: 10s
      timeout: 5s
      retries: 5
    depends_on:
      - read
      - write
      - alloy
      - tempo
    networks:
      - apm

apm相关访问路径

启动apm

image-20250325120442241

Grafana UI

  • 访问地址(这里换成你自己的IP): http://192.168.132.10:3000

  • 账号: admin

  • 密码: admin

image-20250306155441031

Minio UI

  • 访问地址(这里换成你自己的IP): http://192.168.132.10:9001
  • 账号: whiteBrocade
  • 密码: whiteBrocade

image-20250306155533136

Grafana Alloy UI

  • 访问地址(这里换成你自己的IP): http://192.168.132.10:12345

image-20250306155931033

Loki Read/Write组件

  • Read访问地址(这里换成你自己的IP): http://192.168.132.10:3101
  • Write访问地址(这里换成你自己的IP): http://192.168.132.10:3102

image-20250306160337569

Java集成OTEL

Spring项目搭建

pom依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.whiteBrocade</groupId>
    <artifactId>oltp</artifactId>
    <version>v1</version>
    <name>oltp</name>
    <description>oltp</description>
    <properties>
        <java.version>11</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.6.13</spring-boot.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.32</version>
        </dependency>

        <!-- 端点监控 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!-- prometheus依赖 -->
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-prometheus</artifactId>
            <version>1.11.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>com.whitebrocade.otel.OtelApplication</mainClass>
                    <skip>false</skip>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
application.yaml
# 本地服务访问
server:
  # 服务端口
  port: 8080

spring:
  application:
    # 应用服务名
    name: whiteBrocade_OTEL

# 日志
logging:
  level:
    root: INFO
    com.example: DEBUG  # 根据需要调整包名和日志级别
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss.SSS} %level traceID=%X{trace_id} %thread %class{36}:%L - %m%n%wEx"
    file: "%d{yyyy-MM-dd HH:mm:ss.SSS} %level traceID=%X{trace_id} %thread %class{36}:%L - %m%n%wEx"
  # 日志文件存放位置
  file:
    name: ./logs/app.log

# 是否启用springboot的debug调试模式,会打印详细日志信息
debug: false

# 启用服务健康检测,注册中心将通过http://host:port/actuator/health 检测服务的存活,默认10s一次
management:
  endpoints:
    web:
      cors:
        # 跨域配置开放所有,CORS默认处于禁用状态
        allowed-origins: "*"
        allowed-methods: "*"
      discovery:
        # 启用一个接口可以返回所有端点信息
        enabled: true
      exposure:
        include:
          # 开放所有端点health,info,metrics,通过actuator/+端点名就可以获取相应的信息。开发用*,上生产请取消。默认打开health和info
          - "*"
        # 某些端点除外,不做开启
        exclude:
          - env
          - beans
          - info
          - configprops
          - health
          - heapdump
          - shutdown
          - threaddump
          - loggers
          - conditions
  endpoint:
    shutdown:
      enabled: true
    health:
      # 开启后打印详细信息
      show-details: always
OtelController控制器
import cn.hutool.http.HttpRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;


/**
 * @author whiteBrocade
 * @version 1.0
 */
@Slf4j
@RestController
public class OtelController {

    @GetMapping("/hello")
    public void hello(@RequestParam String name) {
        log.info("hello方法调用: name={}", name);
        String url = "http://localhost:8080/hi";

        HttpRequest.get(url)
                .timeout(5000) // 超时 5 秒
                .execute();
    }

    @GetMapping("/hi")
    public void hi() {
        log.info("hi方法调用");
    }

}
OtelApplication启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class OtelApplication {

    public static void main(String[] args) {
        SpringApplication.run(OtelApplication.class, args);
    }

}

OpenTelemetry(OTEL)的JDK下载

OTEL是OTLP协议的一个实现

opentelemetry-java-instrumentation中点击最新版本下载

image-20250325125501373

将下载到的opentelemetry-javaagent.jar上传到主机的/opt/apm-agents/otel/下

image-20250325125743770

部署Java程序

打包并上传Java程序

image-20250325125852139

启动Java程序

关键参数含义

  • -javaagent:/opt/apm-agents/otel/opentelemetry-javaagent.jar: 添加OpenTelemetry代理插桩

  • -Dotel.exporter.otlp.protocol=grpc: 启用gRPC协议发送OTEL遥感数据

  • -Dotel.exporter.otlp.endpoint=http://localhost:4317: OTEL收集地址(这里是alloy进行收集)

  • -Dotel.logs.exporter=none: log日志不走otel协议推送,因为日志一般会输出到日志文件中,从文件中加载日志能记录整个应用程序的启动过程,而otel需要在应用服务启动中并且agent代理生效后,才能正常抓取otel协议日志数据,因此会有部份缺失;通过Alloy直接抓取本地日志文件内容,能保证日志数据的全生命周期流程完整性

java -javaagent:/opt/apm-agents/otel/opentelemetry-javaagent.jar \
-Dotel.service.name=otel_test \
-Dotel.exporter.otlp.protocol=grpc \
-Dotel.exporter.otlp.endpoint=http://localhost:4317 \
-Dotel.traces.exporter=otlp \
-Dotel.metrics.exporter=otlp \
-Dotel.logs.exporter=none \
-Dotel.metric.export.interval=30000 \
-Dotel.exporter.otlp.insecure=true \
-jar /opt/app/oltp-v1.jar;

命令要在/opt/app下执行, 否则会采集不到日志, 因为日志存储目录是通过./这种相对路径配置的

image-20250325132822399

效果

Grafanac查询初始化

访问http://192.168.132.10:3000的Grafana面板, 点击Expore

  • 选择Loki数据源
  • 设置时间颗粒度
  • 设置查询条件

image-20250325131837069

查询日志

发送hi请求

发送GET请求, http://192.168.132.10:8080/hi

image-20250325132908109

log日志查询

app.log日志查询

image-20250325133036823

loki查询日志

查询hi调用后的日志

alloy采集日志是有时间延迟, 需要等待一会Grfana中才能查询到

image-20250325133848077

查询链路

发送hello请求

image-20250325134307775

log日志查询

image-20250325134547178

loki查询日志

image-20250325134705541

Loki日志与Tempo链路

向springboot服务发起http请求后,后端服务会打印日志并输出到日志文件中,opentelemetry-javaagent.jar代理通过探针技术,抓取springboot运行过程中的数据、跨度、指标等,通过otel协议推送到指定Alloy采集端,进行处理并分发到Loki和Tempo中,以下是通过Loki查询出打印日志数据,通过输出日志行中的所带的TraceID(跟踪埋点ID),点击关联上Tempo查询该TraceId全链接执行过程,展示分析出的每一步耗时与跨度信息等

image-20250325135117010

查询指标

Tempo链路与Promethus指标

image-20250325221712522

查看CPU使用率

image-20250325221825926

参考资料

apm

博客

Grafana 系列文章(一):基于 Grafana 的全栈可观察性 Demo

Grafana Loki 简要指南:关于标签您需要了解的一切

开源日志监控:Grafana Loki 简要指南

日志之Loki详细讲解

使用读写分离模式扩展 Grafana Loki
Loki部署模式
grafana loki的理解与配置(2.9)
轻量级日志系统docker-compose搭建Loki+Grafana+Promtail,配置、部署, 查询全流程
轻量级日志系统-Loki

轻量级日志系统笔记apm

选择 Grafana Alloy组件

开源项目推荐:flog
推荐一个小工具:flog
探索Flog:伪装日志流量的神器
gitcode的flog项目

Docker 环境中配置 Grafana:详细教程与常见配置项解析

项目集成grafana,并用非root用户启动

在docker-compose启动grafana,出现权限错误的解决方案

Docker上的Grafana 7.3.0存在权限问题

grafana重启后模板没有数据了 grafana新建dashboard

Docker安装grafana数据持久化+配置SMTP

springboot+Loki+Loki4j+Grafana搭建轻量级日志系统

tempo/example/docker-compose at main · grafana/tempo

loki/examples/getting-started/docker-compose.yaml at main · grafana/loki

Auto-instrumenting a Java Spring Boot application for traces and logs using OpenTelemetry and Grafana Tempo | Grafana Labs

grafana/intro-to-apm:指标、日志、跟踪和配置文件会话配套代码简介。

【GO】LGTM_Grafana_Tempo(2) — 官方用例改后实操

遥测数据采集工具Grafana Alloy

Grafana,Loki,Tempo,Prometheus,Agent搭建日志链路监控平台

Loki 日志块使用 MinIO 对象存储

SpringBoot+Prometheus采集Metrics指标数据

Grafana+Loki+Promtail 搭建日志收集系统

Grafana Tempo | Grafana Tempo 文档

文档 | OpenTelemetry 中文文档

Grafana 9.1 的新增功能:跟踪到指标允许用户从跟踪跨度导航到选定的数据源 |Grafana 实验室

配置 Tempo 数据源

Provision Grafana

Grafana 所有插件

配置 Tempo 数据源
Grafana离线安装部署以及插件安装

Grafana Plugins
grafana/conf/defaults.ini at main · grafana/grafana

视频

【IT老齐636】Grafana Loki vs ELK

【IT老齐710】Grafana apm分布式日志收集架构

【IT老齐711】apm收集Docker所有实例运行日志

Grafana+Loki+Alloy快速构建企业日志系统

k8s + loki 日志解决方案 (持续更新中)

Loki日志系统-安装、使用、告警