当前位置: 首页 > news >正文

【使用 Loki + Promtail + Grafana 搭建轻量级容器日志分析平台】

使用 Loki + Promtail + Grafana 搭建轻量级容器日志分析平台

摘要

本文介绍如何通过 Docker Compose 快速搭建 Loki 日志存储、Promtail 日志采集和 Grafana 日志可视化/告警的完整流程。用最小化示例演示核心配置、常见问题排查和告警规则设置,帮助读者快速上手。

1、部署步骤

当前环境:

  • host4:192.168.0.224 (已部署prometheus+grafana) 上期搭建过程
  • host1:192.168.0.221(Web主机, 已部署tomcat, mysql)

1.1、部署server端

1.1.1. 创建Loki专用docker-compose文件(host4)

#创建网络
docker network create monitor-netmkdir -p /opt/monitor/loki && cd /opt/monitor/loki
nano docker-compose.yml
services:# 新增Loki日志服务loki:image: grafana/loki:3.4.1container_name: lokirestart: unless-stoppednetworks:- monitor-netports:- "3100:3100"    # HTTP 接口(读取/写入日志)- "9096:9096"   # GRPC 端口(可选对外暴露给开发者调试)command:- "--config.file=/etc/loki/local-config.yml"- "--config.expand-env=true"- "--target=all"   #让 Loki 在单体(monolithic)模式下启动,所有组件都在同一个进程里
#      - "--pattern-ingester.enabled=false"  # 直接禁用volumes:- /etc/localtime:/etc/localtime:ro- /etc/timezone:/etc/timezone:ro- loki_data:/loki- ./loki-config.yml:/etc/loki/local-config.yml:ro
#      - /data:/data  # 挂载整个数据目录environment:- TZ=Asia/Shanghaihealthcheck:test: ["CMD-SHELL", "wget -qO- http://localhost:3100/ready > /dev/null || exit 1"]interval: 15stimeout: 10sretries: 30start_period: 90s  # 给 Loki 更多启动时间deploy:resources:limits:cpus: '1'   # 中等负载日志量memory: 2G   # 保留缓冲区空间# 新增Promtail(采集host4自身日志)promtail:image: grafana/promtail:3.4.1user: "0:996"container_name: promtailrestart: unless-stoppednetworks:- monitor-netvolumes:- /etc/localtime:/etc/localtime:ro- /etc/timezone:/etc/timezone:ro- /var/lib/docker/containers:/var/lib/docker/containers:ro- /var/run/docker.sock:/var/run/docker.sock:ro- ./promtail-config.yml:/etc/promtail/config.ymlcommand: - "-config.file=/etc/promtail/config.yml"environment:- TZ=Asia/Shanghai- LOKI_URL=http://loki:3100depends_on:loki:condition: service_healthydeploy:resources:limits:cpus: '0.3'memory: 300Mvolumes:loki_data:
networks:monitor-net:external: true

小结:将宿主机时区挂载到容器,避免时间偏移;Loki 部署在 host4,Promtail 同时采集本机日志。

1.1.2. 配置文件详解

1.Loki配置 (loki-config.yml):
auth_enabled: falseserver:http_listen_port: 3100grpc_listen_port: 9096log_level: debuggrpc_server_max_concurrent_streams: 1000common:instance_addr: 127.0.0.1path_prefix: /tmp/lokistorage:filesystem:chunks_directory: /tmp/loki/chunksrules_directory: /tmp/loki/rulesreplication_factor: 1ring:kvstore:store: inmemoryquery_range:results_cache:cache:embedded_cache:enabled: truemax_size_mb: 100limits_config:reject_old_samples: false  # 允许旧时间戳日志reject_old_samples_max_age: 168h  # 接受7天内的旧日志metric_aggregation_enabled: trueschema_config:configs:- from: 2020-10-24store: tsdbobject_store: filesystemschema: v13index:prefix: index_period: 24hpattern_ingester:enabled: true  # 禁用此组件(单体模式不需要)metric_aggregation:loki_address: localhost:3100ruler:alertmanager_url: http://localhost:9093frontend:encoding: protobuf

生产建议

  • filesystem改为s3gcs
  • 添加retention_period: 720h # 日志保留30天
3. Grafana数据源配置 (grafana-datasources.yml):

nano grafana-datasources.yml

apiVersion: 1
datasources:
- name: Lokitype: lokiaccess: proxyurl: http://loki:3100isDefault: falseversion: 1jsonData:maxLines: 2000derivedFields:        # 日志关联追踪(关键配置!)- datasourceUid: 'prometheus'  # 关联PrometheusmatcherRegex: 'traceID=(\w+)'  # 从日志提取TraceIDname: 'TraceID'url: '$${__value.raw}'        # 跳转到Trace查看

作用:实现日志与分布式追踪(Jaeger/Tempo)的联动

4. Promtail配置(host4自身日志)

nano promtail-config.yml

server:http_listen_port: 9080grpc_listen_port: 0 # 禁用gRPCpositions:filename: /tmp/positions.yamlclients:- url: http://loki:3100/loki/api/v1/pushscrape_configs:- job_name: host1_dockerlog    # Docker服务发现docker_sd_configs:- host: unix:///var/run/docker.sockrefresh_interval: 15srelabel_configs:      # 标签重写规则- source_labels: ['__meta_docker_container_name']regex: '/(.*)'    # 提取容器名(去除前缀/)target_label: 'container'action: replace- source_labels: ['__meta_docker_container_log_stream']target_label: 'logstream'pipeline_stages:- docker: {}- job_name: host4_syslogstatic_configs:- targets: [localhost]labels:job: sysloghost: host4__path__: /var/log/*log

