作者:飞来来
链接:https://www.jianshu.com/p/a26da0c55255
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。-
原文地址为 https://github.com/logstash/logstash-logback-encoder#composite_encoder
logstash-logback-encoder
提供logback的编码器,布局(layouts)和追加器,来输出到json形式的日志。
同时支持日志事件集合(也就是通常意义的logger输出的日志)和访问事件集合(也就是通常意义的localhost-access的日志)。
logstash原始输入就支持输入成json形式,但是logstash的json形式输出已经发展成更高的可配置性,更加常用化的json形式日志输出器(当然是指输出到elasticsearch和其他接收者)。json输出的结构和json包含的结构是完全可控制的。
目录
- 在你的项目中使用
- 用法
- UDP 追加器
- TCP 追加器
- 链接保持
- 多个目标输出
- 重连延迟
- 写入缓冲区的大小
- SSL链接
- 异步追加器
- 编码器和样式布局器
- 记录事件字段
- 标准字段
- MDC字段
- 上下文文件
- 请求者信息字段
- 自定义字段
- 全局自定义字段
- 具体事件定义字段
- 访问事件字段
- 标准字段
- 请求头字段
- 定制标准化的字段名称
- 定制版本
- 定制时区
- 定制JSON输出工厂和启动器
- 定制日志的名称长度
- 定制追踪栈
- 前缀和后缀
- 复合编码器/样式布局器
- 提供一个普通日志事件集合
- 提供一个访问事件日志集合
- 嵌套式的json提供器
- 模式化的json提供器
- 普通日志事件的格式化
- 访问日志事件的格式化
- 如何debug
在你的项目中使用
maven引入的格式
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>4.10</version>
</dependency>
如果你在运行项目时遇到 ClassNotFoundException/NoClassDeffFoundError/NoSuchMethodError的错误,请确认你在pom文件从mavne仓库引入的需要的具体依赖(和适当的版本)存在于运行时的路径中:
- jackson-databind / jackson-core / jackson-annotations
- logback-core
- logback-classic (required for logging LoggingEvents)
- logback-access (required for logging AccessEvents)
- slf4j-api
在pom中的老版本文件也许能用,但是在pom中的文件版本是针对测试的。
用法
为了输出JSON格式日志,你必须配置logback使用下面两者之一:
- logstash-logback-encoder这个jar包提供的日志追加器,或者
- 通过logstash-logback-encoder提供的logback(或者其他jar包)的编码器和样式布局器的追加器
下面表格描述了logstash-logback-encoder提供的追加器,编码器和样式布局器:
Format | Protocol | Function | LoggingEvent | AccessEvent |
---|---|---|---|---|
Logstash | JSON | Syslog/UDP | Appender | n/a |
Logstash JSON | TCP | Appender | LogstashTcpSocketAppender | LogstashAccessTcpSocketAppender |
any | any | Appender | LoggingEventAsyncDisruptorAppender | AccessEventAsyncDisruptorAppender |
Logstash JSON | any | Encoder | LogstashEncoder | LogstashAccessEncoder |
Logstash JSON | any | Layout | LogstashLayout | LogstashAccessLayout |
General JSON | any | Encoder | LoggingEventCompositeJsonEncoder | AccessEventCompositeJsonEncoder |
General JSON | any | Layout | LoggingEventCompositeJsonLayout | AccessEventCompositeJsonLayout |
这些编码器和样式布局器通常能用在任何logback的追加器(比如RollingFIleAppender)中。
这些普通复合的JSON编码器和样式布局器通过多种JSON提供器配置,通常被用来输出JSON格式文本和数据。logstash编码和样式布局器事实上是一组预定义的普通复合JSON的输出器和样式布局器。
假如你想用标准logstash版本1格式输出,logstash布局器和样式布局器就非常容易配置。假如你想使用深度定制的输出格式,或者使用logstah的版本0输出,可以使用复合编码器和样式布局器。
AsyncDisruptorAppender是一种除了LMAX Disruptor RingBuffer,一种被用来做队列,和阻塞队列对应,其余部分和logback中的AsyncAppender*差不多的追加器。
UDP追加器
在你的logback.xml中使用LogstashSocketAppender,为普通日志事件输出JSON到syslog/UDP 通道。比如:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="stash" class="net.logstash.logback.appender.LogstashSocketAppender">
<host>MyAwesomeSyslogServer</host>
<!-- port is optional (default value shown) -->
<port>514</port>
</appender>
<root level="all">
<appender-ref ref="stash" />
</root>
</configuration>
从内部看,LogstashSocketAppender使用LogstashLayout来展示JSON格式。因此,默认地会使用复合logstash来输出。
你能在最后的学习章节中学到,你能通过LogstashLayout或者LogstashEncoder来定制LogstashSocketAppender。不必一定要在logback的配置<appender>的子节点中配置<layout>或者<encoder>。所有LogstashLayout和LogstashEncoder的属性都能在appender的级别中设定。比如你能指定配置全局变量。
<appender name="stash" class="net.logstash.logback.appender.LogstashSocketAppender">
<host>MyAwesomeSyslogServer</host>
<!-- port is optional (default value shown) -->
<port>514</port>
<customFields>{"appname":"myWebservice"}</customFields>
</appender>
目前没有办法通过syslog/UDP来传输AccessEvents。
在logstash中接受到syslog/UDP的输入,通过在logsatsh的配置中的json编码器来配置syslog或者udp输入,就像:
input {
syslog {
codec => "json"
}
}
TCP追加器
为了通过TCP输出JSON的日志输出,使用配置了LogstashEncoder和LoggingEventCompositeJsonEncoder的LogstashTcpSocketAppender。
在logback.xml中的日志追加器配置举例:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="stash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>127.0.0.1:4560</destination>
<!-- encoder is required -->
<encoder class="net.logstash.logback.encoder.LogstashEncoder" />
</appender>
<root level="DEBUG">
<appender-ref ref="stash" />
</root>
</configuration>
不同于UDP追加器,TCP追加器必须配置编码器。你能使用一个LogstashEncoder,或一个EventCompositeJsonEncoder,或者任何的logback编码器。所有的输出格式配置都在编码器的级别配置。
在追加器内部,TCP追加是异步的(使用LMAX Disruptor RingBuffer)。所有的编码和TCP链接被委托给一个单独的线程。不需要用另外的异步追加来包装TCP追加器(比如AsyncAppender和LoggingEventAsyncDistruptorAppender)。
异步追加器的所有参数会被TCP追加器验证。比如,waitStrategyType and ringBufferSize。
这个TCP追加器永远不会阻塞线程。如果缓冲区是满的(比如网络很慢的情况),这些数据事件会被丢弃。
TCP追加器的连接断开,他会自动重连。然而在断开的情况下,数据事件会丢失。
为了让logstah接受到输入,在logstash配置文件中配置使用json_lines配置logstash的tcp输入,如下:
input {
tcp {
port => 4560
codec => json_lines
}
}
为了保证TCP追加器输出的日志信息能被处理,你应该保证在你的应用存在的情况下,彻底地关闭logstash。
保持连接
如果事件频繁的输出,在服务器闲置超时的情况下,链接一般会断掉。你能通过配置来保持链接处于激活,就像这样:
<appender name="stash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
...
<keepAliveDuration>5 minutes</keepAliveDuration>
</appender>
多个目标输出
TCP追加器能配置尝试连接多个目标输出,如下:
<appender name="stash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>destination1.domain.com:4560</destination>
<destination>destination2.domain.com:4560</destination>
<destination>destination3.domain.com:4560</destination>
...
</appender>
或者
<appender name="stash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>
destination1.domain.com:4560,
destination2.domain.com:4560,
destination3.domain.com:4560
</destination>
...
</appender>
TCP追加器能够使用链接策略决定:
- 顺序连接目的地
- 确定的连接应该被重建(通过连接策略链接下一个目的地)。
日志有时只能发送到一个目的地(而不是所有的目的地)。默认的,追加器将会保持连接到目的地直到断开或者应用主动断开。一些连接策略聚焦于链接重连(如下)。如果链接断开,追加器会试图根据链接策略链接下一个目的地。
以下是可用的链接策略:
策略 | 描述 |
---|---|
首要连接 | (默认的)第一个目的地选择主要的目的地。每个后附加的目的地是次要的选择。策略更加注重首要目的地,除非他关闭了。追加器试图按照顺序连接每个被配置的目的地。如果一个链接断开,追加器会再次尝试顺序连接首要被配置的目的地。 secondaryConnectionTTL能被配置为在一个具体时间后优雅的关闭到次要目的地的链接。这种策略聚焦在追加器重新尝试连接顺序的目的地。secondaryConnectionTTL的值不能影响首要目的地的链接。比如<appender name="stash" class="net.logstash.logback.appender.LogstashTcpSocketAppender"><br /><destination>destination1.domain.com:4560</destination><br /><destination>destination2.domain.com:4560</destination><br /><destination>destination3.domain.com:4560</destination><br /><connectionStrategy><br /><preferPrimary><br /><secondaryConnectionTTL>5 minutes</secondaryConnectionTTL><br /></preferPrimary><br /></connectionStrategy><br /></appender><br /> |
轮询 | 这个策略尝试轮询连接目的地。如果一个连接失败,会试图下一个。<br /> connectionTTL能被配置为在一个具体时间后优雅的关闭目的地的链接。这种策略聚焦在追加器将会重新连接下一个目的地。<br /> 比如:<appender name="stash" class="net.logstash.logback.appender.LogstashTcpSocketAppender"><br /> <destination>destination1.domain.com:4560</destination><br /><destination>destination2.domain.com:4560</destination><br /><destination>destination3.domain.com:4560</destination><br /><connectionStrategy><br /><roundRobin><br /><connectionTTL>5 minutes</connectionTTL><br /></roundRobin><br /> </connectionStrategy><br /> </appender><br /> |
随机 | 这个策略尝试随机链接目的地。如果一个连接失败,会试图随机连接一个目的地。<br /> connectionTTL能被配置为在一个具体时间后优雅的关闭目的地的链接。 这种策略聚焦在追加器将会重新连接随机的一个目的地。<br /> 比如:<appender name="stash" class="net.logstash.logback.appender.LogstashTcpSocketAppender"><br /><destination>destination1.domain.com:4560</destination><br /><destination>destination2.domain.com:4560</destination><br /><destination>destination3.domain.com:4560</destination><br /><connectionStrategy><br /><random><br /><connectionTTL>5 minutes</connectionTTL><br /></random><br /></connectionStrategy><br /></appender><br /> |
你能用你通过实现DestinationConnectionStrategy接口来自己定义的连接策略,然后像如下所示来配置追加器:
<appender name="stash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>destination1.domain.com:4560</destination>
<destination>destination2.domain.com:4560</destination>
<destination>destination3.domain.com:4560</destination>
<connectionStrategy class="your.package.YourDestinationConnectionStrategy">
</connectionStrategy>
</appender>
重连延迟
如果所有的配置地址都连接失败,TCP追假器会默认在试图重连前等待30秒。
能通过设定reconnectionDelay字段改变延迟的时间。
<appender name="stash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
...
<reconnectionDelay>1 second</reconnectionDelay>
</appender>
写入缓冲区的大小
默认的,一个8192大小大小的缓冲区被Socket用来输出到写入流中。你能通过设定writeBufferSize的值的大小来调整。
<appender name="stash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
...
<writeBufferSize>16384</writeBufferSize>
</appender>
writeBufferSize设定为0的情况下,缓冲失效。如果缓冲失效,写入线程会减速,但是他会在严苛的网络环境下防止丢弃日志事件。
SSL链接
使用SSL,在LogstashSocketAppender或者LogstashAccessTcpSocketAppender中的<appender>节点的<ssl>自节点。
查看logback manual如何配置SSL,LogstashTcpSocketAppender的SSL能像logback的SSLSocketAppender*一样被配置。
举例,用JVM默认的密钥和证书库(truststore)来使SSL作用,如下所示:
<appender name="stash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
...
<!-- Enable SSL using the JVM's default keystore/truststore -->
<ssl/>
</appender>
使用不同的证书库(truststore),如下所示:
<appender name="stash" class="net.logstash.logback.appender.LogstashAccessTcpSocketAppender">
...
<!-- Enable SSL and use a different truststore -->
<ssl>
<trustStore>
<location>classpath:server.truststore</location>
<password>${server.truststore.password}</password>
</trustStore>
</ssl>
</appender>
所有通过Logback*TcpSocketAppender提供的logback自定义配置都被支持(比如配置密码规范,协议,算法,提供者等)。
查看logstash文档TCP的输入中关于如何使用SSL。
异步追加器
AsyncDisruptorAppender追加器和logback的AsyncAppender*追加器很相似,除了LMAX Disruptor RingBuffer被用来作为队列的生成器,作为阻塞队列的对立。这种追加器能转移到任何下游的logback追加器。
比如:
<appender name="async" class="net.logstash.logback.appender.LoggingEventAsyncDisruptorAppender">
<appender class="ch.qos.logback.core.rolling.RollingFileAppender">
...
</appender>
</appender>
异步追加器永远不会阻塞日志线程。如果循环buffer区满了(比如网路缓慢),日志事件将会丢弃。
默认的,BlockWaitStrategy被工作线程生产的这个追加器所使用。阻塞线程策略把cup利用率降到最低,但结果是更低的延迟和吞吐率。如果你需要更高的延迟和吞吐率(高CPU利用率),考虑不同方向提供的等待策略,比如SleepingWaitStrategy。
在异步追加器中使用waitStrategyType参数来配置等待策略,像这样:
<appender name="async" class="net.logstash.logback.appender.LoggingEventAsyncDisruptorAppender">
<waitStrategyType>sleeping</waitStrategyType>
<appender class="ch.qos.logback.core.rolling.RollingFileAppender">
...
</appender>
</appender>
支持的策略如下:
等待策略 | 参数 | 实现类 |
---|---|---|
blocking | 无 | BlockingWaitStrategy |
busySpin | 无 | BusySpinWaitStrategy |
liteBlocking | 无 | LiteBlockingWaitStrategy |
sleeping | 无 | SleepingWaitStrategy |
yielding | 无 | YieldWaitStrategy |
phasedBackoff{spinTime,yieldTime,timeUnit,fallbackStrategy} <br /> 比如phasedBackoff{10,60,seconds,blocking} | 1.spinTime-放弃前的等待时间<br /> 2.yieldTim-放弃回落到fallbackStrategy的时间<br /> 3.timeUnit-spinTime和yieldTime的时间单位,时间单位的字符串(比如 second)<br />4.fallbackStrategy-超时回退到等待策略的字符串 | PhasedBackoffWaitStrategy |
timeoutBlocking{timeout,timeUnit}<br />比如:timeoutBlocking{1,minutes} | 1.timeout-抛出异常前的阻塞时间<br />2.时间单位-超时时间的单位(比如:seconds) | TimeoutBlockingWaitStrategy |
查看AsyncDistruptorAppender的其他配置参数(比如:ringBufferSize,producerType,threadNamePrefix,daemon,和droppedWarnFrequency)。
为了保证产生的日志有机会被异步追加器(包含TCP追加器)处理和确保后台线程已经停止,当你的程序退出你需要干净地关闭lobgack。
编码器和样式布局器
你可以使用logstash-logback-encoder库提供的任何编码器和布局器和其他的logback追加器。
比如,输出一个日志事件到文件,可在在你的logabck.xml中使用RollingFileAppender的LogstashEncoder。
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="stash" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>info</level>
</filter>
<file>/some/path/to/your/file.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/some/path/to/your/file.log.%d{yyyy-MM-dd}</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder class="net.logstash.logback.encoder.LogstashEncoder" />
</appender>
<root level="all">
<appender-ref ref="stash" />
</root>
</configuration>
为了输出访问日志事件到文件,向下面一样配置你的logback-access.xml:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="stash" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/some/path/to/your/file.log</file>
<encoder class="net.logstash.logback.encoder.LogstashAccessEncoder" />
</appender>
<appender-ref ref="stash" />
</configuration>
能用配置LogstashEncoder和LogstashAccessEncoder的方法来配置LogstashLayout和LogstashAccesLayout。本文档中的其他的例子使用的是编码器(encoders),但是布局器(layouts)的配置也相同。
为了在logstash接收到文件输入,要想下面这样配置输入文件:
input {
file {
path => "/some/path/to/your/file.log"
codec => "json"
}
}
记录事件字段
下面的描述字段包含了被以下构件记录的事件默认的JSON输出:
- LogstashEncoder
- LogstashLayout,和
- logstash追加器
如果你使用了复合的编码器和布局器,那么写入的字段会根据你提供的配置有所不同。
标准字段
如果没有另外的说明,这些字段将会展示在每个日志事件中。这里列出的字段名称是默认的字段名称。这些字段能被自定义(详见下文)
字段 | 描述 |
---|---|
@timestamp | 日志事件的时间。(yyyy-MM-dd'T'HH:mm:ss.SSSZZ)详见定制时区 |
@version | logstash格式版本 详见定制版本 |
message | 格式化的日志事件消息 |
logger_name | 记录事件的记录器名称 |
thread_name | 记录事件的线程名称 |
level | 事件等级的字符串名称 |
level_value | 事件等级的数字值 |
stack_trace | (仅在抛出事件中打印)抛出事件的追踪栈。栈由换行分隔。 |
tags | (仅在tags字段被发现时有效)没有明确处理任何标记名称。(比如:MarkerFactory.getMarker中的标记会包含在tags中,而Markers中的不会)。 |
MDC字段
默认地,每个在映射诊断上下文(the Mapped Diagnostic Context ,MDC,)(org.slf4j.MDC)中的条目将会作为一个日志事件的字段来展示。这可以通过在编码器,布局器和追加器中指定<includeMdc>false</includeMdc>来完全禁用。你也能在MDC中配置指定的项来包含或者排除,像如下所示:
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeMdcKeyName>key1ToInclude</includeMdcKeyName>
<includeMdcKeyName>key2ToInclude</includeMdcKeyName>
</encoder>
或者
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<excludeMdcKeyName>key1ToExclude</excludeMdcKeyName>
<excludeMdcKeyName>key2ToExclude</excludeMdcKeyName>
</encoder>
当key的名称被指定包含,那么其他所有字段都被排除。当key的名称被指定排除,那么其他所有字段都被包含。同时配置包含key的名称和排除key的名称是错误的。
上下文文件
默认地,每个logback上下文属性(cn.qos.logback.core.Context)将会作为一个日志事件的字段来展示。可以在编码器,布局器和追加器中配置<includeContext>false<includeContext>来禁用。
请注意,logback的1.1.10之前的版本在上下文中默认包含HOSTNAME属性。从1.1.10开始,HOSTNAME被延迟计算,不再被默认包含在其中。
请求者信息字段
编码器吗,布局器和追加器默认的不包含请求者信息。请求者信息是昂贵的计算,应该在繁忙的的环境中避免使用。
如果要打开请求者信息功能,在配置中包含属性<includeCallerData>。
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeCallerData>true</includeCallerData>
</encoder>
如果编码器包含了异步追加器,比如AsyncAppender,LoggingEventAsyncDisruptorAppender,或者LogstashTcpSocketAppender,追加器的includeCallerData属性必须设置为true。
当开启了请求者信息字段功能,日志事件包含以下字段:
字段 | 描述 |
---|---|
caller_class_name | 输出日志事件的类的全量名称 |
caller_method_name | 输出日志事件的方法名称 |
caller_file_name | 输出日志事件的行的名称 |
caller_line_number | 输出日志事件的行号 |
自定义字段
除了上述的字段,你能在日志事件中全域添加其他字段,或者在逐个事件基础上添加。
全局自定义字段
在日志事件中添加自定义字段如下:
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<customFields>{"appname":"myWebservice","roles":["customerorder","auth"],"buildinfo":{"version":"Version 0.1.0-SNAPSHOT","lastcommit":"75473700d5befa953c45f630c6d9105413c16fe1"}}</customFields>
</encoder>
或者在访问日志事件中:
<encoder class="net.logstash.logback.encoder.LogstashAccessEncoder">
<customFields>{"appname":"myWebservice","roles":["customerorder","auth"],"buildinfo":{"version":"Version 0.1.0-SNAPSHOT","lastcommit":"75473700d5befa953c45f630c6d9105413c16fe1"}}</customFields>
</encoder>
具体事件定义字段
记录消息时,你能通过使用
- StructuredArguments提供的结构化字段,或
- Markers提供的标记
来添加字段来输出到JSON。
这两种方式的不同主要有一下两点:
- StructuredArguments在日志事件的格式化消息中(当消息中有这个参数时)和JSON输出中
- 如果使用复合编码器和布局器提供参数,那JSON输出中包含StructuredArguments。
- Markers只会写入到JSON输出中,而不会在日志事件的格式化消息中。
- 如果使用LogstashEncoder/Layout,或者使用logstashMarkers提供的复合encoder/layouts,那么JSON输出中会包含Markers。
即使消息没有包含参数的值,你也能使用StructureArguments。在这个例子中,参数会被写入到JSON输出和非格式化消息中(Markers能提供相同的有效行为)。你一般来说你应该使用StructureArguments,除非你有标志计数器和参数计数不匹配的静态解释器。
StructuredArgments和markers需要构建其他对象。因此,最好的做法是用logger.isXXXEnable()来包含日志行。如果禁用日志级别那么避免构建对象。
使用StructuredArgment的例子:
import static net.logstash.logback.argument.StructuredArguments.*
/*
* Add "name":"value" to the JSON output,
* but only add the value to the formatted message.
*
* The formatted message will be `log message value`
*/
logger.info("log message {}", value("name", "value"));
/*
* Add "name":"value" to the JSON output,
* and add name=value to the formatted message.
*
* The formatted message will be `log message name=value`
*/
logger.info("log message {}", keyValue("name", "value"));
/*
* Add "name":"value" ONLY to the JSON output.
*
* Since there is no parameter for the argument,
* the formatted message will NOT contain the key/value.
*
* If this looks funny to you or to static analyzers,
* consider using Markers instead.
*/
logger.info("log message", keyValue("name", "value"));
/*
* Add multiple key value pairs to both JSON and formatted message
*/
logger.info("log message {} {}", keyValue("name1", "value1"), keyValue("name2", "value2")));
/*
* Add "name":"value" to the JSON output and
* add name=[value] to the formatted message using a custom format.
*/
logger.info("log message {}", keyValue("name", "value", "{0}=[{1}]"));
/*
* In the JSON output, values will be serialized by Jackson's ObjectMapper.
* In the formatted message, values will follow the same behavior as logback
* (formatting of an array or if not an array `toString()` is called).
*
* Add "foo":{...} to the JSON output and add `foo.toString()` to the formatted message:
*
* The formatted message will be `log message <result of foo.toString()>`
*/
Foo foo = new Foo();
logger.info("log message {}", value("foo", foo));
/*
* Add "name1":"value1","name2":"value2" to the JSON output by using a Map,
* and add `myMap.toString()` to the formatted message.
*
* Note the values can be any object that can be serialized by Jackson's ObjectMapper
* (e.g. other Maps, JsonNodes, numbers, arrays, etc)
*/
Map myMap = new HashMap();
myMap.put("name1", "value1");
myMap.put("name2", "value2");
logger.info("log message {}", entries(myMap));
/*
* Add "array":[1,2,3] to the JSON output,
* and array=[1,2,3] to the formatted message.
*/
logger.info("log message {}", array("array", 1, 2, 3));
/*
* Add fields of any object that can be unwrapped by Jackson's UnwrappableBeanSerializer to the JSON output.
* i.e. The fields of an object can be written directly into the JSON output.
* This is similar to the @JsonUnwrapped annotation.
*
* The formatted message will contain `myobject.toString()`
*/
logger.info("log message {}", fields(myobject));
/*
* In order to normalize a field object name, static helper methods can be created.
* For example, `foo(Foo)` calls `value("foo" , foo)`
*/
logger.info("log message {}", foo(foo));
针对所有的结构化参数类型,简单的方法是可用的。比如,代替keyValue(key,value),你能使用kv(key,value)。
比如使用Markers:
import static net.logstash.logback.marker.Markers.*
/*
* Add "name":"value" to the JSON output.
*/
logger.info(append("name", "value"), "log message");
/*
* Add "name1":"value1","name2":"value2" to the JSON output by using multiple markers.
*/
logger.info(append("name1", "value1").and(append("name2", "value2")), "log message");
/*
* Add "name1":"value1","name2":"value2" to the JSON output by using a map.
*
* Note the values can be any object that can be serialized by Jackson's ObjectMapper
* (e.g. other Maps, JsonNodes, numbers, arrays, etc)
*/
Map myMap = new HashMap();
myMap.put("name1", "value1");
myMap.put("name2", "value2");
logger.info(appendEntries(myMap), "log message");
/*
* Add "array":[1,2,3] to the JSON output
*/
logger.info(appendArray("array", 1, 2, 3), "log message");
/*
* Add "array":[1,2,3] to the JSON output by using raw json.
* This allows you to use your own json seralization routine to construct the json output
*/
logger.info(appendRaw("array", "[1,2,3]"), "log message");
/*
* Add any object that can be serialized by Jackson's ObjectMapper
* (e.g. Maps, JsonNodes, numbers, arrays, etc)
*/
logger.info(append("object", myobject), "log message");
/*
* Add fields of any object that can be unwrapped by Jackson's UnwrappableBeanSerializer.
* i.e. The fields of an object can be written directly into the json output.
* This is similar to the @JsonUnwrapped annotation.
*/
logger.info(appendFields(myobject), "log message");
有关将JSON添加到输出的其他不建议使用的方法,请参阅DEPRECATED.md。
访问事件字段
下面的条目描述了,通过
- LogstashAccessEncoder,
- LogstashAccessLayout,and
- logstash的访问追加器
编写的AccessEvent的JSON输出中默认包含的字段。
如果你使用了复合的编码器和布局器,那么写入的字段会根据你提供的配置有所不同。
标准字段
如果没有另外的说明,这些字段将会展示在每个日志事件中。这里列出的字段名称是默认的字段名称。这些字段能被自定义(详见下文)
字段 | 描述 |
---|---|
@timestamp | 日志事件的时间。(yyyy-MM-dd'T'HH:mm:ss.SSSZZ)详见定制时区 |
@version | logstash格式版本 详见定制版本 |
@message | ${remoteHost} - ${remoteUser} [${timestamp}] "${requestUrl}" ${statusCode} ${contentLength}形式的消息 |
@fields.method | HTTP方法 |
@fields.protocol | HTTP协议 |
@fields.status_code | HTTP状态码 |
@fields.requested_url | 请求路径 |
@fields.requested_uri | 请求URI |
@fields.remote_host | 远程主机 |
@fields.HOSTNAME | 远程主机的另一个字段(不确定是否存存在) |
@fields.remote_user | 远程用户 |
@fields.content_length | 内容长度 |
@fields.elapsed_time | 逝去的毫秒数 |
请求头字段
请求和相应的头部不是默认记录的,但是能通过指定特定的字段来开启,像如下:
<encoder class="net.logstash.logback.encoder.LogstashAccessEncoder">
<fieldNames>
<fieldsRequestHeaders>@fields.request_headers</fieldsRequestHeaders>
<fieldsResponseHeaders>@fields.response_headers</fieldsResponseHeaders>
</fieldNames>
</encoder>
详见下文的定制标准化字段名称。
用小写字母写入头部名称(所以只有大小写不同的头名称被视为是相同),设定lowerCaseFieldName为true,像如下:
<encoder class="net.logstash.logback.encoder.LogstashAccessEncoder">
<fieldNames>
<fieldsRequestHeaders>@fields.request_headers</fieldsRequestHeaders>
<fieldsResponseHeaders>@fields.response_headers</fieldsResponseHeaders>
</fieldNames>
<lowerCaseHeaderNames>true</lowerCaseHeaderNames>
</encoder>
定制标准化的字段名称
在编码器或者追加器的配置中使用fieldNames配置字段,能使LoggingEvents和AccessEvents上的标准化字段被自定义。
比如:
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<fieldNames>
<timestamp>time</timestamp>
<message>msg</message>
...
</fieldNames>
</encoder>
通过设定[ignore]字段来忽略输出中的字段。
对于LoggingEvents,参看LogstashFieldName,能定义所有的字段。每个java类中的字段名都是xml节点用于指定字段名的名称(比如,logger,levelValue)。额外地,一个单独的缩写名称集合可以像这样配置:
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<fieldNames class="net.logstash.logback.fieldnames.ShortenedFieldNames"/>
</encoder>
对于LoggingEvents,通过分别指定调用者,mdc和上下文的字段来记录JSON事件中的记录者的信息,MDC属性,上下文属性。
对于访问事件日志,查看LogstashAccessFieldName中,能被配置的所有字段。每个java类中的字段名都是xml节点用于指定字段名的名称(比如,fieldsMethod,fieldsProtocol)
定制版本
版本字段值默认是1。
值能被改变,如下:
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<version>2</version>
</encoder>
也能写成一个字段(代替一个数字):
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<writeVersionAsString>true</writeVersionAsString>
</encoder>
定制时区
默认地,在java平台的默认时区中,时间戳会被记录。你能改变时区:
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<timeZone>UTC</timeZone>
</encoder>
通过Java中的TImeZone.getTimeZone(String id)方法,timeZone的值节点能接受任何字符串参数。
定制JSON输出工厂和启动器
通过创建JsonFactoryDecorator和JsonGeneratorDecorator的实例,用于序列化输出的JsonFactory和JsonGenerator能被自定义。
比如,你能够像这样优雅地打印:
public class PrettyPrintingDecorator implements JsonGeneratorDecorator {
@Override
public JsonGenerator decorate(JsonGenerator generator) {
return generator.useDefaultPrettyPrinter();
}
}
或者自定义mapping对象:
public class ISO8601DateDecorator implements JsonFactoryDecorator {
@Override
public MappingJsonFactory decorate(MappingJsonFactory factory) {
ObjectMapper codec = factory.getCodec();
codec.setDateFormat(new ISO8601DateFormat());
return factory;
}
}
像下面这样,在logback.xml中指定装饰器:
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<jsonGeneratorDecorator class="your.package.PrettyPrintingDecorator"/>
<jsonFactoryDecorator class="your.package.ISO8601DateDecorator"/>
</encoder>
定制日志的名称长度
对于LoggeringEvents,你能像%logger{36}这样普通匹配风格来缩短日志名称的长度。这里能看到缩短的例子:
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<shortenedLoggerNameLength>36</shortenedLoggerNameLength>
</encoder>
定制追踪栈
针对LoggerEvents,追踪栈默认用logback的ExtendThrowableProxyConverter来格式化。但是,为了格式化追踪栈,你能用任何ThrowableHandlingCovert来配置编码器。
在logstash-logback-encoder库中包含一个强大的shortenedThrowableConverter,可以通过以下方式来格式化追踪栈:
- 限制追踪栈每次抛出的数目(实用于每个独立的异常抛出,比如:caused-bys和suppressed)
- 限制追踪的字符串的整体长度
- 缩写类名
- 基于正则表达式过滤不需要的追踪栈元素
- 如果追踪栈应该被记录,使用evaluators来决定
- 普通顺序输出(根记录在最末尾),或者根记录在最开始
- 计算和内联每个异常堆栈的十六进制散列
比如:
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter">
<maxDepthPerThrowable>30</maxDepthPerThrowable>
<maxLength>2048</maxLength>
<shortenedClassNameLength>20</shortenedClassNameLength>
<exclude>sun\.reflect\..*\.invoke.*</exclude>
<exclude>net\.sf\.cglib\.proxy\.MethodProxy\.invoke</exclude>
<evaluator class="myorg.MyCustomEvaluator"/>
<rootCauseFirst>true</rootCauseFirst>
<inlineHash>true</inlineHash>
</throwableConverter>
</encoder>
若果你需要,在任何非JSON格式的输出中,ShortenedthrowableConvert能在PatternLayout中格式化追踪栈。
前缀和后缀
你能指定前缀(在JSON对象之前写入)或后缀(在JSON对象之后写入),也可以同时指定。这可能是你使用日志管道所需要的。例如:
- 如果你使用the Common Event Expression格式的syslog,你需要添加@cee的前缀。
- 如果你使用其他syslog的目的地,你需要添加标准的syslog头部消息。
- 如果你使用Loggly,你也许需要添加自定义的token。
比如,为UDPsyslog添加标准的syslog头部消息,像如下配置:
<configuration>
<conversionRule conversionWord="syslogStart" converterClass="ch.qos.logback.classic.pattern.SyslogStartConverter"/>
<appender name="stash" class="net.logstash.logback.appender.LogstashSocketAppender">
<host>MyAwesomeSyslogServer</host>
<!-- port is optional (default value shown) -->
<port>514</port>
<prefix class="ch.qos.logback.classic.PatternLayout">
<pattern>%syslogStart{USER}</pattern>
</prefix>
</appender>
...
</configuration>
当使用LogstashEncoder,LogstashAccessEncoder或者自定义的encoder,那么前缀是Encoder,而不是Layout,所以你需要在LayoutWrappingEncoder中包含PatternLayout,像如下:
<configuration>
...
<appender ...>
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
...
<prefix class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>@cee:</pattern>
</layout>
</prefix>
</encoder>
</appender>
</configuration>
注意,logback.xml的配置读取器会忽略xml元素中的空白。因此,如果你想用空白来匹配前缀或后缀,那首先添加空白,然后添加像%mdc{keyThatDoesNotExist}这样的串,之后会像<pattern>your pattern %mdc{keyThatDoesNotExist}</pattern>一样。这将会导致logback根据需要输出空格,然后是不存在MDC秘钥的空白字符串。
复合编码器/样式布局器
如果你想在JSON格式和LoggingEventAccessEvents数据中高度自定义,使用LoggingEventCompositeJsonEncoder和AccessEventCompositeJsonEncoder(或者对应的layouts)。
这些encoder/layouts由一个或多个提供JSON输出的JSON生成器复合组成。在复合encoders/layouts中没有提供者被默认配置。你必须自己添加你想要的。
比如:
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<mdc/>
<pattern>
<pattern>
{
"timestamp": "%date{ISO8601}",
"myCustomField": "fieldValue",
"relative": "#asLong{%relative}"
}
</pattern>
</pattern>
<stackTrace>
<throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter">
<maxDepthPerThrowable>30</maxDepthPerThrowable>
<maxLength>2048</maxLength>
<shortenedClassNameLength>20</shortenedClassNameLength>
<exclude>^sun\.reflect\..*\.invoke</exclude>
<exclude>^net\.sf\.cglib\.proxy\.MethodProxy\.invoke</exclude>
<evaluator class="myorg.MyCustomEvaluator"/>
<rootCauseFirst>true</rootCauseFirst>
</throwableConverter>
</stackTrace>
</providers>
</encoder>
logstash-logback-encoder库包含了多个开箱即用的提供器。你甚至能把你自己导出的JsonProvider做成插件。每个提供者都有自己配置项来进一步定制他。
LoggerEvents提供者
对于LoggingEvents,可用的提供器和配置属性(默认附带地)如下:
提供者 | 描述和属性 |
---|---|
timestamp | 时间戳事件 fieldName - 输出的字段名(@timestamp) pattern - 输出格式(yyyy-MM-dd 'T' HH:mm:ss.SSSZZ) timeZone - 时区(当地时间) |
version | LogstashJSON的格式版本 fieldName - 输出的字段名(@version) version - 输出值1 writeAsString - 版本号写成的字符串值(false=写成一个数字) |
message | 格式化的记录事件信息 fieldName - 输出的字段名(message) |
rawMessage | 原生的记录事件消息,和参数被解析的格式化日志相对 fieldName - 输出的字段名(raw_message) |
loggerName | 原生的记录事件消息,和参数被解析的格式化日志相对 fieldName - 输出的字段名(raw_message) |
threadName | 记录消息事件的线程名称 fieldName - 输出的字段名(thread_name) |
logLevel | 记录级别文本(比如INFO,WARN等) fieldName - 输出的字段名(level) |
logLevelValue | 记录级别的数字记录 fieldName - 输出的字段名(level_value) |
callerData | 关于日志输出输出者的位置消息(类/方法/文件/行) fieldName - 子对象的字段名(没有子对象) classFieldName - 类名的字段名(caller_class_name) methodFieldName - 方法名的字段名(caller_method_name) fileFieldName - 文件名的字段名(caller_file_name) lineFieldName - 行号的字段名(caller_line_number) |
stackTrace | 事件的抛出记录的堆栈追踪。堆栈信息通过空行来分隔。 fieldName - 输出的字段名(stack_trace) throwableConverter - ThrowableHandlingConverter用于格式化追踪栈。 |
stackHash | (只有抛出被记录)计算和输出抛出栈的十六进制hash码 帮助定义同时发生的相同错误 fieldName - 输出的字段名(stack_hash) exclude - 在计算错误hash时,正则表达式匹配要排除的追踪栈元素 exclusions - 在计算错误hash时,匹配了要排除的追踪栈元素的正则表达式列表。 |
context | 输出事件的logback上下文。 fieldName - 子对象的名称(不再有更多子对象) |
contextName | logback上下文的输出字段名称 fieldName - 输出的字段名(context) |
mdc | 来自the Mapped Diagnostic Context(MDC)的输出事件。默认包含所有事件。当包含了关键字段,所有其他的字段将会排除。当排除了关键字段,其他关键字段将会被包含。同时包含排除和包含字段是一种错误的配置。 fieldName - 子对象名称(不再有更多的子对象) includeMdckeyName - 包含的关键字名称(all) excludeMdckeyName - 包含的关键字名称(none) |
tags | 作为一个分隔号列表的logback输出标记 fieldName - 输出的字段名(tags) |
logstashMarkers | 在事件定制化字段中,用于输出指定的Logstash标记。 |
nestedField | 被配置的字段中嵌套的JSON对象。 嵌套对象经常通过其他提供者添加到这种提供者中。 详见嵌套式的json提供器 fieldName - 输出的字段名 providers - 被嵌套对象填充的提供者。 |
pattern | 当logback的PatternLayout替换支持的模式,配置的JSON对象字符串的输出字段。 详见模式化的json提供器 pattern - JSON对象字符串(不是默认的) |
arguments | 参数数组对象的输出字段 详见具体事件定义字段 fieldName - 子对象的字段名(没有更多的子对象) includeNonStructureArguments - 包含的参数,且不是StructuredArgument的实体。(默认是false)对象字段名称将是参数索引的nonStructuredArgument前置。 nonStructuredArgment - 对象字段名称的前缀(默认是arg) |
uuid | 作为字段值输出的随机UUID,为想提供记录行唯一身份认证提供了便利。 fieldName - 输出的字段名(uuid) strategy - UUID的普遍性策略(random)。支持的字段有random,time ethernet - 只有“time”策略能用。当被定义后,MAC地址会是UUID的组成之一。将他设置为接口值以使用真实的底层网络接口或特定值(如 00:C0:F0:3D:5B:7C)。 |
AccessEvents提供者
对于AccessEvents,可用的提供器和配置属性(默认附带地)如下:
提供者 | 描述和属性 |
---|---|
timestamp | 时间戳事件 fieldName - 输出的字段名(@timestamp) pattern - 输出格式(yyyy-MM-dd 'T' HH:mm:ss.SSSZZ) timeZone - 时区(当地时间) |
version | LogstashJSON的格式版本 fieldName - 输出的字段名(@version) version - 输出值1 writeAsString - 版本号写成的字符串值(false=写成一个数字) |
message | 形如${remoteHost} - ${remoteUser} [${timestamp}] "${requestUrl}" ${statusCode} ${contentLength} 的消息。fieldName - 输出的字段名(@message) |
method | HTTP的方法。 fieldName - 输出的字段名(@fields.method) |
protocol | HTTP协议 fieldName - 输出的字段名(@fields.protocol) |
statusCode | HTTP状态码 fieldName - 输出的字段名(@fields.status_code) |
requestedUrl | 请求的URL fieldName - 输出的字段名(@fields.requested_url) |
requestedUri | 请求的URI fieldName - 输出的字段名(@fields.requested_uri) |
remoteHost | 远端地址 fieldName - 输出的字段名(@fields.remote_host) |
remoteUser | 远端用户 fieldName - 输出的字段名(@fields.remote_user) |
contentLength | 内容长度 fieldName - 输出的字段名(@fields.content_length) |
elapsedTime | 逝去的毫秒数 fieldName - 输出的字段名(@fields.elapsed_time) |
requestHeaders | 请求头 fieldName - 输出的字段名(没有默认,必须提供) lowerCaseHeaderNames - 小写字母写入头名称(false) |
responseHeaders | 相应头 fieldName - 输出的字段名(没有默认,必须提供) lowerCaseHeaderNames - 小写字母写入头名称(false) |
nestedField | 被配置的字段中嵌套的JSON对象。 嵌套对象经常通过其他提供者添加到这种提供者中。 详见嵌套式的json提供器 fieldName - 输出的字段名 providers - 被嵌套对象填充的提供者。 |
pattern | 当logback的PatternLayout替换支持的模式,配置的JSON对象字符串的输出字段。 详见模式化的json提供器 pattern - JSON对象字符串(不是默认的) |
pattern | 当logback access的PatternLayout替换支持的模式,配置的JSON对象字符串的输出字段。 pattern - JSON对象字符串(没有默认) |
嵌套式的json提供器
在JSON事件输出中,使用嵌套字段提供器,创建子对象。
比如:
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp/>
<nestedField>
<fieldName>@fields</fieldName>
<providers>
<logLevel/>
</providers>
</nestedField>
</providers>
</encoder>
将会产生如下的结果:
{
"@timestamp":"...",
"@fields":{
"level": "DEBUG"
}
}
模式化的json提供器
当使用复合式JSON encoder/layout时,patternJSON提供者能用来对部分记录JSON的输出定义模板。在模板中,encoder/layout会填充值。模板中的每个值都被视作的logack标准PatternLayout的模式。所以能复合字符串(一些常量)和变量转换符(像是日期中的%d)。
模式字符串必须是JSON格式(在模式提供者中被配置)。JSON记录输出包含了JSON对象常量。
比如:
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<!-- provides the timestamp -->
<timestamp/>
<!-- provides the version -->
<version/>
<!-- provides the fields in the configured pattern -->
<pattern>
<!-- the pattern that defines what to include -->
<pattern>
{ "level": "%level" }
</pattern>
</pattern>
</providers>
</encoder>
会产生如下的结果:
{
"@timestamp":"...",
"@version": 1,
"level": "DEBUG"
}
正真厉害的事实是有很多标准转换符,所以你能自定义你记录什么和如何记录。比如:你能从MDC中通过%mdc{mykey}记录单个指定的值。或者,对于访问日志,你可以用%i{User-Agent}记录单个请求头。
你能在你的模式中使用嵌套对象和数组
如果你在一个模式中使用null,数字,或者布尔常量,这些会在JSON结果保持类型不变。然而对于转换模式来说,只有字符串的值将会被搜索。因为这些模式是通过PatternLayout发送的。结果总是一个字符串,甚至是有些你觉得会是数字的值,就像%b(字节发送,在访问日志中)。
你能在logstash一侧处理类型转换或是通过这些编码器使用指定转换提供者。
- #asLong{...} - 评估大括号中的内容,把字符串转换为long类型(或者转换失败变为null)
- #asDouble{...} - 评估大括号中的内容,把字符串转换为Double类型(或者转换失败变为null)
- #asJson{...} - 评估大括号中的内容,把字符串转换为json类型(或者转换失败变为null)
举例:
<pattern>
{
"line_str": "%line",
"line_long": "#asLong{%line}",
"has_message": "#asJson{%mdc{hasMessage}}",
"json_message": "#asJson{%message}"
}
</pattern>
这是记录代码:
MDC.put("hasMessage", "true");
LOGGER.info("{\"type\":\"example\",\"msg\":\"example of json message with type\"}");
将会产生如下的结果:
{
"line_str":"97",
"line_long":97,
"has_message":true,
"json_message":{"type":"example","msg":"example of json message with type"}
}
注意,发送给line_long的值是数字类型,即使在你的模式中这是一个应用文本。json_message字段是json对象,不是字符串。
普通日志事件的格式化
对于记录事件,推荐logback的经典模式PatternLayout。
比如:
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp/>
<pattern>
<pattern>
{
"custom_constant": "123",
"tags": ["one", "two"],
"logger": "%logger",
"level": "%level",
"thread": "%thread",
"message": "%message",
...
}
</pattern>
</pattern>
</providers>
</encoder>
访问日志事件的格式化
对于访问事件,推荐logback访问事件的经典模式PatternLayout。
比如:
<encoder class="net.logstash.logback.encoder.AccessEventCompositeJsonEncoder">
<providers>
<pattern>
<pattern>
{
"custom_constant": "123",
"tags": ["one", "two"],
"remote_ip": "%a",
"status_code": "%s",
"elapsed_time": "%D",
"user_agent": "%i{User-Agent}",
"accept": "%i{Accept}",
"referer": "%i{Referer}",
"session": "%requestCookie{JSESSIONID}",
...
}
</pattern>
</pattern>
</providers>
</encoder>
这也是被AccessEVents使用的特定操作符:
- #nullNA{...} - 如果大括号中的值为破折号,将会被null替代。
你会这么做,因为很多的来自lobback-access的PatternLayout转换符会被解释成没有任何值“-”(比如cookie,头信息或者请求属性)。
所以用这种模式:
<pattern>
{
"default_cookie": "%requestCookie{MISSING}",
"filtered_cookie": "#nullNA{%requestCookie{MISSING}}"
}
</pattern>
将会产生:
{
"default_cookie": "-",
"filtered_cookie": null
}
如何debug
在执行过程中,在logstash-logback-encoder中的encoders/appenders/layouts将会添加logback的状态信息到logback的StatusManager。
默认地,配置阶段中logabck只能在控制台中显示 WARN/ERROR 状态消息。 在真事环境中没有信息会显示(甚至是 WARN/ERROR)。
如果你对定义问题有麻烦(比如事件没有获得),你能打开logback的debug模式,或者在logback手册中添加状态监听器作为详细说明。
作者:飞来来
链接:https://www.jianshu.com/p/a26da0c55255
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。