版本

springboot  2.0.5.RELEASE

架构

springboot 
  logback

grafana alloy

grafana loki

grafana

实施

springboot

  • pom.xml
<!-- 可观测 开始 -->
<!-- 可观测 logs -->
<dependency>
    <groupId>net.logstash.logback</groupId>
    <artifactId>logstash-logback-encoder</artifactId>
    <version>5.2</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-core</artifactId>
    <version>1.2.3</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-access</artifactId>
    <version>1.2.3</version>
</dependency>
<!-- 可观测 结束 -->
  • logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <appender name="CONSOLE_JSON" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <fieldNames>
                <timestamp>timestamp</timestamp>
                <message>message</message>
                <logger>logger</logger>
                <thread>thread</thread>
                <level>level</level>
                <levelValue>[ignore]</levelValue>
            </fieldNames>
            <includeMdc>true</includeMdc>
            <throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter">
                <maxDepthPerThrowable>50</maxDepthPerThrowable>
                <rootCauseFirst>true</rootCauseFirst>
            </throwableConverter>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="CONSOLE_JSON" />
    </root>
    <logger name="org.springframework" level="WARN"/>
    <logger name="org.apache" level="WARN"/>
    <logger name="com.netflix" level="WARN"/>
    <logger name="reactor" level="WARN"/>
    <logger name="org.eclipse.jetty" level="WARN"/>
    <logger name="com.ibatis" level="DEBUG"/>
    <logger name="java.sql" level="DEBUG"/>
    <logger name="dao" level="DEBUG"/>
    <logger name="com.gh" level="DEBUG"/>
    <logger name="com.ghjl" level="DEBUG"/>
</configuration>

alloy

discovery.kubernetes "pods" {
  role = "pod"
  namespaces {
    names = ["n1", "n2", "n3"]
  }
}
discovery.relabel "pod_logs" {
  targets = discovery.kubernetes.pods.targets
  rule {
    source_labels = ["__meta_kubernetes_pod_label_item_name"]
    target_label  = "item_name"
  }
  rule {
    source_labels = ["__meta_kubernetes_pod_label_env_type"]
    target_label  = "env_type"
  }
  rule {
    source_labels = ["__meta_kubernetes_pod_label_app_name"]
    target_label  = "app_name"
  }
  rule {
    source_labels = ["__meta_kubernetes_pod_node_name"]
    target_label  = "node_name"
  }
  rule {
    source_labels = ["__meta_kubernetes_pod_name"]
    target_label  = "pod_name"
  }
  rule {
    source_labels = ["__meta_kubernetes_namespace"]
    target_label  = "pod_namespace"
  }
  rule {
    source_labels = ["__meta_kubernetes_pod_container_name"]
    target_label  = "container_name"
  }
}
loki.source.kubernetes "pod_logs" {
  targets    = discovery.relabel.pod_logs.output
  forward_to = [loki.process.pod_logs.receiver]
}
loki.process "pod_logs" {     
  stage.cri {}
  stage.json {
    expressions = {
      level      = "level",
      message    = "message",
      logger     = "logger",
      thread     = "thread",
      trace_id   = "trace_id",
      span_id    = "span_id",
      stackTrace = "stack_trace",
    }
  }
  stage.template {
    source   = "message"
    template = "{{ if .trace_id }}trace_id={{ .trace_id }} {{ end }}{{ .Value }}"
  }
  stage.labels {
    values = {
      level         = "",
      item_name     = "item_name",
      env_type      = "env_type",
      app_name      = "app_name",
      pod_name      = "pod_name",
      pod_namespace = "pod_namespace",
      node_name     = "node_name",
    }
  }
  forward_to = [loki.write.default.receiver]
}
loki.write "default" {
  endpoint {
    name = "default"
    url  = "http://loki-gateway/loki/api/v1/push"
    headers = {
      "X-Scope-OrgID" = "tenant1",
    }
  }
  external_labels = {
    cluster = "kubernetes",
  }
}
otelcol.receiver.otlp "default" {
  grpc {
    endpoint = "0.0.0.0:4317"
  }
  http {
    endpoint = "0.0.0.0:4318"
  }
  output {
    traces = [otelcol.processor.batch.default.input]
  }
}
otelcol.processor.batch "default" {
  timeout         = "1s"
  send_batch_size     = 8192
  send_batch_max_size = 8192
  output {
    traces = [otelcol.exporter.otlp.tempo.input]
  }
}
otelcol.exporter.otlp "tempo" {
  client {
    endpoint = "http://tempo-distributed-distributor.monitoring.svc.cluster.local:4317"
    tls {
      insecure = true
    }
  }
  timeout = "10s"
}

