NHN Cloud NHN Cloud Meetup!

(Spring Boot)LoggingとProfile戦略

はじめに

spring-bootでは、Logbackを拡張して便利なlogging設定ができます。application.ymlの設定を読み込んだり、特定のprofileのアクションを実行できます。この記事からSpring Bootでどのようにloggingを設定するのか分かるでしょう。spring-bootを使って、profileを活用する方法を共有したいと思います。

ここではLogbackの基礎的な情報はガイドしません。
spring-bootはLogbackの他にも、log4jなどをサポートしていますが、ここではLogbackだけを考慮して作成しました。

Logback拡張(Extentions)

spring-bootではLogbackを使用します。Web Applicationを起動するとclasspath内で環境設定ファイル(logback.xml(logback-text.xml))を検索し、Logbackを初期化させます。この時はまだSpringが駆動される前の時点です。spring-bootではlogback-spring.xmlを使ってSpringがLogbackを駆動できるようサポートします。それによって、profileやapplication.xmlに設定されたプロパティを読み取ることができます。

まずはLoggerの登録方法を調べてみよう。

Logger登録とlevel設定

従来のLogbackでLoggerを登録していた方式を調べてみよう。
まず、以下のようにソース上でログを残します。

@Slf4j
@Component
public class SomeService {
    public void log() {
        log.info("hello");
    }
}

logback.xmlで、ログが残るように設定します。

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <logger name="com.parfait.study.simplelogging.service.SomeService" level="INFO"/>

    <root level="warn">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

上記では、グローバルにlog levelがwarn以上のログだけを残していますが、SomeServiceログはinfo以上なら残すようにしました。

02:15:04.557 [restartedMain] INFO  c.p.s.s.service.SomeService - hello

spring-bootを使用するなら、logback.xmlは必要ありません。

application.yml

logging:
  level:
    root: warn
    com.parfait.study.simplelogging.service.SomeService: info

application.ymlの設定だけでLoggerとlevelを設定できます。

Spring Boot Properties

spring-bootではapplication.ymlに定義したプロパティもloggingが動作するようにサポートしてくれます。その中で利用できそうなものをいくつか選んでみました。

key default
logging.file 絶対パスや相対パスで、ログファイル名を指定する。
logging.file.path logging.fileの値がないときに動作する。指定されたパスにspring.logでログを残す。
logging.file.max-size ログファイルのサイズが指定された閾値を超えたとき、ファイル名にindexを追加して、新しいファイルを作成する。

例:spring1.log、spring2.log
logging.file.max-history 下記で別途説明する。
logging.level * pathに基づいてLoggerのレベルを指定する。

(補足)logging.file.max-history
File AppenderはRollingFileAppenderを使用します。ファイルのrollingポリシーがspring-boot 1.5.xまでは、SizeBasedTriggeringPolicyでファイルサイズが特定の値に達すると、新しいログファイルを残す機能のみサポートすることになっていました。そのため時間が経過したログを削除するには、サーバーから別にcrondを回すか、別途logback-spring.xmlの設定が必要でした。しかしspring-boot 2.0.0からrollingポリシーがSizeAndTimeBasedRollingPolicyに変更され、デフォルト設定でログが日別に残るようになりました。(例:spring.2018-01-01.log)logging.file.max-historyで指定した日数が経過するとログは自動削除されます。

デフォルト設定

次にspring-bootがどのようなサポートをしているか、springのデフォルト設定を見てみよう。

base.xml

<included>
    <include resource="org/springframework/boot/logging/logback/defaults.xml" />
    <property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}"/>
    <include resource="org/springframework/boot/logging/logback/console-appender.xml" />
    <include resource="org/springframework/boot/logging/logback/file-appender.xml" />
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="FILE" />
    </root>
</included>
  • <include resource=”…/defaults.xml”>defaults.xmlで追加設定を読み込みます。Console Log PatternとFile Log Patternのデフォルト値が定義されており、その他tomcathibernateなどモジュールのlog levelが設定されています。
  • <property name=”LOG_FILE” …>${LOG_FILE}がなければ${LOG_PATH}を呼びます。${LOG_PATH}がなければ${LOG_TEMP}を呼びます。このような形式でapplication.ymlのプロパティの呼出を把握できます。
  • <include resource=”…/console-appender.xml”>:Console Appenderの設定を読み込みます。
  • <include resource=”…/file-appender.xml”>:File Appenderの設定を読み込みます。

console-appender.xml

<included>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>
</included>

file-appender.xml

<included>
    <appender name="FILE"
        class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
        <file>${LOG_FILE}</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
            <maxFileSize>${LOG_FILE_MAX_SIZE:-10MB}</maxFileSize>
            <maxHistory>${LOG_FILE_MAX_HISTORY:-0}</maxHistory>
        </rollingPolicy>
    </appender>
</included>
  • rollingPolicy:前述のとおり、SizeAndTimeBasedRollingPolicyにポリシーが変更されました。ログを日別に残し、maxHistoryで指定した日数が経過すると自動で削除されます。2.0.xより古いバージョンを使用している場合、上記の設定をコピーして使おう。

application.ymlのpropertyインポート

アプリケーションからリモートのログサーバーにログを送信し、ログサーバーのアドレスをapplication.ymlからlogserver.host: http://logserver.comに定義したとしましょう。この時、以下のようにapplication.ymlを参照できます。

