1.ログファサードライブラリ
log4jの後に標準のjava.util.loggingが作られたが、log4jが使いやすかったため、完全には普及しなかった。
そのためログライブラリに依存しないようなインタフェースが必要とされた。
ライブラリ毎にログライブラリが異なると、1つのファイルにログをまとめるのが大変になるので、基本的にはSLF4Jを使用するのが良い。
1-1. SLF4J(Simple Logging Facade For Java)
SLF4J + logbackまたは、SLF4J + Log4j2の構成が多いようだ。
Commons Loggingが使いにくいために作成されたみたいで、SLF4Jを使えない
理由が無ければ、これを使うのが良さそう。
ライセンスはMIT?
https://qiita.com/okey01/items/f949c6b4174056adf266
必要なJAR
API(必須):slf4j-api-*.jar
ログ実装へのハインディング(どれか1つ)
Log4j:slf4j-log4j12-*.jar
Log4j2:log4j-slf4j-impl-*.jar
Logback:logback-classic-*.jar
java.util.logging:slf4j-jdk14-*.jar
1-1-1. アダプタ、ブリッジ
jclはJakarta Commons Logging
julはjava.util.logging
の略。
1-1-2. 参考サイト
1-2. Commons Logging
Commons LoggingとJDK標準のロギングの例
1-2-1.導入
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
1-2-2.プロパティファイル
handlers=java.util.logging.ConsoleHandler, java.util.logging.FileHandler
.level=FINEST
# FileHandlerの設定
java.util.logging.FileHandler.pattern=C:/sample/sample.log
java.util.logging.FileHandler.limit=500000
java.util.logging.FileHandler.count=3
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$s %2$s %5$s%6$s\r\n
levelは以下を設定できる。
SEVERE (最高値):障害
WARNING (警告):潜在的な問題
INFO:メッセージ情報
CONFIG:静的な構成メッセージ
FINE:トレース情報
FINER:かなり詳細なトレースメッセージ
FINEST (最低値):非常に詳細なトレースメッセージ
あとALLとOFFもある。
以下の3段階のみ使用するのがわかりやすいと考えている。
エラー:SEVERE
インフォ:INFO
デバッグ:FINEST
1-2-3.ソースコード
private static Log _log = LogFactory.getLog(Sample.class);
_log.debug("デバッグメッセージ");
_log.info("インフォメッセージ");
_log.error("エラーメッセージ");
1-2-4.使用方法
起動時引数に「-Djava.util.logging.config.file=ファイルパス」を指定する。
1-2-5.トラブル対応
・標準出力にデバッグ情報が出力されない。
→「java.util.logging.ConsoleHandler.level=FINEST」を追加する。
・ファイルに追記されない
→「java.util.logging.FileHandler.append=true」を追加する。
・特定のクラスだけ詳細ログを指定したい。
→「1-2-6」に記載する。
1-2-6.特定のクラスだけ詳細ログを指定したい。
package logtest.sub1;
public class Sub1 {
private static Log _log = LogFactory.getLog(Sub1.class);
上記のように各ソースファイル上に自身のクラスを指定しているので、
プロパティファイルで以下の用に設定したクラス単位に指定する。
logtest.sub1.Sub1.level=WARNING
logtest.sub1.Sub1.handlers=java.util.logging.ConsoleHandler
1-3. JBoss Logging
2.ログ出力ライブラリ
2-1.java.util.logging
2-1-1.logging.propertiesについて
パラメータの内容は、
1.日付
2.呼び出し元を表す文字列。パッケージ名付きクラス名+メソッド名
3.ロガーの名前、パッケージ付きクラス名
4.ログレベル
5.メッセージ
6.throw
https://docs.oracle.com/javase/jp/8/docs/api/java/util/logging/SimpleFormatter.html
2-2.Log4j
サポート切れ。log4j2を使用した方が良い。
2-2-1.インストール
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2-2-2.設定ファイル
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %5p %c{1} - %m%n
### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=log/log4j.log
log4j.appender.file.Append=true
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d %5p %c{1} - %m%n
log4j.rootLogger=debug, stdout, file
ConversionPatternについて
・クラス名:「%C」。「%C{1}」とすればパッケージ名は省略可能。
・メソッド名 %M
・プロセスID 非対応
・スレッドID 非対応
複数ファイルにログファイルを分ける
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%C{1} %M %t %d %5p %c{1} - %m%n
### direct messages to file mylog.log ###
log4j.appender.file1=org.apache.log4j.FileAppender
log4j.appender.file1.File=log/log4j1.log
log4j.appender.file1.Append=true
log4j.appender.file1.layout=org.apache.log4j.PatternLayout
log4j.appender.file1.layout.ConversionPattern=%d %5p %c{1} - %m%n
### direct messages to file mylog.log ###
log4j.appender.file2=org.apache.log4j.FileAppender
log4j.appender.file2.File=log/log4j2.log
log4j.appender.file2.Append=true
log4j.appender.file2.layout=org.apache.log4j.PatternLayout
log4j.appender.file2.layout.ConversionPattern=%d %5p %c{1} - %m%n
#
log4j.logger.kero.domain=debug, file1
log4j.logger.kero.view=debug, file2
log4j.logger.kero.infrastructure=debug, file2
log4j.rootLogger=debug, stdout
2-2-3.プロパティファイルの指定
javaの起動オプションで以下の用に指定する。
- プロパティファイル
-Dlog4j.configuration=file:/C:/tmp/mylog4j.properties
- XMLファイル
-Dlog4j.configuration=file:/C:/tmp/mylog4j.xml -Dlog4j.configuratorClass=org.apache.log4j.xml.DOMConfigurator
2-3.Log4j2
2-3-1.インストール
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.9.1</version>
</dependency>
2-3-2.設定ファイル
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration>
<Configuration status="OFF">
<Properties>
<Property name="ptn">%d{yyyy-MM-dd HH:mm:ss.SSS} p=%-5p c=%c t=%t C=%C F=%F M=%M L=%L m=%m %n</Property>
</Properties>
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="${ptn}" />
</Console>
<RollingFile name="file" fileName="log/log4j2.log"
filePattern="log/%d{yyyyMM}/log4j2_%d{yyyyMMdd}_%i.log">
<PatternLayout pattern="${ptn}" />
<Policies>
<OnStartupTriggeringPolicy />
<SizeBasedTriggeringPolicy size="2 MB" />
<TimeBasedTriggeringPolicy />
</Policies>
<DefaultRolloverStrategy max="10" />
</RollingFile>
</Appenders>
<Loggers>
<Root level="all">
<AppenderRef ref="console" />
<AppenderRef ref="file" />
</Root>
</Loggers>
</Configuration>
ソース
package ml.kerotori.log4j;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Test1 {
static Logger logger = LogManager.getLogger(Test1.class);
public static void main(String[] args) {
logger.trace("Test1のmainメソッド");
}
}
2-4.Logback
2-4-1.インストール
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.3</version>
</dependency>
↑こんなに必要なかった。logback-classicだけで良かった。
↑上は間違い。slf4jは必要。ソースコード上で、SLF4Jのインタフェースを使えなかった・・・。
2-4-2.設定ファイル
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date{yyyy-MM-dd HH:mm:ss} [%thread] %level %logger{0} - %msg \(%file:%line\)%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/logback.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/app.%d{yyyy-MM-dd}.log.tar.gz</fileNamePattern>
<maxHistory>7</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} - %m%n</pattern>
</encoder>
</appender>
<root level="DEBUG" additivity="false">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
</configuration>
2-4-3.ソース
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Test2 {
private static Logger log = LoggerFactory.getLogger(Test2.class);
public static void main(String[] args) {
log.debug("Test2のmainメソッド");
}
}
参考
Javaのロギングライブラリの歴史と現状をふんわり把握する(初学者向け)
https://qiita.com/nisshiee/items/c5388f1d472ec86295e0
3.SLF4J+logback
3-1.インストール
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
「slf4j-api」「logback-core」「logback-classic」が必要になるが「logback-classic」が他に依存している。ただ、SLF4Jを入れないと、ソースコード上でSLF4Jのインタフェースが使えなかった。
3-2.使い方
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Sample {
private static final Logger log = LoggerFactory.getLogger(Sample.class);
public static void main(String[] args) {
log.trace("traceです。");
log.error("errorです。");
}
SLF4Jがインターフェース、Logbackが実装なので、import文はSLF4Jとする。
3-3.lombokを使う場合
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Sample {
public static void main(String[] args) {
log.trace("traceです。");
log.error("errorです。");
3-4.設定ファイル
3-4-1.設定ファイルの置き場所
logback.xmlファイルを「/src/main/respources」の下に配置する。
Webアプリの場合は、「/WEB-INF/classes」の下に配置する。(WEB-INFの直下やlibの下に置いたが読み込まれなかった。)
-Dlogback.configurationFileを指定すると任意の場所のファイルを読み込む。
3-4-2. Mavenのモジュール構成でのプロパティファイル
Mavenでモジュール構成で複数のサブプロジェクトが存在する場合に、設定ファイルを共通化する方法
親プロジェクトのsrc/main/resourcesに「logback.xml」を配置し、
親のpom.xmlに以下の用に記載する。
<build>
<resources>
<resource>
<directory>${project.basedir}/../src/main/resources</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
・・・省略
3-5.設定ファイルのカスタマイズ
3-5-1.プロセスIDを出力
3-5-2.メソッド名を出力
「%M」で可能。詳しくは以下のサイトを確認。
http://logback.qos.ch/manual/layouts_ja.html
3-5-3.pattern例
サンプルコード
package log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogOut {
private static final Logger log = LoggerFactory.getLogger(LogOut.class);
public static String Message() {
log.trace("traceです。");
log.error("errorです。");
System.out.println("標準出力です");
return "Messageです";
}
}
設定例 | 出力結果 |
---|---|
%d{yyyy-MM-dd HHss.SSS} - %m%n | 2021-03-14 17:35:53.384 - errorです。 |
%d{yyyy-MM-dd HHss.SSS} - %M [%thread] %level %m%n | 2021-03-14 21:12:32.372 - Message [http-nio-8080-exec-7] ERROR errorです。 |
%d{yyyy-MM-dd HHss.SSS} - %logger{10} %M [%thread] %level %m%n | 2021-03-14 21:20:52.103 - log.LogOut Message [http-nio-8080-exec-1] ERROR errorです。 |
3-5-4.特定のクラスのログを出さない
3-6.トラブル対応
3-6-1.Mavenからテスト実行でエラーが発生する。
■現象
Maven実行時の先頭に以下のエログが出力される。
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/C:/pleiades/eclipse/plugins/org.eclipse.m2e.maven.runtime.slf4j.simple_1.16.0.20200610-1735/jars/slf4j-simple-1.7.5.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [file:/C:/pleiades/eclipse/configuration/org.eclipse.osgi/7/0/.cp/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.SimpleLoggerFactory]
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/C:/pleiades/eclipse/plugins/org.eclipse.m2e.maven.runtime.slf4j.simple_1.16.0.20200610-1735/jars/slf4j-simple-1.7.5.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [file:/C:/pleiades/eclipse/configuration/org.eclipse.osgi/7/0/.cp/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.SimpleLoggerFactory]
テスト実行すると以下のエラーが発生する。
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[WARNING] Corrupted STDOUT by directly writing to native stream in forked JVM 1. See FAQ web page and the dump file C:\Users\けろ\git\javaUtils2\Domain\target\surefire-reports\2021-01-17T12-39-22_446-jvmRun1.dumpstream
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 0, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for javaUtils 0.0.1-SNAPSHOT:
[INFO]
[INFO] javaUtils .......................................... SUCCESS [ 0.515 s]
[INFO] Domain ............................................. FAILURE [ 0.835 s]
[INFO] Infrastructure ..................................... SKIPPED
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.449 s
[INFO] Finished at: 2021-01-17T12:39:22+09:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.22.1:test (default-test) on project Domain: There are test failures.
[ERROR]
[ERROR] Please refer to C:\Users\けろ\git\javaUtils2\Domain\target\surefire-reports for the individual test results.
[ERROR] Please refer to dump files (if any exist) [date].dump, [date]-jvmRun[N].dump and [date].dumpstream.
[ERROR] The forked VM terminated without properly saying goodbye. VM crash or System.exit called?
[ERROR] Command was cmd.exe /X /C "C:\pleiades\java\11\bin\java -javaagent:C:\\Users\\けろ\\.m2\\repository\\org\\jacoco\\org.jacoco.agent\\0.8.3\\org.jacoco.agent-0.8.3-runtime.jar=destfile=C:\\Users\\けろ\\git\\javaUtils2\\Domain\\target\\jacoco.exec -jar C:\Users\けろ\AppData\Local\Temp\surefire10670104236856242318\surefirebooter7391686314887198056.jar C:\Users\けろ\AppData\Local\Temp\surefire10670104236856242318 2021-01-17T12-39-22_446-jvmRun1 surefire16778607688627256100tmp surefire_012236412007154186616tmp"
[ERROR] Error occurred in starting fork, check output in log
[ERROR] Process Exit Code: 1
[ERROR] org.apache.maven.surefire.booter.SurefireBooterForkException: The forked VM terminated without properly saying goodbye. VM crash or System.exit called?
[ERROR] Command was cmd.exe /X /C "C:\pleiades\java\11\bin\java -javaagent:C:\\Users\\けろ\\.m2\\repository\\org\\jacoco\\org.jacoco.agent\\0.8.3\\org.jacoco.agent-0.8.3-runtime.jar=destfile=C:\\Users\\けろ\\git\\javaUtils2\\Domain\\target\\jacoco.exec -jar C:\Users\けろ\AppData\Local\Temp\surefire10670104236856242318\surefirebooter7391686314887198056.jar C:\Users\けろ\AppData\Local\Temp\surefire10670104236856242318 2021-01-17T12-39-22_446-jvmRun1 surefire16778607688627256100tmp surefire_012236412007154186616tmp"
[ERROR] Error occurred in starting fork, check output in log
[ERROR] Process Exit Code: 1
[ERROR] at org.apache.maven.plugin.surefire.booterclient.ForkStarter.fork(ForkStarter.java:669)
[ERROR] at org.apache.maven.plugin.surefire.booterclient.ForkStarter.run(ForkStarter.java:282)
[ERROR] at org.apache.maven.plugin.surefire.booterclient.ForkStarter.run(ForkStarter.java:245)
[ERROR] at org.apache.maven.plugin.surefire.AbstractSurefireMojo.executeProvider(AbstractSurefireMojo.java:1183)
[ERROR] at org.apache.maven.plugin.surefire.AbstractSurefireMojo.executeAfterPreconditionsChecked(AbstractSurefireMojo.java:1011)
[ERROR] at org.apache.maven.plugin.surefire.AbstractSurefireMojo.execute(AbstractSurefireMojo.java:857)
[ERROR] at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:137)
[ERROR] at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:210)
[ERROR] at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:156)
[ERROR] at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:148)
[ERROR] at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:117)
[ERROR] at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:81)
[ERROR] at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:56)
[ERROR] at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
[ERROR] at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:305)
[ERROR] at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:192)
[ERROR] at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:105)
[ERROR] at org.apache.maven.cli.MavenCli.execute(MavenCli.java:957)
[ERROR] at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:289)
[ERROR] at org.apache.maven.cli.MavenCli.main(MavenCli.java:193)
[ERROR] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[ERROR] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[ERROR] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[ERROR] at java.base/java.lang.reflect.Method.invoke(Method.java:566)
[ERROR] at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:282)
[ERROR] at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:225)
[ERROR] at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:406)
[ERROR] at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:347)
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException
[ERROR]
[ERROR] After correcting the problems, you can resume the build with the command
[ERROR] mvn <args> -rf :Domain
# Created at 2021-01-17T12:29:56.634
Corrupted STDOUT by directly writing to native stream in forked JVM 1. Stream 'FATAL ERROR in native method: processing of -javaagent failed, appending to system class path failed'.
■対応方法
使用している開発環境は「pleiades-2020-06-java-win-64bit-jre_20200702.zip」
上記モジュールにバグがある?
https://stackoverflow.com/questions/63755390/multiple-slf4j-bindings-with-m2e-in-eclipse-2020-06
とりあえず「pleiades-2020-12-java-win-64bit-jre_20201222.zip」を使ってみたらm
最初の警告は同じように出力されたが、エラーが発生せず、テストは実行された。
3-6-2.logback.xmlファイルの内容が反映されない(WebApp)
logback.xmlファイルを変更してもTomcatに反映されなかった。
Tomcat起動時に以下のようなログが出ている。
情報: CATALINA_BASE: D:\pleiades-2020-12-java-win-64bit-jre_20201222\pleiades\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0
その場所を開いて、調べた結果、以下の場所にlogback.xmlファイルがあった。
D:\pleiades-2020-12-java-win-64bit-jre_20201222\pleiades\workspace.metadata.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\WebSample\WEB-INF\classes
そのファイルを直接編集したら反映されることを確認した。
3-6-3.相対パスの出力先が不明。
ファイルの出力先を「log/logback.txt」とした場合の出力先がわからなかったので調べた。
以下に出力されていた。
D:\pleiades-2020-12-java-win-64bit-jre_20201222\pleiades\eclipse\logs