标签意义
host: 服务器标识
container: 容器名称
job: 日志类型(syslog/docker)

1.1.3.启动容器 Loki 与 Promtail

cd /opt/monitor/lokidocker compose up -d

验证

  • docker exec loki curl -s http://localhost:3100/ready 应返回 ready
  • docker logs promtail | grep -i error 确认无明显报错。

1.1.4. 监控Loki自身

在Prometheus配置中prometheus.yml添加:

scrape_configs:- job_name: 'loki'static_configs:- targets: ['loki:3100']  # 容器内地址- job_name: 'promtail'static_configs:- targets: ['promtail:9080']  # 容器内地址

1.2、多主机采集配置

1.2.1. 在host1创建Promtail配置

cat > promtail-config-host1.yml <<EOF

server:http_listen_port: 9080positions:filename: /tmp/positions.yamlclients:- url: http://192.168.0.224:3100/loki/api/v1/push  # 使用IP地址确保可达scrape_configs:
- job_name: host1_dockerlogdocker_sd_configs:- host: unix:///var/run/docker.sockrefresh_interval: 15srelabel_configs:- source_labels: ['__meta_docker_container_name']regex: '/(.*)'target_label: 'container'replacement: '$1'action: replace- source_labels: ['__meta_docker_container_log_stream']target_label: 'logstream'pipeline_stages:- docker: {}- job_name: host1_syslogstatic_configs:- targets: [localhost]labels:job: sysloghost: host1__path__: /var/log/**/*.log  # 递归匹配所有日志- job_name: tomcat-host1static_configs:- targets: [localhost]labels:job: tomcathost: host1app: webapp__path__: /tomcat_logs/*.log  # 使用挂载路径pipeline_stages:- regex:expression: '^(?P<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3}) (?P<level>\w+)'- labels:level:
  • clients.url 填写 Loki-Service 的 IP
  • __path__ 确保只采集 .log 文件,避免提取到 .gz 产生“timestamp too old”报错

1.2.2. 启动host1的Promtail

nano docker-compose.yaml

services:promtail_host1:image: grafana/promtail:3.4.1container_name: promtail_host1network_mode: hostrestart: unless-stoppedvolumes:- /etc/localtime:/etc/localtime:ro- /etc/timezone:/etc/timezone:ro- /var/lib/docker/containers:/var/lib/docker/containers:ro- /var/run/docker.sock:/var/run/docker.sock:ro- /var/log:/var/log:ro                     # 系统日志目录- /var/lib/docker/volumes/app_tomcat_logs/_data:/tomcat_logs:ro   # Tomcat 日志目录- ./promtail-config-host1.yml:/etc/promtail/config.ymlcommand:- "-config.file=/etc/promtail/config.yml"- "-config.expand-env=true"environment:TZ: "Asia/Shanghai"

docker compose up -d

或者直接启动

docker run -d --name promtail_host1 \--network=host \-v /etc/localtime:/etc/localtime:ro \-v /etc/timezone:/etc/timezone:ro \-v /var/lib/docker/containers:/var/lib/docker/containers:ro \-v /var/run/docker.sock:/var/run/docker.sock:ro \-v /var/log:/var/log:ro \-v /var/lib/docker/volumes/app_tomcat_logs/_data:/tomcat_logs:ro \-v ./promtail-config-host1.yml:/etc/promtail/config.yml \grafana/promtail:3.4.1 \-config.file=/etc/promtail/config.yml

注意:记得挂载 /etc/localtime,确保容器时间与宿主机一致。

检查Promtail状态

docker logs -f promtail_host1 
docker logs promtail_host1 --tail 50 | grep -i "error\|warn"

1.3 验证

需安装jq
apt update && apt install -y jq

1.3.1. 检查标签集合是否完整
#检查标签集合
curl -sG "http://localhost:3100/loki/api/v1/label/host/values" | jq
# 应该输出"status": "success","data": ["host1","host4"]
# 查询所有标签键(在host4执行)
curl -sG "http://localhost:3100/loki/api/v1/labels" | jq
#应输出"status": "success","data": ["app","container","filename","host","job","logstream","service_name"]
1.3.2. 验证主机日志采集
# 检查host1的日志是否采集成功(在host4执行)
curl -sG "http://localhost:3100/loki/api/v1/query_range" \--data-urlencode 'query={host="host1"}' \--data-urlencode 'limit=5' | jq '.data.result[].values'
1.3.3. 检查容器日志
# 检查Tomcat日志(在host4执行)
curl -sG "http://localhost:3100/loki/api/v1/query_range" \--data-urlencode 'query={container="ry-tomcat"}' \--data-urlencode 'limit=5' | jq# 检查MySQL错误日志
curl -sG "http://localhost:3100/loki/api/v1/query_range" \--data-urlencode 'query={container="ry-mysql", logstream="stderr"}' \--data-urlencode 'limit=5' | jq
1.3.4.其他调试

1.网络连通性测试

# 在host1上测试连接Loki
docker exec promtail_host1 curl -v http://192.168.0.224:3100/ready

2.promethus Target 状态

https://192.168.0.224/prometheus/targets

