1.原因:
每次用tail -f 在服务器上翻日志太麻烦,线上问题排查时有时找不到日志。本文将带你从零开始,搭建一套企业级的ELK日志平台,让你的日志管理变得简单、高效、可视化。
传统日志管理的痛点
登录服务器
ssh user@prod-server
查看日志
tail -f /var/log/app/app.log
搜索错误
grep "ERROR" /var/log/app/app.log
跨服务器查日志?噩梦开始...
痛点总结:
日志分散在多台服务器,排查问题需要来回切换, 搜索能力有限,复杂的查询难以实现,无法可视化分析,只能看原始文本日志,文件轮转后,历史日志难以追溯
2.分析:
功能介绍:
|-------------------|----|--------------------------|
| Elasticsearch | ES | 分布式搜索和分析引擎,负责日志的存储和索引 |
| Logstash | - | 日志采集管道,支持从多种来源采集、过滤、转换数据 |
| Kibana | - | 可视化平台,提供图表、仪表盘、日志浏览界面 |
3.解决:
3.1 版本选择
本文使用 ELK 7.17.15,这是7.x系列的最后一个稳定版本,兼容性好,社区文档丰富。
| 组件 | 版本 | 说明 |
|---|---|---|
| Elasticsearch | 7.17.15 | 长期支持版本 |
| Kibana | 7.17.15 | 与ES版本保持一致 |
| Logstash | 7.17.15 | 与ES版本保持一致 |
| Spring Boot | 3.x | 使用logback-logstash-encoder |
3.2 macOS 安装(使用二进制包)
安装一般不管win10\mac\linux,都是官网、华为云、阿里云、清华云等镜像搜索下载。
备注:我的是mac安装包,如果不能下载留言,我上传或者发送。
创建安装目录
mkdir -p ~/elk && cd ~/elk
下载 Elasticsearch (Apple Silicon 版本)
curl -L -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.17.15-darwin-aarch64.tar.gz
下载 Kibana
curl -L -O https://artifacts.elastic.co/downloads/kibana/kibana-7.17.15-darwin-x86_64.tar.gz
下载 Logstash
curl -L -O https://artifacts.elastic.co/downloads/logstash/logstash-7.17.15-darwin-x86_64.tar.gz
解压
for file in *.tar.gz; do tar -xzf "$file"; done
重命名
mv elasticsearch-7.17.15 elasticsearch
mv kibana-7.17.15-darwin-x86_64 kibana
mv logstash-7.17.15-darwin-x86_64 logstash
3.3 启动
3.3.1 启动ES:
# 进入 ES 目录并后台启动 cd ~/elk/elasticsearch ./bin/elasticsearch -d
localhost:9002

3.3.2 启动kibana
cd ~/elk/kibana
nohup ./bin/kibana > ~/elk/logs/kibana.log 2>&1 &
localhost:5601

3.3.3 启动logstash
先建测试包
config目录下新建 springboot-logstash.conf
java
input {
file {
path => "/tmp/spring-logs/*.log"
codec => json
start_position => "beginning"
sincedb_path => "/dev/null"
}
}
output {
elasticsearch {
hosts => ["localhost:9200"]
index => "springboot-logs-%{+YYYY.MM.dd}"
}
stdout { codec => rubydebug }
}

启动命令:
logstash -f /path/to/config.conf
4. springboot 项目关联
4.1 新建项目 elk_dm

pom.xml
java
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.13</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.dp.ccrr</groupId>
<artifactId>elk_dm</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>elk_dm</name>
<description>elk_dm</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>7.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
resource :
logback-spring.xml
java
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<springProperty scope="context" name="appName" source="spring.application.name" defaultValue="app"/>
<!-- JSON 格式输出到文件 -->
<appender name="JSON_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/tmp/spring-logs/${appName}.log</file>
<!-- 添加滚动策略(同时解决 TriggeringPolicy 警告) -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/tmp/spring-logs/${appName}.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>7</maxHistory>
</rollingPolicy>
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<customFields>{"service":"${appName}","environment":"dev"}</customFields>
</encoder>
</appender>
<!-- 控制台输出(开发环境) -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 开发环境:输出到控制台 -->
<springProfile name="dev">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</springProfile>
<!-- 生产/测试环境:输出 JSON 到文件 -->
<springProfile name="prod,test,default">
<root level="INFO">
<appender-ref ref="JSON_FILE"/>
</root>
</springProfile>
</configuration>
ELKController.java
java
package com.dp.ccrr.elk_dm.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/elk")
public class ELKController {
@RequestMapping("/")
public String index() {
log.info("------------ELKController.index");
return "Hello World!";
}
}
TraceIdFilter.java
java
package com.dp.ccrr.elk_dm.filter;
import jakarta.servlet.*;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.UUID;
@Component
public class TraceIdFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
MDC.put("traceId", UUID.randomUUID().toString().replace("-", ""));
chain.doFilter(request, response);
MDC.clear();
}
}
启动项目:

- 测试:
http://localhost:9200/test-index/_search?pretty

新建信息仓库,

输入springboot-logs*

然后在discover查看

搜索条件:
service elk_dm
today
然后就能看到测试数据了。

Done.