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.logger.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.
| // 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#debugEnablement() debugEnablement()}
- {@link oajr.RestContext.Builder#debugEnablement(Class) debugEnablement(Class)}
- {@link oajr.RestContext.Builder#debugEnablement(DebugEnablement) debugEnablement(DebugEnablement)}
HTTP calls can be logged with the following levels of granularity:
- {@link oajr.logger.CallLoggingDetail}
- {@link oajr.logger.CallLoggingDetail#STATUS_LINE STATUS_LINE}
- {@link oajr.logger.CallLoggingDetail#HEADER HEADER}
- {@link oajr.logger.CallLoggingDetail#ENTITY ENTITY}
The following examples show the output format for each detail type:
| 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 ===================================================================
| 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.logger.CallLogger}
and the default is {@link oajr.logger.BasicCallLogger} 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#callLogger() callLogger()}
- {@link oajr.RestContext.Builder#callLogger(Class) callLogger(Class)}
- {@link oajr.RestContext.Builder#callLogger(CallLogger) 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 {@link oajr.stats.ThrownStore} bean which is configured
via the following:
- {@link oajr.RestContext.Builder}
- {@link oajr.RestContext.Builder#thrownStore() thrownStore()}
- {@link oajr.RestContext.Builder#thrownStore(Class) thrownStore(Class)}
- {@link oajr.RestContext.Builder#thrownStore(ThrownStore) thrownStore(ThrownStore)}
- The {@link oajr.logger.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.
- {@link oajr.debug.DebugEnablement}, {@link oajr.logger.CallLogger}, and
{@link oajr.stats.ThrownStore} can all be defined globally as Spring beans in a Spring Boot environment.