在这里插入图片描述

3.grafana 数据源状态

https://192.168.0.224/grafana/connections/datasources/

在这里插入图片描述

2、配置Grafana日志分析

2.1、Grafana配置

2.1.1:创建基础日志查询
  1. 访问Grafana:http://192.168.0.224:3000
  2. 左侧菜单 ➔ Explore ➔ 选择Loki数据源
  3. 输入查询:{host="host1"}
  4. 点击"Run query"查看所有host1的日志

在这里插入图片描述

2.1.2:创建专用仪表板
  1. 左侧菜单 ➔ Dashboards ➔ New ➔ Import
  2. 导入预置的Loki仪表板(ID:13639
    • 这将自动创建完整的日志监控仪表板-设置-保存

在这里插入图片描述

2.1.2:配置错误日志实时监控面板
  1. 在查询编辑区:
    • 数据源选择:Loki
    • 输入查询语句:{host="host1"} |~ "(?i)error|exception|fail"
    • 点击 Run query
  2. 右侧面板配置:
    • Visualization 选择:Logs
    • Options 选项卡:
      • Show log details:ON
      • Deduplication:Exact match
    • Panel options
      • Title:输入 “Host1 错误日志”
      • Description:添加描述(可选)
  3. 点击右上角 Apply 保存面板

在这里插入图片描述

在这里插入图片描述

2.1.3:添加 Tomcat 应用日志分析面板
  1. 在仪表板页面,点击顶部 Add panel 图标(+号)

  2. 选择 Add new panel

  3. 配置查询:

    {app="webapp"} 
    | logfmt 
    | detected_level != "info"
    

    怎么确定哪些字段(labels)可用?

    • curl -sG "http://localhost:3100/loki/api/v1/query_range"   --data-urlencode 'query={host="host1"}'   --data-urlencode 'limit=1' | jq '.data.result[]'
      {"stream": {"detected_level": "unknown","filename": "/var/log/auth.log","host": "host1","job": "syslog","service_name": "syslog"},
    • 可以随便点击一条日志可以看到Fields

    在这里插入图片描述

  4. 右侧配置Extract fields(可选):

    • Visualization:Logs
    • Transform 选项卡:
      • 搜索 ➔ Extract fields
      • 选择 logfmt 解析器

    在这里插入图片描述

    配置项说明
    Source选择你日志的源字段:– 如果日志行在“Line”字段里,就选 Line。– 如果日志在某个自定义 label,下拉里也能看到 labels.<xxx>,但一般默认 Line
    Format如果你日志是 JSON,就选 JSON;如果你的日志行是以 key=value 形式(logfmt)写的,就选 Key+value pairs;如果不确定,可以先试 Auto,Grafana 会自动探测。
    + Add path点击后会出现一个新的行,用来指定要抽取的字段名。
    Replace all fields通常保持关闭(Off)。关闭时,它会保留原来的所有列,只把新抽取出来的字段加进来。开了之后,面板里只会剩下抽取后的字段。
    • 只有当你希望拆出“labels 里没有、却只在原文文本里出现的那段”时,才考虑用 Extract fields 从“Line”里做复杂的解析。但绝大部分场景下,生产端/Promtail 端把好用的 KV 先丢到 labels 就已经足够,Grafana 只要把这些 labels 拆一拆就能用了

设置面板标题:“Tomcat 非 Info 日志” --> Apply

在这里插入图片描述

2.1.4:创建日志源分布饼图
  1. 再次 Add panel

  2. 输入查询:

    sum by (job) (count_over_time({host="host1"}[5m]))
    
  3. 可视化配置:

    • 右上角Visualization搜索:Pie chart
    • Pie chart type:Pie
  4. 设置标题:“Host1 日志源分布”

在这里插入图片描述

2.1.5:使用 “Explore” 功能
  1. 左侧菜单 ➔ Explore(指南针图标)

  2. 选择 Loki 数据源

  3. 在查询框中输入:

    {container="ry-tomcat"} | logfmt | level = "error"
    

    或者手动选择Builder

    在这里插入图片描述

  4. 点击 Run query 查看结果

  5. 点击右上角 Add to dashboard 可将此查询保存为面板

在这里插入图片描述

  • 点击右上角 “Add to dashboard” → 选择 “Existing dashboard” → 指定 “Host1 日志监控” → 点击 “Add”随后会弹出一个面板编辑的对话框,你可以给它取个名称(例如 “Tomcat 错误等级日志”),调好格式(Logs、Color by level、Deduplication 等),然后点击 “Apply” 就会把它放到仪表板里。一旦完成,进入仪表板就能看到一个新的“Tomcat 错误等级日志”面板。

最后别忘了按仪表板右上角的“Save”图标,把整个仪表板保存一次

  • 输入名字(例如 “Host1 日志监控”),选择合适的文件夹,然后点击 Save
  • 这样即使下次重启 Grafana,或者别人在别的浏览器打开,你都能看到刚才做的所有面板。

2.2、 为仪表板添加模板变量(Variables),让它更灵活

目前很多查询里,你都写死了 {host="host1"}{app="webapp"}{job="tomcat"}。如果后续想让同一个仪表板用于监控不同主机/不同应用/不同环境,建议改用变量(Dashboard → Settings → Variables)来动态控制。简单示例:

  1. 创建 host 变量

    • Dashboard 右上角点击 Settings(齿轮)VariablesNew

    • Name: host

    • Type: Query

    • Data source: 选择你的 Loki

    • Query:

      • Query type : Label values
      • Label : host

      这会列出当前时段(Global 时间范围)内所有打上过 host 标签的值,比如 host1host2 等。

    • Preview of values:确认下拉里能看到 host1

    • 保存。

    在这里插入图片描述

  2. 创建 job 变量

    • 同样再添加一个变量:

      • Name: job

      • Type: Query

      • Data source: Loki

      • Query:

        • Query type : Label values
        • Label : job

        这一步会列出在选定 host(比如 host1)范围内可见的所有 job 标签值(如 tomcatsyslog)。

    • 这样当你从下拉里先选 host1,下面 job 下拉就会自动只展示 host1 上跑过的 job。

  3. 修改面板 Query,让它使用变量

    • 例如,之前写死的“Tomcat 错误日志”面板:

      {host="host1", job="tomcat"} |~ "(?i)error|exception|fail"
      

      改成:

      {host="$host", job="$job"} |~ "(?i)error|exception|fail"
      
    • “Host1 日志源分布”饼图原来是:

      sum by (job) (count_over_time({host="host1"}[5m]))
      

      改成:

      sum by (job) (count_over_time({host="$host"}[5m]))
      
    • 这样在仪表板左上角会自动多出两个下拉菜单 [ host ] [ job ],你切换主机或切换 job,所有面板都会联动刷新。

  4. 再把“Tomcat 应用日志分析”用同样思路改写为:

    {host="$host", app="$job"} | logfmt | detected_level!="info"
    
    • 或者把 appjob 再区分开。
    • 关键点是:所有原来写死的标签都改用 ${变量名},多一个灵活性。

好处

  • 同一个仪表板可以横向监控多台主机、多套应用,只要有人在下拉里选,这些面板就自动帮你刷新。
  • 也方便后续别人拿这套 Dashboard 只改少量变量就能直接套用,不用再手动跑那些长查询。

2.3、在 Promtail 端或 Loki pipeline 里提取 msg

2.3.1.在 Loki 查询中用 regexp 把“消息体”抽到 msg Label

假设你的 Tomcat 日志长这样(没有现成的 msg= 字段,只有纯文本):

21-May-2025 20:30:32.098 INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive [/opt/tomcat/webapps/ROOT.war]
21-May-2025 20:30:32.059 ERROR [main] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [ROOT] appears to have started a thread named [quartzScheduler_Worker-7] which has failed to stop it. This is very likely to create memory leak. Stack trace of [quartzScheduler_Worker-7] [Thread-5]

我们想把 Deploying web application archive …The web application [ROOT] appears to have started… 这一整段当作 msg。常见的做法是:

  1. 先用 | logfmt 处理一遍,保证所有的 app="webapp"detected_level="error" 之类的 KV 都被拆进了 labels
  2. 再接一个 | regexp 管道,把 ] (也就是最后一个方括号加一个空格)之后的所有字符,用一个 named capture 抓到 msg label。

例如,你当前的查错误日志语句(近 30 分钟)可能是:

{host="host1"} | logfmt | detected_level="error"

要让它“把 msg 也抽出来”,可以改成:

swift复制编辑{host="host1"} 
| logfmt 
| detected_level="error" 
| regexp "\\] (?P<msg>.*)"

解释一下这行:

  • {host="host1"}:筛选出 Host1 上的所有日志;
  • | logfmt:把已经打到 detected_levelapp 等 KV 拿出来,并存到 labels
  • | detected_level="error":只保留 detected_levelerror 的那一条;
  • | regexp "\\] (?P<msg>.*)"
    • 这里的正则 \\] 匹配到日志中最后一个 ](也就是 [main]、包名这种结束的那个),以及紧跟着的一个空格;
    • (?P<msg>.*) 则把这个 ] 后面所有剩余的字符全部“抓”到一个名叫 msg 的 label 里。

