blob: f80bc6f671f44e20fa230d85b99166583dbb22d2 [file] [log] [blame]
////
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
////
= Scoped Context
The link:../log4j-api/apidocs/org/apache/logging/log4j/ScopedContext.html[`ScopedContext`]
is available in Log4j API releases 2.24.0 and greater.
The `ScopedContext` is similar to the ThreadContextMap in that it allows key/value pairs to be included
in many log events. However, the pairs in a `ScopedContext` are only available to
application code and log events running within the scope of the `ScopeContext` object.
The `ScopeContext` is essentially a builder that allows key/value pairs to be added to it
prior to invoking a method. The key/value pairs are available to any code running within
that method and will be included in all logging events as if they were part of the `ThreadContextMap`.
ScopedContext is immutable. Each invocation of the `where` method returns a new ScopedContext.Instance
with the specified key/value pair added to those defined in previous ScopedContexts.
[source,java]
----
ScopedContext.where("id", UUID.randomUUID())
.where("ipAddress", request.getRemoteAddr())
.where("loginId", session.getAttribute("loginId"))
.where("hostName", request.getServerName())
.run(new Worker());
private class Worker implements Runnable {
private static final Logger LOGGER = LogManager.getLogger(Worker.class);
public void run() {
LOGGER.debug("Performing work");
String loginId = ScopedContext.get("loginId");
}
}
----
The values in the ScopedContext can be any Java object. However, objects stored in the
context Map will be converted to Strings when stored in a LogEvent. To aid in
this Objects may implement the Renderable interface which provides a `render` method
to format the object. By default, objects will have their toString() method called
if they do not implement the Renderable interface.
Note that in the example above `UUID.randomUUID()` returns a UUID. By default, when it is
included in LogEvents its toString() method will be used.
== Thread Support
ScopedContext provides support for passing the ScopedContext and the ThreadContext to
child threads by way of an ExecutorService. For example, the following will create a
ScopedContext and pass it to a child thread.
[source,java]
----
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(5);
ExecutorService executorService = new ThreadPoolExecutor(1, 2, 30, TimeUnit.SECONDS, workQueue);
Future<?> future = ScopedContext.where("id", UUID.randomUUID())
.where("ipAddress", request.getRemoteAddr())
.where("loginId", session.getAttribute("loginId"))
.where("hostName", request.getServerName())
.run(executorService, new Worker());
try {
future.get();
} catch (ExecutionException ex) {
logger.warn("Exception in worker thread: {}", ex.getMessage());
}
private class Worker implements Runnable {
private static final Logger LOGGER = LogManager.getLogger(Worker.class);
public void run() {
LOGGER.debug("Performing work");
String loginId = ScopedContext.get("loginId");
}
}
----
ScopeContext also supports call methods in addition to run methods so the called functions can
directly return values.
== Nested ScopedContexts
ScopedContexts may be nested. Becuase ScopedContexts are immutable the `where` method may
be called on the current ScopedContext from within the run or call methods to append new
key/value pairs. In addition, when passing a single key/value pair the run or call method
may be combined with a where method as shown below.
[source,java]
----
ScopedContext.runWhere("key1", "value1", () -> {
assertThat(ScopedContext.get("key1"), equalTo("value1"));
ScopedContext.where("key2", "value2").run(() -> {
assertThat(ScopedContext.get("key1"), equalTo("value1"));
assertThat(ScopedContext.get("key2"), equalTo("value2"));
});
});
----
ScopedContexts ALWAYS inherit the key/value pairs from their parent scope. key/value pairs may be removed from the context by passing a null value with the key. Note that where methods that accept a Map MUST NOT include null keys or values in the map.