※当サイトの記事には、広告・プロモーションが含まれます。

Apache Log4jいろいろと面倒な件

japan.zdnet.com

⇧ お金の問題ですかね...オープンソースは一部の人たちの善意で成り立っているような部分もありそうですし...

Apache Log4jとは?

どうやら、

logging.apache.org

Apache Logging Servicesというプロジェクトの1つで管理されているライブラリらしいですと。

ちなみに、上記以外にも、

github.com

⇧ logとJavaで絞ったところ、Javaでlogに関係しそうなライブラリは他にもある模様。

Log4jは、バージョン1系と2系があるらしく、2系を使うには、

logging.apache.org

You need at least the log4j-api-2.x and the log4j-core-2.x jar files.

The other jars are necessary if your application calls the API of another logging framework and you want to route logging calls to the Log4j 2 implementation.

https://logging.apache.org/log4j/2.x/faq.html

⇧ 2系用のライブラリを読み込む必要があるそうな。

2系はと言うと、

www.itmedia.co.jp

⇧ かなり深刻な脆弱性を出したことでも有名ですかね。

SLF4Jと一緒に使えるらしく、

You can use the log4j-to-slf4j adapter jar when your application calls the Log4j 2 API and you want to route logging calls to a SLF4J implementation.

https://logging.apache.org/log4j/2.x/faq.html

⇧ その場合は依存関係も追加が必要ってことですかね。

SLF4J(Simple Logging Facade for Java)とは?

公式のドキュメントによりますと、

www.slf4j.org

The Simple Logging Facade for Java (SLF4J) serves as a simple facade or abstraction for various logging frameworks (e.g. java.util.logging, logback, log4j) allowing the end user to plug in the desired logging framework at deployment time.

https://www.slf4j.org/

⇧ ログのフレームワークを取り換え可能にしてくれるものらしい。

イメージ的には、

⇧ 上図のような感じで、様々なlogging frameworkを利用できるようにしてくれてるらしい。

SLF4Jは「facade pattern」というデザインパターンを使っているようなので、Wikipediaさんに聞いてみた。

The facade pattern (also spelled façade) is a software-design pattern commonly used in object-oriented programming. Analogous to a facade in architecture, a facade is an object that serves as a front-facing interface masking more complex underlying or structural code. A facade can:

  • improve the readability and usability of a software library by masking interaction with more complex components behind a single (and often simplified) API
  • provide a context-specific interface to more generic functionality (complete with context-specific input validation)
  • serve as a launching point for a broader refactor of monolithic or tightly-coupled systems in favor of more loosely-coupled code

https://en.wikipedia.org/wiki/Facade_pattern

Usage

A Facade is used when an easier or simpler interface to an underlying object is desired.

Alternatively, an adapter can be used when the wrapper must respect a particular interface and must support polymorphic behavior. A decorator makes it possible to add or alter behavior of an interface at run-time.

Pattern Intent
Adapter Converts one interface to another so that it matches what the client is expecting
Decorator Dynamically adds responsibility to the interface by wrapping the original code
Facade Provides a simplified interface

The facade pattern is typically used when

  • a simple interface is required to access a complex system,
  • a system is very complex or difficult to understand,
  • an entry point is needed to each level of layered software, or
  • the abstractions and implementations of a subsystem are tightly coupled.

https://en.wikipedia.org/wiki/Facade_pattern

⇧ 使いどころは、複雑なシステムに対してアクセスする必要がある場合に有効ですと。

SLF4J + Log4jで使ってみる

SLF4J + Log4jで使ってみます。

ちなみに、

www.infoq.com

Log4jのバージョン1系は、2015年8月にEOL(End Of Life)になっていることから、これから新規で開発するようなケースではLog4jのバージョン1系が利用されることは無いかと。

で、

github.com

⇧ サポートを終了するのは構わんのだけど、引き続きバージョン1系を利用せざるを得ない人もおると思うので、必要な依存関係とかの情報は載せておいて欲しいかな...

とりあえず、

logging.apache.org

⇧ 公式のドキュメントによりますと、

logging.apache.org

Compile

The following is a list of compile dependencies for this project. These dependencies are required to compile and run the application:

https://www.slf4j.org/

⇧ とあるので、上記の2つの依存関係を追加するのと、SLF4Jの依存関係を追加する感じになるんかな?

あとは、log4j.xmlの設定が必要っぽいのだけど、

logging.apache.org

⇧ キュメントに記載が無い...

バージョン2系のドキュメントには、

logging.apache.org

By default, Log4j looks for a configuration file named log4j2.xml (not log4j.xml) in the classpath.

https://logging.apache.org/log4j/2.x/faq.html

⇧ という記載があるので、おそらく、

って感じになるんかな?

とりあえず、

logging.apache.org

