A Jakarta Servlet and JAX-RS filter that logs HTTP request/response pairs. Supports SLF4J and JDBC backends out of the box, with a plugin API for customization.
| Artifact | Description |
|---|---|
logging-filter |
Core library — servlet filter, JAX-RS filter, SLF4J and JDBC loggers |
logging-filter-vertx |
Optional — RemoteAddressResolver for Quarkus / Vert.x deployments |
Core library:
<dependency>
<groupId>io.github.alterioncorp</groupId>
<artifactId>logging-filter</artifactId>
<version>1.1.1</version>
</dependency>Quarkus / Vert.x client-IP support (optional, add alongside core):
<dependency>
<groupId>io.github.alterioncorp</groupId>
<artifactId>logging-filter-vertx</artifactId>
<version>1.1.1</version>
</dependency>Register the filter in web.xml:
<filter>
<filter-name>LoggingFilter</filter-name>
<filter-class>io.github.alterioncorp.loggingfilter.LoggingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoggingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>By default, the filter logs to SLF4J. To enable JDBC logging (or use multiple loggers):
<filter>
<filter-name>LoggingFilter</filter-name>
<filter-class>io.github.alterioncorp.loggingfilter.LoggingFilter</filter-class>
<init-param>
<param-name>logging-filter.loggers</param-name>
<param-value>io.github.alterioncorp.loggingfilter.loggers.JdbcLoggerImpl</param-value>
</init-param>
</filter>All configuration properties can be set as servlet init-param values or as JVM system properties (system properties take precedence).
ContainerLoggingFilter is a CDI-managed @ApplicationScoped bean annotated with @Provider. It is auto-discovered when META-INF/beans.xml is present with bean-discovery-mode="annotated" — no explicit registration needed.
Configuration is read exclusively from JVM system properties (no web.xml equivalent for JAX-RS). The same property names apply.
Client IP resolution: X-Forwarded-For is checked first. If absent, an optional RemoteAddressResolver CDI bean is called. For Quarkus/Vert.x, add the logging-filter-vertx dependency — it ships a @RequestScoped resolver that reads the socket peer address from the Vert.x HttpServerRequest. For other JAX-RS runtimes, implement RemoteAddressResolver yourself (see Custom client-IP resolver).
Other limitations compared to the servlet filter:
- No form-body logging.
- No session ID — JAX-RS has no session concept.
| Property | Default | Description |
|---|---|---|
logging-filter.loggers |
Slf4JLoggerImpl |
Comma-separated list of InfoLogger implementation class names |
logging-filter.param-names-to-hide |
(none) | Comma-separated list of query parameter names whose values will be masked (e.g. password) |
jboss.node.name |
(none) | Server name to include in each log entry |
Attributes are key-value strings collected from MDC, request headers, or system properties and attached to each RequestInfo/ResponseInfo. They can be referenced by logger plugins (e.g. prepended to SLF4J output).
| Property | Default | Description |
|---|---|---|
logging-filter.attributes.from-mdc |
(none) | Comma-separated MDC key names to capture |
logging-filter.attributes.from-header |
(none) | Comma-separated attrName:HeaderName pairs (use bare name when attr name equals header name) |
logging-filter.attributes.from-sysprop |
(none) | Comma-separated attrName:syspropName pairs |
Header lookup is case-insensitive.
Entries are logged at INFO level. Request headers are logged at DEBUG level.
| Property | Default | Description |
|---|---|---|
logging-filter.slf4j.plugin |
Slf4JLoggerPluginDefaultImpl |
Plugin class to use |
logging-filter.slf4j.show-session-id |
false |
Include the HTTP session ID in each log entry |
logging-filter.slf4j.prepend-attributes |
(none) | Comma-separated attribute names to prepend to each log entry (missing values appear as ---) |
Each request/response pair produces two log entries (tab-separated) under the loggers LoggingFilter.rqst and LoggingFilter.resp:
2024-03-07 10:23:44.801 INFO LoggingFilter.rqst - 192.168.1.42 --- POST /api/users?role=admin
2024-03-07 10:23:44.923 INFO LoggingFilter.resp - 192.168.1.42 201 POST /api/users?role=admin
With logging-filter.param-names-to-hide=password and logging-filter.slf4j.show-session-id=true:
2024-03-07 10:23:44.801 INFO LoggingFilter.rqst - 192.168.1.42 --- POST /login?username=alice&password=***** A1B2C3D4E5F6
2024-03-07 10:23:44.923 INFO LoggingFilter.resp - 192.168.1.42 200 POST /login?username=alice&password=***** A1B2C3D4E5F6
With logging-filter.attributes.from-mdc=tenantId and logging-filter.slf4j.prepend-attributes=tenantId:
2024-03-07 10:23:44.801 INFO LoggingFilter.rqst - acme 192.168.1.42 --- POST /api/users
2024-03-07 10:23:44.923 INFO LoggingFilter.resp - acme 192.168.1.42 201 POST /api/users
Request headers are logged at DEBUG level when debug logging is enabled for those loggers. The request entry uses --- as the response code placeholder.
The JDBC logger queues records in memory and writes them to the database in batches asynchronously. The log table is created automatically if it does not exist.
| Property | Default | Description |
|---|---|---|
logging-filter.jdbc.data_source_jndi |
java:jboss/datasources/RequestLog |
JNDI name of the DataSource to use |
logging-filter.jdbc.plugin |
JdbcLoggerPluginDefaultImpl |
Plugin class to use |
logging-filter.batch.write_period_in_millis |
5000 |
How often (ms) the in-memory queue is flushed to the database |
A sample row in the default table (REQUEST_LOG_1):
| ID | START_TIMESTAMP | CLIENT_INFO | SERVER_INFO | METHOD | PATH | PARAMS | DURATION | RESPONSE_STATUS | SESSION_ID |
|---|---|---|---|---|---|---|---|---|---|
| 1 | 2024-03-07 10:23:44.801 | 192.168.1.42 | node-1 | POST | /api/users | role=admin | 122 | 201 | A1B2C3D4E5F6 |
Schema (REQUEST_LOG_1):
| Column | Type | Description |
|---|---|---|
ID |
BIGINT IDENTITY |
Auto-generated primary key |
START_TIMESTAMP |
DATETIME |
When the request was received |
CLIENT_INFO |
NVARCHAR(256) |
Client IP (from X-Forwarded-For or RemoteAddr) |
SERVER_INFO |
NVARCHAR(256) |
Server name (jboss.node.name) |
METHOD |
NVARCHAR(8) |
HTTP method |
PATH |
NVARCHAR(256) |
Request URI |
PARAMS |
NVARCHAR(MAX) |
URL-encoded query string |
DURATION |
INT |
Request duration in milliseconds |
RESPONSE_STATUS |
SMALLINT |
HTTP response status code |
SESSION_ID |
NVARCHAR(256) |
HTTP session ID at response time |
Extend Slf4JLoggerPluginDefaultImpl and override getRequestValuesToLog / getResponseValuesToLog:
public class MySlf4JPlugin extends Slf4JLoggerPluginDefaultImpl {
@Override
public List<Object> getRequestValuesToLog(RequestInfo info) {
List<Object> values = super.getRequestValuesToLog(info);
values.add(info.getAttribute("correlationId"));
return values;
}
}Then configure:
<init-param>
<param-name>logging-filter.slf4j.plugin</param-name>
<param-value>com.example.MySlf4JPlugin</param-value>
</init-param>To make custom fields available via info.getAttribute(...), configure the attribute collector:
<init-param>
<param-name>logging-filter.attributes.from-header</param-name>
<param-value>correlationId:X-Correlation-Id</param-value>
</init-param>Extend JdbcLoggerPluginDefaultWithAdditionalColumns:
public class MyJdbcPlugin extends JdbcLoggerPluginDefaultWithAdditionalColumns {
@Override
protected int getAdditionalColumnCount() { return 1; }
@Override
protected String getAdditionalColumnName(int i) { return "CORRELATION_ID"; }
@Override
protected int getAdditionalColumnSqlType(int i) { return Types.NVARCHAR; }
@Override
protected String getAdditionalColumnTypeForTableCreate(int i) { return "NVARCHAR(256)"; }
@Override
protected Object getAdditionalValueToInsert(int i, RequestInfo requestInfo, ResponseInfo responseInfo) {
return requestInfo.getAttribute("correlationId");
}
}Changing the schema requires overriding getDataVersion() to return a new value — this causes a new table to be created.
For JAX-RS runtimes other than Quarkus/Vert.x, implement RemoteAddressResolver and expose it as a CDI bean:
@RequestScoped
public class MyRemoteAddressResolver implements RemoteAddressResolver {
@Override
public String getRemoteAddress() {
// obtain socket peer address from your runtime's APIs
return ...;
}
}ContainerLoggingFilter discovers the bean automatically via @Inject Instance<RemoteAddressResolver>. No code changes needed in the filter.
Each logger registers an MBean that lets you enable or disable it at runtime without restarting the application. The object name pattern is:
io.github.alterioncorp.loggingfilter:type=<LoggerClass>,instance=<contextPath>/<filterName>
Apache License 2.0