Logback
Log4jの創設者により開発されたロギングフレームワーク。SLF4Jとのネイティブ統合、自動設定リロード、条件付き処理、アーカイブログの圧縮機能を提供。成熟した信頼性の高いソリューションとして広く採用されている。
GitHub概要
qos-ch/logback
The reliable, generic, fast and flexible logging framework for Java.
スター3,128
ウォッチ145
フォーク1,310
作成日:2009年8月20日
言語:Java
ライセンス:Other
トピックス
なし
スター履歴
データ取得日時: 2025/7/17 23:31
ライブラリ
Logback
概要
LogbackはSLF4J(Simple Logging Facade for Java)の標準実装であり、Log4jの後継として設計されたJavaロギングフレームワークです。優れたパフォーマンス、自動設定リロード、条件処理、豊富なアペンダーを提供し、Spring Bootでデフォルト採用されています。自動設定とSpringとの深い統合により、現代的なJava開発に最適化されており、マイクロサービスアーキテクチャでも優秀なパフォーマンスを発揮します。
詳細
Logback 1.4.14は2025年現在、Spring Bootエコシステムにおける標準的選択肢として確固たる地位を維持しています。Log4jの作者によって開発され、その経験を活かした設計により、より高い性能と使いやすさを実現。自動設定リロード機能、条件付きログ処理、MD-コンテキスト(Mapped Diagnostic Context)によるスレッドローカル情報管理、豊富なアペンダー(Console、File、Rolling、SMTP、Database等)を提供し、企業レベルのJavaアプリケーション開発に必要な全ての機能を備えています。
主な特徴
- 自動設定リロード: 設定ファイルの変更を自動検出してホットリロード
- 条件処理: XMLファイル内での条件分岐による環境別設定
- 豊富なアペンダー: ファイル、コンソール、ローリング、SMTP、データベース等の多様な出力先
- MD-コンテキスト: スレッドローカルな診断情報の追加・管理機能
- 高速パフォーマンス: Log4jを上回る実行速度とメモリ効率
- Spring Boot統合: デフォルト採用による深い統合とゼロ設定での利用
メリット・デメリット
メリット
- Spring Bootでのデフォルト採用により設定不要で即座に利用開始可能
- SLF4Jの標準実装として他のロギングライブラリとの相互運用性が高い
- 自動設定リロードにより本番環境でのデバッグ作業が効率的
- 豊富なアペンダーにより企業レベルの複雑なログ要件に対応
- Spring Profilesとの統合による環境別設定管理が容易
- 構造化ログ出力とJSON形式対応による現代的な可観測性要件への対応
デメリット
- 高度な機能を使いこなすためにはXML設定の理解が必要
- Log4j2と比較して非同期ロギングのパフォーマンスがやや劣る
- 複雑な設定ファイルは可読性と保守性の課題となる可能性
- SLF4J依存のため他のファサードライブラリとの選択肢が制限される
- Spring Boot以外の環境では初期設定の手間が発生
- 軽量なアプリケーションには機能がオーバースペックとなる場合がある
参考ページ
書き方の例
インストールと基本セットアップ
<!-- Maven dependency -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.14</version>
</dependency>
<!-- SLF4J API (通常は自動的に含まれる) -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
// Gradle dependency
implementation 'ch.qos.logback:logback-classic:1.4.14'
implementation 'org.slf4j:slf4j-api:2.0.9'
// 最もシンプルな使用例(設定ファイル不要)
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SimpleExample {
private static final Logger logger = LoggerFactory.getLogger(SimpleExample.class);
public static void main(String[] args) {
logger.debug("Debug message");
logger.info("Info message");
logger.warn("Warning message");
logger.error("Error message");
// パラメータ付きログ
String user = "john_doe";
int userId = 12345;
logger.info("User {} logged in with ID {}", user, userId);
}
}
基本的なロギング操作(レベル、フォーマット)
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
public class BasicLoggingExample {
private static final Logger logger = LoggerFactory.getLogger(BasicLoggingExample.class);
private static final Logger serviceLogger = LoggerFactory.getLogger("service");
private static final Logger dbLogger = LoggerFactory.getLogger("database");
public static void main(String[] args) {
// 基本的なログ出力
logger.trace("This is a trace message");
logger.debug("This is a debug message");
logger.info("Application started successfully");
logger.warn("This is a warning message");
logger.error("This is an error message");
// パラメータ化ログメッセージ(推奨)
String operation = "database_query";
long duration = 150L;
logger.info("Operation '{}' completed in {} ms", operation, duration);
// MDC(Mapped Diagnostic Context)の使用
MDC.put("userId", "user123");
MDC.put("sessionId", "sess456");
logger.info("Processing user request");
// 複数のロガーによる分類
serviceLogger.info("Service layer processing");
dbLogger.debug("Database connection established");
// MDCクリア(重要)
MDC.clear();
// 例外ログ
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
logger.error("Arithmetic error occurred", e);
}
// 構造化ログ情報
logger.info("User action recorded: action={}, timestamp={}, success={}",
"login", System.currentTimeMillis(), true);
}
}
高度な設定とカスタマイズ(設定ファイル、アペンダー等)
<!-- logback-spring.xml - Spring Boot推奨設定ファイル -->
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- プロパティ定義 -->
<property name="LOG_PATH" value="logs"/>
<property name="APP_NAME" value="myapp"/>
<!-- Springプロファイル対応 -->
<springProfile name="development">
<property name="LOG_LEVEL" value="DEBUG"/>
</springProfile>
<springProfile name="production">
<property name="LOG_LEVEL" value="INFO"/>
</springProfile>
<!-- コンソールアペンダー -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- カラー付きコンソールアペンダー(開発環境用) -->
<appender name="COLORED_CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%green(%d{HH:mm:ss.SSS}) %blue([%thread]) %highlight(%-5level) %magenta(%logger{36}) - %msg%n</pattern>
</encoder>
</appender>
<!-- ファイルアペンダー -->
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${LOG_PATH}/${APP_NAME}.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- ローリングファイルアペンダー -->
<appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${APP_NAME}-rolling.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${APP_NAME}.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>300MB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- エラー専用ファイル -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/error.log</file>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/error.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<maxHistory>90</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 非同期アペンダー -->
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="ROLLING_FILE"/>
<queueSize>1000</queueSize>
<discardingThreshold>0</discardingThreshold>
<includeCallerData>false</includeCallerData>
</appender>
<!-- ロガー設定 -->
<logger name="com.mycompany.myapp" level="${LOG_LEVEL}"/>
<logger name="org.springframework" level="WARN"/>
<logger name="org.hibernate" level="WARN"/>
<logger name="database" level="DEBUG" additivity="false">
<appender-ref ref="ROLLING_FILE"/>
</logger>
<!-- ルートロガー -->
<root level="INFO">
<springProfile name="development">
<appender-ref ref="COLORED_CONSOLE"/>
</springProfile>
<springProfile name="production">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ASYNC_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</springProfile>
</root>
</configuration>
構造化ログと現代的な可観測性対応
<!-- JSON構造化ログ設定 -->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>7.4</version>
</dependency>
<!-- logback-spring.xml with JSON support -->
<configuration>
<!-- JSON構造化ログアペンダー -->
<appender name="JSON_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/structured.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/structured.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp/>
<logLevel/>
<loggerName/>
<message/>
<mdc/>
<arguments/>
<stackTrace/>
<pattern>
<pattern>
{
"service": "my-service",
"version": "1.0.0"
}
</pattern>
</pattern>
</providers>
</encoder>
</appender>
<!-- ELK Stack用設定 -->
<appender name="ELK_JSON" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/elk.json</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/elk.%d{yyyy-MM-dd}.%i.json.gz</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeContext>true</includeContext>
<includeMdc>true</includeMdc>
<customFields>{"environment":"production","service":"my-app"}</customFields>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="JSON_FILE"/>
<appender-ref ref="ELK_JSON"/>
</root>
</configuration>
// 構造化ログの利用例
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import net.logstash.logback.argument.StructuredArguments;
public class StructuredLoggingExample {
private static final Logger logger = LoggerFactory.getLogger(StructuredLoggingExample.class);
public void processUserOrder(String userId, String orderId, double amount) {
// MDCによるコンテキスト情報設定
MDC.put("userId", userId);
MDC.put("orderId", orderId);
MDC.put("traceId", generateTraceId());
try {
logger.info("Processing order",
StructuredArguments.kv("amount", amount),
StructuredArguments.kv("currency", "USD"),
StructuredArguments.kv("operation", "order_processing"));
// ビジネスロジック処理
processPayment(amount);
logger.info("Order processed successfully",
StructuredArguments.kv("status", "success"),
StructuredArguments.kv("duration_ms", 250));
} catch (Exception e) {
logger.error("Order processing failed",
StructuredArguments.kv("status", "error"),
StructuredArguments.kv("error_type", e.getClass().getSimpleName()),
e);
} finally {
MDC.clear();
}
}
private void processPayment(double amount) {
// 支払い処理のシミュレーション
logger.debug("Payment processing started",
StructuredArguments.kv("amount", amount));
}
private String generateTraceId() {
return "trace_" + System.currentTimeMillis();
}
}
エラーハンドリングとパフォーマンス最適化
<!-- パフォーマンス最適化設定 -->
<configuration scan="true" scanPeriod="30 seconds">
<!-- 非同期ログ設定 -->
<appender name="ASYNC_CONSOLE" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="CONSOLE"/>
<queueSize>2000</queueSize>
<discardingThreshold>100</discardingThreshold>
<includeCallerData>false</includeCallerData>
<neverBlock>true</neverBlock>
</appender>
<!-- 条件付きアペンダー -->
<if condition='property("spring.profiles.active").contains("production")'>
<then>
<appender name="SMTP" class="ch.qos.logback.classic.net.SMTPAppender">
<smtpHost>smtp.company.com</smtpHost>
<to>[email protected]</to>
<from>[email protected]</from>
<subject>Critical Error in ${APP_NAME}</subject>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
</layout>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<cyclicBufferTracker class="ch.qos.logback.core.spi.CyclicBufferTracker">
<bufferSize>10</bufferSize>
</cyclicBufferTracker>
</appender>
</then>
</if>
<!-- データベースアペンダー -->
<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
<connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
<driverClass>org.postgresql.Driver</driverClass>
<url>jdbc:postgresql://localhost:5432/logdb</url>
<user>loguser</user>
<password>logpass</password>
</connectionSource>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARN</level>
</filter>
</appender>
<root level="INFO">
<appender-ref ref="ASYNC_CONSOLE"/>
<if condition='property("spring.profiles.active").contains("production")'>
<then>
<appender-ref ref="SMTP"/>
<appender-ref ref="DB"/>
</then>
</if>
</root>
</configuration>
// パフォーマンス監視とエラーハンドリング
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Service;
@Service
public class PerformanceOptimizedService {
private static final Logger logger = LoggerFactory.getLogger(PerformanceOptimizedService.class);
private static final Logger performanceLogger = LoggerFactory.getLogger("performance");
public void performCriticalOperation(String operationId) {
long startTime = System.currentTimeMillis();
// トレーシング情報の設定
MDC.put("operationId", operationId);
MDC.put("startTime", String.valueOf(startTime));
try {
logger.info("Critical operation started");
// 重要な処理のシミュレーション
simulateProcessing();
long duration = System.currentTimeMillis() - startTime;
// パフォーマンスログ
performanceLogger.info("Operation completed successfully in {} ms", duration);
// アラート基準のチェック
if (duration > 1000) {
logger.warn("Operation took longer than expected: {} ms", duration);
}
} catch (Exception e) {
long duration = System.currentTimeMillis() - startTime;
// エラー時の詳細ログ
logger.error("Critical operation failed after {} ms", duration, e);
// 重要なエラーの場合は即座にアラート
if (isCriticalError(e)) {
logger.error("CRITICAL ERROR: Immediate attention required", e);
}
throw e;
} finally {
MDC.clear();
}
}
private void simulateProcessing() throws Exception {
// 処理時間のシミュレーション
Thread.sleep(100 + (int)(Math.random() * 200));
// エラーのシミュレーション
if (Math.random() < 0.1) {
throw new RuntimeException("Simulated processing error");
}
}
private boolean isCriticalError(Exception e) {
return e instanceof NullPointerException ||
e instanceof OutOfMemoryError ||
e.getMessage().contains("database");
}
}
フレームワーク統合と実用例
// Spring Boot統合例
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@SpringBootApplication
public class LogbackSpringBootApplication {
private static final Logger logger = LoggerFactory.getLogger(LogbackSpringBootApplication.class);
public static void main(String[] args) {
logger.info("Starting Spring Boot application with Logback");
SpringApplication.run(LogbackSpringBootApplication.class, args);
logger.info("Application started successfully");
}
}
// Spring MVC Controller with Logback
import org.springframework.web.bind.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
@RestController
@RequestMapping("/api/users")
public class UserController {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
@GetMapping("/{id}")
public User getUser(@PathVariable String id, HttpServletRequest request) {
// リクエスト固有の情報をMDCに設定
MDC.put("userId", id);
MDC.put("requestId", request.getHeader("X-Request-ID"));
MDC.put("userAgent", request.getHeader("User-Agent"));
try {
logger.info("Retrieving user information");
User user = userService.findById(id);
if (user != null) {
logger.info("User found successfully");
} else {
logger.warn("User not found");
}
return user;
} catch (Exception e) {
logger.error("Error retrieving user information", e);
throw e;
} finally {
MDC.clear();
}
}
}
# application.properties でのLogback設定
# ログレベル設定
logging.level.root=INFO
logging.level.com.mycompany=DEBUG
logging.level.org.springframework=WARN
# ファイル出力設定
logging.file.name=logs/application.log
logging.file.max-size=10MB
logging.file.max-history=30
# パターン設定
logging.pattern.console=%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
# Spring Boot Actuator統合
management.endpoints.web.exposure.include=loggers
management.endpoint.loggers.enabled=true
// テスト環境でのLogback設定
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import org.slf4j.LoggerFactory;
@SpringBootTest
@ActiveProfiles("test")
public class LogbackTestExample {
@Test
public void testLoggingOutput() {
// テスト用のListAppender作成
Logger logger = (Logger) LoggerFactory.getLogger("com.mycompany.test");
ListAppender<ILoggingEvent> listAppender = new ListAppender<>();
listAppender.start();
logger.addAppender(listAppender);
// ログ出力
logger.info("Test log message");
logger.error("Test error message");
// ログ出力の検証
List<ILoggingEvent> logsList = listAppender.list;
assertEquals(2, logsList.size());
assertEquals("Test log message", logsList.get(0).getMessage());
assertEquals(Level.INFO, logsList.get(0).getLevel());
assertEquals("Test error message", logsList.get(1).getMessage());
assertEquals(Level.ERROR, logsList.get(1).getLevel());
}
}