Skip to main content
phase4 uses SLF4J for all logging, which means you can plug in any SLF4J-compatible backend. The example server applications ship with Apache Log4j 2.

How phase4 logging works

All phase4 loggers are created through Phase4LoggerFactory, which wraps the standard SLF4J LoggerFactory. The wrapper adds two features:
  • Thread-local prefix — a string prepended to every log message on the current thread.
  • Thread-local suffix — a string appended to every log message on the current thread.
These per-thread decorations are managed by Phase4LogCustomizer and are useful for correlating log output with a specific incoming message or request.

Logger naming

All loggers follow the standard Java package name convention. The root logger for the entire library is:
com.helger.phase4
Key sub-packages:
Logger / packageWhat it covers
com.helger.phase4All phase4 classes
com.helger.phase4.configConfiguration loading
com.helger.phase4.cryptoCrypto factory initialization and key loading
com.helger.phase4.incomingIncoming message processing
com.helger.phase4.dumpMessage dump operations
com.helger.phase4.senderOutgoing message sending
org.apache.wss4jWSS4J signing, verification, encryption, decryption
org.apache.httpApache HttpClient HTTP transport

Basic log4j2 configuration

Place a log4j2.xml file on the classpath (e.g., src/main/resources/log4j2.xml):
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="info" strict="true">
  <Appenders>
    <Console name="STDOUT" target="SYSTEM_OUT">
      <PatternLayout pattern="[%date{ISO8601}] [phase4] [%-5level] [%thread] %msg -- %location%n" />
    </Console>
  </Appenders>
  <Loggers>
    <Root level="info">
      <AppenderRef ref="STDOUT" />
    </Root>
  </Loggers>
</Configuration>
This configuration outputs all INFO and above log events to stdout with a timestamp, level, thread name, message, and source location.

Enabling debug logging for phase4

To see detailed phase4 activity (e.g., message parsing, PMode resolution, signature operations), lower the log level for the com.helger.phase4 package:
log4j2.xml
<Loggers>
  <Logger name="com.helger.phase4" level="debug" additivity="false">
    <AppenderRef ref="STDOUT" />
  </Logger>
  <Root level="info">
    <AppenderRef ref="STDOUT" />
  </Root>
</Loggers>

Enabling HTTP wire logging

To log the raw bytes of every HTTP request and response sent by the Apache HttpClient, enable debug logging for the Apache HTTP wire logger:
log4j2.xml
<Loggers>
  <!-- Raw HTTP bytes (very verbose) -->
  <Logger name="org.apache.http.wire" level="debug" additivity="false">
    <AppenderRef ref="STDOUT" />
  </Logger>
  <!-- HTTP headers only -->
  <Logger name="org.apache.http.headers" level="debug" additivity="false">
    <AppenderRef ref="STDOUT" />
  </Logger>
  <Root level="info">
    <AppenderRef ref="STDOUT" />
  </Root>
</Loggers>
HTTP wire logging produces very large output that includes message payloads. Enable it only for short debugging sessions and never in production.

Enabling WSS4J security debug logging

log4j2.xml
<Logger name="org.apache.wss4j" level="debug" additivity="false">
  <AppenderRef ref="STDOUT" />
</Logger>

Per-thread log prefix and suffix

Phase4LogCustomizer lets you annotate log messages with a per-thread prefix or suffix. This is useful for correlating messages in multi-threaded servers:
import com.helger.phase4.logging.Phase4LogCustomizer;

// Set a prefix for the current thread's phase4 log output
Phase4LogCustomizer.setThreadLocalLogPrefix("[msg:" + messageID + "] ");
try {
    // ... process the message ...
} finally {
    // Always clear to avoid leaking context to the next request on this thread
    Phase4LogCustomizer.clearThreadLocals();
}
Or use the built-in helper that clears automatically:
import com.helger.phase4.logging.Phase4LogCustomizer;

Phase4LogCustomizer.runWithLogPrefixAndSuffix(
    "[msg:" + messageID + "] ",
    null,
    () -> {
        // All phase4 log statements inside this block will have the prefix
        processMessage();
    }
);

Custom log message transformation

If you need to transform all log messages globally (not per-thread), you can extend Phase4LoggerFactory or wrap the loggers. However, the standard approach is to use the thread-local prefix/suffix mechanism above.

Complete log4j2.xml for development

The following configuration enables debug output for phase4 while keeping other libraries at INFO:
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" strict="true">
  <Appenders>
    <Console name="STDOUT" target="SYSTEM_OUT">
      <PatternLayout pattern="[%date{ISO8601}] [%-5level] [%thread] %logger{36} - %msg%n" />
    </Console>
  </Appenders>
  <Loggers>
    <!-- Detailed phase4 output -->
    <Logger name="com.helger.phase4" level="debug" additivity="false">
      <AppenderRef ref="STDOUT" />
    </Logger>
    <!-- WSS4J security operations -->
    <Logger name="org.apache.wss4j" level="info" additivity="false">
      <AppenderRef ref="STDOUT" />
    </Logger>
    <Root level="info">
      <AppenderRef ref="STDOUT" />
    </Root>
  </Loggers>
</Configuration>

Build docs developers (and LLMs) love