做完这一步之后,Loki 会把每条符合条件的日志多加一个标签 msg="<日志正文>"。你可以在 Grafana → Explore 里把这条管道跑一遍,展开任意一条错误日志,检查 Fields,就会看到多了一个 msg 字段,并且它的值就是那条日志最后面的文字内容。


2.3.2.直接用正则从 syslog 行里提取 msg

假设你的 syslog 行像这样:

May 21 20:30:32 host1 zabbix_agentd[12345]: UserParameter=/foo/bar Check OK
May 21 20:31:00 host1 zabbix_agentd[12345]: ERROR: something went wrong

最简单的做法是在 Loki 查询里,用:

{host="host1", job="syslog"} 
| regexp ": (?P<msg>.*)"

意思是:

  • : 匹配到冒号和一个空格(因为进程后面总是 “进程名[PID]: ”),
  • (?P<msg>.*) 则把余下全部当作 msg 这个标签。

完整示例:

{host="host1", job="syslog"} 
| regexp ": (?P<msg>.*)"
|~ "(?i)error"

解释:

  1. {host="host1", job="syslog"}:先把所有 Host1 上的 syslog 日志挑出来;
  2. | regexp ": (?P<msg>.*)":把每行中 “冒号 空格 后面剩余的那段” 都存到 labels.msg
  3. |~ "(?i)error":按正则再只保留包含(不区分大小写)“error” 的行。

