blob: 0a39634eddf04d3970ba7acb1dbc8fc3801a5c7d [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.
*/
package org.apache.logging.log4j;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.function.Supplier;
import org.apache.logging.log4j.spi.ScopedContextProvider;
import org.apache.logging.log4j.util.ProviderUtil;
/**
* Context that can be used for data to be logged in a block of code.
* <p>
* While this is influenced by ScopedValues from Java 21 it does not share the same API. While it can perform a
* similar function as a set of ScopedValues it is really meant to allow a block of code to include a set of keys and
* values in all the log events within that block. The underlying implementation must provide support for
* logging the ScopedContext for that to happen.
* </p>
* <p>
* The ScopedContext will not be bound to the current thread until either a run or call method is invoked. The
* contexts are nested so creating and running or calling via a second ScopedContext will result in the first
* ScopedContext being hidden until the call is returned. Thus the values from the first ScopedContext need to
* be added to the second to be included.
* </p>
* <p>
* The ScopedContext can be passed to child threads by including the ExecutorService to be used to manage the
* run or call methods. The caller should interact with the ExecutorService as if they were submitting their
* run or call methods directly to it. The ScopedContext performs no error handling other than to ensure the
* ThreadContext and ScopedContext are cleaned up from the executed Thread.
* </p>
* @since 2.24.0
*/
public final class ScopedContext {
private static final ScopedContextProvider provider =
ProviderUtil.getProvider().getScopedContextProvider();
private ScopedContext() {}
/**
* Creates a ScopedContext Instance with a key/value pair.
*
* @param key the key to add.
* @param value the value associated with the key.
* @return the Instance constructed if a valid key and value were provided. Otherwise, either the
* current Instance is returned or a new Instance is created if there is no current Instance.
*/
public static Instance where(final String key, final Object value) {
return provider.newScopedContext(key, value);
}
/**
* Adds a key/value pair to the ScopedContext being constructed.
*
* @param key the key to add.
* @param supplier the function to generate the value.
* @return the ScopedContext being constructed.
*/
public static Instance where(final String key, final Supplier<Object> supplier) {
return where(key, supplier.get());
}
/**
* Creates a ScopedContext Instance with a Map of keys and values.
* @param map the Map.
* @return the ScopedContext Instance constructed.
*/
public static Instance where(final Map<String, ?> map) {
return provider.newScopedContext(map);
}
public static Instance withThreadContext() {
return provider.newScopedContext(true);
}
/**
* Creates a ScopedContext with a single key/value pair and calls a method.
* @param key the key.
* @param value the value associated with the key.
* @param task the Runnable to call.
*/
public static void runWhere(final String key, final Object value, final Runnable task) {
provider.newScopedContext(key, value).run(task);
}
/**
* Creates a ScopedContext with a single key/value pair and calls a method on a separate Thread.
* @param key the key.
* @param value the value associated with the key.
* @param executorService the ExecutorService to dispatch the work.
* @param task the Runnable to call.
*/
public static Future<Void> runWhere(
final String key, final Object value, final ExecutorService executorService, final Runnable task) {
return provider.newScopedContext(key, value).run(executorService, task);
}
/**
* Creates a ScopedContext with a Map of keys and values and calls a method.
* @param map the Map.
* @param task the Runnable to call.
*/
public static void runWhere(final Map<String, ?> map, final Runnable task) {
provider.newScopedContext(map).run(task);
}
/**
* Creates a ScopedContext with a single key/value pair and calls a method.
* @param key the key.
* @param value the value associated with the key.
* @param task the Runnable to call.
*/
public static <R> R callWhere(final String key, final Object value, final Callable<R> task) throws Exception {
return provider.newScopedContext(key, value).call(task);
}
/**
* Creates a ScopedContext with a single key/value pair and calls a method on a separate Thread.
* @param key the key.
* @param value the value associated with the key.
* @param executorService the ExecutorService to dispatch the work.
* @param task the Callable to call.
*/
public static <R> Future<R> callWhere(
final String key, final Object value, final ExecutorService executorService, final Callable<R> task) {
return provider.newScopedContext(key, value).call(executorService, task);
}
/**
* Creates a ScopedContext with a Map of keys and values and calls a method.
* @param map the Map.
* @param task the Runnable to call.
*/
public static <R> R callWhere(final Map<String, ?> map, final Callable<R> task) throws Exception {
return provider.newScopedContext(map).call(task);
}
/**
* Return the object with the specified key from the current context.
* @param key the key.
* @return the value of the key or null.
*/
public static Object get(String key) {
return provider.getValue(key);
}
/**
* Return String value of the key from the current ScopedContext, if there is one and the key exists.
* @param key The key.
* @return The value of the key in the current ScopedContext.
*/
public static String getString(String key) {
return provider.getString(key);
}
/**
* A holder of scoped context data.
*/
public interface Instance {
/**
* Adds a key/value pair to the ScopedContext being constructed.
*
* @param key the key to add.
* @param value the value associated with the key.
* @return the ScopedContext being constructed.
*/
Instance where(String key, Object value);
/**
* Adds a key/value pair to the ScopedContext being constructed.
*
* @param key the key to add.
* @param supplier the function to generate the value.
* @return the ScopedContext being constructed.
*/
Instance where(String key, Supplier<Object> supplier);
/**
* Creates an Instance to declare the ThreadContext should be included.
*
* @return the ScopedContext being constructed.
*/
Instance withThreadContext();
/**
* Executes a code block that includes all the key/value pairs added to the ScopedContext.
*
* @param task the code block to execute.
*/
void run(Runnable task);
/**
* Executes a code block that includes all the key/value pairs added to the ScopedContext on a different Thread.
*
* @param executorService The ExecutorService to use.
* @param task the code block to execute.
* @return a Future representing pending completion of the task
*/
Future<Void> run(ExecutorService executorService, Runnable task);
/**
* Executes a code block that includes all the key/value pairs added to the ScopedContext on a different Thread.
*
* @param key the key to add.
* @param value the value associated with the key.
* @param executorService The ExecutorService to use.
* @param task the code block to execute.
* @return a Future representing pending completion of the task
*/
Future<Void> runWhere(String key, Object value, ExecutorService executorService, Runnable task);
/**
* Executes a code block that includes all the key/value pairs added to the ScopedContext on a different Thread.
*
* @param key the key to add.
* @param supplier the function to generate the value.
* @param executorService The ExecutorService to use.
* @param task the code block to execute.
* @return a Future representing pending completion of the task
*/
Future<Void> runWhere(String key, Supplier<Object> supplier, ExecutorService executorService, Runnable task);
/**
* Executes a code block that includes all the key/value pairs added to the ScopedContext.
*
* @param task the code block to execute.
* @return the return value from the code block.
*/
<R> R call(Callable<R> task) throws Exception;
/**
* Executes a code block that includes all the key/value pairs added to the ScopedContext on a different Thread.
*
* @param executorService The ExecutorService to use.
* @param task the code block to execute.
* @return a Future representing pending completion of the task
*/
<R> Future<R> call(ExecutorService executorService, Callable<R> task);
/**
* Executes a code block that includes all the key/value pairs added to the ScopedContext on a different Thread.
*
* @param key the key to add.
* @param value the value associated with the key.
* @param executorService The ExecutorService to use.
* @param task the code block to execute.
* @return a Future representing pending completion of the task
*/
<R> Future<R> callWhere(String key, Object value, ExecutorService executorService, Callable<R> task);
/**
* Executes a code block that includes all the key/value pairs added to the ScopedContext on a different Thread.
*
* @param key the key to add.
* @param supplier the function to generate the value.
* @param executorService The ExecutorService to use.
* @param task the code block to execute.
* @return a Future representing pending completion of the task
*/
<R> Future<R> callWhere(
String key, Supplier<Object> supplier, ExecutorService executorService, Callable<R> task);
/**
* Wraps the provided Runnable method with a Runnable method that will instantiate the Scoped and Thread
* Contexts in the target Thread before the caller's run method is called.
* @param task the Runnable task to perform.
* @return a Runnable.
*/
Runnable wrap(Runnable task);
/**
* Wraps the provided Callable method with a Callable method that will instantiate the Scoped and Thread
* Contexts in the target Thread before the caller's call method is called.
* @param task the Callable task to perform.
* @return a Callable.
*/
<R> Callable<R> wrap(Callable<R> task);
}
}