mcpField
J
java-logging

java-logging

github.com/claude-dev-suite開発4647 回インストール2 日前に更新
リポジトリ

java-logging について

Java開発者向けのロギングスキルで、SLF4Jの利用、LogbackとLog4j2の設定、構造化JSONログ、運用上の指針を扱います。パラメーター化メッセージ、コンテキストの記録、機密データの保護を重視します。

主な機能

  1. 01SLF4J APIの利用パターン
  2. 02LogbackとLog4j2の設定例
  3. 03MDCを使った構造化JSONログ
  4. 04ログレベルとパラメーター化メッセージ
  5. 05セキュリティとトラブルシューティングの指針

活用シーン

  1. 01Javaアプリケーションのロギング設定
  2. 02アプリケーションログへのリクエストコンテキスト追加
  3. 03ログの欠落や設定不備の調査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/claude-dev-suite/claude-dev-suite --skill java-logging

インストール後、Claude Code で/java-loggingを入力して読み込まれたか確認します。

同梱ファイルquick-ref/log-levels.md

詳細

Java ロギング

アーキテクチャの概要

┌─────────────────────────────────────────────────────────────┐
│                    Application Code                         │
│              (uses SLF4J API only)                          │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                      SLF4J Facade                           │
│              (abstraction layer)                            │
└─────────────────────────────────────────────────────────────┘
                              │
              ┌───────────────┼───────────────┐
              ▼               ▼               ▼
        ┌──────────┐   ┌──────────┐   ┌──────────────┐
        │ Logback  │   │ Log4j2   │   │ java.util    │
        │          │   │          │   │ .logging     │
        └──────────┘   └──────────┘   └──────────────┘

ルール:常に SLF4J API を使用してコードを記述してください。実装はランタイム依存関係です。

SLF4J API の使用方法

基本的なロギング
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class UserService {
    private static final Logger log = LoggerFactory.getLogger(UserService.class);

    public User findUser(Long id) {
        log.debug("Finding user with id: {}", id);

        try {
            User user = repository.findById(id);
            log.info("User found: {}", user.getEmail());
            return user;
        } catch (Exception e) {
            log.error("Failed to find user with id: {}", id, e);
            throw e;
        }
    }
}
Lombok を使用する場合
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class OrderService {
    public void processOrder(Order order) {
        log.info("Processing order: {}", order.getId());
    }
}
ログレベル
レベル用途例
TRACE非常に詳細なデバッグループの反復、変数の値
DEBUGデバッグ情報メソッドの開始/終了、クエリパラメーター
INFOビジネスイベントユーザーのログイン、注文の登録
WARN潜在的な問題非推奨 API の使用、再試行
ERROR対応が必要なエラー例外の捕捉、操作の失敗
log.trace("Entering loop iteration {}", i);
log.debug("Query parameters: userId={}, status={}", userId, status);
log.info("Order {} placed successfully", orderId);
log.warn("Payment retry attempt {} of {}", attempt, maxRetries);
log.error("Failed to process payment for order {}", orderId, exception);
パラメーター化メッセージ(ベストプラクティス)
// GOOD - uses parameterized logging (efficient)
log.debug("Processing user {} with role {}", userId, role);

// BAD - string concatenation (always evaluated)
log.debug("Processing user " + userId + " with role " + role);

// For expensive operations, use isEnabled check
if (log.isDebugEnabled()) {
    log.debug("Complex data: {}", computeExpensiveDebugInfo());
}

Logback の設定

Spring Boot(logback-spring.xml)
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>

    
    <property name="LOG_PATH" value="${LOG_PATH:-logs}"/>
    <property name="LOG_FILE" value="${LOG_FILE:-application}"/>

    
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) [%thread] %cyan(%logger{36}) - %msg%n</pattern>
        </encoder>
    </appender>

    
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${LOG_FILE}.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${LOG_FILE}.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
            <maxFileSize>100MB</maxFileSize>
            <maxHistory>30</maxHistory>
            <totalSizeCap>3GB</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    
    <appender name="JSON" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${LOG_FILE}-json.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${LOG_FILE}-json.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <includeMdcKeyName>traceId</includeMdcKeyName>
            <includeMdcKeyName>userId</includeMdcKeyName>
        </encoder>
    </appender>

    
    <appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
        <queueSize>512</queueSize>
        <discardingThreshold>0</discardingThreshold>
        <appender-ref ref="FILE"/>
    </appender>

    
    <logger name="com.yourcompany" level="DEBUG"/>
    <logger name="org.springframework" level="INFO"/>
    <logger name="org.hibernate.SQL" level="DEBUG"/>
    <logger name="org.hibernate.type.descriptor.sql" level="TRACE"/>

    
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="ASYNC_FILE"/>
    </root>

    
    <springProfile name="prod">
        <root level="INFO">
            <appender-ref ref="JSON"/>
        </root>
    </springProfile>
</configuration>
application.yml の設定
logging:
  level:
    root: INFO
    com.yourcompany: DEBUG
    org.springframework.web: INFO
    org.hibernate.SQL: DEBUG
  pattern:
    console: "%d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %cyan(%logger{36}) - %msg%n"
    file: "%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n"
  file:
    name: logs/application.log
  logback:
    rollingpolicy:
      max-file-size: 100MB
      max-history: 30

