java-logging

Architecture Overview

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "java-logging" with this command: npx skills add claude-dev-suite/claude-dev-suite/claude-dev-suite-claude-dev-suite-java-logging

Java Logging

Architecture Overview

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

Rule: Always code to SLF4J API. Implementation is a runtime dependency.

SLF4J API Usage

Basic Logging

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;
    }
}

}

With Lombok

import lombok.extern.slf4j.Slf4j;

@Slf4j public class OrderService { public void processOrder(Order order) { log.info("Processing order: {}", order.getId()); } }

Log Levels

Level Purpose Example

TRACE

Very detailed debugging Loop iterations, variable values

DEBUG

Debugging information Method entry/exit, query params

INFO

Business events User login, order placed

WARN

Potential issues Deprecated API used, retry attempt

ERROR

Errors requiring attention Exception caught, operation failed

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);

Parameterized Messages (Best Practice)

// 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 Configuration

Spring Boot (logback-spring.xml)

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

&#x3C;!-- Properties -->
&#x3C;property name="LOG_PATH" value="${LOG_PATH:-logs}"/>
&#x3C;property name="LOG_FILE" value="${LOG_FILE:-application}"/>

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

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

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

&#x3C;!-- Async Appender for Performance -->
&#x3C;appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
    &#x3C;queueSize>512&#x3C;/queueSize>
    &#x3C;discardingThreshold>0&#x3C;/discardingThreshold>
    &#x3C;appender-ref ref="FILE"/>
&#x3C;/appender>

&#x3C;!-- Logger Configuration -->
&#x3C;logger name="com.yourcompany" level="DEBUG"/>
&#x3C;logger name="org.springframework" level="INFO"/>
&#x3C;logger name="org.hibernate.SQL" level="DEBUG"/>
&#x3C;logger name="org.hibernate.type.descriptor.sql" level="TRACE"/>

&#x3C;!-- Root Logger -->
&#x3C;root level="INFO">
    &#x3C;appender-ref ref="CONSOLE"/>
    &#x3C;appender-ref ref="ASYNC_FILE"/>
&#x3C;/root>

&#x3C;!-- Profile-specific configuration -->
&#x3C;springProfile name="prod">
    &#x3C;root level="INFO">
        &#x3C;appender-ref ref="JSON"/>
    &#x3C;/root>
&#x3C;/springProfile>

</configuration>

application.yml Configuration

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 Configuration

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>

&#x3C;Appenders>
    &#x3C;Console name="Console" target="SYSTEM_OUT">
        &#x3C;PatternLayout pattern="${LOG_PATTERN}"/>
    &#x3C;/Console>

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

    &#x3C;!-- Async for high performance -->
    &#x3C;Async name="AsyncFile">
        &#x3C;AppenderRef ref="File"/>
    &#x3C;/Async>
&#x3C;/Appenders>

&#x3C;Loggers>
    &#x3C;Logger name="com.yourcompany" level="debug"/>
    &#x3C;Root level="info">
        &#x3C;AppenderRef ref="Console"/>
        &#x3C;AppenderRef ref="AsyncFile"/>
    &#x3C;/Root>
&#x3C;/Loggers>

</Configuration>

Structured Logging (JSON)

Dependencies

<!-- For Logback --> <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();
    }
}

}

Structured Log Output

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"));

Output:

{ "@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" }

Best Practices

DO

  • Use SLF4J API everywhere

  • Use parameterized messages log.info("User {}", userId)

  • Include correlation IDs (traceId, requestId)

  • Log at appropriate levels

  • Use async appenders in production

  • Configure log rotation

  • Use JSON format for log aggregation

DON'T

  • Don't use string concatenation in log messages

  • Don't log sensitive data (passwords, tokens, PII)

  • Don't log inside tight loops without level check

  • Don't use System.out.println for logging

  • Don't catch and swallow exceptions silently

Security

// 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 ""; }

When NOT to Use This Skill

  • SLF4J API-only questions: Use slf4j skill for API usage patterns

  • Logback configuration details: Use logback skill for XML config

  • Node.js/Python projects: Use language-appropriate logging skills

  • Application code patterns: Focus on SLF4J API, not implementation

  • Framework migration: Consult migration-specific guides

Anti-Patterns

Anti-Pattern Why It's Bad Solution

Using System.out.println No control, no persistence, no filtering Use SLF4J logger

String concatenation in logs Always evaluated, performance hit Use parameterized logging: log.info("User {}", id)

Not using async appenders in production Blocks application threads Wrap with AsyncAppender

Logging without MDC in multi-threaded apps Loses request context Use MDC for correlation IDs

DEBUG level in production Performance impact, disk usage Use INFO or WARN in production

Not masking sensitive data Security/compliance violation Filter passwords, tokens, PII before logging

Quick Troubleshooting

Issue Cause Solution

NoClassDefFoundError: StaticLoggerBinder Missing SLF4J implementation Add Logback or Log4j2 dependency

Multiple bindings warning Multiple implementations on classpath Keep only one: Logback OR Log4j2

Logs not appearing Wrong log level or missing config Check logback.xml and log levels

Performance degradation Synchronous appenders Use AsyncAppender wrapper

Logs not rotating Missing rolling policy Configure RollingFileAppender

MDC values not showing Pattern missing %X{key} Add MDC placeholders to log pattern

Reference

  • Quick Reference: Log Levels

  • SLF4J Manual

  • Logback Documentation

  • Log4j2 Documentation

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

cron-scheduling

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

token-optimization

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

webrtc

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

react-19

No summary provided by upstream source.

Repository SourceNeeds Review