集中式日志管理(Graylog、Logstash、Fluentd)
本指南解释了如何将日志发送到集中式日志管理系统,如 Graylog、Logstash (Elastic Stack 或 ELK - Elasticsearch、Logstash、Kibana 内部) 或 Fluentd (EFK - Elasticsearch、Fluentd、Kibana 内部)。
本文档是 Quarkus 可观察性参考指南的一部分,该指南介绍了此组件和其他可观察性相关组件。
集中日志有很多不同的方法(如果您正在使用 Kubernetes,最简单的方法是记录到控制台,并要求您的集群管理员在集群内部集成一个中央日志管理器)。在本指南中,我们将介绍如何使用受支持的 Quarkus 扩展以受支持的标准格式(如 Graylog Extended Log Format (GELF)、Elastic Common Schema (ECS) 或 OpenTelemetry Log 信号)将日志发送到外部工具。
先决条件
要完成本指南,您需要
-
大约 15 分钟
-
一个 IDE
-
已安装 JDK 17+ 并正确配置了
JAVA_HOME
-
Apache Maven 3.9.9
-
Docker 和 Docker Compose 或 Podman,以及 Docker Compose
-
如果您想使用它,可以选择 Quarkus CLI
-
如果您想构建本机可执行文件(或者如果您使用本机容器构建,则为 Docker),可以选择安装 Mandrel 或 GraalVM 并进行适当的配置
示例应用程序
以下示例都将基于相同的示例应用程序,您可以通过以下步骤创建该应用程序。
创建一个带有 REST 扩展的应用程序。您可以使用以下命令来创建它
对于 Windows 用户
-
如果使用 cmd,(不要使用反斜杠
\
并将所有内容放在同一行上) -
如果使用 Powershell,请将
-D
参数用双引号括起来,例如"-DprojectArtifactId=centralized-logging"
出于演示目的,我们创建一个端点,除了记录一句话之外什么也不做。您无需在应用程序内部执行此操作。
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.jboss.logging.Logger;
@Path("/logging")
@ApplicationScoped
public class LoggingResource {
private static final Logger LOG = Logger.getLogger(LoggingResource.class);
@GET
public void log() {
LOG.info("Some useful log message");
}
}
使用 Socket 处理程序以 ECS(Elastic Common Schema)格式将日志发送到 Elastic Stack (ELK)
您可以使用 TCP 输入以 ECS 格式将日志发送到 Logstash。为此,我们将使用 quarkus-logging-json
扩展以 JSON 格式格式化日志,并使用 socket 处理程序将日志发送到 Logstash。
在 $HOME/pipelines/ecs.conf
中创建以下文件
input {
tcp {
port => 4560
codec => json
}
}
filter {
if ![span][id] and [mdc][spanId] {
mutate { rename => { "[mdc][spanId]" => "[span][id]" } }
}
if ![trace][id] and [mdc][traceId] {
mutate { rename => {"[mdc][traceId]" => "[trace][id]"} }
}
}
output {
stdout {}
elasticsearch {
hosts => ["http://elasticsearch:9200"]
}
}
然后配置您的应用程序以 JSON 格式记录日志
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-logging-json</artifactId>
</dependency>
implementation("io.quarkus:quarkus-logging-json")
并指定 Logstash 端点的主机和端口。为了符合 ECS,请指定日志格式。
# to keep the logs in the usual format in the console
quarkus.log.console.json.enabled=false
quarkus.log.socket.enable=true
quarkus.log.socket.json.enabled=true
quarkus.log.socket.endpoint=localhost:4560
# to have the exception serialized into a single text element
quarkus.log.socket.json.exception-output-type=formatted
# specify the format of the produced JSON log
quarkus.log.socket.json.log-format=ECS
最后,启动组成 Elastic Stack 的组件
-
Elasticsearch
-
Logstash
-
Kibana
您可以通过以下 docker-compose.yml
文件执行此操作,该文件可以通过 docker-compose up -d
启动
# Launch Elasticsearch
version: '3.2'
services:
elasticsearch:
image: docker.io/elastic/elasticsearch:9.0.2
ports:
- "9200:9200"
- "9300:9300"
environment:
ES_JAVA_OPTS: "-Xms512m -Xmx512m"
discovery.type: "single-node"
cluster.routing.allocation.disk.threshold_enabled: false
networks:
- elk
logstash:
image: docker.io/elastic/logstash:9.0.2
volumes:
- source: $HOME/pipelines
target: /usr/share/logstash/pipeline
type: bind
ports:
- "12201:12201/udp"
- "5000:5000"
- "9600:9600"
networks:
- elk
depends_on:
- elasticsearch
kibana:
image: docker.io/elastic/kibana:9.0.2
ports:
- "5601:5601"
networks:
- elk
depends_on:
- elasticsearch
networks:
elk:
driver: bridge
启动您的应用程序,您应该会看到您的日志到达 Elastic Stack 中;您可以使用 Kibana(可通过 https://:5601/ 访问)来访问它们。
使用 Syslog 处理程序将日志发送到 Fluentd
您可以使用 Syslog 输入将日志发送到 Fluentd。与 GELF 输入相反,Syslog 输入不会在一个事件中呈现多行日志。
首先,您需要创建一个带有 Elasticsearch 插件的 Fluentd 镜像。您可以使用以下 Dockerfile,该文件应在 fluentd
目录中创建。
FROM fluent/fluentd:v1.3-debian
RUN ["gem", "install", "fluent-plugin-elasticsearch", "--version", "3.7.0"]
然后,您需要在 $HOME/fluentd/fluent.conf
中创建一个 Fluentd 配置文件
<source>
@type syslog
port 5140
bind 0.0.0.0
message_format rfc5424
tag system
</source>
<match **>
@type elasticsearch
host elasticsearch
port 9200
logstash_format true
</match>
然后,启动组成 EFK Stack 的组件
-
Elasticsearch
-
Fluentd
-
Kibana
您可以通过以下 docker-compose.yml
文件执行此操作,该文件可以通过 docker-compose up -d
启动
version: '3.2'
services:
elasticsearch:
image: docker.io/elastic/elasticsearch:9.0.2
ports:
- "9200:9200"
- "9300:9300"
environment:
ES_JAVA_OPTS: "-Xms512m -Xmx512m"
discovery.type: "single-node"
cluster.routing.allocation.disk.threshold_enabled: false
networks:
- efk
fluentd:
build: fluentd
ports:
- "5140:5140/udp"
volumes:
- source: $HOME/fluentd
target: /fluentd/etc
type: bind
networks:
- efk
depends_on:
- elasticsearch
kibana:
image: docker.io/elastic/kibana:9.0.2
ports:
- "5601:5601"
networks:
- efk
depends_on:
- elasticsearch
networks:
efk:
driver: bridge
最后,配置您的应用程序以使用 Syslog 将日志发送到 EFK
quarkus.log.syslog.enable=true
quarkus.log.syslog.endpoint=localhost:5140
quarkus.log.syslog.protocol=udp
quarkus.log.syslog.app-name=quarkus
quarkus.log.syslog.hostname=quarkus-test
启动您的应用程序,您应该会看到您的日志到达 EFK 中:您可以使用 Kibana(可通过 https://:5601/ 访问)来访问它们。
使用 OpenTelemetry Logging 发送日志
OpenTelemetry Logging 能够将日志发送到兼容的 OpenTelemetry 收集器。其用法在指南 使用 OpenTelemetry Logging 中进行了描述。
使用 logging-gelf
扩展发送日志
此扩展已弃用,我们建议考虑本指南中描述的替代方案。 |
quarkus-logging-gelf
扩展将向 Quarkus 使用的基础日志记录后端 (jboss-logmanager) 添加一个 GELF 日志处理程序。默认情况下,它是禁用的,如果您启用它但仍使用另一个处理程序(默认情况下启用控制台处理程序),您的日志将被发送到两个处理程序。
您可以通过在项目基本目录中运行以下命令,将 logging-gelf
扩展添加到您的项目
quarkus extension add logging-gelf
./mvnw quarkus:add-extension -Dextensions='logging-gelf'
./gradlew addExtension --extensions='logging-gelf'
这会将以下依赖项添加到您的构建文件中
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-logging-gelf</artifactId>
</dependency>
implementation("io.quarkus:quarkus-logging-gelf")
配置 GELF 日志处理程序以将日志发送到端口 12201 上的外部 UDP 端点
quarkus.log.handler.gelf.enabled=true
quarkus.log.handler.gelf.host=localhost
quarkus.log.handler.gelf.port=12201
将日志发送到 Graylog
建议改用 Syslog 处理程序。 |
要将日志发送到 Graylog,您首先需要启动组成 Graylog 堆栈的组件
-
MongoDB
-
Elasticsearch
-
Graylog
您可以通过以下 docker-compose.yml
文件执行此操作,该文件可以通过 docker-compose up -d
启动
version: '3.2'
services:
elasticsearch:
image: docker.io/elastic/elasticsearch:9.0.2
ports:
- "9200:9200"
environment:
ES_JAVA_OPTS: "-Xms512m -Xmx512m"
discovery.type: "single-node"
cluster.routing.allocation.disk.threshold_enabled: false
networks:
- graylog
mongo:
image: mongo:4.0
networks:
- graylog
graylog:
image: graylog/graylog:4.3.0
ports:
- "9000:9000"
- "12201:12201/udp"
- "1514:1514"
environment:
GRAYLOG_HTTP_EXTERNAL_URI: "http://127.0.0.1:9000/"
# CHANGE ME (must be at least 16 characters)!
GRAYLOG_PASSWORD_SECRET: "forpasswordencryption"
# Password: admin
GRAYLOG_ROOT_PASSWORD_SHA2: "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918"
networks:
- graylog
depends_on:
- elasticsearch
- mongo
networks:
graylog:
driver: bridge
然后,您需要在 Graylog 中创建一个 UDP 输入。您可以从 Graylog Web 控制台(系统 → 输入 → 选择 GELF UDP)(可通过 https://:9000 访问)或通过 API 执行此操作。
此 curl 示例将创建一个 GELF UDP 类型的新输入,它使用 Graylog 的默认登录名 (admin/admin)。
curl -H "Content-Type: application/json" -H "Authorization: Basic YWRtaW46YWRtaW4=" -H "X-Requested-By: curl" -X POST -v -d \
'{"title":"udp input","configuration":{"recv_buffer_size":262144,"bind_address":"0.0.0.0","port":12201,"decompress_size_limit":8388608},"type":"org.graylog2.inputs.gelf.udp.GELFUDPInput","global":true}' \
https://:9000/api/system/inputs
启动您的应用程序,您应该会看到您的日志到达 Graylog 中。
将日志发送到 Logstash / Elastic Stack (ELK)
建议改用 OpenTelemetry Logging 或 Socket 处理程序。 |
Logstash 默认带有一个可以理解 GELF 格式的输入插件,我们将首先创建一个启用此插件的管道。
在 $HOME/pipelines/gelf.conf
中创建以下文件
input {
gelf {
port => 12201
}
}
output {
stdout {}
elasticsearch {
hosts => ["http://elasticsearch:9200"]
}
}
最后,启动组成 Elastic Stack 的组件
-
Elasticsearch
-
Logstash
-
Kibana
您可以通过以下 docker-compose.yml
文件执行此操作,该文件可以通过 docker-compose up -d
启动
# Launch Elasticsearch
version: '3.2'
services:
elasticsearch:
image: docker.io/elastic/elasticsearch:9.0.2
ports:
- "9200:9200"
- "9300:9300"
environment:
ES_JAVA_OPTS: "-Xms512m -Xmx512m"
discovery.type: "single-node"
cluster.routing.allocation.disk.threshold_enabled: false
networks:
- elk
logstash:
image: docker.io/elastic/logstash:9.0.2
volumes:
- source: $HOME/pipelines
target: /usr/share/logstash/pipeline
type: bind
ports:
- "12201:12201/udp"
- "5000:5000"
- "9600:9600"
networks:
- elk
depends_on:
- elasticsearch
kibana:
image: docker.io/elastic/kibana:9.0.2
ports:
- "5601:5601"
networks:
- elk
depends_on:
- elasticsearch
networks:
elk:
driver: bridge
启动您的应用程序,您应该会看到您的日志到达 Elastic Stack 中;您可以使用 Kibana(可通过 https://:5601/ 访问)来访问它们。
将日志发送到 Fluentd (EFK)
建议改用 OpenTelemetry Logging 或 Socket 处理程序。 |
首先,您需要创建一个带有所需插件的 Fluentd 镜像:elasticsearch 和 input-gelf。您可以使用以下 Dockerfile,该文件应在 fluentd
目录中创建。
FROM fluent/fluentd:v1.3-debian
RUN ["gem", "install", "fluent-plugin-elasticsearch", "--version", "3.7.0"]
RUN ["gem", "install", "fluent-plugin-input-gelf", "--version", "0.3.1"]
您可以构建镜像或让 docker-compose 为您构建它。
然后,您需要在 $HOME/fluentd/fluent.conf
中创建一个 fluentd 配置文件
<source>
type gelf
tag example.gelf
bind 0.0.0.0
port 12201
</source>
<match example.gelf>
@type elasticsearch
host elasticsearch
port 9200
logstash_format true
</match>
最后,启动组成 EFK Stack 的组件
-
Elasticsearch
-
Fluentd
-
Kibana
您可以通过以下 docker-compose.yml
文件执行此操作,该文件可以通过 docker-compose up -d
启动
version: '3.2'
services:
elasticsearch:
image: docker.io/elastic/elasticsearch:9.0.2
ports:
- "9200:9200"
- "9300:9300"
environment:
ES_JAVA_OPTS: "-Xms512m -Xmx512m"
discovery.type: "single-node"
cluster.routing.allocation.disk.threshold_enabled: false
networks:
- efk
fluentd:
build: fluentd
ports:
- "12201:12201/udp"
volumes:
- source: $HOME/fluentd
target: /fluentd/etc
type: bind
networks:
- efk
depends_on:
- elasticsearch
kibana:
image: docker.io/elastic/kibana:9.0.2
ports:
- "5601:5601"
networks:
- efk
depends_on:
- elasticsearch
networks:
efk:
driver: bridge
启动您的应用程序,您应该会看到您的日志到达 EFK 中:您可以使用 Kibana(可通过 https://:5601/ 访问)来访问它们。
Elasticsearch 索引注意事项
请注意,默认情况下,Elasticsearch 会通过检测其类型自动映射未知字段(如果在索引设置中未禁用)。如果您使用日志参数(默认情况下包含)或如果您启用 MDC 包含(默认情况下禁用),这可能会变得棘手,因为第一个日志将定义索引中消息参数(或 MDC 参数)字段的类型。
想象一下以下情况
LOG.info("some {} message {} with {} param", 1, 2, 3);
LOG.info("other {} message {} with {} param", true, true, true);
启用日志消息参数后,发送到 Elasticsearch 的第一个日志消息将具有一个 MessageParam0
参数,其类型为 int
;这将配置索引,使其具有 integer
类型的字段。当第二条消息到达 Elasticsearch 时,它将具有一个 MessageParam0
参数,其布尔值为 true
,这将生成索引错误。
要解决此限制,您可以通过配置 quarkus.log.handler.gelf.include-log-message-parameters=false
来禁用通过 logging-gelf
发送日志消息参数,或者您可以配置您的 Elasticsearch 索引以将这些字段存储为文本或关键字,Elasticsearch 将自动进行从 int/boolean 到 String 的转换。
有关 Graylog 的以下文档(但其他中央日志记录堆栈也存在相同的问题):自定义索引映射。
配置参考
配置通过常用的 application.properties
文件完成。
构建时固定的配置属性 - 所有其他配置属性都可以在运行时覆盖
配置属性 |
类型 |
默认 |
---|---|---|
布尔值 |
|
|
Logstash/Graylog 主机的主机名/IP 地址。默认情况下,它使用 UDP,在主机名前面加上 tcp: 以切换到 TCP,例如:"tcp:localhost" 环境变量: 显示更多 |
字符串 |
|
整数 |
|
|
字符串 |
|
|
是否将 Stack-Trace 发布到 StackTrace 字段。 环境变量: 显示更多 |
布尔值 |
|
仅当 环境变量: 显示更多 |
整数 |
|
是否执行 Stack-Trace 过滤 环境变量: 显示更多 |
布尔值 |
|
Java 日期模式,请参阅 环境变量: 显示更多 |
字符串 |
|
|
||
字符串 |
|
|
类型 |
默认 |
|
附加字段值。 环境变量: 显示更多 |
字符串 |
必需 |
附加字段类型规范。支持的类型:String、long、Long、double、Double 和 discover。Discover 是默认值(如果未指定),它会根据可解析性发现字段类型。 环境变量: 显示更多 |
字符串 |
|
是否包含 MDC 中的所有字段。 环境变量: 显示更多 |
布尔值 |
|
发送附加字段,其值从 MDC 中获得。字段名称以逗号分隔。示例:mdcFields=Application,Version,SomeOtherFieldName 环境变量: 显示更多 |
字符串 |
|
动态 MDC 字段允许您基于一个或多个正则表达式提取 MDC 值。多个正则表达式以逗号分隔。MDC 条目的名称用作 GELF 字段名称。 环境变量: 显示更多 |
字符串 |
|
基于模式的附加字段和 MDC 字段类型规范。键值对以逗号分隔。示例:my_field.*=String,business\..*\.field=double 环境变量: 显示更多 |
字符串 |
|
最大消息大小(以字节为单位)。如果超过消息大小,则附加程序将以多个块提交消息。 环境变量: 显示更多 |
整数 |
|
包含来自日志事件的消息参数 环境变量: 显示更多 |
布尔值 |
|
包含源代码位置 环境变量: 显示更多 |
布尔值 |
|
字符串 |
||
绕过主机名解析。如果您没有设置 环境变量: 显示更多 |
布尔值 |
|
此扩展使用 logstash-gelf
库,该库允许通过系统属性进行更多配置选项,您可以在此处访问其文档:https://logging.paluch.biz/。