Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,18 @@ public Class<?> getCallerClass(final String fqcn) {
}

public Class<?> getCallerClass(final String fqcn, final String pkg) {
return walker.walk(s -> s.dropWhile(f -> !f.getClassName().equals(fqcn)).
dropWhile(f -> f.getClassName().equals(fqcn)).dropWhile(f -> !f.getClassName().startsWith(pkg)).
findFirst()).map(StackWalker.StackFrame::getDeclaringClass).orElse(null);
return getCallerClass(fqcn, pkg, 0);
}

public Class<?> getCallerClass(final String fqcn, final String pkg, final int skipDepth) {
return walker.walk(s -> s
.dropWhile(f -> !f.getClassName().equals(fqcn))
.dropWhile(f -> f.getClassName().equals(fqcn))
.dropWhile(f -> !f.getClassName().startsWith(pkg))
.skip(skipDepth)
.findFirst())
.map(StackWalker.StackFrame::getDeclaringClass)
.orElse(null);
}

public Class<?> getCallerClass(final Class<?> anchor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,9 @@ public LoggerContext getContext(final String fqcn, final ClassLoader loader, fin
public void removeContext(final LoggerContext removeContext) {
// do nothing
}

@Override
public boolean isClassLoaderDependent() {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,17 @@ LoggerContext getContext(String fqcn, ClassLoader loader, Object externalContext
* @param context The context to remove.
*/
void removeContext(LoggerContext context);

/**
* Determines whether or not this factory and perhaps the underlying
* ContextSelector behavior depend on the callers classloader.
*
* This method should be overridden by implementations, however a default method is provided which always
* returns {@code true} to preserve the old behavior.
*
* @since 2.15.0
*/
default boolean isClassLoaderDependent() {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,14 @@ public Class<?> getCallerClass(final int depth) {
// migrated from Log4jLoggerFactory
@PerformanceSensitive
public Class<?> getCallerClass(final String fqcn, final String pkg) {
return getCallerClass(fqcn, pkg, 0);
}

@PerformanceSensitive
public Class<?> getCallerClass(final String fqcn, final String pkg, final int skipDepth) {
if (skipDepth < 0) {
throw new IllegalArgumentException("skipDepth cannot be negative");
}
boolean next = false;
Class<?> clazz;
for (int i = 2; null != (clazz = getCallerClass(i)); i++) {
Expand All @@ -127,7 +135,9 @@ public Class<?> getCallerClass(final String fqcn, final String pkg) {
continue;
}
if (next && clazz.getName().startsWith(pkg)) {
return clazz;
return skipDepth == 0
? clazz
: getCallerClass(i + skipDepth);
}
}
// TODO: return Object.class
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,36 @@ public static StackTraceElement getStackTraceElement(final int depth) {
return stackLocator.getStackTraceElement(depth + 1);
}

/**
* Equivalent to {@link #getCallerClass(String, String)} with an empty {@code pkg}.
*/
// migrated from ClassLoaderContextSelector
@PerformanceSensitive
public static Class<?> getCallerClass(final String fqcn) {
return getCallerClass(fqcn, Strings.EMPTY);
}

// migrated from Log4jLoggerFactory
/**
* Equivalent to {@link #getCallerClass(String, String, int)} with {@code skipDepth = 0}.
*/
@PerformanceSensitive
public static Class<?> getCallerClass(final String fqcn, final String pkg) {
return stackLocator.getCallerClass(fqcn, pkg);
}

/**
* Search for a calling class.
*
* @param fqcn Root class name whose caller to search for.
* @param pkg Package name prefix that must be matched after the {@code fqcn} has been found.
* @param skipDepth Number of stack frames to skip after the {@code fqcn} and {@code pkg} have been matched.
* @return The caller class that was matched, or null if one could not be located.
*/
@PerformanceSensitive
public static Class<?> getCallerClass(final String fqcn, final String pkg, final int skipDepth) {
return stackLocator.getCallerClass(fqcn, pkg, skipDepth);
}

// added for use in LoggerAdapter implementations mainly
@PerformanceSensitive
public static Class<?> getCallerClass(final Class<?> anchor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,9 @@ public LoggerContext getContext(final String fqcn, final ClassLoader loader, fin
@Override
public void removeContext(final LoggerContext context) {
}

@Override
public boolean isClassLoaderDependent() {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* 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.core.async;

import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.impl.ContextAnchor;
import org.apache.logging.log4j.core.selector.ContextSelector;

import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
* Returns either this Thread's context or the default {@link AsyncLoggerContext}.
* Single-application instances should prefer this implementation over the {@link AsyncLoggerContextSelector}
* due the the reduced overhead avoiding classloader lookups.
*/
public class BasicAsyncLoggerContextSelector implements ContextSelector {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still need to add a commit to mention this class in the documentation.


private static final AsyncLoggerContext CONTEXT = new AsyncLoggerContext("AsyncDefault");

@Override
public void shutdown(String fqcn, ClassLoader loader, boolean currentContext, boolean allContexts) {
LoggerContext ctx = getContext(fqcn, loader, currentContext);
if (ctx != null && ctx.isStarted()) {
ctx.stop(DEFAULT_STOP_TIMEOUT, TimeUnit.MILLISECONDS);
}
}

@Override
public boolean hasContext(String fqcn, ClassLoader loader, boolean currentContext) {
LoggerContext ctx = getContext(fqcn, loader, currentContext);
return ctx != null && ctx.isStarted();
}

@Override
public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext) {
final LoggerContext ctx = ContextAnchor.THREAD_CONTEXT.get();
return ctx != null ? ctx : CONTEXT;
}


@Override
public LoggerContext getContext(
final String fqcn,
final ClassLoader loader,
final boolean currentContext,
final URI configLocation) {
final LoggerContext ctx = ContextAnchor.THREAD_CONTEXT.get();
return ctx != null ? ctx : CONTEXT;
}

@Override
public void removeContext(final LoggerContext context) {
// does not remove anything
}

@Override
public boolean isClassLoaderDependent() {
return false;
}

@Override
public List<LoggerContext> getLoggerContexts() {
return Collections.singletonList(CONTEXT);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,11 @@ public void removeContext(final org.apache.logging.log4j.spi.LoggerContext conte
}
}

@Override
public boolean isClassLoaderDependent() {
return selector.isClassLoaderDependent();
}

@Override
public Cancellable addShutdownCallback(final Runnable callback) {
return isShutdownHookEnabled() ? shutdownCallbackRegistry.addShutdownCallback(callback) : null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ public void removeContext(final LoggerContext context) {
// does not remove anything
}

@Override
public boolean isClassLoaderDependent() {
return false;
}

@Override
public List<LoggerContext> getLoggerContexts() {
final List<LoggerContext> list = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,12 @@ public void removeContext(final LoggerContext context) {
}
}

@Override
public boolean isClassLoaderDependent() {
// By definition the ClassLoaderContextSelector depends on the callers class loader.
return true;
}

@Override
public List<LoggerContext> getLoggerContexts() {
final List<LoggerContext> list = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,5 +126,15 @@ default LoggerContext getContext(String fqcn, ClassLoader loader, Map.Entry<Stri
*/
void removeContext(LoggerContext context);


/**
* Determines whether or not this ContextSelector depends on the callers classloader.
* This method should be overridden by implementations, however a default method is provided which always
* returns {@code true} to preserve the old behavior.
*
* @return true if the class loader is required.
* @since 2.15.0
*/
default boolean isClassLoaderDependent() {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,15 @@
package org.apache.logging.log4j.core.selector;

import org.apache.logging.log4j.core.async.AsyncLoggerContextSelector;
import org.apache.logging.log4j.core.async.BasicAsyncLoggerContextSelector;

public class CoreContextSelectors {

public static final Class<?>[] CLASSES = new Class<?>[] { ClassLoaderContextSelector.class, BasicContextSelector.class, AsyncLoggerContextSelector.class };
public static final Class<?>[] CLASSES = new Class<?>[] {
ClassLoaderContextSelector.class,
BasicContextSelector.class,
AsyncLoggerContextSelector.class,
BasicAsyncLoggerContextSelector.class
};

}
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,11 @@ public void removeContext(final LoggerContext context) {
}
}

@Override
public boolean isClassLoaderDependent() {
return false;
}

@Override
public LoggerContext removeContext(final String name) {
return CONTEXT_MAP.remove(name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,9 @@ public void testContextNameIsAsyncLoggerContextWithClassHashCode() {
assertEquals(expectedContextName, context.getName());
}

@Test
public void testDependentOnClassLoader() {
final AsyncLoggerContextSelector selector = new AsyncLoggerContextSelector();
assertTrue(selector.isClassLoaderDependent());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,5 +106,10 @@ public List<LoggerContext> getLoggerContexts() {
public void removeContext(final LoggerContext context) {
// does not remove anything
}

@Override
public boolean isClassLoaderDependent() {
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ public void testAsyncLogWritesToLog() throws Exception {

final String location = "testAsyncLogWritesToLog";
assertTrue("no location", !line1.contains(location));

assertTrue(LogManager.getFactory().isClassLoaderDependent());
}

// NOTE: only define one @Test method per test class with Async Loggers to prevent spurious failures
Expand Down
Loading