4、日志监控与采集:日志分类与ELK技术栈实战

日志这东西,说简单也简单,说复杂真能坑死人。我做了这么多年风控系统,见过太多因为日志没搞好,线上出问题查半天找不到根因的案例。今天咱们就把日志监控与采集这块彻底聊透。

4.1 日志分类:三种日志各司其职

先说说日志分类。很多团队一开始就把所有日志往一个文件里写,结果排查问题时像大海捞针。我个人习惯把日志分成三类:业务日志、系统日志、审计日志。

日志类型 核心用途 典型内容 保留周期
业务日志 记录业务流转过程 用户请求、规则命中、决策结果 30-90天
系统日志 监控系统运行状态 CPU/内存、接口耗时、异常堆栈 7-30天
审计日志 满足合规与追溯需求 操作人、操作时间、变更内容 1年以上

关键点:业务日志和审计日志虽然看起来像,但用途完全不同。业务日志关注「发生了什么」,审计日志关注「谁干的」。千万别混在一起。

我在项目中遇到过一件事:某次线上风控误杀率突然飙升,业务方催着要排查。结果发现业务日志和审计日志写到了同一个索引里,查询时互相干扰,花了整整半天才定位到问题。从那以后,我强制要求三类日志必须物理隔离。

4.2 ELK技术栈搭建:从零到可用的实战

ELK说白了就是三个组件:Elasticsearch存数据、Logstash收数据、Kibana看数据。我建议再加个Filebeat做轻量采集,这样架构更稳。

先看整体架构图:

应用服务 应用服务 应用服务 Filebeat 日志采集 Filebeat 日志采集 Kafka 消息队列(可选) Logstash 解析与过滤 Elasticsearch 存储 Kibana 可视化 应用层 采集层 传输层 处理层 存储/展示层

搭建ELK其实不复杂,但有几个坑得提前知道。我直接给出一份可用的docker-compose配置:

version: '3'
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.17.0
    environment:
      - discovery.type=single-node
      - ES_JAVA_OPTS=-Xms2g -Xmx2g
    ports:
      - "9200:9200"
    volumes:
      - es_data:/usr/share/elasticsearch/data

  logstash:
    image: docker.elastic.co/logstash/logstash:7.17.0
    ports:
      - "5044:5044"
    volumes:
      - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
    depends_on:
      - elasticsearch

  kibana:
    image: docker.elastic.co/kibana/kibana:7.17.0
    ports:
      - "5601:5601"
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    depends_on:
      - elasticsearch

volumes:
  es_data:

注意:ES的JVM堆内存千万别超过物理内存的50%,否则会被系统OOM Killer干掉。我曾经在生产环境吃过这个亏,半夜三点爬起来调参数。

4.3 日志采集Agent设计:轻量、可靠、可扩展

采集Agent是日志系统的「触角」。我推荐用Filebeat,原因很简单:它够轻,资源占用不到20MB内存,对业务几乎零影响。

但光用Filebeat还不够,你得设计好采集策略。我总结了几条原则:

  • 就近采集:Agent部署在应用服务器本地,不走网络转发
  • 断点续传:Filebeat自带这个能力,但要注意磁盘空间
  • 多行合并:Java异常栈跨多行,需要配置multiline规则

给你看一个我常用的Filebeat配置:

filebeat.inputs:
- type: log
  enabled: true
  paths:
    - /var/log/business/*.log
    - /var/log/system/*.log
  multiline:
    pattern: '^\d{4}-\d{2}-\d{2}'
    negate: true
    match: after
  fields:
    log_type: business

output.logstash:
  hosts: ["logstash:5044"]
  loadbalance: true

小技巧:多行合并的正则一定要测试充分。我见过有人把时间戳格式写错,结果所有异常栈都被拆成了单行,排查问题时根本没法看。

4.4 日志格式规范:统一才有价值

日志格式不统一,ELK再强也白搭。我强制团队使用JSON格式,原因很简单:Logstash解析JSON比正则快10倍以上。

这是我推荐的业务日志格式:

{
  "timestamp": "2024-01-15T10:30:00.123+08:00",
  "level": "INFO",
  "trace_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "service": "risk-engine",
  "module": "rule-evaluate",
  "action": "decision",
  "user_id": "u_10086",
  "request_id": "req_202401151030001",
  "rule_id": "rule_001",
  "rule_name": "高频交易检测",
  "decision": "reject",
  "cost_ms": 45,
  "extra": {
    "ip": "192.168.1.100",
    "device_id": "device_abc123"
  }
}

为什么要有trace_id?你想想看,一个请求经过网关、风控引擎、规则库、数据库,如果没有trace_id,你怎么串起整个调用链?我在项目中遇到过,线上有个请求超时了,但日志里只有零散的信息,查了三个小时才发现是某个规则调了外部接口超时。

系统日志的格式相对简单,但一定要包含线程名和类名:

{
  "timestamp": "2024-01-15T10:30:00.456+08:00",
  "level": "ERROR",
  "thread": "http-nio-8080-exec-3",
  "logger": "com.risk.engine.RuleEngine",
  "message": "规则执行异常: rule_001",
  "stack_trace": "java.lang.Exception: ...",
  "cost_ms": 5000
}

核心原则:日志是给人看的,也是给机器查的。JSON格式既方便Logstash解析,也方便Kibana做聚合查询。别再用那种「2024-01-15 INFO 用户登录成功」的纯文本格式了,那是十年前的做法。

嗯,日志这块内容不少,但核心就三点:分类清晰、采集可靠、格式统一。把这三点做到位,你的风控系统监控就有了坚实的基础。下次线上出问题,你就能在几分钟内定位到根因,而不是像无头苍蝇一样乱翻日志。


公众号:蓝海资料掘金营,微信deep3321