在 Explore 里测试:

  • 切到 Explore → 选 Loki 数据源 → Logs 模式,把上面三行拷进去,点 Run query

  • 下方返回的日志行,点击任一行最右侧的“>”展开它,你会看到

    Fields
    ─────────────────────────
    host         host1
    job          syslog
    msg          ERROR: something went wrong
    

    如果有多种情况,第一行的 msg 也会是 “UserParameter=/foo/bar Check OK” 这种。只要能看到 msg,说明我们的正则生效了。


2.3.3、在 Promtail/Loki Pipeline 里预先提取 msg

有时候我们会把“提取消息体”这件事提前放在 Promtail 端做,或者在 Loki 的 pipeline_stages(如果你在 Loki 单独搭了一个 ingester pipeline)里就已帮你把 msg 做成了 label。此时,Grafana 就可以跳过“使用 regexp 抽取 msg” 这一步,直接用 TopK 去统计现成的 msg

举例:如果你在 Promtail 的配置里,已经用过 json stage 或者 regex stage,把像下面这样的配:

scrape_configs:
- job_name: tomcat-host1static_configs:- targets: [ localhost ]labels:job: tomcathost: host1app: webapp__path__: /tomcat_logs/*.logpipeline_stages:- regex:expression: '\\] (?P<msg>.*)' # 比如,提取日志中“] ”后面的全部内容到 msg
#        expression: '^(?P<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3}) (?P<level>\w+)'- labels:msg:
#        level:
#测试
echo "03-Jun-2025 11:00:00.000 INFO [main] com.example.Test Something happened MSG_TAG" >>  /var/lib/docker/volumes/app_tomcat_logs/_data/test4.log

在这里插入图片描述

那么进到 Loki 里的每条日志就自动带有 msg="<正文>" 这个标签。此时,你在 Grafana 里只要写:

topk(10, sum by (msg) (count_over_time({host="$host", job="tomcat", detected_level="error"}[30m])
))

就能直接拿到 Top 10 的 msg 次数,无需再在 Query 里加 | regexp。然后同样把它可视化为 Table,Field Name${msg},就能列出“过去 30 分钟最热的 10 条错误消息”。

⚠️ 注意事项

避免 label 爆炸
level 做成 label 没问题,因为值有限(INFO、ERROR、WARN 等),msg 做 label 不建议用于大规模生产环境,因为它的值太多样,容易导致 label cardinality 爆炸。建议只在调试期间用 msg 做 label。

2.4、增加更细化的统计面板

除了“错误日志”、“非 Info 日志”、“日志源分布”,可以再添加一些下列常见的、对运维和分析很有帮助的面板:

2.4.1. 日志量趋势折线图

  • Query:

    sum by (job) (rate({host="$host"}[1m]))
    

    或者

    sum by (job) (count_over_time({host="$host"}[1m]))
    

    这个指标能实时显示各 job 在“过去 1 分钟”里的日志条数变化趋势,比饼图侧重“时间序列”更直观。

  • Visualization:选择 Time series(或 Graph),设置 legend 为 ${job}

  • 这样可以观察日志峰值、流量突增、平稳期等。

2.4.2.各级别日志分布柱状图(Bar chart)

  • Query:

    sum by (detected_level) (count_over_time({host="$host", job="$job"}[5m]))
    
    • 这会告诉你在所选主机+应用下,“过去 5 分钟”里各个 ERROR/WARN/INFO/DEBUG 级别日志的数量。
  • Visualization:选择 Bar chart → X 轴填 detected_level,Y 轴是 数量。

  • 既能看到“错误日志剧增”,也能做服务健康度的大致评估。

  1. Top N 错误消息(Table)

    • 如果你已经在 Loki pipeline 或 在 Grafana Transform 里把“消息”字段(msg)拆出来,那就可以做一个 Table 面板,列出“过去 30 分钟最常出现的 10 条错误文本”:

      topk(10, sum by (msg) (count_over_time({host="$host", detected_level="error"}[30m])))
      
    • Visualization:Table

    • 这样你一眼就能看到,当前系统里“最热”的那几条错误堆栈或关键字。

  2. 异常数量告警阈值面板(Stat/Gauge)

    • Query:

      sum by (job) (count_over_time({host="$host", detected_level="error"}[1m]))
      

      或者

      sum(count_over_time({host="$host", detected_level="error"}[5m]))
      
    • Visualization:Stat(或者 Gauge

    • 配置一个阈值:比如当“过去 1 分钟错数量 > 5”时变红、提示报警。

    • 也可以把它做成 Alert 规则,当数值超过阈值时触发通知。

  3. 日志大小/行数占比面板(Bar gauge)

    • Query:

      sum by (job, detected_level) (count_over_time({host="$host"}[5m]))
      
    • 然后把 Visualization 选 Time series 并打开 “Stacking” → “Normal” 模式,这样能看到在一段时间里,各 job、各级别的日志是如何堆叠,哪个 job 占的百分比多、哪个级别最热。


3、为关键面板添加告警(Alerting)

3.1、配置 Grafana SMTP(编辑 grafana.ini)

Grafana 要能发邮件,必须在其配置文件 grafana.ini 中启用并填写 smtp 段。下面以“用 Docker Compose 方式部署 Grafana”为例,演示如何挂载、修改和重启。

3.1.1. 准备一个自定义的 grafana.ini

  1. 编辑配置文件

    mkdir -p grafana-provisioning/confignano grafana-provisioning/config/grafana.ininano grafana.ini
    
  2. 在该目录下新建 grafana.ini,将默认配置复制过来并加以修改。最简单的做法是从 Grafana 官方仓库拷贝 “默认 grafana.ini”(也可以先在已有容器里导出一份)。这里我们直接创建一个精简版,专门覆盖 SMTP 部分:

    #################################### SMTP / Emailing qq为例 #############################
    [smtp]
    ;enabled = false                      ; ← 把 enabled 改为 true
    enabled = true
    host = smtp.qq.com:465           ; ← 修改为你的 SMTP 服务器地址和端口
    user = 123321@qq.com  ; ← SMTP 登录用户名
    # If your SMTP server requires a password, 设置成真实密码
    password = osdfdsfsadfsa		# 邮箱SMTP授权码(非登录密码)
    ; If your SMTP server SPEFICALLY不需要登录,以空用户名为例
    ; user = 
    ; password = 
    ;skip_verify = false                ; 如果你想跳过 TLS 验证,可改成 true
    from_address = 123321@qq.com     ; ← 邮件 From 字段,与上面的邮箱保持一致
    from_name = Grafana                    ; ← 发件人名称
    ; EHLO identity in SMTP dialog (defaults to instance_name if set, else hostname of the system)
    ;ehlo_identity = # ******************************************************
    # 下面三行是 TLS/SSL 相关,如果你的 SMTP 需要 SSL 直接用 465 端口,则:
    ; startTLS policy (defaults to true if port is 587)
    #startTLS_policy = NoStartTLS
    # 例如:
    #host = smtp.example.com:465
    #skip_verify = true
    #startTLS_policy = NoStartTLS
    # ******************************************************# 如果你的 SMTP 服务地址是 Gmail,可以参考:
    # host = smtp.gmail.com:587
    # user = yourgmail@gmail.com
    # password = your_gmail_app_password   ; ← 注意:Gmail 要用 “应用专用密码”
    # from_address = yourgmail@gmail.com
    # from_name = Grafana################## 下面其余配置保持默认不动 #######################
    [log]
    mode = console
    level = info[paths]
    data = /var/lib/grafana
    logs = /var/log/grafana
    plugins = /var/lib/grafana/plugins
    provisioning = /etc/grafana/provisioning
    • 以上内容示例了最小化启用 SMTP 的方式,实际生产中可以保留 grafana.ini 里更多其他模块的配置。重点是把 [smtp] 里的 enabled = truehostuserpasswordfrom_addressfrom_name 等项补充好。
    • 注意:
      • host = smtp.example.com:587:如果你的 SMTP 只在 587/STARTTLS 上跑,就填 :587,如果是真正的 SSL over TLS(如某些服务用 465 端口),就写 smtp.xxx.com:465 并设置 startTLS_policy = NoStartTLSskip_verify = true
      • user/password:请按真实账户或“应用专用密码”填写。
      • from_address:建议与 user 保持一致或在你所使用域名下的一个死信箱。
  3. 保存 grafana-provisioning/config/grafana.ini,确保文件权限正常且 Docker 进程可以读到它:

    ls -l grafana-provisioning/config/grafana.ini
    

3.1.2. 修改 Docker Compose,将自定义 grafana.ini 挂载进容器

假设你原来已有一个类似下面的 docker-compose.yml(含 Grafana 服务):

services:grafana:volumes:# 这一行需要挂载我们自己的 grafana.ini:- ./grafana-provisioning/config/grafana.ini:/etc/grafana/grafana.ini

将以上 docker-compose.yml 放到 ~/grafana/ 目录下,调整好相对路径后,执行:

docker compose down   # 如果 Grafana 已经在跑,先停掉
docker compose up -d  # 再后台启动docker exec -it grafana cat /etc/grafana/grafana.ini

3.1.3. 验证 Grafana 内部是否正确加载了 SMTP 配置

  1. 查看 Grafana 容器日志,确认它没有报 “failed to load grafana.ini” 或者 “smtp disabled” 等错误:

    docker logs grafana --tail 50
    

    如果看到开头有大段 “Starting Grafana …” 然后没有特别的关于 SMTP 的 WARN/ERROR,就说明配置文件没问题。

  2. 进入 Grafana Web UI (假设地址为 http://<你的服务器IP>:3000),登录后:

    • 左侧菜单 → “Alerting”“Contact points”(旧版面板可能在 “Alerting → Notification channels”)
    • 点击右上角 “New contact point”(或 “New channel”)。
    • 如果 SMTP 配置生效,当你选择 “Email” 这个类型时,表单下方不会出现 “SMTP is not configured” 的警告,而是可以直接填写邮箱相关字段(例如 “To” 地址等)。
  3. 填入收件地址,点击Test, 验证邮箱是否能收到邮件, 通过后保存

在这里插入图片描述

  1. Notification policies 通知策略默认选择刚创建的邮箱

    在这里插入图片描述

3.2、为关键面板添加告警(Alerting)

日志分析如果仅是“看”和“追踪”,有时当问题发生时你才去察觉。更高级的做法是:

  1. 定位告警场景

    • 例如“过去 5 分钟里 Tomcat 错误日志条数 > 10,就要发邮件/短信告警”。
    • 或者 “syslog 里出现关键字 ‘CRITICAL’ 超过 5 条,就要通知值班人员立即干预”。
  2. 在面板里创建 Alert 规则

    • 打开某个面板(比如前面做的 Stat 面板,显示“过去 1 分钟内 Tomcat 错误日志条数”),点击右上角 **+**号。

    • Create alert rule ,新建一个告警:

      • Query:保留你当前的 LogQL Metric 查询,如

        sum(count_over_time({host="host1", job="tomcat"}|detected_level="error" [1m]))
      • 如果你只在 Grafana 查询层用 | logfmtdetected_level,那就只能按管道过滤

        • #这里的level是在promtail-config-host1.yml配置的
          - job_name: tomcat-host1static_configs:- targets: [ localhost ]labels:job: tomcathost: host1app: webapp__path__: /tomcat_logs/*.logpipeline_stages:- regex:expression: '^(?P<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3}) (?P<level>\w+)'- labels:level:
          
      • 条件(Condition):IS ABOVE 10

      • 触发时:Notification policies → 选择你已配置好的通知渠道(Email、Slack、PagerDuty 等)

      • 测试是否有数据, 最近没有日志可以调时间范围, 如:1天

      • 在这里插入图片描述

      • 测试规则: 5分钟内10调info就触发

        •  sum(count_over_time({host="host1", job="tomcat"} | detected_level = `info` [5m]))
          
    • 保存告警后,只要指标持续超过阈值,Grafana 就会主动推送警报给你。

注意

  • Loki 原生对日志告警的支持是“基于 LogQL Metric”查询,所以你要先把“Error 日志条数”写成 count_over_time(...) 这种 Metric,再去做阈值判断。
  • 如果你在 5 分钟内累积出现了 20 条错误,那么 count_over_time(...[5m]) 会在“此时” 直接返回 20,符合范围就报警。
  • 最好把告警面板放在仪表板最显眼的位置,比如第一行第一个格子,这样一目了然。

4. 常见问题 & 解决

4.1 Promtail 报 timestamp too old

  • 原因:采集到旧归档(.gz)文件里的日志行,造成时间戳比 Loki reject_old_samples_max_age 还要早。
  • 解决
    • promtail-config-host1.yml 中的 __path__: /var/log/**/*.log 改为只匹配 .log,不含 .gz
    • 或者在 scrape_configs 下加 ignore_older: 24h,忽略超过 24 小时前修改的文件。

