Logging contextual information
Table of contents
With Armeria's Logback integration, you can log the properties of the
RequestContext
of the request being handled. RequestContextExportingAppender
is
a Logback appender that exports the properties of the current RequestContext
to
MDC (mapped diagnostic context).
First, you need the armeria-logback
dependency:
dependencies {
implementation platform('com.linecorp.armeria:armeria-bom:1.31.3')
...
implementation 'com.linecorp.armeria:armeria-logback'
}
Then, let's look at the following example:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} %X{remote.ip} %X{tls.cipher}
%X{req.headers.user-agent} %X{attrs.some_value} %msg%n</pattern>
</encoder>
</appender>
<appender name="RCEA" class="com.linecorp.armeria.common.logback.RequestContextExportingAppender">
<appender-ref ref="CONSOLE" />
<export>remote.ip</export>
<export>tls.cipher</export>
<export>req.headers.user-agent</export>
<export>attrs.some_value:com.example.AttrKeys#SOME_KEY</export>
<!-- ... or alternatively:
<exports>remote.ip, remote.port, tls.cipher,
req.headers.user-agent,
attrs.some_value:com.example.AttrKeys#SOME_KEY</exports>
-->
<!-- ... or with wildcard:
<export>req.*</export>
-->
<!-- ... or with custom MDC key:
<export>remote_id=remote.id</export>
<export>UA=req.headers.user-agent</export>
<export>some_value=attr:com.example.AttrKeys#SOME_KEY</export>
-->
</appender>
...
</configuration>
The above configuration defines an appender called RCEA
which exports the following:
remote.ip
- the IP address of the remote peer,
tls.cipher
- the SSL/TLS cipher suite of the connection,
req.headers.user-agent
- the user agent of the client,
attrs.some_value
- a custom attribute set via
RequestContext.setAttr(AttributeKey.valueOf(AttrKeys.class, "SOME_KEY"), "SOME_VALUE")
- a custom attribute set via
... to the MDC property map and forwards the log message to the appender CONSOLE
, as defined in the
<appender-ref />
element.
There are three types of properties you can export using RequestContextExportingAppender
.
Built-in properties
A built-in property is a common property available for most requests. See the complete list of the built-in
properties and their MDC keys at BuiltInProperty
.
You can also use wildcard character *
instead of listing all properties. For example:
"*"
"req.*"
HTTP request and response headers
When the session protocol of the current connection is HTTP, a user can export HTTP headers of the current
request and response. The MDC key of the exported header is "req.headers.<lower-case header name>"
or
"res.headers.<lower-case header name>"
. For example:
"req.headers.user-agent"
"res.headers.set-cookie"
Custom attributes
A user can attach an arbitrary custom attribute to a RequestContext
by using
RequestContext custom attributes to store the information associated with the
request being handled.
RequestContextExportingAppender
can export such attributes to the MDC property map as well.
Unlike other property types, you need to specify the full name of an attribute as well as its alias.
For example, if you want to export an attribute com.example.Foo#ATTR_BAR
with the alias bar
, you need to add
<export>attrs.bar:com.example.Foo#ATTR_BAR</export>
to the XML configuration. The resulting MDC key to
access the attribute value is attrs.bar
, which follows the form of attrs.<alias>
.
Using an alternative string converter for a custom attribute
By default, RequestContextExportingAppender
uses Object.toString()
to convert an attribute value
into an MDC value. If you want an alternative string representation of an attribute value, you can define
a Function
class with a public no-args constructor that transforms an attribute value into a String
:
package com.example;
public class SomeValue {
public final String value;
@Override
public String toString() {
// Too verbose for logging
return "SomeValue(value=" + value + ')';
}
}
public class MyStringifier implements Function<SomeValue, String> {
@Override
public String apply(SomeValue o) {
return o.value;
}
}
Once the Function
is implemented, specify the fully-qualified class name of the Function
implementation
as the 3rd component of the <export />
element in the XML configuration:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
...
<appender name="RCEA" class="com.linecorp.armeria.common.logback.RequestContextExportingAppender">
...
<export>attrs.some_value:com.example.AttrKeys#SOME_KEY:com.example.MyStringifier</export>
...
</appender>
...
</configuration>
Customizing MDC keys
You can override the pre-defined MDC key by prepending an alias and an equals sign (=) to it.
For example, if you want to change req.id
to request_id
, use request_id=req.id
.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
...
<appender name="RCEA" class="com.linecorp.armeria.common.logback.RequestContextExportingAppender">
...
<export>remote_id=remote.id</export>
<export>UA=req.headers.user-agent</export>
<export>some_value=attr:com.example.AttrKeys#SOME_KEY</export>
...
</appender>
...
</configuration>
Note that a custom MDC key cannot be used with a wildcard expression *
or req.*
.
Specifying a prefix for MDC keys
You can specify a prefix for MDC keys using the <prefix>
element.
If you want to add a prefix armeria.
to all MDC keys,
you need to add <prefix>armeria.</prefix>
to the XML configuration.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
...
<appender name="RCEA" class="com.linecorp.armeria.common.logback.RequestContextExportingAppender">
...
<!-- set the prefix of MDC keys -->
<prefix>armeria.</prefix>
<export>remote.id</export>
<export>req.headers.user-agent</export>
<export>some_value=attr:com.example.AttrKeys#SOME_KEY</export>
...
</appender>
...
</configuration>
When you want to specify a different prefix, you can define <prefix>
, <export>
, and <exports>
elements
in the <exportGroup>
element.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
...
<appender name="RCEA" class="com.linecorp.armeria.common.logback.RequestContextExportingAppender">
...
<!-- set the prefix of exports which is not wrapped with the <exportGroup> element -->
<prefix>armeria.</prefix>
<export>remote.id</export>
<export>req.headers.user-agent</export>
...
<exportGroup>
<!-- set the prefix of exports in this <exportGroup> -->
<prefix>some_prefix.</prefix>
<export>some_value=attr:com.example.AttrKeys#SOME_KEY</export>
...
</exportGroup>
<exportGroup>
<!-- if <prefix> is not defined, no prefix is added to exports -->
<export>tracking_id=attr:com.example.AttrKeys#TRACKING_ID_KEY</export>
...
</exportGroup>
</appender>
...
</configuration>