-
Notifications
You must be signed in to change notification settings - Fork 50
Custom Fields
Custom fields are user-defined key-value pairs that you can add to your structured logs. They allow you to enrich your logs with business-specific data, such as an order_id, customer_id, or any other metric that helps you trace, debug, or analyze your application's behavior.
This article first covers how to add custom fields directly from your application code, then discusses advanced methods for programmatic field injection, and finally explains the different JSON output formats and their configuration.
There are several ways to add custom fields to your logs directly from your Java code. These methods work regardless of the final JSON format.
With SLF4J 2.0+, the Fluent Logging API provides a modern, chainable way to add key-value pairs directly to a log statement. This is the recommended approach for adding context to a single log event. cf-java-logging-support version 4.0.0 and higher automatically recognizes these key-value pairs.
// Requires SLF4J 2.0+
LOG.atInfo()
.addKeyValue("order_id", "A-12345")
.addKeyValue("payment_method", "credit_card")
.log("Payment processed for order.");The MDC is ideal for adding context that should be present in all log messages within a specific scope, such as a single web request.
import org.slf4j.MDC;
// Add a field to the context
MDC.put("order_id", "A-12345");
MDC.put("tenant_id", "T01");
LOG.info("Starting order processing..."); // This log will contain order_id and tenant_id
LOG.warn("Inventory is low."); // This log will also contain the same fields
// It's a good practice to clear the MDC after the request is processed
MDC.clear();For applications on older SLF4J versions, you can pass a CustomField object as a log parameter. This is useful for adding values relevant only to a single log statement.
import static com.sap.hcp.cf.logging.common.customfields.CustomField.customField;
// ...
LOG.info("Payment received for order {}", customField("order_id", "A-12345"));If a key is duplicated, the precedence is: Fluent API / CustomField parameter > MDC fields.
For more advanced use cases, such as programmatically adding complex or dynamically derived data to every log message, you can implement a ContextFieldSupplier.
There are three interfaces available:
-
com.sap.hcp.cf.logging.common.serialization.ContextFieldSupplier: A common interface that works for both Logback and Log4j2. -
com.sap.hcp.cf.logback.converter.api.LogbackContextFieldSupplier: A Logback-specific interface. -
com.sap.hcp.cf.log4j2.converter.api.Log4jContextFieldSupplier: A Log4j2-specific interface.
There are two ways to register your implementation:
This is the easiest way to register a supplier.
- Create a class that implements one of the interfaces above.
- Create a file named after the fully qualified interface name in the META-INF/services/ directory of your project.
- Add the fully qualified name of your implementation class to this file. The library will automatically discover and load your supplier at runtime.
You can also register your supplier explicitly in your logback.xml or log4j2.xml file.
Logback (logback.xml)
<appender name="STDOUT-JSON" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="com.sap.hcp.cf.logback.encoder.JsonEncoder">
<!-- Register a common supplier -->
<contextFieldSupplier>com.example.MyCommonSupplier</contextFieldSupplier>
<!-- Register a Logback-specific supplier -->
<logbackContextFieldSupplier>com.example.MyLogbackSupplier</logbackContextFieldSupplier>
</encoder>
</appender>Log4j2 (log4j2.xml)
<JsonPatternLayout>
<!-- Register a common supplier -->
<contextFieldSupplier class="com.example.MyCommonSupplier" />
<!-- Register a Log4j2-specific supplier -->
<log4jContextFieldSupplier class="com.example.MyLog4j2Supplier" />
</JsonPatternLayout>The library supports two JSON formats for custom fields, automatically selecting the correct one based on your environment.
This is the default behavior required by the modern SAP Cloud Logging service. Custom fields are added as top-level key-value pairs, which is intuitive and easy to query.
Example Log:
{
"written_at": "2023-10-27T12:00:00.000Z",
"level": "INFO",
"msg": "Order processed successfully",
"order_id": "A-12345"
}No special configuration is needed to enable this format.
This format is required by the older SAP BTP Application Logging service. Custom fields are bundled into a special nested JSON object named #cf.
Example Log:
{
"msg": "Order processed successfully",
"#cf": {
"string": [
{"k": "order_id", "v": "A-12345", "i": 0}
]
}
}This format is activated automatically if the library detects a binding to the application-logs service.
You can also force it by setting the environment variable LOG_GENERATE_APPLICATION_LOGGING_CUSTOM_FIELDS to true.
This configuration is only required if you are targeting the SAP BTP Application Logging service and need to generate the nested #cf format.
You must declare the keys of your custom fields in the logging configuration.
Logback Example (logback.xml)
<encoder class="com.sap.hcp.cf.logback.encoder.JsonEncoder">
<!-- Fields to be moved into the #cf object -->
<customFields>order_id,customer_id,payment_method</customFields>
<!-- Fields to be copied to #cf but also kept at the top level -->
<retainedFields>tenant_id</retainedFields>
</encoder>Log4j2 Example (log4j2.xml)
<JsonPatternLayout>
<!-- This field will be moved into the #cf object -->
<customField mdcKeyName="order_id" />
<!-- This field will be in #cf AND at the top level -->
<customField mdcKeyName="tenant_id" retainOriginal="true" />
</JsonPatternLayout>