{title:'Logging / Debugging', created:'9.0.0'}

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

- {@link oajr.annotation.Rest}
  - {@link oajr.annotation.Rest#debug() debug}
  - {@link oajr.annotation.Rest#debugOn() debugOn}
- {@link oajr.annotation.RestOp}
  - {@link oajr.annotation.RestOp#debug() debug}

Debug mode enables the following:

- HTTP request/response bodies are cached in memory for logging purposes.
- HTTP requests/responses are logged to the registered {@link oajr.logging.RestLogger}.

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 {@link oajr.annotation.Rest#debugOn() @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 {@link oajr.annotation.Rest#debugOn() @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:

- {@link oajr.debug.DebugEnablement}
- {@link oajr.debug.BasicDebugEnablement}
- {@link oajr.RestContext.Builder}
  - {@link oajr.RestContext.Builder#createDebugEnablement(BeanStore,Supplier) createDebugEnablement(BeanStore,Supplier)}
  - {@link oajr.RestContext.Builder#debugEnablement() debugEnablement()}
  - {@link oajr.RestContext.Builder#debugEnablement(Consumer) debugEnablement(Consumer)}

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

- {@link oajr.logging.RestLoggingDetail}
  - {@link oajr.logging.RestLoggingDetail#STATUS_LINE STATUS_LINE}
  - {@link oajr.logging.RestLoggingDetail#HEADER HEADER}
  - {@link oajr.logging.RestLoggingDetail#ENTITY ENTITY}

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

##### STATUS_LINE

| WARNING: [500] HTTP POST /foo?foo=bar

##### HEADER

| 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 {@link oajr.logging.RestLogger} and the default is {@link oajr.logging.BasicRestLogger} which produces the output above. It can be changed through any of the following:

- {@link oajr.annotation.Rest}
  - {@link oajr.annotation.Rest#callLogger() callLogger()}
- {@link oajr.RestContext.Builder}
  - {@link oajr.RestContext.Builder#createCallLogger(BeanStore,Supplier) createCallLogger(BeanStore,Supplier)}
  - {@link oajr.RestContext.Builder#callLogger() callLogger()}
  - {@link oajr.RestContext.Builder#callLogger(Consumer) callLogger(Consumer)}

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 {@link oajr.stats.ThrownStore} bean which is configured via the following:

- {@link oajr.RestContext.Builder}
  - {@link oajr.RestContext.Builder#createThrownStore(BeanStore,Supplier,RestContext) createThrownStore(BeanStore,Supplier)}
  - {@link oajr.RestContext.Builder#thrownStore() thrownStore()}
  - {@link oajr.RestContext.Builder#thrownStore(Consumer) thrownStore(Consumer)}

- The {@link oajr.logging.BasicTestRestLogger} 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.
- {@link oajr.debug.DebugEnablement}, {@link oajr.logging.RestLogger}, and {@link oajr.stats.ThrownStore} can all be defined globally as Spring beans in a Spring Boot environment.