统一日志管理架构设计

本文将从源头手把手的教你如何一步步的搭建日志管理系统,用来统一管理企业各个系统的日志。本文中的配置可以一并复制直接使用,均已经验证过。

涉及到的工具如下:
日志存储:ElasticSearch 8.19
日志查询展示:Kibana 8.19
日志抽取工具:Logstash/FileBeat 8.19

日志输出

系统日志

通常是操作系统输出的日志,中间件输出的日志,例如Nginx、Tomcat、MQ服务、Redis服务等等,不同的系统会输出不同格式的日志。需要根据其配置,设置合理的输出路径和输出格式即可。由于不同的服务日志格式差异很大,这里不一一列举。

Java项目日志

与系统日志不同,项目日志的可控制性比较大,灵活性也比较大,因此需要遵循一定的原则,否则会导致各种问题。

统一输出格式

日志输出格式尽量带上关键的完整信息,并且容易分割,后续需要用工具对日志进行解析并拆分成各自的字段,可直接使用如下logback-spring.xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="false">
    <springProperty name="applicationName" scope="context" source="spring.application.name"/>
    <property name="log.path" value="/var/local/service-name"/>

    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %level [${applicationName},%X{traceId:-},%X{spanId:-}] %logger{50} %M:%line - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/service-name.log</file>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %level [${applicationName},%X{traceId:-},%X{spanId:-}] %logger{50} %M:%line - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/service-name.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>50MB</maxFileSize>
            <maxHistory>90</maxHistory>
            <totalSizeCap>20GB</totalSizeCap>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
    </appender>

    <appender name="asyncAppender" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="file" />
        <neverBlock>true</neverBlock>
        <queueSize>2048</queueSize>
    </appender>

    <root level="${logging.level.root:-INFO}">
        <appender-ref ref="console"/>
        <appender-ref ref="asyncAppender"/>
    </root>

</configuration>

配置有几点注意事项:

  • 使用了异步日志输出:在系统被直接杀死的时候可能会丢失部分日志,但是性能较好。
  • %X{traceId:-},%X{spanId:-} 链路配置信息:方便在微服务架构中,获取完整的日志链路信息,后续会具体讲解。
  • 尽量保持统一的输出格式,方便一起采集。

链路信息

如何给Springboot项目的日志增加链路配置信息,只需要2步即可实现:

  1. 增加maven配置
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-tracing-bridge-brave</artifactId>
        </dependency>
  1. 增加上一步的logback配置,就可以看到日志会输出链路日志,格式如下:

2025-10-26 12:03:37.188 [http-nio-8080-exec-1] INFO [TraceDemo,68fd9d99873d8398d0c19822dc6c6416,d0c19822dc6c6416] com.pxd.tracedemo.controller.HelloController sayHello:13 - Hello World

68fd9d99873d8398d0c19822dc6c6416:对应logback中配置的traceId变量。
d0c19822dc6c6416:对应logback中配置的spanId变量。
对于traceId和spanId的含义,请大家自行搜索,这里不过多介绍,主要用traceId来标记一个请求经过了多个微服务后,可以将所有的日志串联起来,日志汇聚后方便排查问题。

日志汇聚

日志输出好了之后,这一步就是将日志统一汇聚到ES中。

工具介绍:
filebeat:采集日志并传输给LogStash。
logstash: 接受filebeat的日志,并存储到ES中。

Filebeat配置

这里以springboot项目输出的日志为例,使用FileBeat工具来采集日志,FileBeat的配置文件如下:

filebeat.inputs:
  - type: log
    enabled: true
    paths:
      - /var/local/service_name/*.log
    fields:
      logType: java-log #新增字段用来区分日志类型,方便后续解析的时候过滤
      env: prod #新增字段用来标记测试环境:test、正式环境:prod
      project: hr #新增字段用来标记项目名
    fields_under_root: true
    multiline.pattern: '^\d{4}-\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2}:\d{1,2}'  # 用来将多行日志合并成一行
    multiline.negate: true
    multiline.match: after
#============================= Filebeat modules ===============================
filebeat.config.modules:
  path: ${path.config}/modules.d/*.yml
  reload.enabled: true

# ============================== logstash =====================================
output.logstash:
  hosts: ["localhost:5044"] #将采集到的日志输出到logstash中
  enabled: true

注意:

  • Filebeat可以在每台服务器上启动一个即可,用来汇聚服务器上面的所有日志。
  • multiline配置是用来将多行的异常日志合并成一行处理,方便后续的查询。
  • fields配置用来新增字段

Logstash配置

logstash配置/etc/logstash/conf.d/logstash.conf如下:

# 1. 输入模块:接收来自Filebeat的日志
input {
  beats {
    port => 5044
  }
}

filter {
  # 步骤1:使用grok解析单行日志(根据实际日志格式编写正则)
  if [logType] =="java-log" {
  grok {
    # 定义匹配规则:时间 级别 [服务名] 内容
    match => {
      "message" => '%{TIMESTAMP_ISO8601:log_time} \[%{DATA:thread}\] %{DATA:level} \[%{DATA:app_name},%{DATA:trace_id},%{DATA:span_id}\] %{JAVACLASS:class} %{JAVAMETHOD:method}:(%{NUMBER:line}) - (?m)%{GREEDYDATA:msg}'
    }
    # 若匹配失败,添加标签标记(便于后续排查)
    tag_on_failure => ["_grokparsefailure"]
    remove_field => ["message", "event.original", "ecs.version"]
  }
}

  # 步骤2:将解析出的log_time转换为ES的@timestamp字段(统一时间格式)
  date {
    match => ["log_time", "yyyy-MM-dd HH:mm:ss.SSS"]  # 匹配log_time的格式
    target => "@timestamp"  # 覆盖默认的@timestamp(日志到达时间)
    timezone => "Asia/Shanghai"  # 指定时区(避免UTC偏差)
    # 若时间解析失败,添加标签
    tag_on_failure => ["_dateparsefailure"]
  }

  # 步骤3:添加自定义字段(如环境、日志来源)
  mutate {
    # 可选:将log_level转换为小写(统一格式)
    lowercase => ["level"]
  }
}

# 3. 输出模块:将处理后的日志存储到Elasticsearch
output {
if [logType] =="java-log" {
  elasticsearch {
    hosts => ["http://127.0.0.1:9200"]
    index => "%{project}-%{env}-%{+YYYY.MM}"
    user => "elastic"
    password => "VZwTLd8IcriW8Grmt7Ap"

    # 可选:设置索引模板(控制字段类型、分词等)
    template_name => "springboot-logs-template"  # 模板名称
  }
}

  # 可选:同时输出到控制台(用于调试)
  stdout {
    codec => rubydebug  # 以结构化格式打印到控制台
  }
}

启动logstash前先创建好索引生命周期和索引模版:

  • 索引生命周期配置:java-log-ilm
PUT _ilm/policy/java-log-ilm
{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "set_priority": {
            "priority": 100
          },
          "rollover": {
            "max_age": "30d",
            "max_primary_shard_size": "50gb"
          }
        }
      },
      "warm": {
        "min_age": "30d",
        "actions": {
          "set_priority": {
            "priority": 50
          }
        }
      },
      "cold": {
        "min_age": "60d",
        "actions": {
          "set_priority": {
            "priority": 0
          }
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": {
          "delete": {
            "delete_searchable_snapshot": true
          }
        }
      }
    }
  }
}

索引生命周期:可以根据配置进行定制化修改,规定多久删除,具体配置是什么含义请自行搜索。

  • 索引模版配置:springboot-logs-template,定义索引字段名称与字段类型,方便进行搜索。
{
  "template": {
    "settings": {
      "index": {
        "lifecycle": {
          "name": "java-log-ilm"
        },
        "mode": "standard",
        "routing": {
          "allocation": {
            "include": {
              "_tier_preference": "data_content"
            }
          }
        },
        "number_of_replicas": "0"
      }
    },
    "mappings": {
      "dynamic": "true",
      "dynamic_templates": [],
      "date_detection": false,
      "numeric_detection": false,
      "properties": {
        "@timestamp": {
          "type": "date"
        },
        "app_name": {
          "type": "keyword"
        },
        "class": {
          "type": "keyword"
        },
        "level": {
          "type": "keyword"
        },
        "line": {
          "type": "integer"
        },
        "msg": {
          "type": "text"
        },
        "method": {
          "type": "keyword"
        },
        "span_id": {
          "type": "keyword"
        },
        "thread": {
          "type": "keyword"
        },
        "trace_id": {
          "type": "keyword"
        }
      }
    },
    "aliases": {}
  }
}

字段类型区别:

  • keyword: 适用于精确查询,不会分词。
  • text:适合全文搜索,会进行分词。

日志查询

日志查询工具:kiana
根据索引名称配置查询视图:
在这里插入图片描述
在这里插入图片描述

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