本文目录导读:

- 核心思路
- 方案一:Spring Boot + Filebeat + Logstash + ES (强烈推荐)
- 方案二:Spring Boot + Logstash Logback Encoder (直接推送)
- 方案三:Java App 直连 Elasticsearch (不推荐)
- 生产环境最佳实践总结
整合ELK(Elasticsearch、Logstash、Kibana)到Java应用中,主要有三种主流方式,选择哪种取决于你的技术栈(是否使用Spring Boot)以及日志的实时性要求。
下面我将按照最佳实践的顺序,为你详细拆解这三种方案。
核心思路
Java应用产生日志 → 通过某种方式传输 → Logstash 处理/解析 → Elasticsearch 存储/索引 → Kibana 可视化查询。
Spring Boot + Filebeat + Logstash + ES (强烈推荐)
这是生产环境中最稳定、侵入性最低的方案,利用Filebeat作为轻量级日志采集器,读取日志文件,避免直接写ES或Logstash带来的网络阻塞。
架构图:
Java App (logback/log4j2) → 写入日志文件 (如 /var/log/app.log) → Filebeat 读取 → Logstash → Elasticsearch → Kibana
步骤详解:
-
Java应用配置(logback-spring.xml) 无需任何特殊依赖,只需配置好日志格式(建议用JSON格式,以便Logstash解析)。
<!-- 让日志以JSON格式输出,方便Logstash解析 --> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>/var/log/myapp/app.log</file> <encoder class="net.logstash.logback.encoder.LogstashEncoder"/> <!-- 或者使用 PatternLayout 并输出 key=value 格式 --> <!-- <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> --> </appender> -
Filebeat配置(filebeat.yml) 安装Filebeat,配置它读取你的日志文件。
filebeat.inputs: - type: log enabled: true paths: - /var/log/myapp/*.log # 你的日志路径 # 如果日志是多行堆栈,需要配置 multiline multiline: pattern: '^\\d{4}' # 以年份开头的行是新的日志行 negate: true match: after output.logstash: hosts: ["your_logstash_host:5044"] # Logstash的监听地址 -
Logstash配置(logstash.conf) 接收数据,解析并输出到ES。
input { beats { port => 5044 } } filter { # 如果日志是JSON格式,可以直接解析 json { source => "message" target => "json_content" # 将解析后的字段放入json_content对象 } # 如果非JSON,可以用grok解析 # grok { # match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} \[%{DATA:thread}\] %{LOGLEVEL:level} %{DATA:logger} - %{GREEDYDATA:msg}" } # } # 常用字段补充 mutate { add_field => { "app_name" => "my-java-app" } remove_field => ["message"] # 可选:移除原始消息 } } output { elasticsearch { hosts => ["http://your_es_host:9200"] index => "myapp-logs-%{+YYYY.MM.dd}" # 按日期分索引 } # stdout { codec => rubydebug } # 调试用 } -
启动与验证
- 启动ES、Logstash、Kibana。
- 重启Java应用,启动Filebeat。
- 在Kibana中创建索引模式
myapp-logs-*,即可看到日志。
Spring Boot + Logstash Logback Encoder (直接推送)
特点:最省事,无需Filebeat,Java应用直接通过Socket将日志推送给Logstash,适合中小规模服务,或不想折腾Filebeat的场景。
架构图:
Java App (logstash-logback-encoder) → TCP Socket → Logstash → Elasticsearch → Kibana
添加Maven依赖
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>7.4</version> <!-- 请用最新版 -->
</dependency>
配置 logback-spring.xml
<configuration>
<!-- 应用名称 -->
<springProperty scope="context" name="appName" source="spring.application.name" defaultValue="myapp"/>
<!-- 追加器:发送到Logstash -->
<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<!-- Logstash地址 -->
<destination>your_logstash_host:4560</destination>
<!-- 连接超时 -->
<reconnectionDelay>10000</reconnectionDelay>
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<!-- 添加自定义字段 -->
<customFields>{"app_name":"${appName}", "env":"prod"}</customFields>
</encoder>
</appender>
<!-- 根日志级别 -->
<root level="INFO">
<appender-ref ref="LOGSTASH"/>
<!-- 通常还会保留一个控制台/文件日志 -->
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
Logstash配置(logstash.conf)
input {
tcp {
port => 4560
codec => json_lines # 解码JSON行
}
}
filter {
# 这里是纯净的JSON,通常不需要额外filter
# 可以做一些字段清理
mutate {
remove_field => ["@version", "port"]
}
}
output {
elasticsearch {
hosts => ["http://localhost:9200"]
index => "springboot-logs-%{+YYYY.MM.dd}"
}
}
优点:配置简单,日志直接结构化。 缺点:如果Logstash挂了,Java应用会一直重试连接,可能导致应用阻塞(虽然有超时阈值),或者在日志量极大时影响应用性能。
Java App 直连 Elasticsearch (不推荐)
特点:直接写ES,适合极简单的测试或Serverless函数。
引入ES客户端依赖
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.x</version>
</dependency>
代码中手动发送
@Component
public class ElkLoggerService {
@Autowired
private ElasticsearchClient esClient;
public void logError(String message, Exception e) {
try {
LogDocument doc = LogDocument.builder()
.level("ERROR")
.message(message)
.stacktrace(ExceptionUtils.getStackTrace(e))
.timestamp(Instant.now())
.build();
IndexResponse response = esClient.index(i -> i
.index("app-logs")
.document(doc)
);
} catch (IOException ex) {
log.error("Failed to send log to ES", ex);
}
}
}
缺点:
- 必须写大量样板代码。
- 容易引发
Bulk queue满导致OOM。 - 耦合了业务代码,不推荐生产使用。
生产环境最佳实践总结
| 方案 | 适用场景 | 性能影响 | 复杂度 | 稳定性 |
|---|---|---|---|---|
| Filebeat + Logstash | 大规模、高并发、万无一失 | 极低 | 中 | 最高 |
| Logstash Encoder | 中小项目、快速接入、不想维护Filebeat | 低 (TCP阻塞风险) | 低 | 高 |
| 直连ES | 测试、非关键日志 | 高 | 高 | 低 |
关键建议:
-
必须用JSON格式:无论哪种方案,Java日志输出为JSON(使用
LogstashEncoder)是最优选择,ES存储和Kibana可视化会方便太多。 -
异步Appender:Spring Boot 默认的
Logback已经是异步的(AsyncAppender),但如果你使用方案二(TCP直推),建议再包一层AsyncAppender:<appender name="ASYNC_LOGSTASH" class="ch.qos.logback.classic.AsyncAppender"> <appender-ref ref="LOGSTASH"/> <queueSize>512</queueSize> <discardingThreshold>0</discardingThreshold> </appender> -
索引生命周期管理(ILM):在ES中配置ILM策略,自动删除30天前的日志,防止磁盘爆满。
-
安全:如果ELK集群不对外开放,建议启用X-Pack安全认证,并在Logstash output中配置用户名密码。
快速开始的示例项目结构:
myapp/
├── src/main/resources/logback-spring.xml # 配置JSON输出 + TCP推送
├── docker-compose/ # 本地的ELK
│ ├── docker-compose.yml
│ ├── logstash/
│ │ └── pipeline/
│ │ └── logstash.conf
│ └── filebeat/ # (如需)
│ └── filebeat.yml
你可以根据你的实际技术栈和团队维护能力,选择最合适的方案。从方案二(Logstash Logback Encoder) 开始是最快看到效果的。