Core infrastructure and basic components for the Logback logging framework, providing appenders, encoders, layouts, filters, and event processing pipeline
—
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.
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();
}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();
}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);
}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();
}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();
}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 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);
}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();
}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);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";
}
}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;
}
}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();When working with encoders and layouts, character encoding is crucial:
For modern logback usage, prefer encoders over layouts:
layout configuration with encoderLayoutWrappingEncoderEncoder<E> for new custom formattersLayoutWrappingEncoderThe 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