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にスライドすると取り込む事が出来ます。
アプリのポイント
・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
サーバーで実行した結果
◆Linux環境の準備
サンプルアプリが完成したので、WARファイルでエクスポートし、AWS環境で実行していきます。
AWS上にログの送信サーバー用と受信サーバー用の2サーバーEC2で作成します。
EC2はLinuxであればなんでもよく、セキュリティーグループのインバウンドの設定にてポート24224が開いていれば大丈夫です。
サーバーが完成したら以下の通り実施。
概要
●送信サーバー側のみ
・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]"}