Class Logging


public class Logging extends Object
Table of content


Internal logging used by the commercetools Composable Commerce client itself. Uses slf4j logger named commercetools.

Logger configuration

The ClientBuilder allows the customization of the log levels used for different events. By default responses will be logged with Level.INFO and errors with Level.ERROR. The ClientBuilder.withInternalLoggerFactory(InternalLoggerFactory, Level, Level, Level, Map, ResponseLogFormatter, ErrorLogFormatter) method can be used to change these defaults. Please see also InternalLoggerMiddleware for further details

        .withInternalLoggerFactory(ApiInternalLoggerFactory::get, Level.INFO, // log level for API responses
            Level.INFO, // log level deprecation notices
            Level.ERROR, // default log level for exceptions
            Collections.singletonMap(ConcurrentModificationException.class, Level.DEBUG) // custom log level for specific exceptions
        // ...

See the test code.

Log Information

The default logger middleware logs following information per level:


In case of an ApiHttpException the requests HTTP method name, URI and response status code will be logged. For any other exception happening the cause and the exception will be logged.


By default any response by the API will be logged with the requests HTTP method name, URI and response status code. In case a deprecation header was submitted in the response an info entry with the deprecation notice will be logged.

The used log level for these events can be configured while instantiating the InternalLoggerMiddleware.


The request and the response object will be logged as a string representation including headers and body. Sensitive data like auth token and passwords will be redacted.


The request and response will be logged with a pretty printed output. Sensitive data like auth token and passwords will be redacted.

Logger hierarchy

The loggers form a hierarchy separated by a dot. The root logger is commercetools.

The child loggers of commercetools Composable Commerce correspond with the names of the API endpoints, so for example commercetools.categories for Categories and commercetools.product-types for Product Types.

The grandchild loggers refer to actions. For example, commercetools.categories.request refers to performing requests via HTTPs to commercetools Composable Commerce for Categories and commercetools.categories.response refers to the response for Categories.

The logger makes use of different log levels, so for example commercetools.categories.response logs on debug level the http response from the platform (abbreviated example):

 [OkHttp] DEBUG commercetools.categories.response - io.vrap.rmf.base.client.ApiHttpResponse@33e37acd[statusCode=200,headers=...,textInterpretedBody={"limit":20,"offset":0,"count":4,"total":4,"results":[{"id":"ca7f64dc-ab41-4e7f-b65b-bfc25bf01111","version":1,"createdAt":"2021-01-06T09:21:55.445Z","lastModifiedAt":"2021-01-06T09:21:55.445Z","key":"random-key-15aab6ee-9a48-4fdc-a8c3-8ff9ff390d60","name":{"key-6bc3cc62-1995-43f4":"value-random-string-c3ff7f1a-6371-4c0c-a3b9-5060eca08b8a"},"slug":{"key-6bc3cc62-1995-43f4":"value-random-string-c3ff7f1a-6371-4c0c-a3b9-5060eca08b8a"},"description":{"key-6bc3cc62-1995-43f4":"value-random-string-c3ff7f1a-6371-4c0c-a3b9-5060eca08b8a"},"ancestors":[],"orderHint":"random-string-a99e6941-3225-4766-923a-4a654652532f","externalId":"random-id-100b3851-29b4-47c1-aef3-860b4cd28f54"}]}]

commercetools.categories.response logs on trace level additional the formatted http response from the API (abbreviated example):

 [OkHttp] TRACE commercetools.categories.response - 200
   "limit" : 20,
   "offset" : 0,
   "count" : 4,
   "total" : 4,
   "results" : [ {
     "id" : "bcb15128-b69e-47b6-81c4-1ea5f9a63b83",
     "version" : 1,
     "lastMessageSequenceNumber" : 1,
     "createdAt" : "2021-01-06T09:21:50.485Z",
     "lastModifiedAt" : "2021-01-06T09:21:50.485Z",
     "createdBy" : {
       "clientId" : "h-QvaF3NpsjPBWeXa6TUOnq0",
       "isPlatformClient" : false
     "key" : "random-key-f45535f5-1111-4bfb-98ac-164234ac2c73",
     "name" : {
       "key-b7d87008-9526-47b3-8b80" : "value-random-string-baf1a1ae-3726-4573-bec5-2c53c7d1114a"
     "slug" : {
       "key-b7d87008-9526-47b3-8b80" : "value-random-string-baf1a1ae-3726-4573-bec5-2c53c7d1114a"
     "description" : {
       "key-b7d87008-9526-47b3-8b80" : "value-random-string-baf1a1ae-3726-4573-bec5-2c53c7d1114a"
     "ancestors" : [ ],
     "orderHint" : "random-string-6b88f299-d4ca-491a-9d7c-3463e718c8d2",
     "externalId" : "random-id-b98a5ab4-5413-40b3-8119-eb5ac2b5afbb",
     "metaTitle" : {
       "key-b7d87008-9526-47b3-8b80" : "value-random-string-baf1a1ae-3726-4573-bec5-2c53c7d1114a"
     "metaKeywords" : {
       "key-b7d87008-9526-47b3-8b80" : "value-random-string-baf1a1ae-3726-4573-bec5-2c53c7d1114a"
     "metaDescription" : {
       "key-b7d87008-9526-47b3-8b80" : "value-random-string-baf1a1ae-3726-4573-bec5-2c53c7d1114a"
   }, {
   } ]

commercetools.products.responses.queries logs only HTTP GET requests and commercetools.products.responses.commands logs only HTTP POST/DELETE requests.

 [pool-1-thread-1] DEBUG commercetools.products.request.commands - io.vrap.rmf.base.client.ApiHttpRequest@1d2c6948[method=POST,uri="",headers=[...],textInterpretedBody={"productType":{"id":"cda39953-23af-4e85-abb0-5b89517ec5f2","typeId":"product-type"},"name":{"random-string-b66de021-d2fa-4262-8837-94a6992a8cdc":"random-string-da28a010-e66b-49d4-a18b-f1bfc145d2f6"},...}]
 [pool-1-thread-1] DEBUG commercetools.products.request.queries - io.vrap.rmf.base.client.ApiHttpRequest@53667fdb[method=GET,uri="",headers=[...],textInterpretedBody=empty body]

Usage of Mapped Diagnostic Context

The SDK uses CompletableFutures extensively and the ExecutorService typically has a thread pool to execute the futures. This could lead to the fact that the future is executed on different threads. In case the MDC is used by the application the context could get lost while executing the futures. As the CompletableFutures don't allow attaching a Context you have to make them and the ExecutorService context aware so that they will restore the MDC before executing the future tasks.

The SDK additionally supports the attachment of a Context to a client ContextApiHttpClient, the ApiHttpRequest and the ApiHttpResponse by implementing the ContextAware interface

The ProjectApiRoot also allows to add a Context to it. This is useful to create a context aware root from a global instance when needed.

ProjectApiRoot globalRoot = ProjectApiRoot.of("test");
ProjectApiRoot apiRoot = ProjectApiRoot.withContext(globalRoot, new MDCContext());

See the test code.

The above example creates a MDCContext and take a copy of the MDC. The ApiHttpClient will be wrapped in a ContextApiHttpClient and set the context to all requests and responses executed.

The InternalLoggerMiddleware will restore the MDC before logging and clean up afterwards to avoid pollution of threads with unrelated context data. The same applies for the ConcurrentModificationMiddleware

The usage of the Context object in the SDK allows further extensions to transport request related context data e.g. for telemetry purposes.

  • Constructor Details

    • Logging

      public Logging()