Log4j2を使ってみる(設定ファイル一切無しで)、その3
Log4j2を使ってみる(設定ファイル一切無しで)、その2の続きである。
自前のConfigurationなしにログを出力した場合には、DefaultConfigurationというものが使われ、最初に以下のようなERRORが表示され、その後のログはエラーのみがコンソールに表示される。
ERROR StatusLogger No Log4j 2 configuration file found. Using default configuration (logging only errors to the console), or user programmatically provided configurations. Set system property 'log4j2.debug' to show Log4j 2 internal initialization logging. See https://logging.apache.org/log4j/2.x/manual/configuration.html for instructions on how to configure Log4j 2
Log4j2を使ってみる(設定ファイル一切無しで)、その1に書いたように、コンフィグ作成後に作成したロガーしか有効にはならないため、初期化とロガー作成の順序が問題になったのだが、新たなコンフィグを作らずデフォルトのコンフィグのままで、そこにアペンダを付け加えていくようにはできるだろうか?
前回のWrapperを以下のように変更してみた。
※以下のプログラムは動作しないことがある。以下のエラーが発生する場合があるのだ
java.lang.ClassCastException: class org.apache.logging.log4j.simple.SimpleLoggerContext cannot be cast to class org.apache.logging.log4j.core.LoggerContext (org.apache.logging.log4j.simple.SimpleLoggerContext and org.apache.logging.log4j.core.LoggerContext are in unnamed module of loader 'app')
at nato.base.clog.CLogConfig.initialize(CLogConfig.java:105)
at nato.base.webServer.guice.NatoServletContextListener.<clinit>(NatoServletContextListener.java:101)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
「context = (LoggerContext)LogManager.getContext(false);」の部分について、開発環境では「org.apache.logging.log4j.core.LoggerContext」が戻ってくるのに、本番環境では「org.apache.logging.log4j.simple.SimpleLoggerContext」が戻ってくるため、キャストエラーになってしまう。
package foo.bar;
import java.nio.file.*;
import org.apache.logging.log4j.*;
import org.apache.logging.log4j.core.*;
import org.apache.logging.log4j.core.appender.*;
import org.apache.logging.log4j.core.config.*;
import org.apache.logging.log4j.core.layout.*;
import org.apache.logging.log4j.status.*;
/**
* Log4jのデフォルトコンフィギュレーションそのままを、プログラムで変更。
* @author ysugimura
*/
public class Wrapper {
Configuration configuration;
LoggerContext context;
org.apache.logging.log4j.core.Logger ctxRootLogger;
LoggerConfig configRootLogger;
public Wrapper(Level level) {
// Log4j2のDefaultConfigurationの出すWarningを消す
// https://stackoverflow.com/questions/27994264/log4j2-disable-no-log4j2-configuration-file-found-print-when-configuring-pr
StatusLogger.getLogger().setLevel(Level.OFF);
// コンテキストからコンフィギュレーションを得る
context = (LoggerContext)LogManager.getContext(false);
configuration = context.getConfiguration();
// --> org.apache.logging.log4j.core.config.DefaultConfiguration
// なぜかRootLoggerという名前のものが二種類ある
ctxRootLogger = context.getRootLogger();
configRootLogger = configuration.getRootLogger();
// デフォルトコンフィグではエラーしか出さないので指定レベルに変更
setAllLevels("", level);
// デフォルトコンフィグのデフォルトコンソールアペンダを削除
for (String name: ctxRootLogger.getAppenders().keySet()) {
removeAppender(name);
}
}
public void setAllLevels(String parent, Level level) {
Configurator.setAllLevels(parent, level);
}
public void consoleAppender(String name, String layout) {
ConsoleAppender appender = ConsoleAppender.newBuilder()
.setName(name)
.setLayout(PatternLayout.newBuilder().withPattern(layout).build())
.build();
appender.start();
configuration.addAppender(appender);
ctxRootLogger.addAppender(
context.getConfiguration().getAppender(appender.getName())
);
context.updateLoggers();
}
public void fileAppender(String name, String layout, Path output) {
FileAppender appender = FileAppender.newBuilder()
.setName(name)
.withAppend(false)
.withFileName(output.toString())
.setLayout(PatternLayout.newBuilder().withPattern(layout).build())
.setConfiguration(context.getConfiguration())
.build();
appender.start();
configuration.addAppender(appender);
ctxRootLogger.addAppender(context.getConfiguration().getAppender(appender.getName()));
context.updateLoggers();
}
public void removeAppender(String name) {
configRootLogger.removeAppender(name);
context.updateLoggers();
}
}
テストは以下だ。仮にWrapperの初期化が遅れても、初期化後ではちゃんと機能するはずだ。
package foo.bar;
import java.nio.file.*;
import org.apache.logging.log4j.*;
public class Main {
static Wrapper wrapper = new Wrapper(Level.INFO);
static Logger log = LogManager.getLogger(Main.class);
public static void main(String[]args) {
log.info("info 1");
log.trace("trace 1");
wrapper.consoleAppender("CONSOLE", "%d{MM/dd HH:mm} %-5p %c{1} - %m%n");
log.info("info 2");
log.trace("trace 2");
wrapper.setAllLevels("foo.bar", Level.TRACE);
log.info("info 3");
log.trace("trace 3");
wrapper.fileAppender("FILE", "%d{MM/dd HH:mm} %-5p %c{1} - %m%n", Paths.get("logging.txt"));
log.info("info 4");
log.trace("trace 4");
wrapper.removeAppender("CONSOLE");
log.info("info 5");
log.trace("trace 5");
wrapper.removeAppender("FILE");
log.info("info 6");
log.trace("trace 6");
}
}