Java 日志

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

Log4J

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

  1. <appender name="LOGSTASH" class="org.apache.log4j.net.SocketAppender">
  2. <param name="RemoteHost" value="logstash_hostname" />
  3. <param name="ReconnectionDelay" value="60000" />
  4. <param name="LocationInfo" value="true" />
  5. <param name="Threshold" value="DEBUG" />
  6. </appender>

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

  1. <root>
  2. <level value="INFO"/>
  3. <appender-ref ref="OTHERPLACE"/>
  4. <appender-ref ref="LOGSTASH"/>
  5. </root>

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

  1. log4j.rootLogger=DEBUG, logstash
  2. ###SocketAppender###
  3. log4j.appender.logstash=org.apache.log4j.net.SocketAppender
  4. log4j.appender.logstash.Port=4560
  5. log4j.appender.logstash.RemoteHost=logstash_hostname
  6. log4j.appender.logstash.ReconnectionDelay=60000
  7. log4j.appender.logstash.LocationInfo=true

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

Logstash

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

  1. input {
  2. log4j {
  3. type => "log4j-json"
  4. port => 4560
  5. }
  6. }

异常堆栈测试验证

运行起来 logstash 后,编写如下一个简单 log4j 程序:

  1. import org.apache.log4j.Logger;
  2. public class HelloExample{
  3. final static Logger logger = Logger.getLogger(HelloExample.class);
  4. public static void main(String[] args) {
  5. HelloExample obj = new HelloExample();
  6. try{
  7. obj.divide();
  8. }catch(ArithmeticException ex){
  9. logger.error("Sorry, something wrong!", ex);
  10. }
  11. }
  12. private void divide(){
  13. int i = 10 /0;
  14. }
  15. }

编译运行:

  1. # 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
  2. # 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 的终端输出看到如下事件记录:

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

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

JSON Event layout

如果无法采用 socketappender ,必须使用文件方式的,其实 Log4J 有一个 layout 特性,用来控制日志输出的格式。和 Nginx 日志自己拼接 JSON 输出类似,也可以通过 layout 功能,记录成 JSON 格式。推荐使用:https://github.com/logstash/log4j-jsonevent-layout