三姉妹年子ママのtoi toi toi!日記

長女5歳、次女4歳、三女3歳の子育て中です。いつの間にか3人の子供に囲まれて日々過ごしています。3年育休から復職し、慌ただしい日々ですが、この日々を少しでも残しておきたいと思い書いています。最近は特に子供の教育についてばかり考えています。

Fluentdのmultilineを使ってスタックトーレスを1行に変換し転送~エラー出力のサンプルアプリあり

ここ数週間の備忘録UPです。

【やりたかったこと】
Tomcatのデフォルト標準出力ではメッセージに日付等のヘッダー情報が付与されない為、log4jを利用し日付のヘッダーログを出力する。
これにより、スタックトレースを1つのブロックとして認識させるようにし、Fluentdのmultilineにてスタックトレースを1行に変換し転送させる。

◆開発環境にてサンプルアプリの準備
まず、
●ecripseにてエラーを出すアプリを作成する
●ecripseにlog4j2のJARファイル取り込む
●ecripseにlog4j2.xmlを配置する
を実施。
ecripseのイメージは以下。
log4j2.xmlの配置場所とlog4j2のJARファイルの置き場所に注意。
JARファイルはPCにダウンロードしたファイルをecripseにスライドすると取り込む事が出来ます。
f:id:mirea-no-k:20181229211609p:plain

アプリのポイント
・NullPointerExceptionを発生
・log4j2のLoggerでエラー出力
アプリのコード

package Sample;

import java.io.IOException;
import java.io.PrintWriter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class HelloWorldServlet
 */
@WebServlet("/HelloWorldServlet")
public class HelloWorldServlet extends HttpServlet {
 private static final long serialVersionUID = 1L;
 
    /**
     * @see HttpServlet#HttpServlet()
     */
    public HelloWorldServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

 /**
  * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
  */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
     Logger logger = LogManager.getLogger(HelloWorldServlet.class);
     try { 
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head>");
        out.println("<title>Hello World!</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h1>Hello World!</h1>");
        out.println("<p>First Servlet</p>");
        out.println("</body></html>");
        
        throw new NullPointerException();
        
     } catch (Exception e) {
      //ex.printStackTrace();
      logger.error("error", e);
     }
    }

}

log4j2.xmlのコード

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<configuration status="OFF">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <!-- <PatternLayout pattern="%d{DEFAULT} [%t] %-5level %msg%n"/> -->
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %msg%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console"/>
        </Root>
        <Logger name="test" level="error" additivity="false">
            <AppenderRef ref="Console"/>
        </Logger>
        <Logger name="test2" level="info">
            <AppenderRef ref="Console" />
        </Logger>
    </Loggers>
</configuration>

次に
●ecripseにTomcat9を登録し実行
Tomcatの登録方法を以下のサイトを参考にした。
EclipseにTomcatを登録する手順 | ITSakura

今回はVM引数に追加

-Dlog4j.configurationFile=file://C:\apache-tomcat-9.0.13\apache-tomcat-9.0.13\lib\log4j2.xml

f:id:mirea-no-k:20181230232941p:plain

サーバーで実行した結果
f:id:mirea-no-k:20181230233318p:plain

◆Linux環境の準備
サンプルアプリが完成したので、WARファイルでエクスポートし、AWS環境で実行していきます。
AWS上にログの送信サーバー用と受信サーバー用の2サーバーEC2で作成します。
EC2はLinuxであればなんでもよく、セキュリティーグループのインバウンドの設定にてポート24224が開いていれば大丈夫です。
f:id:mirea-no-k:20181230234513p:plain

サーバーが完成したら以下の通り実施。
概要
●送信サーバー側のみ
・Tomcat9インストール
・サンプルアプリのWARファイル(log4j2のJARファイルはWARファイルの中に取り込まれてる)
・log4j2.xml
●送信サーバーよ受信サーバー両方
・Fluentdインストール

●送信サーバー側のみ
TomcatをPCにダウンロード、TelnetにてZIPファイルを転送し、unzipで解答する。
xmlやWARファイルを以下のように配置する。
/opt/apache-tomcat-9.0.14
/opt/apache-tomcat-9.0.14/lib/log4j2.xml
/opt/apache-tomcat-9.0.14/webapps/Test.war

Tomcatを起動・停止して稼働確認を行う。
/opt/apache-tomcat-9.0.14/binの下のシェルを実行できない場合は以下のコマンドを。

