Writing Application Logs - SAP/cf-java-logging-support GitHub Wiki

This guide explains how to configure your application to produce structured, JSON-formatted application logs using cf-java-logging-support.

Structured logging is essential in cloud environments. Instead of plain text, log messages are written as JSON objects. This allows log aggregation systems to easily parse, index, and search your logs. This library enriches your logs with crucial contextual information, such as a correlation_id, which helps you trace a single business transaction across multiple microservices.

The Basics

The library integrates with the Simple Logging Facade for Java (SLF4J), the standard API for logging in Java. You can use it with either of the two most popular logging implementations: Logback or Log4j2.

To get started, you need to:

  1. Choose a logging implementation (Logback or Log4j2).
  2. Add the correct Maven dependencies to your project.
  3. Configure the JSON encoder or layout in your logging configuration file.

This guide will walk you through the steps for each implementation.

Using with Logback

Maven Dependencies

Add the following dependencies to your pom.xml. The cf-java-logging-support-logback artifact will automatically pull in the necessary core library.

<!-- pulls cf-java-logging-support-core feature -->
<!-- SLF4J API -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.13</version> <!-- Use the latest 2.x version -->
</dependency>

<!-- cf-java-logging-support for Logback -->
<dependency>
   <groupId>com.sap.hcp.cf.logging</groupId>
   <artifactId>cf-java-logging-support-logback</artifactId>
   <version>${cf-logging-version}</version>
</dependency>

<!-- Logback Implementation -->
<dependency>
   <groupId>ch.qos.logback</groupId>
   <artifactId>logback-classic</artifactId>
   <version>1.4.14</version> <!-- Use a recent version compatible with SLF4J 2.x -->
</dependency>

Logging Configuration

In your logback.xml file, configure your console appender to use the com.sap.hcp.cf.logback.encoder.JsonEncoder. This encoder is responsible for creating the JSON-formatted log messages.

Basic logback.xml Configuration:

<configuration debug="false" scan="false">
   <appender name="STDOUT-JSON" class="ch.qos.logback.core.ConsoleAppender">
      <encoder class="com.sap.hcp.cf.logback.encoder.JsonEncoder"/>
   </appender>
   <root level="${LOG_ROOT_LEVEL:-WARN}">
      <appender-ref ref="STDOUT-JSON" />
   </root>
</configuration>

Logback Customization

The JsonEncoder can be customized with several options. The following example shows all available configurations within the <encoder> tag.

<configuration debug="false" scan="false">
   <appender name="STDOUT-JSON" class="ch.qos.logback.core.ConsoleAppender">
      <encoder class="com.sap.hcp.cf.logback.encoder.JsonEncoder">
         <maxStacktraceSize>56320</maxStacktraceSize>
         <sendDefaultValues>false</sendDefaultValues>
	 <contextFieldSupplier>com.sap.hcp.cf.logging.common.serialization.FullVcapEnvFieldSupplier</contextFieldSupplier>
         <logbackContextFieldSupplier>MyLogbackContextFieldSupplier</logbackContextFieldSupplier>
         <charset>utf-8</charset>
         <jsonBuilder>MyJsonBuilder</jsonBuilder>
         <!-- See Custom Fields documentation for these -->
         <customField>myCustomFieldName</customField>
         <retainField>myCustomFieldName</retainField>
      </encoder>
   </appender>
   <!-- ... rest of configuration ... -->
</configuration>
XML-Element Default (if omitted) Explanation
<maxStacktraceSize> 56320 The maximum size for a stack trace before it is truncated. This prevents log messages from being dropped by logging pipelines due to excessive size.
<sendDefaultValues> false If true, fields with default values (e.g., - for strings) will be included in the JSON output. The default keeps messages smaller.
<contextFieldSupplier> (none) A custom class implementing ContextFieldSupplier to add extra fields to every log message, such as VCAP application details.
<logbackContextFieldSupplier> (none) A Logback-specific supplier that allows inspecting the ILoggingEvent.
<charset> utf-8 The character encoding for the output.
<jsonBuilder> JSON.builder() Will create an instance of the configured class as JSON.Builder. This allows modification of the generated JSON, e.g. special escaping or formatting.

Note: For details on <customField>, <retainField>, <contextFieldSupplier>, and <logbackContextFieldSupplier> see the article on Custom Fields.

Using with Log4j2

Maven Dependencies

Add the following dependencies to yourpom.xml. The cf-java-logging-support-log4j2 artifact provides the bridge to Log4j2.

<!-- SLF4J API -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.13</version> <!-- Use the latest 2.x version -->
</dependency>

