Package com.netflix.blitz4j

Source Code of com.netflix.blitz4j.LoggingContext

/*
* Copyright 2012 Netflix, Inc.
*
*    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 com.netflix.blitz4j;

import java.util.concurrent.TimeUnit;

import org.apache.log4j.Level;
import org.apache.log4j.MDC;
import org.apache.log4j.spi.LocationInfo;
import org.apache.log4j.spi.LoggingEvent;

import com.netflix.servo.monitor.Monitors;
import com.netflix.servo.monitor.Stopwatch;
import com.netflix.servo.monitor.Timer;

/**
* The utility class that caches the context of logging such as location
* information.
*
* <p>
* It is expensive to find out the location information (ie) calling class, line
* number etc of the logger and hence caching would be useful whenever possible.
* This class also generates location information slightly more efficiently than
* log4j.
* <p>
*
* @author Karthik Ranganathan
*
*/
public class LoggingContext {

    public static final String CONTEXT_LEVEL = "contextlevel";
    private static final BlitzConfig CONFIGURATION = LoggingConfiguration.getInstance().getConfiguration();
    private static final String LOCATION_INFO = "locationInfo";
    private ThreadLocal<StackTraceElement> stackLocal = new ThreadLocal<StackTraceElement>();
    private ThreadLocal<LoggingEvent> loggingEvent = new ThreadLocal<LoggingEvent>();
    private ThreadLocal<Level> contextLevel = new ThreadLocal<Level>();

    private static final LoggingContext instance = new LoggingContext();
    private Timer stackTraceTimer = Monitors.newTimer("getStacktraceElement",
            TimeUnit.NANOSECONDS);

    private LoggingContext() {
        try {
            Monitors.registerObject(this);
        } catch (Throwable e) {
            if (CONFIGURATION.shouldPrintLoggingErrors()) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Gets the starting calling stack trace element of a given stack which
     * matches the given class name. Given the wrapper class name, the match
     * continues until the last stack trace element of the wrapper class is
     * matched.
     *
     * @param stackClass
     *            - The class to be matched for. Get the last matching class
     *            down the stack
     * @return - StackTraceElement which denotes the calling point of given
     *         class or wrapper class
     */
    public StackTraceElement getStackTraceElement(Class stackClass) {

        Stopwatch s = stackTraceTimer.start();
        Throwable t = new Throwable();
        StackTraceElement[] stArray = t.getStackTrace();
        int stackSize = stArray.length;
        StackTraceElement st = null;
        for (int i = 0; i < stackSize; i++) {
            boolean found = false;
            while (stArray[i].getClassName().equals(stackClass.getName())) {
                ++i;
                found = true;
            }
            if (found) {
                st = stArray[i];
            }
        }

        s.stop();

        return st;
    }

    /**
     * Get the location information of the calling class
     *
     * @param wrapperClassName
     *            - The wrapper that indicates the caller
     * @return the location information
     */
    public LocationInfo getLocationInfo(Class wrapperClassName) {
        LocationInfo locationInfo = null;

        try {
            if (stackLocal.get() == null) {
                stackLocal.set(this.getStackTraceElement(wrapperClassName));
            }

            locationInfo = new LocationInfo(stackLocal.get().getFileName(),
                    stackLocal.get().getClassName(), stackLocal.get()
                            .getMethodName(), stackLocal.get().getLineNumber()
                            + "");
        } catch (Throwable e) {
            if (CONFIGURATION
                    .shouldPrintLoggingErrors()) {
                e.printStackTrace();
            }
        }
        return locationInfo;
    }

    /**
     * Clears any logging information that was cached for the purpose of
     * logging.
     */
    private void clearLocationInfo() {
        MDC.remove(LOCATION_INFO);
        stackLocal.set(null);
    }

    public static LoggingContext getInstance() {
        return instance;
    }

    /**
     * Generate the location information of the given logging event and cache
     * it.
     *
     * @param event
     *            The logging event for which the location information needs to
     *            be determined.
     * @return The location info object contains information about the logger.
     */
    public LocationInfo generateLocationInfo(LoggingEvent event) {
        // If the event is not the same, clear the cache
        if (event != loggingEvent.get()) {
            loggingEvent.set(event);
            clearLocationInfo();
        }
        LocationInfo locationInfo = null;
        try {
            locationInfo = (LocationInfo) LoggingContext
                    .getInstance()
                    .getLocationInfo(Class.forName(event.getFQNOfLoggerClass()));
            if (locationInfo != null) {
                MDC.put(LOCATION_INFO, locationInfo);
            }
        } catch (Throwable e) {
            if (CONFIGURATION !=null && CONFIGURATION
                    .shouldPrintLoggingErrors()) {
                e.printStackTrace();
            }
        }
        return locationInfo;
    }

    /**
     * Get the location information of the logging event. If the information has
     * been cached it is retrieved from the MDC (for asynchronous events MDCs
     * are retained), else it is generated.
     *
     * @param event
     *            - The logging event
     * @return- The location information of the logging event.
     */
    public LocationInfo getLocationInfo(LoggingEvent event) {
        if (event != loggingEvent.get()) {
            loggingEvent.set(event);
            clearLocationInfo();
        }
        // For async appenders, the locationInfo is set in the MDC and not with
        // the thread since the thread that processes the logging is different
        // from the one that
        // generates location information.
        LocationInfo locationInfo = (LocationInfo) event.getMDC(LOCATION_INFO);
        if (locationInfo == null) {
            locationInfo = this.generateLocationInfo(event);
        }

        return locationInfo;
    }
   
    /**
     * Set the context {@link Level} for the request-based logging
     * @param level - The level of logging to be enabled for this request
     */
    public void setContextLevel(Level level) {
        MDC.put(CONTEXT_LEVEL, level);
    }
   
    /**
     * Clears the context {@link Level} set for the request-based logging
     */
    public void clearContextLevel() {
        MDC.remove(CONTEXT_LEVEL);
    }
   
   
    /**
     * Get the context {@link Level} for the request-based logging
     * @param level - The level of logging to be enabled for this request
     */
    public Level getContextLevel() {
        return (Level)MDC.get(CONTEXT_LEVEL);
    }
   
   

}
TOP

Related Classes of com.netflix.blitz4j.LoggingContext

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.