4.2 Alert Rule 报 “No data” 或 “failed to execute query”

  • 常见原因:在 Alert 编辑页没把查询模式切为 Metrics,而是留在 Logs
  • 输入的 LogQL 语法写错,比如把 .Status 写成 .State
  • 告警条件写成 IS ABOVE 0,但查询表达式返回的是 instant vector,需用 sum(count_over_time(...)) 明确聚合成单值。
  • 解决:确保 Mode = Metrics,Condition 中引用 query(A, 1m, now),并用 .Status 而非 .State;设置“无数据→OK、错误→Alerting”。

4.3 容器时区与宿主机不一致,导致日志时间错位

  • 原因:默认 Docker 容器时区为 UTC,Promtail 采集时若不指定时区,可能把无时区后缀的日志当成 UTC 解析,造成时差。
  • 解决
    • 在所有关键容器(Promtail、Tomcat)里挂载 /etc/localtime:/etc/localtime:ro/etc/timezone:/etc/timezone:ro
    • 或者在 Promtail pipeline 中,用 timestamp 阶段手动指定带有 +0800 的格式。
docker run -d \--name ry-tomcat \-v /etc/localtime:/etc/localtime:ro \-v /etc/timezone:/etc/timezone:ro \-e TZ=Asia/Shanghai \tomcat:9

