001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.util;
018
019import java.util.Deque;
020import java.util.NoSuchElementException;
021import java.util.function.Predicate;
022
023import org.apache.logging.log4j.status.StatusLogger;
024
025/**
026 * <em>Consider this class private.</em> Provides various methods to determine the caller class. <h3>Background</h3>
027 */
028public final class StackLocatorUtil {
029    private static StackLocator stackLocator = null;
030    private static volatile boolean errorLogged;
031
032    static {
033        stackLocator = StackLocator.getInstance();
034    }
035
036    private StackLocatorUtil() {
037    }
038
039    // TODO: return Object.class instead of null (though it will have a null ClassLoader)
040    // (MS) I believe this would work without any modifications elsewhere, but I could be wrong
041
042    // migrated from ReflectiveCallerClassUtility
043    @PerformanceSensitive
044    public static Class<?> getCallerClass(final int depth) {
045        return stackLocator.getCallerClass(depth + 1);
046    }
047
048    public static StackTraceElement getStackTraceElement(final int depth) {
049        return stackLocator.getStackTraceElement(depth + 1);
050    }
051
052    /**
053     * Equivalent to {@link #getCallerClass(String, String)} with an empty {@code pkg}.
054     */
055    // migrated from ClassLoaderContextSelector
056    @PerformanceSensitive
057    public static Class<?> getCallerClass(final String fqcn) {
058        return getCallerClass(fqcn, Strings.EMPTY);
059    }
060
061    /**
062     * Search for a calling class.
063     *
064     * @param fqcn Root class name whose caller to search for.
065     * @param pkg Package name prefix that must be matched after the {@code fqcn} has been found.
066     * @return The caller class that was matched, or null if one could not be located.
067     */
068    @PerformanceSensitive
069    public static Class<?> getCallerClass(final String fqcn, final String pkg) {
070        return stackLocator.getCallerClass(fqcn, pkg);
071    }
072
073    /**
074     * Gets the ClassLoader of the class that called <em>this</em> method at the location up the call stack by the given
075     * stack frame depth.
076     * <p>
077     * This method returns {@code null} if:
078     * </p>
079     * <ul>
080     * <li>{@code sun.reflect.Reflection.getCallerClass(int)} is not present.</li>
081     * <li>An exception is caught calling {@code sun.reflect.Reflection.getCallerClass(int)}.</li>
082     * <li>Some Class implementations may use null to represent the bootstrap class loader.</li>
083     * </ul>
084     *
085     * @param depth The stack frame count to walk.
086     * @return A class or null.
087     * @throws IndexOutOfBoundsException if depth is negative.
088     */
089    @PerformanceSensitive
090    public static ClassLoader getCallerClassLoader(final int depth) {
091        final Class<?> callerClass = stackLocator.getCallerClass(depth + 1);
092        return callerClass != null ? callerClass.getClassLoader() : null;
093    }
094
095    /**
096     * Search for a calling class.
097     *
098     * @param sentinelClass Sentinel class at which to begin searching
099     * @param callerPredicate Predicate checked after the sentinelClass is found
100     * @return the first matching class after <code>sentinelClass</code> is found.
101     */
102    @PerformanceSensitive
103    public static Class<?> getCallerClass(final Class<?> sentinelClass, final Predicate<Class<?>> callerPredicate) {
104        return stackLocator.getCallerClass(sentinelClass, callerPredicate);
105    }
106
107    // added for use in LoggerAdapter implementations mainly
108    @PerformanceSensitive
109    public static Class<?> getCallerClass(final Class<?> anchor) {
110        return stackLocator.getCallerClass(anchor);
111    }
112
113    // migrated from ThrowableProxy
114    @PerformanceSensitive
115    public static Deque<Class<?>> getCurrentStackTrace() {
116        return stackLocator.getCurrentStackTrace();
117    }
118
119    public static StackTraceElement calcLocation(final String fqcnOfLogger) {
120        try {
121            return stackLocator.calcLocation(fqcnOfLogger);
122        } catch (NoSuchElementException ex) {
123            if (!errorLogged) {
124                errorLogged = true;
125                StatusLogger.getLogger().warn("Unable to locate stack trace element for {}", fqcnOfLogger, ex);
126            }
127            return null;
128        }
129    }
130}