👻

Log4j2.xでログファイル名を日付(YYYYMMDD.log)にする

2024/12/15に公開

Log4j1.xでログファイル名を日付にする記事がネット上にあり、参考にして作られたシステムが結構ある印象です。EOLになって時間が経ってますが備忘録で書いておきたい。

前提

前提としてLog4jは日付(YYYYMMDD.log)をログファイル名には指定できません。ログ名に日付を付けて分割する機能はあります。

具体的に言うと…以下のようにapp.logというファイルにログを書き込み、日が変わると末尾に日付を付けてコピーするような機能です。書き込み先は常にapp.logです。

app.log.20241213
app.log.20241214
app.log

ログファイル名を日付にするためにはログ出力機能の一部を自作するしかないですが、Log4j1→Log4j2など互換性がないアップデートする際に大きな修正が必要になります。

今回やりたいこと

Log4j2でログファイル名を(YYYYMMD.log)にして日次で分割する。

サンプル

StrutsのサンプルWEBアプリを使って実装します
https://struts.apache.org/getting-started/how-to-create-a-struts2-web-application

サンプルのREAD.MEに沿ってmvnのコマンド実行したところエラーが発生。

mvn -e clean package

struts-coreの7.0.0-M9というバージョンが存在しない。とのこと。
https://repository.apache.org/content/groups/snapshots/org/apache/struts/struts2-core/
を見ると最新バージョンは 7.0.0-M11-SNAPSHOT のようなので指定してビルドするも別のエラー。結局 7.0.0-M7-SNAPSHOT を指定することでビルドが通りました。

チュートリアルの手順にそってjettyを起動する

cd basic-struts
mvn jetty:run
・・・
[INFO] Started Server@2c9d90fc{STARTING}[11.0.18,sto=0] @2474ms
[INFO] Scan interval sec = 10

http://localhost:8080/basic-struts/index.jsp
画面が表示されました

web.xml/log4j2.xml/struts.xmlをチュートリアルの通りに修正して、WEBアプリにアクセスするごとにターミナルにログが出るようになりました。

ファイルにログを出力する

設定ファイルを編集してログファイルを作成する

log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Properties>
    <Property name="filename">${date:yyyyMMdd}.log</Property>
  </Properties>
    <Appenders>
        <Console name="STDOUT" target="SYSTEM_OUT">
            <PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
        </Console>
    <File name="File" fileName="${filename}">
      <PatternLayout>
        <pattern>%d %p %C{1.} [%t] %m%n</pattern>
      </PatternLayout>
    </File>
    </Appenders>
    <Loggers>
        <Logger name="com.opensymphony.xwork2" level="info"/>
        <Logger name="org.apache.struts2" level="debug"/>
        <Logger name="org.apache.struts2.edit" level="debug"/>
        <Root level="warn">
            <AppenderRef ref="STDOUT"/>
            <AppenderRef ref="File"/>
        </Root>
    </Loggers>
</Configuration>

相対パスに ${date:yyyyMMdd}.log の形でログを出力するように設定しました。これでファイルが出るようになります。想定している挙動としてはアクセスした日付でファイルが分割されることですが、このケースだとプロセスを起動した日付のログファイルに継続書き込みしてしまいます。そのためアクセスした日毎にファイルを分けるような実装をしなければいけません。

前述した通りlog4j2にはその機能はありません。代替案としてアクセス毎にlog4j2の設定ファイルを読み込み直すという実装にします。

この記事では簡易的にJSPのスクリプトレット中に設定ファイルを再読み込みするように実装します。

index.jsp
<!DOCTYPE html>
・・・
org.apache.logging.log4j.core.LoggerContext ctx = (org.apache.logging.log4j.core.LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
ctx.reconfigure();
%>
・・・
</html>

アクセス毎に設定ファイルを再読み込みするため、設定ファイル中のファイル名も都度評価されてアクセスした日付のファイルが作られます。

テスト

実際にログファイルが分割できるか?試してみます。日次だと試せるタイミングが限られるため時分を追加して確認します。

log4j2.xml
・・・
  <Properties>
    <Property name="filename">${date:yyyyMMdd-HHmm}.log</Property>
  </Properties>
・・・

jettyを再起動してしばらく F5 を押してると分単位でファイルが分割でき、ログファイルの出力先になっています。

この方法でやりたいことは実現できました。しかし、正攻法ではないため、アクセスが多いシステムであれば余計な負荷が掛かる可能性があったり、書き込んだログがきちんと消せるか、など懸念も多いと思います。可能であればAppenderを自作するなどきちんと実装した方が良いでしょう。

というかlog4jの機能の範囲外のことはやらない方が絶対良いです。運用時に脆弱性発覚などの起因でバージョンアップが必要になった際に調査実装に時間が掛かりますし、公式のサポートにのっかるように、実装や運用を調整しましょう!

Discussion