或在 docker-compose.yml 中配置:

services:tomcat:image: tomcat:9volumes:- /etc/localtime:/etc/localtime:ro- /etc/timezone:/etc/timezone:roenvironment:TZ: "Asia/Shanghai"

4.4 Loki 启动报错:both grpc_client_config and (query_frontend_grpc_client or query_scheduler_grpc_client) are set

  • 问题描述:使用 Loki 3.x 时出现如下错误,导致容器无法启动:

    failed parsing config: both `grpc_client_config` and (`query_frontend_grpc_client` or `query_scheduler_grpc_client`) are set at the same time.
    
  • 原因分析

    • 在 Loki v3.0+ 中,模块间通信的配置方式有所改变;
    • 你启用了 target=all 单体模式,此时 不应再显式设置 grpc_client_config
    • 同时配置 grpc_client_config 和新的 query_frontend_grpc_clientquery_scheduler_grpc_client 会导致配置冲突。
  • 解决方法

    • 如果使用 target=all 单体模式(即 Loki 所有模块集中部署在一个进程中):

      • 删除配置文件中 grpc_client_config: 相关段落;
      • 让模块间使用内存自动通信,无需再显式配置 gRPC 客户端;
    • 示例修改前:

      querier:frontend_worker:grpc_client_config:...
      

      修改后:

      querier:frontend_worker:frontend_address: 127.0.0.1:9095
      
  • 建议

    • 使用 target=all 模式时,配置应尽可能简洁;
    • 保持 frontend_address 这类模块间引用,但移除 grpc_client_config
    • 或者使用 Loki 官方 local-config.yaml 为基础,逐步扩展。

4.5 仪表板面板无数据但Explore查询正常

问题现象

  • 在Grafana仪表板中添加新面板后显示"No data"
  • 相同查询在Explore界面能正常返回数据
  • 日志数据实际存在于Loki中