<configuration>

  <springProperty name="host" source="logserver.host" defaultValue="http://dev-logserver.com"/>

  <appender name="REMOTE_LOG_SERVER" class="xxx.yyy.RemoteLogAppender">
    <remoteHost>${host}</remoteHost>
  </appender>

  <logger name="com.parfait.study.simplelogging.service.SomeService" level="INFO"/>

  <root level="warn">
    <appender-ref ref="REMOTE_LOG_SERVER"/>
  </root>
</configuration>

Profile別の環境設定

spring.profiles.activeで指定したprofileに基づいて条件を追加できます。

<springProfile name="dev">
  <appender name="CONSOLE" class="..."></appender>
  <root>
    <appender-ref ref="CONSOLE"/>
  </root>
</springProfile>

<springProfile name="alpha">
    <appender name="FILE" class="..."></appender>
  <root>
    <appender-ref ref="FILE"/>
  </root>
</springProfile>

<springProfile name="staging">
  <!-- staging profileの場合 -->
</springProfile>

<springProfile name="!production">
  <!-- production profileでない場合 -->
</springProfile>

上記は、Spring Referenceのサンプルを若干修正したものです。

Profile戦略

先に見たprofileは、アプリケーションの配布周期をそのまま追っています。

  • dev:開発段階。ここでCONSOLEにログを残す。
  • alpha:検証環境段階。ここからサーバーに配置するのでFILEにログを残す。

一見したところ、特に問題はないようです。ここで先ほど作ったリモートログサーバーにログをエクスポートするAppenderを追加してみよう。beta profileではFILEとREMOTE_LOG_SERVER Appenderを使用します。

<springProfile name="beta">
  <appender name="FILE" class="..."></appender>
  <appender name="REMOTE_LOG_SERVER" class="..."></appender>
  <root>
    <appender-ref ref="FILE"/>
    <appender-ref ref="REMOTE_LOG_SERVER"/>
  </root>
</springProfile>

問題が発生しました。重複が生じました。alphaもFILE Appenderを宣言し、betaもFILE Appenderを宣言しました。どうすればよいでしょうか?

<springProfile name="alpha,beta">
  <appender name="FILE" class="..."></appender>
  <root>
    <appender-ref ref="FILE"/>
  </root>
</springProfile>
<springProfile name="beta">
  <appender name="REMOTE_LOG_SERVER" class="..."></appender>
  <root>
    <appender-ref ref="REMOTE_LOG_SERVER"/>
  </root>
</springProfile>

ここで提示したい解決策は、profileを組み合わせて使用する、というものです。

<springProfile name="console-logging">
  <appender name="CONSOLE" class="..."></appender>
</springProfile>

<springProfile name="file-logging">
    <appender name="FILE" class="..."></appender>
</springProfile>

<springProfile name="remote-logging">
  <appender name="REMOTE_LOG_SERVER" class="..."></appender>
</springProfile>

<root>
  <springProfile name="console-logging">
    <appender-ref ref="CONSOLE"/>
  </springProfile>

  <springProfile name="file-logging">
    <appender-ref ref="FILE"/>
  </springProfile>

  <springProfile name="remote-logging">
    <appender-ref ref="REMOTE_LOG_SERVER"/>
  </springProfile>
</root>

組み合わせを使うと、はるかに有利な環境を構成できます。配布環境に合わせて設定してみよう。
配布環境によるspring.profiles.active

  • dev:console-logging
  • alpha:file-logging
  • beta:file-logging、remote-logging

組み合わせて希望する環境を簡単に構成できることは分かりましたが、別の問題があります。betaですべてのものを組み合わせて書くにはprofile入力にかなりの労力が必要です。

$ java -Dspring.profiles.active=file-logging,remote-logging -jar simple-logging-0.0.1-SNAPSHOT.jar

このような問題を解決するためにspring.profiles.includeの設定をかけておくことができます。

application-dev.yml

spring.profiles.include: console-logging

application-alpha.yml

spring.profiles.include: file-logging

application-beta.yml

spring.profiles.include: file-logging,remote-logging

配布段階に合わせて予め必要な組み合わせを設定しておこう。
これらの組み合わせは、Loggingだけで使われるものではありません。業務を進める上で、配布環境には柔軟性が必要です。1つのマイルストーンに2つの大きな機能を配布する必要があるとします。たとえば、メーリング機能と、SMS機能の追加があるとしましょう。QAを進行する際に、一方が通過できなかった場合は、その機能を除いて配布する準備が必要になります。このような要件はいくらでもあるでしょう。

まとめ

Profile戦略を共有するためspring-bootが対応しているLoggingについて紹介しました。Logging対応は、”Spring Reference”だけ読んでみても、大体のことが分かります。Spring Bootは、様々な自動設定で私たちの業務を簡単にしてくれます。今回はその簡単な設定から得られる大きな利点を紹介しました。Profileを配布ステップと同一視する傾向があります。しかし、spring-bootspring.profiles.includeができてからProfileを見ると視点が少し変わったようです。私たちは今やアプリケーションの設定をプラグインのように扱うことができるのです。
spring-bootを使う場合は、Javaの設定上に配置段階を書くのはやめて、application-{配布段階}.ymlを作成し、必要な設定を組み合わせて使用しましょう。

NHN Cloud Meetup 編集部

NHN Cloudの技術ナレッジやお得なイベント情報を発信していきます
pagetop