This project just implements logging servlet filter, which logs requests and responses.
I originally had this implementation in io.github.antonsjava:web-filter. But I found some limitations in configuration which I'm not able to implement in original project.
There are two main branches
API is same in both branches
There are three log messages produced by filter
- message displayed before request is processed
- message with request data displayed after request is processed
- message with response data displayed after request is processed
You can configure if the message is displayed and there are options how to configure request
This message looks like
REQ[2] POST /rest/book vvv
In general it contains
- prefix (in example 'REQ'). You can configure different text. If prefix text is null whole message is not diplayed.
- number (in example '[2]'). This is request index counted from start of the application. It is useful to pair this three messages together
- request method (in example 'POST').
- request path (in example '/rest/book').
- constant string (in example 'vvv'). Just marks that other log messages from request processing is bellow this message
This message looks like
REQ[2] POST /rest/book identity(admin) headers(Token: 1234) payload[{"title":"pokus","abstractText":"something long","author":{"id":"t1"}}] size: 70
In general it contains
- prefix (in example 'REQ'). You can configure different text. If prefix text is null whole message is not diplayed.
- number (in example '[2]'). This is request index counted from start of the application. It is useful to pair this three messages together
- request method (in example 'POST').
- request path (in example '/rest/book').
- user identity (in example 'identity(admin)'). Principal name. Present only if configured.
- header info (in example 'headers(Token: 1234)'). Request header info. Present only if configured (You must specify header formatter).
- payload info (in example 'payload[{"ti....] size: 70'). Request body info. Present only if configured (You must specify body formatter).
This message looks like
RES[2] POST /rest/book status: 200 time: 122 headers(Token: 1234) payload[1703929952718] size: 13
In general it contains
- prefix (in example 'RES'). You can configure different text. If prefix text is null whole message is not diplayed.
- number (in example '[2]'). This is request index counted from start of the application. It is useful to pair this three messages together
- request method (in example 'POST').
- request path (in example '/rest/book').
- status (in example 'status: 200'). Response status.
- time (in example 'time: 122'). Response time.
- header info (in example 'headers(Token: 1234)'). Request header info. Present only if configured (You must specify header formatter).
- payload info (in example 'payload[1703929952718] size: 13'). Request body info. Present only if configured (You must specify body formatter).
In spring boot you only create bean of type LogFilter.
LogFilter is configured as list of triplets.
- request condition
- response condition (if this is defined no start request message is displayed and filter processing is done even message is not displayed because of condition)
- output configuration
LogFilter for each request try to find first triplet which fulfil conditions and make output using that configuration.
- you can build condition exactly as you write condition on paper like path().startsWith("/public").and().method().equals("POST")
- you can use custom condition - you can define your own condition
- you can use any() condition - means always true
- you can use several request string conditions in format resolveStringFromRequest().applyStringConditoon() (like .path().contains("boo"))
- you can combine conditoons with not(), or(), and(). lb(), rb() (lb and rb are brackets)
- you can build condition exactly as you write condition on paper like path().startsWith("/public").and().method().equals("POST")
- you can use custom condition - you can define your own condition
- you can use any() condition - means always true
- you can use statusOK() condition - status is 2xx
- you can use several response string conditions in format resolveStringFromRequest().applyStringConditoon() (like .path().contains("boo"))
- you can combine conditions with not(), or(), and(). lb(), rb() (lb and rb are brackets)
If you define response condition
- filter will not print request start message
- filter must process request data even message is not displayed - because response must be evaluated first to know if message must be printed
So use them only of you really need it and with combination with request condition.
Each triplet has own configuration it is based on default configuration. You can define
- reset() - used to reset actual state of configuration (You can remove default settings in that way)
- doNothing() - if this is set to true - no output is produced
- messageConsumer() - function which really prints message (like m -> log.debug(m))
- messageConsumerEnabled() - function which determine if logging is enabled (like () -> log.isDebugEnabled())
- requestStartPrefix() - prefix for request start message (default REQ) (if it is null request start message is not printed)
- requestPrefix() - prefix for request message (default REQ) (if it is null request message is not printed)
- responsePrefix() - prefix for response message (default RES) (if it is null response message is not printed)
- identity() - prncipal name is printed if it is set to true
- requestHeaderFormatter() - function to format request header (default is null) (if it is null no info is printed) There are provided LogFilter.Headers.all() and LogFilter.Headers.listed("name1", "name2") functions.
- requestPayloadFormatter() - function to format request body (default is null) (if it is null no info is printed) There are provided LogFilter.Body.asIs(), LogFilter.Body.Json and LogFilter.Body.Xml functions. For json and xml it is possible to force oneline format of trim long literals.
- responseHeaderFormatter() - function to format response header (default is null) (if it is null no info is printed) There are provided LogFilter.Headers.all() and LogFilter.Headers.listed("name1", "name2") functions.
- responsePayloadFormatter() - function to format response body (default is null) (if it is null no info is printed) There are provided LogFilter.Body.asIs(), LogFilter.Body.Json and LogFilter.Body.Xml functions. For json and xml it is possible to force oneline format of trim long literals.
Here is example of not so real usage.
I usually use it in following way
- for path prefix /actatpr doNothing(true)
- for path prefix (or path prefix and method) where result is binary - do not print payload
- for any format body output to json one line and cut literals to 200 (longer literals are usually some binary data in json)
LogFilter filter = LogFilter.builder()
.defaultConf()
.messageConsumer(message -> log.info(message))
.messageConsumerEnabled(() -> log.isInfoEnabled())
.done()
.inCase() // ignore actuator
.request()
.path().startsWith("/actuator/")
.done()
.conf()
.doNothing(true)
.done()
.done()
.inCase() // just simple line no headers no body
.request()
.path().endsWith("/export").and().method().equals("GET")
.done()
.done()
.inCase() // log as jsons
.request()
.any()
.done()
.conf()
.requestPayloadFormatter(LogFilter.Body.Json.instance().forceOneLine(true).cutStringLiterals(200).format())
.responsePayloadFormatter(LogFilter.Body.Json.instance().forceOneLine(true).cutStringLiterals(200).format())
.done()
.done()
.build();
<dependency>
<groupId>io.github.antonsjava</groupId>
<artifactId>log-servlet-filter</artifactId>
<version>LASTVERSION</version>
</dependency>
You can find LASTVERSION here