根本原因

  1. 时间范围冲突

    • 仪表板使用固定时间范围(如now-1h),而面板配置了不同的相对时间
    • 示例:仪表板设置为Last 6 hours,但面板覆盖为Last 15 minutes
  2. 面板级数据源配置错误

    # 错误配置示例(使用未定义变量)
    datasource: $prometheus  # 应该用具体数据源名
    
  3. 变量未设置默认值

    • 当查询使用{app="$application"}
    • 变量$application未设置默认值或默认值不匹配
  4. 面板缓存未刷新

    • Grafana未自动更新面板查询结果

解决方案

  1. 检查时间范围

    • 编辑面板 → 右侧Time options → 关闭Override relative time
    • 在仪表板右上角设置合理时间范围(推荐Last 6 hours
  2. 修正数据源配置

    • 编辑面板 → 查询编辑器底部 → Data source
    • 直接选择Loki(避免使用变量)
  3. 设置变量默认值

    # 仪表板设置 → Variables
    - name: applicationquery: label_values(app)default: webapp  # 关键设置!
    
  4. 强制刷新数据

    • 点击面板右上角 ⋮ → Refresh
    • 浏览器按Ctrl+Shift+R清除缓存

4.6 容器日志中缺失关键标签(如host

问题现象

  • 在Grafana中无法通过{host="host1"}过滤日志
  • label_values(host)返回空列表

排查步骤

  1. 检查Promtail配置

    docker exec promtail_host1 cat /etc/promtail/config.yml
    # 确认static_configs中有host标签
    
  2. 验证标签注入

    curl -sG "http://loki:3100/loki/api/v1/series?match[]={job='syslog'}" | jq
    

解决方案

- job_name: host_logsstatic_configs:- targets: [localhost]labels:host: host1  # 明确设置主机标签job: syslog__path__: /var/log/*.log

4.7 Grafana日志面板显示原始JSON不解析

问题现象

日志显示为完整JSON字符串:

{"time":"2023-01-01T12:00:00Z", "level":"error", "message":"Failed to connect"}

解决方案

  1. 添加解析管道

    pipeline_stages:- json:expressions:timestamp: timelevel: levelmsg: message- labels:level:  # 提取为标签
    
  2. Grafana字段提取

    • 在Explore或面板中:

      {job="webapp"} | json
      
    • 点击 Detected fields → 选择要显示的字段

5. 小结

  1. Loki + Promtail + Grafana 提供了一个轻量级、可横向扩展的容器日志分析方案,适合云原生场景。
  2. 关键点在于:Promtail 负责采集并打标签,Loki 高效存储带标签的日志、Grafana 负责可视化与告警。
  3. 本文从两台服务器(host4、host1)端到端演示了:
    • Docker Compose 快速部署 Loki 与 Promtail,挂载时区、排除旧日志、采集所有容器和系统日志;
    • 在 Grafana 中创建多个面板:实时错误日志、Tomcat 非 Info 日志、日志源分布、动态变量、Top N 错误消息、日志量趋势等;
    • 完整地将告警功能(SMTP 配置、Panel 告警规则)集成到 Grafana,达到主动通知运维的目的。
  4. 通过“常见问题 & 解决”部分,将遇到的各类坑点(时间戳过旧、Alert Rule 无数据、邮件模板失效、容器时区不同步)一并列出,方便以后快速排查。

后续优化思路

  • 将 Loki 存储由本地文件系统切换到 S3/GCS,提高可扩展性与持久化能力。
  • 在 Promtail 端对日志做更细粒度的 pipeline 处理(如 geoip、user-agent 解析),把有价值的字段提前打成 labels。
  • 利用 Grafana 的高级 Dashboard 功能(变量联动、模板化面板),实现多机房、多环境一键切换。
  • 将告警规则提取到 Loki Ruler 或 Alertmanager,做更灵活的分组路由与抖动策略。

相关文章:

  • dxf、dwg中文字矩阵变换
  • 安装 Nginx
  • QT链接其他项目的C++文件报链接错误
  • sifli 52 反馈standby待机rc10k 15秒校准起来后,底电流会变大
  • 交叉熵的意义
  • Linux-linux和windows创建新进程的区别以及posix_spawn
  • 04.两数之和
  • 数据库同步是什么意思?数据库架构有哪些?
  • 《AI角色扮演反诈技术解析:原理、架构与核心挑战》
  • Linux基础开发工具——yum工具
  • QML技术优势
  • day21 常见的降维算法
  • 英国2025年战略防御评估报告:网络与电磁域成现代战争核心
  • 【学习笔记】Python金融基础
  • Linux 云服务器部署 Flask 项目(含后台运行与 systemd 开机自启)
  • 智能推荐系统:协同过滤与深度学习结合
  • # Vue + OpenLayers 完整项目开发指南
  • 【unity游戏开发入门到精通——通用篇】从零掌握UnityWebRequest:文件下载、表单提交、超时处理、断点续传
  • Devops自动化运维---py基础篇一
  • Android Studio 打包时遇到了签名报错问题:Invalid keystore format
  • web前端和网站开发的区别/中国国家人事人才培训网官网
  • 网站建设开发人员配置/厦门关键词排名提升
  • 企业管理咨询合同/如何优化关键词
  • 域名绑定网站/网店如何做推广
  • 提升网站的访问速度/seo专家是什么意思
  • 安卓app软件公司/seo官网