読者です 読者をやめる 読者になる 読者になる

この日記は私的なものであり所属会社の見解とは無関係です。 GitHub: takahashikzn

logbackへ移行するの巻

Java

Javaにおける標準ログ実装の地位は、随分と長い間Log4Jが担ってきました。
(まさかjava.util.loggingなんて誰も使ってないと思いますが。。。)


で、Log4Jは次期バージョンになるはずだった1.3の開発に失敗して以降、
ろくに新バージョン出さずにいるわけです。その進化をやめてしまった。
(2013-08-16追記: Log4J-2がそろそろリリースされそうではあります)


まぁ現状でLog4Jにさしたる不満もないのですが、なんかスッキリしない。

ぶっちゃけ言うと、『もうLog4Jに飽きた!』わけです。

で、Javaのログ実装のトレンドを追って見たところ


どうやらlogbackが良さそう。
このライブラリの作者は元、Log4Jの主要開発者の一人。(というか創始者)
で、SLF4Jの開発者でもあるという。
当然、logback-slf4jブリッジは用意されていますので、既存のコードは一切書き換えなくてOK。

よし、コレに乗り換えることに決めた!

ivy.xmlの設定

元の設定

<dependencies>
    <dependency org="org.slf4j" name="slf4j-api"/>
    <dependency org="log4j" name="log4j">
      <exclude name="jms" />
      <exclude name="jmxtools" />
      <exclude name="jmxri" />
    </dependency>
    <dependency org="org.slf4j" name="slf4j-log4j12">
      <exclude name="jms" />
      <exclude name="jmxtools" />
      <exclude name="jmxri" />
    </dependency>
    <dependency org="commons-logging" name="commons-logging"/>
</dependencies>


logbackに乗り換えた設定

<dependencies>
    <dependency org="org.slf4j" name="slf4j-api"/>
    <dependency org="ch.qos.logback" name="logback-core"/>
    <dependency org="ch.qos.logback" name="logback-classic"/>
</dependencies>

logback.xmlの設定

公式サイトのドキュメントを参考に、次のようにしました。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE logback>
<configuration>

  <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <append>true</append>
    <file>logs/logback.log</file>

    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
      <fileNamePattern>logback.%i.log.gz</fileNamePattern>
      <minIndex>1</minIndex>
      <maxIndex>10</maxIndex>
    </rollingPolicy>

    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>10MB</maxFileSize>
    </triggeringPolicy>

    <encoder>
      <pattern>%date %-5level [%logger{0}] %thread - %msg%n</pattern>
    </encoder>
  </appender>

  <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%date %-5level [%logger{0}] %thread - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="info">
    <appender-ref ref="file" />
    <appender-ref ref="stdout" />
  </root>

</configuration>


一つ注意点として、EclipseXMLに対応するスキーマ(DTD)がないと警告を出してきます。
しかし、logback.xmlにはDTDが存在しないようです。で、どうすればいいかと言うと
ここに答えがありました。

<!DOCTYPE logback>

を単に宣言するだけでOK。

バイバイ、commons-logging。

さて、残るは問題児のcommons-loggingとオサラバすることですが、
jcl-over-slf4jを使えばcommons-loggingからSLF4Jへブリッジできるとのこと。


しかし残念なことに、いまだcommons-loggingに依存しているライブラリが多数存在しています。
ivy-resolveしても、依存ライブラリとしてcommons-loggingが勝手にincludeされてきてしまう。


で、commons-loggingの依存関係を強制的に排除するにはどうすればいいのか調べたところ、

ivy.xml

<dependencies>

    <dependency org="org.slf4j" name="slf4j-api"/>
    <dependency org="ch.qos.logback" name="logback-core"/>
    <dependency org="ch.qos.logback" name="logback-classic"/>

    <dependency org="org.slf4j" name="jcl-over-slf4j"/>
    
    <!-- commons-loggingを強制排除。nameではなくartifactで指定すること! -->
    <exclude artifact="commons-logging" />

</dependencies>

と書けば、全ての依存ライブラリが更に依存するライブラリ(推移的依存ライブラリ=transitive libraries)
を排除できます。詳しくはコチラを参照。

