Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Designing a Multi-Channel Logging System with Logback

Tech May 9 3

Structured logging frameworks typically segment output into distinct streams to facilitate analysis and debugging. A standard architecture routes traffic across four primary channels: performance metrics, core business operations, critical error traces, and standard console output. Business operations further branch into user actions, API interactions, file handling, chat transcripts, and specific failure scenarios that require isolated file routing.

Performance Tracking Implementation

Capturing execution latency without polluting main logic benefits from an observer-style pattern. The following example demonstrates a lightweight timing utility using SLF4J. Access modifiers are restricted to prevent runtime reconfiguration, and static initialization ensures memory efficiency since logger instances maintain internal stateless queues. Thread isolation is maintained via MDC cleanup in a finally block.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

public class MetricTracker {
    private static final Logger PERFM_LOGGER = LoggerFactory.getLogger("com.app.monitor.perf");
    private static final String OP_KEY = "perf_op";

    public static void recordLatency(String task, long elapsedMs, String context) {
        try {
            MDC.put(OP_KEY, task);
            PERFM_LOGGER.info("[METRICS] [{}] Elapsed: {}ms | {}", task, elapsedMs, context);
        } finally {
            MDC.clear();
        }
    }

    public static class LatencyObserver implements AutoCloseable {
        private final String operation;
        private final long initTimestamp;

        public LatencyObserver(String taskName) {
            this.operation = taskName;
            this.initTimestamp = System.nanoTime();
        }

        @Override
        public void close() {
            finish("");
        }

        public void finish(String extraInfo) {
            long ms = (System.nanoTime() - initTimestamp) / 1_000_000;
            recordLatency(operation, ms, extraInfo);
        }
    }

    // Factory method encourages explicit instantiation
    public static LatencyObserver trackExecution(String action) {
        return new LatencyObserver(action);
    }
}

Contextual Data Injection via MDC

Mapped Diagnostic Context (MDC) operates as a thread-bound map, ideal for propagating request-scoped identifiers like session tokens or client IDs. Instead of manual string concatenation in every log call, configure Logback's pattern layout to inject these values dynamically using %X{key}.

// Inbound request interceptor or filter
MDC.put("clientId", "client_4592");
MDC.put("endpoint", "/v2/inventory/check");
String traceId = MDC.get("traceId");

Corresponding pattern definition:

<!-- Console appender layout definition -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <!-- Injects MDC values directly into the format string -->
        <pattern>%d{ISO8601} [%thread] %-5level [%X{traceId}, %X{clientId}] %logger{36} - %msg%n</pattern>
    </encoder>
</appender>

Logback Configuration Architecture

The logback-spring.xml file orchestrates destination routing, rotation policies, and threshold filtering. Below is a consolidated configuration demonstrating modern routing practices.

Log Level Hierarchy

Trace → Debug → Info → Warn → Error → Fatal

Console Destination

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        <charset>UTF-8</charset>
    </encoder>
</appender>

Daily File Rotation

Automatically archives files based on calendar days and anforces size thresholds before switching. Retention limits prevent disk exhaustion.

<appender name="DAILY_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${LOG_PATH}/application.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>${LOG_PATH}/app.%d{yyyy-MM-dd}.log</fileNamePattern>
        <maxHistory>30</maxHistory>
    </rollingPolicy>
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
        <maxFileSize>100MB</maxFileSize>
    </triggeringPolicy>
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
    </encoder>
</appender>

Isolated Error Routing

Filters ensure only critical exceptions bypass standard streams, aiding rapid incident response.

<appender name="CRITICAL_ERR" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <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}/crit_err.%d{yyyy-MM-dd}.log</fileNamePattern>
        <maxHistory>30</maxHistory>
    </rollingPolicy>
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
    </encoder>
</appender>

Business Metrics Stream

Dedicated routing for operational audits and transaction tracking.

<appender name="BIZ_AUDIT" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>${LOG_PATH}/biz_audit.%d{yyyy-MM-dd}.log</fileNamePattern>
        <maxHistory>30</maxHistory>
    </rollingPolicy>
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
    </encoder>
</appender>

Performance Analytics Channel

Short retention period suits high-frequency metric dumping.

<appender name="PERF_STREAM" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>${LOG_PATH}/metrics.%d{yyyy-MM-dd}.log</fileNamePattern>
        <maxHistory>7</maxHistory>
    </rollingPolicy>
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
    </encoder>
</appender>

Scope & Threshold Management

Package-level declarations override global defaults. Disabling additivity prevents duplicate log emission from parent contexts. External dependencies receive suppressed verbosity to maintain signal-to-noise ratios.

<logger name="com.project.core" level="INFO" additivity="false">
    <appender-ref ref="CONSOLE"/>
    <appender-ref ref="DAILY_FILE"/>
    <appender-ref ref="CRITICAL_ERR"/>
    <appender-ref ref="BIZ_AUDIT"/>
</logger>

<logger name="com.project.core.biz" level="INFO" additivity="false">
    <appender-ref ref="BIZ_AUDIT"/>
    <appender-ref ref="CONSOLE"/>
</logger>

<logger name="com.project.core.monitor" level="INFO" additivity="false">
    <appender-ref ref="PERF_STREAM"/>
</logger>

<!-- Framework noise reduction -->
<logger name="org.springframework" level="WARN"/>
<logger name="org.springframework.web" level="INFO"/>
<logger name="org.hibernate.SQL" level="DEBUG"/>
<logger name="com.amazonaws" level="WARN"/>
<logger name="org.apache.kafka" level="WARN"/>
<logger name="io.netty" level="WARN"/>

<!-- Environment-aware activation -->
<springProfile name="development">
    <logger name="com.project.core" level="DEBUG"/>
    <logger name="org.springframework.web" level="DEBUG"/>
</springProfile>

<springProfile name="production">
    <logger name="com.project.core" level="INFO"/>
    <root level="WARN"/>
</springProfile>

<!-- Fallback handler -->
<root level="INFO">
    <appender-ref ref="CONSOLE"/>
    <appender-ref ref="DAILY_FILE"/>
    <appender-ref ref="CRITICAL_ERR"/>
</root>
Tags: logging

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

SBUS Signal Analysis and Communication Implementation Using STM32 with Fus Remote Controller

Overview In a recent project, I utilized the SBUS protocol with the Fus remote controller to control a vehicle's basic operations, including movement, lights, and mode switching. This article is aimed...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.