The goal of this library is to add an ability to pass some context throughout an application even between different threads (in asynchronous application).
It is implemented using a ThreadLocal
variable and decoration of Executor
/ExecutionContext
. The implementation also copies the context values
into MDC so that you can see those values in your logging statements and can name threads according to some format.
The library consists of two modules. The core
is the library itself, netty
is an extension for the Netty library.
It has both Scala and Java APIs.
Most of you won't need to work with this library as it should be integrated into other libraries for seamless cooperation. The only thing that is required and you might need to do
yourself is to wrap all your executors (Executor
, ExecutorService
, ExecutionContext
, EventLoopGroup
) in Continuity wrappers.
dependencies {
compile 'com.avast.continuity:continuity-core_?:1.0-SNAPSHOT'
}
dependencies {
compile 'com.avast.continuity:continuity-netty_?:1.0-SNAPSHOT'
}
<dependency>
<groupId>com.avast.continuity</groupId>
<artifactId>continuity-core_${build.scala.version}</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.avast.continuity</groupId>
<artifactId>continuity-netty_${build.scala.version}</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
If you want to see a value from MDC in your logs you can the %mdc
specifier.
This is a recommended format for the Kluzo trace ID:
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%-10mdc{traceId}] [%thread] %-35logger{35}: %msg \(%file:%line\)%n%xThrowable{full}</pattern>
One thing is to see the context values in your logs but there are other tools where this information might be beneficial but wouldn't be visible (VisualVM, ...).
Continuity allows you to set a ThreadNamer which can change the name of the thread
according to your needs. It is IdentityThreadNamer by default but there is
ContinuityContextThreadNamer which can be used to alter the thread name according
to the provided format with keys from the context.
Please note that operations with threads name are quite expensive and it's not recommended to use the thread naming functionality on high-throughput systems
(e.g. >1000 req/s in case of HTTP server, but may differ due to other circumstances).
val executor = Continuity.wrapExecutionContext(ExecutionContext.global)
Continuity.withContext("myContext" -> "value") {
executor.execute(new Runnable {
override def run(): Unit = {
println(Continuity.getFromContext("myContext"))
logger.info("logging message")
}
})
}