<!-- cf-java-logging-support for Log4j2 -->
<dependency>
   <groupId>com.sap.hcp.cf.logging</groupId>
   <artifactId>cf-java-logging-support-log4j2</artifactId>
   <version>${cf-logging-version}</version>
</dependency>

<!-- Log4j2 SLF4J Bridge & Core Implementation -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j2-impl</artifactId>
    <version>2.23.1</version> <!-- Use a recent version -->
</dependency>

Logging Configuration

In your log4j2.xml file, configure your console appender to use the JsonPatternLayout. Make sure to include the com.sap.hcp.cf.log4j2.layout package in your configuration.

Basic log4j2.xml Configuration:

<Configuration status="warn" strict="true" packages="com.sap.hcp.cf.log4j2.converter,com.sap.hcp.cf.log4j2.layout">
   <Appenders>
      <Console name="STDOUT-JSON" target="SYSTEM_OUT">
         <JsonPatternLayout />
      </Console>
   </Appenders>
   <Loggers>
      <Root level="${LOG_ROOT_LEVEL:-WARN}">
         <AppenderRef ref="STDOUT-JSON" />
      </Root>
   </Loggers>
</Configuration>     

Log4j2 Customization

The JsonPatternLayout can be customized with several attributes and child elements.

Advanced log4j2.xml Configuration:

<Configuration status="warn" packages="com.sap.hcp.cf.log4j2.layout,com.sap.hcp.cf.log4j2.converter">
   <Appenders>
      <Console name="STDOUT-JSON" target="SYSTEM_OUT">
         <JsonPatternLayout charset="utf-8" maxStacktraceSize="56320" sendDefaultValues="false">
            <contextFieldSupplier class="com.sap.hcp.cf.logging.common.serialization.FullVcapEnvFieldSupplier" />
            <log4jContextFieldSupplier class="com.your.company.MyLog4jContextFieldSupplier" />
            <customField mdcKeyName="custom-field" retainOriginal="true" />
         </JsonPatternLayout>
      </Console>
   </Appenders>
   <!-- ... rest of configuration ... -->
</Configuration>
XML-Attribute/Element Default (if omitted) Explanation
maxStacktraceSize 56320 The maximum size in bytes for a stack trace before it is truncated.
sendDefaultValues false If true, fields with default values will be included in the JSON output.
<contextFieldSupplier> (none) A custom class implementing ContextFieldSupplier to add extra fields.
<log4jContextFieldSupplier> (none) A Log4j2-specific supplier that allows inspecting the LogEvent.
charset utf-8 The character encoding for the output.
jsonBuilder JSON.builder() Will create an instance of the configured class as JSON.Builder. This allows modification of the generated JSON, e.g. special escaping or formatting.

For documentation on <customField>, <retainField>, <contextFieldSupplier>, and <log4jContextFieldSupplier> see the article on Custom Fields.

Best Practices for Application Logging

Logging Exceptions and Stack Traces

To log an exception correctly, pass the Throwable object as the last argument to your SLF4J logging call. The JsonLayout will automatically detect it and serialize the stack trace into a single, correctly-formatted stacktrace field in the JSON output.

Correct Way:

private static final Logger LOGGER = LoggerFactory.getLogger(MyClass.class);

try {
    // some operation that fails
} catch (Exception ex) {
    // Pass the exception object as the last parameter
    LOGGER.error("An error occurred while processing the request", ex);
}

Incorrect Way:

// DO NOT do this. It will print a multi-line trace and break structured logging.
LOGGER.error("An error occurred: " + ex.getMessage());
ex.printStackTrace();

This ensures the entire stack trace is captured within a single log message, associated with its request context (correlation_id), and is automatically truncated if it's too large.

Using Markers for Categories

You can add categorical information to your logs using SLF4J Markers. The library will place the marker's name in the categories field of the JSON log. This is useful for filtering logs in your analysis tool.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;

public class MyRepository {
    private static final Logger LOGGER = LoggerFactory.getLogger(MyRepository.class);
    private static final Marker DB_MARKER = MarkerFactory.getMarker("database");

    public void findUser(String id) {
        LOGGER.info(DB_MARKER, "Querying database for user with id: {}", id);
        // ... database logic ...
    }
}

Note: Avoid using marker names that are reserved internally by this library. See the Markers class for the list of reserved names.

Adding Custom Fields

For adding arbitrary key-value pairs to your logs, please refer to the dedicated guide on Custom Fields.

⚠️ **GitHub.com Fallback** ⚠️