chmod u+x *.sh

以下のURLにて”HelloWorld”画面が表示されれば成功。

http://X.X.X.X:8080/Test/HelloWorldServlet

●送信サーバーよ受信サーバー両方
Fluentdインストールします。
Fluentdインストールは以下で紹介しているのでそちらを参照。
EC2にFluentdをインストールしてS3へ転送する方法 - 三姉妹年子ママのtoi toi toi!日記

root権限で実行するように以下のファイルを編集。
/opt/td-agent/etc/systemd/td-agent.service

[Service]
User=root
Group=root

両サーバーのconfファイルをそれぞれ編集し、td-agentをrestartします。
/etc/td-agent/td-agent.conf

送信サーバー

<source>
  @type tail
  path /opt/apache-tomcat-9.0.14/logs/catalina.out
  pos_file /var/log/td-agent/catalina.pos
  tag catalina.out
  <parse>
#     @type none
    @type multiline
    format_firstline /\d{4}-\d{1,2}-\d{1,2}/
    format1 /^(?<time>\d{4}-\d{1,2}-\d{1,2} \d{1,2}:\d{1,2}:\d{1,2}) \[(?<thread>.*)\] (?<level>[^\s]+)(?<message>.*)/
  </parse>
</source>


<match catalina.out>
  @type forward
  <server>
    host X.X.X.X
    port 24224
  </server>
  flush_interval 10s
</match>

受信サーバー

<source>
  @type forward
  port 24224
  bind 0.0.0.0
  tag catalina.out
</source>

<match catalina.out>
  @type file
  path /var/log/td-agent/test.log
  time_slice_format %Y%m%d
  time_slice_wait 1m
</match>


これでサンプルのエラーログが1行になって転送されます。
catalina.outの生ログ

2018-12-30 15:28:54 [http-nio-8080-exec-4] ERROR error
java.lang.NullPointerException: null
        at Sample.HelloWorldServlet.doGet(HelloWorldServlet.java:47) [classes/:?]
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) [servlet-api.jar:?]
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) [servlet-api.jar:?]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) [catalina.jar:9.0.14]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:9.0.14]
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) [tomcat-websocket.jar:9.0.14]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:9.0.14]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:9.0.14]
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199) [catalina.jar:9.0.14]
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [catalina.jar:9.0.14]
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490) [catalina.jar:9.0.14]
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [catalina.jar:9.0.14]
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [catalina.jar:9.0.14]
        at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:668) [catalina.jar:9.0.14]
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [catalina.jar:9.0.14]
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [catalina.jar:9.0.14]
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) [tomcat-coyote.jar:9.0.14]
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-coyote.jar:9.0.14]
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834) [tomcat-coyote.jar:9.0.14]
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1417) [tomcat-coyote.jar:9.0.14]
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-coyote.jar:9.0.14]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_181]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_181]
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-util.jar:9.0.14]
        at java.lang.Thread.run(Thread.java:748) [?:1.8.0_181]

1行になったログ

2018-12-30T15:28:52+00:00       catalina.out    {"thread":"http-nio-8080-exec-6","level":"ERROR","message":" error\njava.lang.NullPointerException: null\n\tat Sample.HelloWorldServlet.doGet(HelloWorldServlet.java:47) [classes/:?]\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:634) [servlet-api.jar:?]\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:741) [servlet-api.jar:?]\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) [catalina.jar:9.0.14]\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:9.0.14]\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) [tomcat-websocket.jar:9.0.14]\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:9.0.14]\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:9.0.14]\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199) [catalina.jar:9.0.14]\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [catalina.jar:9.0.14]\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490) [catalina.jar:9.0.14]\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [catalina.jar:9.0.14]\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [catalina.jar:9.0.14]\n\tat org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:668) [catalina.jar:9.0.14]\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [catalina.jar:9.0.14]\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [catalina.jar:9.0.14]\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) [tomcat-coyote.jar:9.0.14]\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-coyote.jar:9.0.14]\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834) [tomcat-coyote.jar:9.0.14]\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1417) [tomcat-coyote.jar:9.0.14]\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-coyote.jar:9.0.14]\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_181]\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_181]\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-util.jar:9.0.14]\n\tat java.lang.Thread.run(Thread.java:748) [?:1.8.0_181]"}