All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nl.talsmasoftware.context.log4j2.threadcontext.Log4j2ThreadContextManager Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2016-2022 Talsma ICT
 *
 * Licensed 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 nl.talsmasoftware.context.log4j2.threadcontext;

import nl.talsmasoftware.context.Context;
import nl.talsmasoftware.context.ContextManagers;
import nl.talsmasoftware.context.clearable.ClearableContextManager;
import org.apache.logging.log4j.CloseableThreadContext;
import org.apache.logging.log4j.ThreadContext;

import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Manager to propagate the Log4j 2 {@link ThreadContext} data from one thread to another.
 * 

* As {@code ThreadContext} already manages its own thread-local state, * getting the active context is delegated to the {@code ThreadContext}. * This means that closing the resulting context from {@link #getActiveContext()} will have * no effect, because its data is not managed by this library. However, calling * {@link #clear()} will clear the {@code ThreadContext} data of the current thread.
* Methods of this manager may have no effect when the {@code ThreadContext} has been disabled * (see Log4j 2 manual). *

* Initializing a new context through {@link #initializeNewContext(Log4j2ThreadContextSnapshot)} will * add the context data on top of the existing one, if any: {@code ThreadContext} stack values * are pushed on top of the existing stack; map entries are added to the existing map, only * replacing existing ones in case of a map key conflict.
* Closing a context returned from {@link #initializeNewContext(Log4j2ThreadContextSnapshot)} will reset * the {@code ThreadContext} to the values it had before the context was created.
* This means that closing nested contexts out-of-order will probably result in an undesirable state.
* It is therefore strongly advised to use Java's {@code try-with-resources} statement to ensure proper * closing when nesting {@code ThreadContext} contexts. *

* This manager and the contexts it creates are not suited to create {@code ThreadContext} 'scopes' * within the same thread. Log4j 2's {@link CloseableThreadContext} should be used for that instead. *

* Log4j 2 supports making {@code ThreadContext} inheritable, i.e. to have it use * {@link InheritableThreadLocal}. In some cases this might solve the problem of propagating * context from one thread to another. However in all cases where threads are reused (such * as thread pool executors) this will not work. It is therefore recommended to always * prefer using this library to cover all use cases. *

* As with all manager implementations of this library there is usually no need to directly * interact with the manager classes. Instead Java's {@code ServiceLoader} makes sure they * are loaded as services. If an instance of this class is needed nonetheless it can be obtained * through the {@link #provider()} method. * * @see Log4j 2 Thread Context manual */ public class Log4j2ThreadContextManager implements ClearableContextManager { /** * Singleton instance of this class. */ private static final Log4j2ThreadContextManager INSTANCE = new Log4j2ThreadContextManager(); /** * Returns the singleton instance of the {@code Log4j2ThreadContextManager}. *

* The ServiceLoader supports a static {@code provider()} method to resolve services since Java 9. * * @return The Log4j 2 {@code ThreadContext} manager. */ public static Log4j2ThreadContextManager provider() { return INSTANCE; } /** * Creates a new context manager. * * @see #provider() * @deprecated This constructor only exists for usage by {@code ServiceLoader}. The singleton instance * obtained from {@link #provider()} should be used to avoid unnecessary instantiations. */ @Deprecated public Log4j2ThreadContextManager() { } /** * Returns a context consisting of the active Log4j 2 {@link ThreadContext} data from the current thread. *

* Please note: Because these values are managed by Log4j 2 itself and not * by this library, closing the resulting context has no effect. * * @return Context containing the active Log4j 2 {@code ThreadContext} data */ public Context getActiveContext() { return new ReadonlyLog4j2ThreadContext(Log4j2ThreadContextSnapshot.captureFromCurrentThread()); } /** * Initializes a new context for the Log4j 2 {@link ThreadContext} of the current thread. * The data of the context is applied on top of existing data (if any) only replacing * conflicting {@code ThreadContext} map entries but keeping all other existing data. * * @param value non-{@code null} data for the {@code ThreadContext} * @return The new active context containing the specified value * which should be closed by the caller at the end of its lifecycle from the same thread. */ public Context initializeNewContext(final Log4j2ThreadContextSnapshot value) { if (value == null) { throw new NullPointerException("value must not be null"); } // Capture current ThreadContext as 'previous' and make the given data the 'new current' ThreadContext final Log4j2ThreadContextSnapshot previous = Log4j2ThreadContextSnapshot.captureFromCurrentThread(); value.applyToCurrentThread(); // Add ThreadContext data on top of existing return new ManagedLog4j2ThreadContext(previous, value); } /** * Clears the current Log4j 2 {@code ThreadContext} of the calling thread. */ public void clear() { ThreadContext.clearAll(); } @Override public String toString() { return getClass().getSimpleName(); } private static final class ReadonlyLog4j2ThreadContext implements Context { private final Log4j2ThreadContextSnapshot snapshot; private ReadonlyLog4j2ThreadContext(Log4j2ThreadContextSnapshot snapshot) { this.snapshot = snapshot; } public Log4j2ThreadContextSnapshot getValue() { return snapshot; } public void close() { // No-op. We don't manage the Log4j 2 ThreadContext, so we shouldn't close it either. } @Override public String toString() { return getClass().getSimpleName() + '{' + snapshot + '}'; } } private static final class ManagedLog4j2ThreadContext implements Context { private final Log4j2ThreadContextSnapshot previous, value; private final AtomicBoolean closed; private ManagedLog4j2ThreadContext(Log4j2ThreadContextSnapshot previous, Log4j2ThreadContextSnapshot value) { this.previous = previous; this.value = value; this.closed = new AtomicBoolean(false); ContextManagers.onActivate(Log4j2ThreadContextManager.class, value, previous); } public Log4j2ThreadContextSnapshot getValue() { return value; } public void close() { if (closed.compareAndSet(false, true)) { // Restore previous; overwrite current ThreadContext ThreadContext.clearAll(); previous.applyToCurrentThread(); ContextManagers.onDeactivate(Log4j2ThreadContextManager.class, value, previous); } } @Override public String toString() { return getClass().getSimpleName() + '{' + (closed.get() ? "closed" : value) + '}'; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy