CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-ch-qos-logback--logback-core

Core infrastructure and basic components for the Logback logging framework, providing appenders, encoders, layouts, filters, and event processing pipeline

Pending
Overview
Eval results
Files

encoders-layouts.mddocs/

Encoders and Layouts

Event encoding and transformation system for converting log events into formatted byte arrays or strings. Encoders are the modern approach for formatting log output, while layouts provide the traditional string-based formatting interface.

Capabilities

Encoder Interface

The primary interface for transforming log events into byte arrays, supporting headers, footers, and character encoding.

/**
 * Interface for transforming events into byte arrays.
 * Encoders are the recommended approach for formatting log output.
 */
public interface Encoder<E> extends ContextAware, LifeCycle {
    /**
     * Get header bytes to write at the beginning of the output.
     * @return header bytes, or null if no header
     */
    byte[] headerBytes();
    
    /**
     * Encode a single event into a byte array.
     * @param event the event to encode
     * @return encoded bytes for the event
     */
    byte[] encode(E event);
    
    /**
     * Get footer bytes to write at the end of the output.
     * @return footer bytes, or null if no footer
     */
    byte[] footerBytes();
}

EncoderBase

Base implementation providing common encoder functionality and lifecycle management.

/**
 * Base encoder implementation with lifecycle management.
 */
public abstract class EncoderBase<E> extends ContextAwareBase implements Encoder<E> {
    private boolean started = false;
    
    /**
     * Check if the encoder is started.
     * @return true if started
     */
    public boolean isStarted();
    
    /**
     * Start the encoder. Subclasses should override for initialization.
     */
    public void start();
    
    /**
     * Stop the encoder. Subclasses should override for cleanup.
     */
    public void stop();
    
    /**
     * Default implementation returns null (no header).
     * @return null
     */
    public byte[] headerBytes();
    
    /**
     * Default implementation returns null (no footer).
     * @return null
     */
    public byte[] footerBytes();
}

LayoutWrappingEncoder

Adapter that wraps a Layout to work with the Encoder interface, providing charset conversion and immediate flush control.

/**
 * Encoder that wraps a Layout, converting string output to bytes.
 * Provides the bridge between the old Layout interface and modern Encoder interface.
 */
public class LayoutWrappingEncoder<E> extends EncoderBase<E> {
    /**
     * Set the layout to wrap.
     * @param layout the layout to use for formatting
     */
    public void setLayout(Layout<E> layout);
    
    /**
     * Get the current layout.
     * @return the wrapped layout
     */
    public Layout<E> getLayout();
    
    /**
     * Set the character encoding for string-to-bytes conversion.
     * @param charset the charset to use (default: UTF-8)
     */
    public void setCharset(Charset charset);
    
    /**
     * Get the current character encoding.
     * @return the charset being used
     */
    public Charset getCharset();
    
    /**
     * Control immediate flush behavior (used by appenders).
     * @param immediateFlush true to flush immediately after each event
     */
    public void setImmediateFlush(boolean immediateFlush);
    
    /**
     * Check if immediate flush is enabled.
     * @return true if immediate flush is enabled
     */
    public boolean isImmediateFlush();
    
    /**
     * Encode event by calling layout.doLayout() and converting to bytes.
     * @param event the event to encode
     * @return UTF-8 encoded bytes of the formatted event
     */
    public byte[] encode(E event);
}

Layout Interface

Traditional string-based formatting interface for transforming events into formatted strings.

/**
 * Interface for transforming events into formatted strings.
 * Layouts are wrapped by LayoutWrappingEncoder for modern usage.
 */
public interface Layout<E> extends ContextAware, LifeCycle {
    /**
     * Format an event as a string.
     * @param event the event to format
     * @return formatted string representation
     */
    String doLayout(E event);
    
    /**
     * Get header string for file output.
     * @return header string, or null if no header
     */
    String getFileHeader();
    
    /**
     * Get footer string for file output.
     * @return footer string, or null if no footer
     */
    String getFileFooter();
    
    /**
     * Get header string for presentation (e.g., HTML table header).
     * @return presentation header, or null if none
     */
    String getPresentationHeader();
    
    /**
     * Get footer string for presentation (e.g., HTML table footer).
     * @return presentation footer, or null if none
     */
    String getPresentationFooter();
    
    /**
     * Get the content type of the formatted output.
     * @return content type (e.g., "text/plain", "text/html")
     */
    String getContentType();
}

LayoutBase

Base implementation for layouts providing lifecycle management and default header/footer behavior.

/**
 * Base layout implementation with lifecycle support.
 */
public abstract class LayoutBase<E> extends ContextAwareBase implements Layout<E> {
    private boolean started = false;
    
    /**
     * Check if the layout is started.
     * @return true if started
     */
    public boolean isStarted();
    
    /**
     * Start the layout.
     */
    public void start();
    
    /**
     * Stop the layout.
     */
    public void stop();
    
    /**
     * Subclasses must implement this method to format events.
     * @param event the event to format
     * @return formatted string
     */
    public abstract String doLayout(E event);
    
    /**
     * Default implementation returns null (no file header).
     * @return null
     */
    public String getFileHeader();
    
    /**
     * Default implementation returns null (no file footer).
     * @return null
     */
    public String getFileFooter();
    
    /**
     * Default implementation returns null (no presentation header).
     * @return null
     */
    public String getPresentationHeader();
    
    /**
     * Default implementation returns null (no presentation footer).
     * @return null
     */
    public String getPresentationFooter();
    
    /**
     * Default implementation returns "text/plain".
     * @return "text/plain"
     */
    public String getContentType();
}

EchoLayout

Simple layout that returns the event's string representation.

/**
 * Simple layout that returns event.toString().
 * Useful for testing and simple output scenarios.
 */
public class EchoLayout<E> extends LayoutBase<E> {
    /**
     * Format event using its toString() method.
     * @param event the event to format
     * @return event.toString()
     */
    public String doLayout(E event);
}

Utility Classes

ByteArrayUtil

Utility methods for byte array operations used in encoding.

/**
 * Utility methods for byte array operations.
 */
public class ByteArrayUtil {
    /**
     * Convert hex string to byte array at specified offset.
     * @param hexString hex string to convert
     * @param ba target byte array
     * @param offset offset in target array
     */
    public static void hexStringToByteArray(String hexString, byte[] ba, int offset);
    
    /**
     * Convert byte array to hex string.
     * @param ba byte array to convert
     * @return hex string representation
     */
    public static String toHexString(byte[] ba);
}

NonClosableInputStream

InputStream wrapper that prevents closing, useful for protecting system streams.

/**
 * InputStream wrapper that prevents the close() method from actually closing the stream.
 * Useful for protecting System.in and other shared streams.
 */
public class NonClosableInputStream extends FilterInputStream {
    /**
     * Create a non-closable wrapper around an InputStream.
     * @param is the input stream to wrap
     */
    public NonClosableInputStream(InputStream is);
    
    /**
     * Override close() to do nothing, preventing actual stream closure.
     */
    @Override
    public void close();
}

Usage Examples

Basic Layout Wrapping Encoder

import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
import ch.qos.logback.core.layout.EchoLayout;
import java.nio.charset.StandardCharsets;

// Create and configure encoder with layout
LayoutWrappingEncoder<Object> encoder = new LayoutWrappingEncoder<>();
encoder.setContext(context);

// Set layout
EchoLayout<Object> layout = new EchoLayout<>();
layout.setContext(context);
layout.start();

encoder.setLayout(layout);
encoder.setCharset(StandardCharsets.UTF_8);
encoder.setImmediateFlush(true);
encoder.start();

// Use with appender
appender.setEncoder(encoder);

Custom Layout Implementation

import ch.qos.logback.core.LayoutBase;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class TimestampLayout<E> extends LayoutBase<E> {
    private DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
    private String prefix = "";
    
    @Override
    public String doLayout(E event) {
        StringBuilder sb = new StringBuilder();
        sb.append('[').append(LocalDateTime.now().format(formatter)).append(']');
        if (!prefix.isEmpty()) {
            sb.append(' ').append(prefix);
        }
        sb.append(' ').append(event.toString());
        sb.append(System.lineSeparator());
        return sb.toString();
    }
    
    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }
    
    public String getPrefix() {
        return prefix;
    }
    
    public void setDatePattern(String pattern) {
        this.formatter = DateTimeFormatter.ofPattern(pattern);
    }
    
    @Override
    public String getContentType() {
        return "text/plain";
    }
}

Custom Encoder Implementation

import ch.qos.logback.core.encoder.EncoderBase;
import java.nio.charset.StandardCharsets;

public class JsonEncoder<E> extends EncoderBase<E> {
    private boolean includeTimestamp = true;
    
    @Override
    public byte[] headerBytes() {
        return "[".getBytes(StandardCharsets.UTF_8);
    }
    
    @Override
    public byte[] encode(E event) {
        StringBuilder json = new StringBuilder();
        json.append("{");
        
        if (includeTimestamp) {
            json.append("\"timestamp\":\"")
                .append(System.currentTimeMillis())
                .append("\",");
        }
        
        json.append("\"message\":\"")
            .append(event.toString().replace("\"", "\\\""))
            .append("\"}");
        
        return json.toString().getBytes(StandardCharsets.UTF_8);
    }
    
    @Override
    public byte[] footerBytes() {
        return "]".getBytes(StandardCharsets.UTF_8);
    }
    
    public void setIncludeTimestamp(boolean includeTimestamp) {
        this.includeTimestamp = includeTimestamp;
    }
    
    public boolean isIncludeTimestamp() {
        return includeTimestamp;
    }
}

Using Encoder with File Headers and Footers

import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.encoder.LayoutWrappingEncoder;

// Create encoder that produces headers and footers for XML files
public class XmlEncoder<E> extends EncoderBase<E> {
    @Override
    public byte[] headerBytes() {
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<events>\n"
            .getBytes(StandardCharsets.UTF_8);
    }
    
    @Override
    public byte[] encode(E event) {
        return String.format("  <event>%s</event>\n", event.toString())
            .getBytes(StandardCharsets.UTF_8);
    }
    
    @Override
    public byte[] footerBytes() {
        return "</events>\n".getBytes(StandardCharsets.UTF_8);
    }
}

// Use with file appender
FileAppender<Object> fileAppender = new FileAppender<>();
fileAppender.setContext(context);
fileAppender.setFile("events.xml");

XmlEncoder<Object> xmlEncoder = new XmlEncoder<>();
xmlEncoder.setContext(context);
xmlEncoder.start();

fileAppender.setEncoder(xmlEncoder);
fileAppender.start();

Character Encoding Considerations

When working with encoders and layouts, character encoding is crucial:

  • Default Encoding: LayoutWrappingEncoder uses UTF-8 by default
  • Platform Encoding: Avoid relying on platform default encoding
  • Explicit Configuration: Always set charset explicitly for predictable behavior
  • Byte Array Handling: Encoders work with byte arrays, eliminating encoding ambiguity

Migration from Layouts to Encoders

For modern logback usage, prefer encoders over layouts:

  1. Replace layout configuration with encoder
  2. Wrap existing layouts with LayoutWrappingEncoder
  3. Implement Encoder<E> for new custom formatters
  4. Set explicit charset on LayoutWrappingEncoder

The encoder approach provides better control over output format, character encoding, and integration with modern appender implementations.

Install with Tessl CLI

npx tessl i tessl/maven-ch-qos-logback--logback-core

docs

appenders.md

configuration.md

encoders-layouts.md

filters-evaluators.md

index.md

model-framework.md

network-logging.md

patterns.md

rolling-policies.md

utilities.md

tile.json