⇧ バージョン1系からバージョン2系へのマイグレーションのドキュメントに記載されてる内容をlog4j.xmlの設定の全量とするしかないっぽい...

バージョン1系とバージョン2系の差分を確認していて、

Note that system property interpolation via the ${foo} syntax has been extended to allow property lookups from many different sources. See the Lookups documentation for more details. For example, using a lookup for the system property named catalina.base, in Log4j 1.x, the syntax would be ${catalina.base}. In Log4j 2, the syntax would be ${sys:catalina.base}.

https://logging.apache.org/log4j/2.x/faq.html

システムプロパティとかを設定できるらしいのだけど、バージョン1系とバージョン2系で記述方法が異なるっぽい。

バージョン2系の記述方法は、

logging.apache.org

⇧ 上記にまとまってました。

Spring Bootとか使ってる場合は、

qiita.com

spring.pleiades.io

ロギングシステムに応じて、次のファイルがロードされます。

https://qiita.com/kazokmr/items/7d8323cd2033b233c261

⇧ Spring Bootが良しなに設定ファイルを作成してくれるっぽい?

長々と脱線しましたが、Log4jについてはバージョン2系を利用することにします。EclipseMavenプロジェクトを作成して、ファイルを追加。

ファイルの中身は以下のような感じになりました。

■/logging-log4j/pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>log4j</groupId>
	<artifactId>logging-log4j</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<properties>
		<java.version>1.8</java.version>
		<maven.compiler.target>${java.version}</maven.compiler.target>
		<maven.compiler.source>${java.version}</maven.compiler.source>
	</properties>
	<dependencies>
		<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.10</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-reload4j -->
<!--
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-reload4j</artifactId>
			<version>2.0.7</version>
		</dependency>
-->
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-slf4j-impl</artifactId>
			<version>2.20.0</version>
		</dependency>

		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-core</artifactId>
			<version>2.20.0</version>
		</dependency>
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-api</artifactId>
			<version>2.20.0</version>
		</dependency>
	</dependencies>
</project>

■/logging-log4j/src/main/resources/log4j2.xml

<Configuration status="debug">
  <Appenders>
    <Console name="CONSOLE" target="SYSTEM_OUT">
      <PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
    </Console>
    <File name="TRACE" fileName="${sys:log-location}/${sys:app}-trace.log">
      <PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
    </File>
    <Async name="ASYNC">
      <AppenderRef ref="TRACE"/>
      <AppenderRef ref="CONSOLE"/>
    </Async>
  </Appenders>
  <Loggers>
    <Root level="debug">
      <AppenderRef ref="ASYNC"/>
    </Root>
  </Loggers>
</Configuration>

■/logging-log4j/src/test/resources/log4j2.xml

<Configuration status="debug">
  <Appenders>
    <Console name="CONSOLE" target="SYSTEM_OUT">
      <PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
    </Console>
    <File name="TRACE" fileName="${sys:log-location}/${sys:test}-trace.log">
      <PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
    </File>
    <Async name="ASYNC">
      <AppenderRef ref="TRACE"/>
      <AppenderRef ref="CONSOLE"/>
    </Async>
  </Appenders>
  <Loggers>
    <Root level="debug">
      <AppenderRef ref="ASYNC"/>
    </Root>
  </Loggers>
</Configuration>

■/logging-log4j/src/main/java/logger/LoggerTest.java

package controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LoggerTest {

	final static Logger slf4jLogger = LoggerFactory.getLogger(LoggerTest.class);
	
	public static void main(String[] args) {
		slf4jLogger.debug("テストLoggerTest#mainメソッド");
		System.out.println("テストテスト");
	}

	private void logger () {
		slf4jLogger.debug("リフレクションテストLoggerTest#loggerメソッド");
		System.out.println("リフレクションテストテスト");
	}
}

■/logging-log4j/src/test/java/sub/SubTest.java

package sub;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.junit.jupiter.api.Test;

import controller.LoggerTest;

public class SubTest {

	static {
		System.setProperty("log4j.configurationFile","log4j2.xml");

		System.setProperty("log-location", "evidence/subtest");
		System.setProperty("test", "subtest");
	}
	
	@Test
	public void test01() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		System.out.println(System.getProperty("log-location"));
		System.out.println(System.getProperty("test"));
		LoggerTest.main(null);
		System.out.print("OK");
		
		LoggerTest loggerTest = new LoggerTest();
		Method method = LoggerTest.class.getDeclaredMethod("logger");
		method.setAccessible(true);
		method.invoke(loggerTest);
	}
	
}

で、実行すると、

コンソールとファイル出力されました。

う~む、設定がいろいろと面倒な感じですかね...

あと、Log4jに限らないんだけど、相変わらず、必要な依存関係が分かり辛くて依存関係を特定するのに膨大な時間がかかるという...

毎度モヤモヤ感が半端ない...

今回はこのへんで。