SLF4J + Logback
JavaとKotlinで人気のロギング組み合わせ。SLF4Jファサードとlogback実装の組み合わせにより、柔軟性と性能のバランスを実現。既存のJavaエコシステムとの完全互換性を保持し、エンタープライズ環境で信頼性が高い。
ライブラリ
SLF4J + Logback
概要
SLF4J + Logbackは「Java エンタープライズ向けの最強ロギングコンビネーション」として確立された、Javaアプリケーションにおける事実上の標準ロギングソリューションです。SLF4J(Simple Logging Facade for Java)の統一APIとLogbackの高性能実装を組み合わせることで、シンプルな開発体験と本格的なエンタープライズ機能を両立。Spring Boot、Spring Framework、Hibernate等の主要フレームワークでデフォルト採用され、設定の柔軟性、高いパフォーマンス、豊富な出力オプション、自動リロード機能などにより、小規模開発から大規模エンタープライズシステムまで幅広く対応可能な包括的ロギングソリューションです。
詳細
SLF4J + Logback 2025年版は Java エンタープライズロギングの決定版として成熟を極めています。両ライブラリは同一開発者(Ceki Gülcü)により設計されているため完璧な統合性を誇り、SLF4JのシンプルなAPIとLogbackの高度な機能が seamlessly 連携。XML、Groovy、プログラムによる柔軟な設定システム、時間・サイズベースのファイルローテーション、非同期ロギング、フィルタリング、条件分岐ロジック、JMX統合、ステータス管理システムなど企業レベルの要件を完全網羅。MDC(Mapped Diagnostic Context)による分散トレーシング対応、マーカーベースの高度なログ分類、構造化ログ対応、自動設定監視・リロード機能により、DevOps環境での運用効率を最大化します。
主な特徴
- 完璧な統合性: 同一設計者による最適化された組み合わせ
- 高性能非同期ロギング: AsyncAppenderによる高スループット実現
- 柔軟な設定システム: XML/Groovy/プログラム設定と自動リロード
- 豊富なAppender: Console、File、RollingFile、DB、JMS、メール等
- 高度なフィルタリング: 条件ベースの動的ログ制御
- エンタープライズ機能: JMX統合、ステータス管理、設定検証
メリット・デメリット
メリット
- Java エコシステムでの圧倒的な普及率と成熟した実装品質
- SLF4J API の柔軟性とLogback実装の高性能を同時享受
- Spring Boot等の主要フレームワークでのゼロ設定統合
- XML/Groovy/プログラムによる設定の柔軟性と自動リロード
- 非同期ロギングによる高いパフォーマンスとスループット
- 豊富なAppenderとLayoutによる多様な出力オプション
- 企業環境に必要なセキュリティ、監査、運用機能の完備
デメリット
- 高機能ゆえの初期設定の複雑さと学習コストの高さ
- XML設定ファイルの肥大化と可読性低下の可能性
- 豊富な機能による設定ミスのリスクと効果的な設定の困難さ
- メモリ使用量が単純なロギングライブラリより多い
- 非同期ロギング時のログ順序保証とメモリ管理の考慮必要
- 設定の複雑性により問題発生時のトラブルシューティング困難
参考ページ
書き方の例
インストール・セットアップ
<!-- Maven依存関係(Spring Boot使用時は自動含有) -->
<dependencies>
<!-- SLF4J API -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.17</version>
</dependency>
<!-- Logback Classic(SLF4J実装) -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.18</version>
</dependency>
<!-- Logback Core(Classic に含まれるが明示的に指定) -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.5.18</version>
</dependency>
<!-- 高度な機能(オプション) -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
<version>1.5.18</version>
</dependency>
<!-- JSON形式ログ出力(オプション) -->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>8.0</version>
</dependency>
</dependencies>
// Gradle依存関係
dependencies {
// SLF4J + Logback
implementation 'org.slf4j:slf4j-api:2.0.17'
implementation 'ch.qos.logback:logback-classic:1.5.18'
// 追加機能
implementation 'ch.qos.logback:logback-access:1.5.18'
implementation 'net.logstash.logback:logstash-logback-encoder:8.0'
}
基本的なログ出力
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
public class EnterpriseLoggingService {
private static final Logger logger = LoggerFactory.getLogger(EnterpriseLoggingService.class);
// マーカー定義
private static final Marker AUDIT_MARKER = MarkerFactory.getMarker("AUDIT");
private static final Marker BUSINESS_MARKER = MarkerFactory.getMarker("BUSINESS");
private static final Marker SECURITY_MARKER = MarkerFactory.getMarker("SECURITY");
public void processBusinessTransaction(String transactionId, String userId, double amount) {
// MDCに文脈情報を設定
MDC.put("transactionId", transactionId);
MDC.put("userId", userId);
MDC.put("amount", String.valueOf(amount));
MDC.put("requestId", generateRequestId());
try {
logger.info(BUSINESS_MARKER, "ビジネストランザクション開始: transactionId={}", transactionId);
// セキュリティチェック
if (amount > 100000) {
logger.warn(SECURITY_MARKER, "高額取引が検出されました: amount={}, userId={}", amount, userId);
}
// 監査ログ
logger.info(AUDIT_MARKER, "取引承認: transactionId={}, userId={}, amount={}",
transactionId, userId, amount);
// ビジネスロジック実行
validateTransaction(transactionId, userId, amount);
executeTransaction(transactionId, amount);
recordTransaction(transactionId);
logger.info(BUSINESS_MARKER, "ビジネストランザクション完了: transactionId={}", transactionId);
logger.info(AUDIT_MARKER, "取引完了: transactionId={}, status=SUCCESS", transactionId);
} catch (ValidationException e) {
logger.warn(BUSINESS_MARKER, "トランザクション検証エラー: {}", e.getMessage());
logger.warn(AUDIT_MARKER, "取引拒否: transactionId={}, reason={}", transactionId, e.getMessage());
throw e;
} catch (Exception e) {
logger.error(BUSINESS_MARKER, "トランザクション処理エラー: transactionId={}", transactionId, e);
logger.error(AUDIT_MARKER, "取引エラー: transactionId={}, error={}", transactionId, e.getMessage());
throw new TransactionException("取引処理に失敗しました", e);
} finally {
// MDCクリア
MDC.clear();
}
}
// 構造化ログの活用例
public void generateDetailedReport(String reportType, Map<String, Object> parameters) {
MDC.put("reportType", reportType);
MDC.put("reportId", generateReportId());
try {
logger.info("レポート生成開始: type={}", reportType);
// パラメータの詳細ログ
if (logger.isDebugEnabled()) {
logger.debug("レポートパラメータ:");
parameters.forEach((key, value) ->
logger.debug(" {}={}", key, value));
}
long startTime = System.currentTimeMillis();
// レポート生成処理
generateReport(reportType, parameters);
long duration = System.currentTimeMillis() - startTime;
// パフォーマンスログ
if (duration > 5000) {
logger.warn("レポート生成が遅延: type={}, duration={}ms", reportType, duration);
} else {
logger.info("レポート生成完了: type={}, duration={}ms", reportType, duration);
}
} finally {
MDC.clear();
}
}
private String generateRequestId() {
return "req_" + System.currentTimeMillis() + "_" + Thread.currentThread().getId();
}
private String generateReportId() {
return "rpt_" + System.currentTimeMillis();
}
private void validateTransaction(String transactionId, String userId, double amount) {
// 検証ロジック
}
private void executeTransaction(String transactionId, double amount) {
// 実行ロジック
}
private void recordTransaction(String transactionId) {
// 記録ロジック
}
private void generateReport(String reportType, Map<String, Object> parameters) {
// レポート生成ロジック
}
}
ログレベル設定
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogbackConfiguration {
private static final Logger logger = LoggerFactory.getLogger(LogbackConfiguration.class);
// プログラムによるログレベル変更
public static void changeLogLevel(String loggerName, Level level) {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
ch.qos.logback.classic.Logger targetLogger = loggerContext.getLogger(loggerName);
Level oldLevel = targetLogger.getLevel();
targetLogger.setLevel(level);
logger.info("ログレベル変更: logger={}, {} -> {}", loggerName, oldLevel, level);
}
// ルートロガーレベル変更
public static void changeRootLogLevel(Level level) {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
ch.qos.logback.classic.Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
Level oldLevel = rootLogger.getLevel();
rootLogger.setLevel(level);
logger.info("ルートログレベル変更: {} -> {}", oldLevel, level);
}
// 動的設定リロード
public static void reloadConfiguration(String configFile) {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
try {
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(loggerContext);
// 既存設定をクリア
loggerContext.reset();
// 新しい設定をロード
configurator.doConfigure(configFile);
logger.info("ログ設定を再読み込みしました: {}", configFile);
} catch (JoranException e) {
logger.error("ログ設定の再読み込みに失敗しました: {}", e.getMessage(), e);
}
}
// ログレベルの一括確認
public static void checkAllLogLevels() {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
logger.info("現在のログレベル設定一覧:");
// ルートロガー
ch.qos.logback.classic.Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
logger.info("ROOT: {}", rootLogger.getLevel());
// 全ロガーの確認
loggerContext.getLoggerList().stream()
.filter(log -> log.getLevel() != null)
.forEach(log -> logger.info("{}: {}", log.getName(), log.getLevel()));
}
}
構造化ログ
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import net.logstash.logback.argument.StructuredArguments;
import net.logstash.logback.marker.Markers;
import java.util.Map;
import java.util.HashMap;
public class StructuredLoggingWithLogback {
private static final Logger logger = LoggerFactory.getLogger(StructuredLoggingWithLogback.class);
// JSON構造化ログの例(Logstash Encoderを使用)
public void structuredApiLogging(String endpoint, String method, int statusCode, long responseTime) {
// MDCによる文脈情報
MDC.put("service", "user-api");
MDC.put("version", "1.2.0");
MDC.put("environment", "production");
try {
// 構造化された追加情報
Map<String, Object> requestDetails = new HashMap<>();
requestDetails.put("endpoint", endpoint);
requestDetails.put("method", method);
requestDetails.put("statusCode", statusCode);
requestDetails.put("responseTimeMs", responseTime);
requestDetails.put("timestamp", System.currentTimeMillis());
// StructuredArgumentsを使用したJSON出力
if (statusCode >= 400) {
logger.warn("API エラーレスポンス: {}",
StructuredArguments.entries(requestDetails));
} else if (responseTime > 1000) {
logger.warn("API 応答遅延: {}",
StructuredArguments.entries(requestDetails));
} else {
logger.info("API リクエスト処理完了: {}",
StructuredArguments.entries(requestDetails));
}
// マーカーを使用した分類
if (statusCode >= 500) {
logger.error(Markers.append("alertType", "server_error"),
"サーバーエラーが発生: endpoint={}, statusCode={}", endpoint, statusCode);
}
} finally {
MDC.clear();
}
}
// 複雑な業務データの構造化ログ
public void businessEventLogging(String eventType, Object eventData) {
MDC.put("eventType", eventType);
MDC.put("eventId", generateEventId());
try {
// イベントデータを構造化形式で記録
logger.info("ビジネスイベント発生: eventType={}, data={}",
eventType,
StructuredArguments.value("eventData", eventData));
// 特定の業務イベントの詳細ログ
if ("ORDER_CREATED".equals(eventType)) {
Map<String, Object> orderDetails = extractOrderDetails(eventData);
logger.info("注文作成イベント詳細: {}",
StructuredArguments.entries(orderDetails));
}
} finally {
MDC.clear();
}
}
// パフォーマンス監視ログ
public void performanceLogging(String operationName, long duration, Map<String, Object> metrics) {
MDC.put("operation", operationName);
try {
Map<String, Object> perfData = new HashMap<>(metrics);
perfData.put("duration_ms", duration);
perfData.put("timestamp", System.currentTimeMillis());
// パフォーマンス閾値による分類
if (duration > 5000) {
logger.error(Markers.append("alertType", "performance_critical"),
"重大なパフォーマンス問題: operation={}, metrics={}",
operationName, StructuredArguments.entries(perfData));
} else if (duration > 1000) {
logger.warn(Markers.append("alertType", "performance_warning"),
"パフォーマンス警告: operation={}, metrics={}",
operationName, StructuredArguments.entries(perfData));
} else {
logger.debug("パフォーマンス記録: operation={}, metrics={}",
operationName, StructuredArguments.entries(perfData));
}
} finally {
MDC.clear();
}
}
private String generateEventId() {
return "evt_" + System.currentTimeMillis() + "_" +
Integer.toHexString((int)(Math.random() * 0xFFFF));
}
private Map<String, Object> extractOrderDetails(Object eventData) {
// イベントデータから注文詳細を抽出
Map<String, Object> details = new HashMap<>();
details.put("orderId", "12345");
details.put("customerId", "user123");
details.put("amount", 999.99);
return details;
}
}
設定ファイル例
<!-- logback-spring.xml (Spring Boot推奨) -->
<configuration debug="false" scan="true" scanPeriod="30 seconds">
<!-- プロパティ定義 -->
<property name="LOG_HOME" value="logs" />
<property name="APP_NAME" value="myapp" />
<property name="PATTERN_CONSOLE" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{requestId}] %logger{36} - %msg%n" />
<property name="PATTERN_FILE" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{userId}:%X{requestId}] %logger{36} - %msg%n" />
<!-- コンソールAppender -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${PATTERN_CONSOLE}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 一般ファイルAppender(ローリング) -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/${APP_NAME}.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/${APP_NAME}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${PATTERN_FILE}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- エラー専用ファイルAppender -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/${APP_NAME}-error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/${APP_NAME}-error.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>90</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${PATTERN_FILE}</pattern>
<charset>UTF-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 非同期Appender(パフォーマンス向上) -->
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="FILE" />
<queueSize>1024</queueSize>
<discardingThreshold>0</discardingThreshold>
<maxFlushTime>5000</maxFlushTime>
<includeCallerData>false</includeCallerData>
</appender>
<!-- JSON形式ログAppender -->
<appender name="JSON_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/${APP_NAME}-json.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/${APP_NAME}-json.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp />
<version />
<logLevel />
<message />
<mdc />
<arguments />
<stackTrace />
</providers>
</encoder>
</appender>
<!-- 監査ログ専用Appender -->
<appender name="AUDIT_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/audit.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/audit.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>365</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{userId}:%X{sessionId}] %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
<evaluator>
<expression>marker != null && marker.contains("AUDIT")</expression>
</evaluator>
<onMismatch>DENY</onMismatch>
<onMatch>ACCEPT</onMatch>
</filter>
</appender>
<!-- 環境別設定 -->
<springProfile name="development">
<logger name="com.example" level="DEBUG" />
<logger name="org.springframework.web" level="DEBUG" />
<root level="DEBUG">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
</springProfile>
<springProfile name="production">
<logger name="com.example" level="INFO" />
<logger name="org.springframework" level="WARN" />
<logger name="org.hibernate" level="WARN" />
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="ASYNC_FILE" />
<appender-ref ref="ERROR_FILE" />
<appender-ref ref="JSON_FILE" />
<appender-ref ref="AUDIT_FILE" />
</root>
</springProfile>
<!-- ステータス管理 -->
<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
</configuration>
パフォーマンス最適化
import ch.qos.logback.classic.AsyncAppender;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import org.slf4j.LoggerFactory;
public class LogbackPerformanceOptimization {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(LogbackPerformanceOptimization.class);
// 非同期Appenderの動的設定
public static void setupAsyncAppender(String loggerName, String targetAppenderName) {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
Logger targetLogger = loggerContext.getLogger(loggerName);
// 既存のAppenderを取得
Appender<ILoggingEvent> targetAppender = targetLogger.getAppender(targetAppenderName);
if (targetAppender == null) {
logger.warn("対象Appenderが見つかりません: {}", targetAppenderName);
return;
}
// 非同期Appenderを作成
AsyncAppender asyncAppender = new AsyncAppender();
asyncAppender.setContext(loggerContext);
asyncAppender.setName("ASYNC_" + targetAppenderName);
// パフォーマンス最適化設定
asyncAppender.setQueueSize(1024); // キューサイズ
asyncAppender.setDiscardingThreshold(0); // 破棄閾値(0=破棄しない)
asyncAppender.setMaxFlushTime(5000); // 最大フラッシュ時間
asyncAppender.setIncludeCallerData(false); // 呼び出し元情報を含めない(高速化)
asyncAppender.setNeverBlock(true); // ノンブロッキング
// 対象Appenderを追加
asyncAppender.addAppender(targetAppender);
asyncAppender.start();
// ロガーから元のAppenderを削除し、非同期Appenderを追加
targetLogger.detachAppender(targetAppender);
targetLogger.addAppender(asyncAppender);
logger.info("非同期Appenderを設定しました: logger={}, appender={}", loggerName, targetAppenderName);
}
// 大量ログ処理の最適化
public static void optimizedBulkLogging(List<String> dataList) {
logger.info("大量データ処理開始: {} 件", dataList.size());
int batchSize = 1000;
long startTime = System.currentTimeMillis();
for (int i = 0; i < dataList.size(); i += batchSize) {
int endIndex = Math.min(i + batchSize, dataList.size());
List<String> batch = dataList.subList(i, endIndex);
// バッチレベルでのログ出力(個別項目ログは避ける)
if (logger.isDebugEnabled()) {
logger.debug("バッチ処理: [{}-{}] / {}", i + 1, endIndex, dataList.size());
}
// 実際の処理
processBatch(batch);
// 進捗ログ(間引き)
if ((i / batchSize) % 10 == 0) {
long currentTime = System.currentTimeMillis();
double avgTimePerBatch = (currentTime - startTime) / (double)(i / batchSize + 1);
logger.info("処理進捗: {}/{} バッチ完了 (平均 {:.2f}ms/バッチ)",
(i / batchSize + 1), (dataList.size() + batchSize - 1) / batchSize, avgTimePerBatch);
}
}
long totalTime = System.currentTimeMillis() - startTime;
logger.info("大量データ処理完了: {} 件, 総時間: {}ms", dataList.size(), totalTime);
}
// メモリ効率的なログ文字列生成
private static final ThreadLocal<StringBuilder> LOG_BUFFER =
ThreadLocal.withInitial(() -> new StringBuilder(512));
public static void memoryEfficientLogging(Map<String, Object> largeDataMap) {
if (!logger.isDebugEnabled()) {
return;
}
StringBuilder buffer = LOG_BUFFER.get();
buffer.setLength(0); // クリア
buffer.append("Large data summary: ");
int count = 0;
for (Map.Entry<String, Object> entry : largeDataMap.entrySet()) {
if (count > 0) buffer.append(", ");
buffer.append(entry.getKey()).append("=").append(entry.getValue());
// 大きすぎる場合は省略
if (++count >= 10) {
buffer.append(", ... (").append(largeDataMap.size() - 10).append(" more)");
break;
}
}
logger.debug(buffer.toString());
}
// 条件付きログの最適化
public static void conditionalLogging(String operation, Object data, boolean isHighPriority) {
// 高優先度または特定の条件でのみ詳細ログ
if (isHighPriority || logger.isTraceEnabled()) {
logger.info("詳細操作ログ: operation={}, data={}", operation, data);
} else {
// 通常は簡略ログ
logger.debug("操作実行: {}", operation);
}
}
// ログ設定の動的調整
public static void adjustLoggingForPerformance(boolean highPerformanceMode) {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
if (highPerformanceMode) {
// 高性能モード: ログレベルを上げ、詳細ログを抑制
loggerContext.getLogger("com.example.detailed").setLevel(ch.qos.logback.classic.Level.WARN);
loggerContext.getLogger("org.springframework").setLevel(ch.qos.logback.classic.Level.ERROR);
logger.info("高性能モードに切り替えました");
} else {
// 通常モード: 詳細ログを有効化
loggerContext.getLogger("com.example.detailed").setLevel(ch.qos.logback.classic.Level.DEBUG);
loggerContext.getLogger("org.springframework").setLevel(ch.qos.logback.classic.Level.WARN);
logger.info("通常モードに切り替えました");
}
}
private static void processBatch(List<String> batch) {
// バッチ処理の実装
try {
Thread.sleep(10); // 処理時間の模擬
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}