起動してみた


さて、移行を終えてTomcatを起動してみましたが、全く問題なく動作しています。

しかも、設定ファイル(logback.xml)の妥当性について、こんなデバッグログを出してくれます。
コレ、結構便利。

23:10:42,377 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
23:10:42,377 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
23:10:42,380 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback.xml] at [file:/C:/java/eclipse/3.6/workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmp1/wtpwebapps/hcxsfa/WEB-INF/classes/logback.xml]
23:10:42,499 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - debug attribute not set
23:10:42,501 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.rolling.RollingFileAppender]
23:10:42,511 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [file]
23:10:42,590 |-INFO in ch.qos.logback.core.rolling.FixedWindowRollingPolicy@58dd2d - No compression will be used
23:10:42,646 |-WARN in ch.qos.logback.core.rolling.RollingFileAppender[file] - This appender no longer admits a layout as a sub-component, set an encoder instead.
23:10:42,646 |-WARN in ch.qos.logback.core.rolling.RollingFileAppender[file] - To ensure compatibility, wrapping your layout in LayoutWrappingEncoder.
23:10:42,646 |-WARN in ch.qos.logback.core.rolling.RollingFileAppender[file] - See also http://logback.qos.ch/codes.html#layoutInsteadOfEncoder for details
23:10:42,647 |-INFO in ch.qos.logback.core.rolling.RollingFileAppender[file] - Active log file name: logs/logback.log
23:10:42,647 |-INFO in ch.qos.logback.core.rolling.RollingFileAppender[file] - File property is set to [logs/logback.log]
23:10:42,650 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
23:10:42,656 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [stdout]
23:10:42,659 |-WARN in ch.qos.logback.core.ConsoleAppender[stdout] - This appender no longer admits a layout as a sub-component, set an encoder instead.
23:10:42,659 |-WARN in ch.qos.logback.core.ConsoleAppender[stdout] - To ensure compatibility, wrapping your layout in LayoutWrappingEncoder.
23:10:42,659 |-WARN in ch.qos.logback.core.ConsoleAppender[stdout] - See also http://logback.qos.ch/codes.html#layoutInsteadOfEncoder for details
23:10:42,659 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [org.apache.commons] to INFO
23:10:42,659 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [org.apache.commons.beanutils] to DEBUG
23:10:42,659 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [freemarker] to INFO
23:10:42,659 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [com.opensymphony.xwork2.config.ConfigurationManager] to INFO
23:10:42,660 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [com.opensymphony.xwork2.conversion] to INFO
23:10:42,660 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [com.opensymphony.xwork2.ognl.OgnlValueStack] to ERROR
23:10:42,660 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor] to INFO
23:10:42,660 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [com.opensymphony.xwork2.interceptor.PrefixMethodInvocationUtil] to INFO
23:10:42,660 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [com.opensymphony.xwork2.util.profiling.UtilTimerStack] to DEBUG
23:10:42,660 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [org.apache.struts2.components.template.FreemarkerTemplateEngine] to INFO
23:10:42,660 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [org.apache.struts2.components.UIBean] to INFO
23:10:42,660 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [org.apache.struts2.convention.SEOActionNameBuilder] to INFO
23:10:42,660 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [org.apache.struts2.convention.PackageBasedActionConfigBuilder] to INFO
23:10:42,660 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [org.apache.struts2.convention.DefaultResultMapBuilder] to INFO
23:10:42,660 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [org.springframework.beans.CachedIntrospectionResults] to INFO
23:10:42,660 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [org.springframework.beans.factory.support.DefaultListableBeanFactory] to WARN
23:10:42,660 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [org.springframework.context.annotation.ClassPathBeanDefinitionScanner] to INFO
23:10:42,660 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [org.springframework.core.io.PathMatchingResourcePatternResolver] to INFO
23:10:42,660 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [org.springframework.transaction.support.TransactionSynchronizationManager] to DEBUG
23:10:42,661 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to INFO
23:10:42,661 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [stdout] to Logger[ROOT]