loki

# loki-values-retention.yaml
# Loki 完整 Helm values —— 基于现有部署追加保留策略与 Compactor 自动清理
# 用法: helm upgrade loki -n monitoring grafana/loki --repo https://grafana.github.io/helm-charts -f ./loki-values-retention.yaml
# 2026-07-15

# ============================================================
# 部署模式与镜像
# ============================================================
deploymentMode: Distributed
global:
  imageRegistry: harbor.xxx.com/public

# ============================================================
# 后端组件(Distributed 模式下使用 microservices,backend 不启用)
# ============================================================
backend:
  replicas: 0
read:
  replicas: 0
write:
  replicas: 0
singleBinary:
  replicas: 0

# ============================================================
# Bloom 组件(当前未启用)
# ============================================================
bloomBuilder:
  replicas: 0
bloomGateway:
  replicas: 0
bloomPlanner:
  replicas: 0

# ============================================================
# 微服务副本数
# ============================================================
distributor:
  replicas: 3
  maxUnavailable: 2

ingester:
  replicas: 3
  zoneAwareReplication:
    enabled: false

querier:
  replicas: 3
  maxUnavailable: 2

queryFrontend:
  replicas: 2
  maxUnavailable: 1

queryScheduler:
  replicas: 2

indexGateway:
  replicas: 2
  maxUnavailable: 1

compactor:
  replicas: 1

# ============================================================
# Chunk 缓存 (memcached)
# ============================================================
chunksCache:
  resources:
    requests:
      cpu: 500m
      memory: 1Gi
    limits:
      memory: 2Gi

# ============================================================
# 查询结果缓存 (memcached)
# ============================================================
resultsCache:
  resources:
    requests:
      cpu: 500m
      memory: 1229Mi
    limits:
      memory: 1229Mi

# ============================================================
# S3 存储密钥(CEPH RGW secret)
# ============================================================
defaults:
  extraEnvFrom:
  - secretRef:
      name: loki-bucket-claim

# ============================================================
# Loki 核心配置
# ============================================================
loki:
  # -------- Schema --------
  schemaConfig:
    configs:
    - from: "2024-04-01"
      index:
        period: 24h
        prefix: loki_index_
      object_store: s3
      schema: v13
      store: tsdb

  # -------- S3 存储后端 (Ceph RGW) --------
  storage:
    type: s3
    bucketNames:
      admin: loki-data
      chunks: loki-data
      ruler: loki-data
    s3:
      endpoint: http://rook-ceph-rgw-ceph-objectstore.rook-ceph.svc.cluster.local:80
      region: us-east-1
      s3ForcePathStyle: true

  # -------- 日志保留策略 --------
  limits_config:
    # 查询限制
    max_cache_freshness_per_query: 10m
    query_timeout: 300s
    split_queries_by_interval: 15m
    volume_enabled: true

    # 写入保护:拒绝时间戳超过 7 天的旧日志
    reject_old_samples: true
    reject_old_samples_max_age: 168h

    # ======== 核心:日志保留 30 天 ========
    retention_period: 720h

    # 可选:按标签定义差异化保留策略
    retention_stream:
    #   # 示例:特定命名空间的日志保留 7 天
    #   - selector: '{namespace="test"}'
    #     priority: 1
    #     period: 168h
    #   # 示例:重要应用的日志保留 90 天
    #   - selector: '{app_name="payment-service"}'
    #     priority: 2
    #     period: 2160h

    # 每个租户最大活跃流数,防止标签爆炸
    max_global_streams_per_user: 100000

  # -------- Ingester 内存控制 --------
  ingester:
    # Chunk 空闲 30 分钟后刷写到存储
    chunk_idle_period: 30m
    # Chunk 存活超过 2 小时强制刷写
    max_chunk_age: 2h
    # 刷写后在内存中保留 30 分钟供查询
    chunk_retain_period: 30m
    # WAL 在关机时刷写
    wal:
      flush_on_shutdown: true

  # -------- Compactor 自动清理 --------
  compactor:
    # 启用保留期自动删除
    retention_enabled: true
    # 标记删除后延迟 2 小时再物理删除(避免误删)
    retention_delete_delay: 2h
    # 并行删除 worker 数
    retention_delete_worker_count: 2
    # 删除请求持久化到 S3,重启不丢失
    delete_request_store: s3

QA

注意springboot 2.0.5.RELEASE的依赖版本