Log4j2 の設定

log4j2-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Properties>
        <Property name="LOG_PATH">logs</Property>
        <Property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n</Property>
    </Properties>

    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="${LOG_PATTERN}"/>
        </Console>

        <RollingFile name="File" fileName="${LOG_PATH}/app.log"
                     filePattern="${LOG_PATH}/app-%d{yyyy-MM-dd}-%i.log.gz">
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <SizeBasedTriggeringPolicy size="100MB"/>
                <TimeBasedTriggeringPolicy/>
            </Policies>
            <DefaultRolloverStrategy max="30"/>
        </RollingFile>

        
        <Async name="AsyncFile">
            <AppenderRef ref="File"/>
        </Async>
    </Appenders>

    <Loggers>
        <Logger name="com.yourcompany" level="debug"/>
        <Root level="info">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="AsyncFile"/>
        </Root>
    </Loggers>
</Configuration>

構造化ロギング(JSON)

依存関係

<dependency>
    <groupId>net.logstash.logback</groupId>
    <artifactId>logstash-logback-encoder</artifactId>
    <version>7.4</version>
</dependency>
MDC(Mapped Diagnostic Context)
import org.slf4j.MDC;

@Component
public class RequestLoggingFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        try {
            MDC.put("traceId", generateTraceId());
            MDC.put("userId", getCurrentUserId());
            MDC.put("requestPath", ((HttpServletRequest) request).getRequestURI());

            chain.doFilter(request, response);
        } finally {
            MDC.clear();
        }
    }
}
構造化ログの出力
import static net.logstash.logback.argument.StructuredArguments.*;

log.info("Order processed",
    kv("orderId", order.getId()),
    kv("customerId", order.getCustomerId()),
    kv("amount", order.getTotal()),
    kv("currency", "USD"));

出力:

{
  "@timestamp": "2025-01-15T10:30:00.000Z",
  "level": "INFO",
  "logger": "com.example.OrderService",
  "message": "Order processed",
  "orderId": "ORD-12345",
  "customerId": "CUST-789",
  "amount": 99.99,
  "currency": "USD",
  "traceId": "abc123",
  "userId": "user456"
}

ベストプラクティス

推奨事項
  • あらゆる場所で SLF4J API を使用する
  • パラメーター化メッセージを使用する log.info("User {}", userId)
  • 相関 ID(traceId、requestId)を含める
  • 適切なログレベルを使用する
  • 本番環境では非同期アペンダーを使用する
  • ログローテーションを設定する
  • ログ集約には JSON 形式を使用する
禁止事項
  • ログメッセージで文字列連結を使用しない
  • 機密データ(パスワード、トークン、個人識別情報)をログに記録しない
  • レベルを確認せずに高頻度のループ内でログを記録しない
  • ロギングに System.out.println を使用しない
  • 例外を捕捉して、何もせずに握りつぶさない
セキュリティ
// BAD - logs sensitive data
log.info("User login: email={}, password={}", email, password);

// GOOD - mask sensitive data
log.info("User login: email={}", maskEmail(email));

// Helper
private String maskEmail(String email) {
    int atIndex = email.indexOf('@');
    if (atIndex > 2) {
        return email.substring(0, 2) + "***" + email.substring(atIndex);
    }
    return "***";
}

このスキルを使用しない場合

  • SLF4J API のみについての質問:API の使用パターンには slf4j スキルを使用してください
  • Logback の設定の詳細:XML 設定には logback スキルを使用してください
  • Node.js/Python プロジェクト:各言語に適したロギングスキルを使用してください
  • アプリケーションコードのパターン:実装ではなく、SLF4J API に重点を置いてください
  • フレームワークの移行:移行に特化したガイドを参照してください

アンチパターン

アンチパターン問題点解決策
System.out.println の使用制御できず、永続化されず、フィルタリングもできないSLF4J ロガーを使用する
ログでの文字列連結常に評価され、パフォーマンスが低下するパラメーター化ロギングを使用する:log.info("User {}", id)
本番環境で非同期アペンダーを使用しないアプリケーションのスレッドをブロックするAsyncAppender でラップする
マルチスレッドアプリで MDC を使用せずにロギングするリクエストのコンテキストが失われる相関 ID には MDC を使用する
本番環境で DEBUG レベルを使用するパフォーマンスへの影響、ディスク使用量の増加本番環境では INFO または WARN を使用する
機密データをマスキングしないセキュリティ/コンプライアンス違反ログ記録の前にパスワード、トークン、個人識別情報をフィルタリングする

トラブルシューティングの早見表

問題原因解決策
NoClassDefFoundError: StaticLoggerBinderSLF4J 実装がないLogback または Log4j2 の依存関係を追加する
複数バインディングの警告クラスパス上に複数の実装があるLogback または Log4j2 のいずれか 1 つだけを残す
ログが表示されないログレベルの誤り、または設定がないlogback.xml とログレベルを確認する
パフォーマンスの低下同期アペンダーAsyncAppender ラッパーを使用する
ログがローテーションされないローリングポリシーがないRollingFileAppender を設定する
MDC の値が表示されないパターンに %X{key} がないログパターンに MDC プレースホルダーを追加する

参考資料

コメント