ELK stack权威指南
上QQ阅读APP看书,第一时间看更新

3.6 Java日志

之前在2.2节有关codec的介绍中曾经提到过,对Java日志,除了使用multiline做多行日志合并以外,还可以直接通过Log4J写入logstash里。本节就讲述如何在Java应用环境做到这点。

3.6.1 Log4J配置

首先,需要配置Java应用的Log4J设置,启动一个内置的SocketAppender。修改应用的log4j.xml配置文件,添加如下配置段:

<appender name=“LOGSTASH” class=“org.apache.log4j.net.SocketAppender”>
<param name=“RemoteHost” value=“logstash_hostname” />
<param name=“ReconnectionDelay” value=“60000” />
<param name=“LocationInfo” value=“true” />
<param name=“Threshold” value=“DEBUG” />
</appender>

然后把这个新定义的appender对象加入root logger里,可以跟其他已有logger共存:

<root>
<level value=“INFO”/>
<appender-ref ref=“OTHERPLACE”/>
<appender-ref ref=“LOGSTASH”/>
</root>

如果是log4j.properties配置文件,则对应配置如下:

log4j.rootLogger=DEBUG, logstash
###SocketAppender###
log4j.appender.logstash=org.apache.log4j.net.SocketAppender
log4j.appender.logstash.Port=4560
log4j.appender.logstash.RemoteHost=logstash_hostname
log4j.appender.logstash.ReconnectionDelay=60000
log4j.appender.logstash.LocationInfo=true

Log4J会持续尝试连接你配置的logstash_hostname这个地址,建立连接后,即开始发送日志数据。

3.6.2 Logstash配置

Java应用端的配置完成以后,开始设置Logstash的接收端。配置如下所示,其中4560端口是Log4J SocketAppender的默认对端端口:

input {
  log4j {
    type =>“log4j-json”
    port => 4560
  }
}

3.6.3 异常堆栈测试验证

运行Logstash后,编写一个简单的Log4J程序:

import org.apache.log4j.Logger;
public class HelloExample{
    final static Logger logger = Logger.getLogger(HelloExample.class);
    public static void main(String[] args) {
        HelloExample obj = new HelloExample();
        try{
            obj.divide();
        }catch(ArithmeticException ex){
            logger.error(“Sorry, something wrong!”, ex);
        }
    }
    private void divide(){
        int i = 10 /0;
    }
}

编译运行:

# javac -cp ./logstash-1.5.0.rc2/vendor/bundle/jruby/1.9/gems/logstash-input-
    log4j-0.1.3-java/lib/log4j/log4j/1.2.17/log4j-1.2.17.jar HelloExample.java
# java -cp .:./logstash-1.5.0.rc2/vendor/bundle/jruby/1.9/gems/logstash-input-
    log4j-0.1.3-java/lib/log4j/log4j/1.2.17/log4j-1.2.17.jar HelloExample

这样即可在Logstash的终端输出看到如下事件记录:

{“message” =>“Sorry, something wrong!”,“@version” =>“1”,“@timestamp” =>“2015-07-02T13:24:45.727Z”,“type” =>“log4j-json”,“host” =>“127.0.0.1:52420”,“path” =>“HelloExample”,“priority” =>“ERROR”,“logger_name” =>“HelloExample”,“thread” =>“main”,“class” =>“HelloExample”,“file” =>“HelloExample.java:9”,“method” =>“main”,“stack_trace” =>“java.lang.ArithmeticException: / by zero\n\tat HelloExample.
    divide(HelloExample.java:13)\n\tat HelloExample.main(HelloExample.java:7)”
}

可以看到,异常堆栈直接记录在单行内了。

3.6.4 JSON Event layout

如果无法采用SocketAppender,必须使用文件方式的,其实Log4J有一个layout特性,用来控制日志输出的格式。和Nginx日志自己拼接JSON输出类似,也可以通过layout功能记录成JSON格式。

Logstash官方提供了扩展包,可以通过mvnrepository.com搜索下载:

# wget http://central.maven.org/maven2/net/logstash/log4j/jsonevent-layout/1.7/
    jsonevent-layout-1.7.jar

或者直接编辑自己项目的pom.xml添加依赖:

<dependency>
    <groupId>net.logstash.log4j</groupId>
    <artifactId>jsonevent-layout</artifactId>
    <version>1.7</version>
</dependency>

然后修改项目的log4j.properties文件如下:

log4j.rootCategory=WARN, RollingLog
log4j.appender.RollingLog=org.apache.log4j.DailyRollingFileAppender
log4j.appender.RollingLog.Threshold=TRACE
log4j.appender.RollingLog.File=api.log
log4j.appender.RollingLog.DatePattern=.yyyy-MM-dd
log4j.appender.RollingLog.layout=net.logstash.log4j.JSONEventLayoutV1

如果是log4j.xml,则修改如下:

<appender name=“Console” class=“org.apache.log4j.ConsoleAppender”>
    <param name=“Threshold” value=“TRACE” />
    <layout class=“net.logstash.log4j.JSONEventLayoutV1” />
</appender>

生成的文件就是符合Logstash标准的JSON格式了,Logstash使用下面配置读取:

input {
    file {
        codec => json
        path => [“/path/to/log4j.log”]
    }
}

生成的Logstash事件如下:

{
“mdc”:{},
“line_number”:“29”,
“class”:“org.eclipse.jetty.examples.logging.EchoFormServlet”,
“@version”:1,
“source_host”:“jvstratusmbp.local”,
“thread_name”:“qtp513694835-14”,
“message”:“Got request from 0:0:0:0:0:0:0:1%0 using Mozilla\/5.0 (Macintosh;
      Intel Mac OS X 10_9_1) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/32.0.
      1700.77 Safari\/537.36”,
“@timestamp”:“2014-01-27T19:52:35.738Z”,
“level”:“INFO”,
“file”:“EchoFormServlet.java”,
“method”:“doPost”,
“logger_name”:“org.eclipse.jetty.examples.logging.EchoFormServlet”
}

可以看到,同样达到了效果。

如果你使用的不是Log4J而是logback项目来记录Java日志,Logstash官方也有类似的扩展包,在pom.xml中改成如下定义即可:

<dependency>
  <groupId>net.logstash.logback</groupId>
  <artifactId>logstash-logback-encoder</artifactId>
  <version>4.4</version>
</dependency>