Skip to main content

Logging / Debugging

The REST APIs provides supports enabling logging of HTTP requests and responses through the following annotations:

RestdebugdebugOnRestOpdebug

Debug mode enables the following:

  • HTTP request/response bodies are cached in memory for logging purposes.
  • HTTP requests/responses are logged to the registered CallLogger.

The possible annotation values are:

  • true - Debug is enabled for all requests.
  • false - Debug is disabled for all requests.
  • conditional - Debug is enabled only for requests that have a Debug: true header.
Example
// Enable debugging on all op calls on this resource
@Rest(debug="true")
public class MyResource {...}

These annotations support SVL variables, so it's possible to define them as a system property for example.

// Enable via system property 'MyResource.debug'
@Rest(debug="$S{MyResource.debug}")
public class MyResource {...}

The @Rest(debugOn) annotation can also be used to enable debugging. It takes a comma-delimited list of key-value pairs, the keys being class or method names, and the values being one of true|false|conditional.

// Turn on debug per-request on the class and always on the doX() method
@Rest(debugOn="MyResource=conditional,MyResource.doX=true")
public class MyResource {...}

The primary advantage of @Rest(debugOn) is that you can control debugging externally such as through a system property or environment variable:

// Turn on debug per-request on the class and always on the doX() method
@Rest(debugOn="$E{DEBUG}")
public class MyResource {...}

Debugging can also be enabled programmatically with the use of the following APIs:

DebugEnablementBasicDebugEnablementRestContext.BuilderdebugEnablement() debugEnablement(Class) debugEnablement(DebugEnablement)

HTTP calls can be logged with the following levels of granularity:

CallLoggingDetailSTATUS_LINE HEADER ENTITY

The following examples show the output format for each detail type:

STATUS_LINE

WARNING: [500] HTTP POST /foo?foo=bar
WARNING:
=== HTTP Call (incoming) ===================================================
[500] HTTP POST /foo?foo=bar
Request length: 3 bytes
Response code: 500
Response length: 3 bytes
Exec time: 20ms
---Request Headers---
Foo: bar
---Response Headers---
Foo: bar
`Content-Type: text/plain`
=== END ===================================================================

ENTITY

WARNING:
=== HTTP Call (incoming) ===================================================
[500] HTTP POST /foo?foo=bar
Request length: 3 bytes
Response code: 500
Response length: 3 bytes
Exec time: 20ms
---Request Headers---
Foo: bar
---Response Headers---
Foo: bar
`Content-Type: text/plain`
---Request Content UTF-8---
Foo
---Request Content Hex---
46 6F 6F
---Response Content UTF-8---
Foo
---Response Content Hex---
46 6F 6F
=== END ===================================================================

The interface responsible for generating the log entries is CallLogger and the default is BasicCallLogger which produces the output above.

It can be changed through any of the following:

RestcallLogger()RestContext.BuildercallLogger() callLogger(Class) callLogger(CallLogger)

The call logger uses logging rules to map requests to logging detail levels.

By default, these are the logging rules:

RestLogger
.create()
.beanStore(beanStore) // Allow injected beans in constructor
.normalRules( // Rules when debugging is not enabled
RestLoggerRule.create() // Log 500+ errors with status-line and header information
.statusFilter(x -> x >= 500)
.level(SEVERE)
.requestDetail(HEADER)
.responseDetail(HEADER)
.build(),
RestLoggerRule.create() // Log 400-500 errors with just status-line information
.statusFilter(x -> x >= 400)
.level(WARNING)
.requestDetail(STATUS_LINE)
.responseDetail(STATUS_LINE)
.build()
)
.debugRules( // Rules when debugging is enabled
RestLoggerRule.create() // Log everything with full details
.level(SEVERE)
.requestDetail(ENTITY)
.responseDetail(ENTITY)
.build()
);

Thrown exceptions get logged with a stack trace hash and a counter like below:

WARNING: [500,9b85cc96.13] HTTP POST /foo?foo=bar

Stack trace hashes are controlled by the ThrownStore bean which is configured via the following:

RestContext.BuilderthrownStore()thrownStore(Class)thrownStore(ThrownStore)
note
  • The BasicTestCallLogger class is useful for testing and allows you to suppress logging when testing error conditions by passing in a noTrace=true query parameter or No-Trace: true header on requests.
  • DebugEnablement, CallLogger, and ThrownStore can all be defined globally as Spring beans in a Spring Boot environment.