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

com.sri.ai.util.log.LogX Maven / Gradle / Ivy

There is a newer version: 1.3.3
Show newest version
/*
 * Copyright (c) 2013, SRI International
 * All rights reserved.
 * Licensed under the The BSD 3-Clause License;
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 * 
 * http://opensource.org/licenses/BSD-3-Clause
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 
 * Neither the name of the aic-util nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.sri.ai.util.log;

import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.MDC;
import org.slf4j.Marker;
import org.slf4j.ext.LoggerWrapper;
import org.slf4j.profiler.Profiler;

import com.google.common.annotations.Beta;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

/**
 * A Class for providing high level log output representing the execution trace
 * of a program.
 * 
 * @author oreilly
 * 
 */
@Beta
public class LogX extends LoggerWrapper {
	private static final String MDC_KEY_SUFFIX_LOGX_TRACE_LEVEL       = "::LogX:Trace:Level";
	private static final String MDC_KEY_SUFFIX_LOGX_PROFILE_INFO      = "::LogX:Profile:Info";
	private static final String MDC_KEY_SUFFIX_LOGX_ROOT_PROFILE_INFO = "::LogX:RootProfile:Info";
	private static final LogX _defaultLogX = LogXFactory.getLogX(LogX.class);
	//
	private Cache> activeProfilers          = CacheBuilder.newBuilder().weakKeys().build();
	private Cache           activeRootProfilerStart  = CacheBuilder.newBuilder().weakKeys().build();

	/**
	 * 
	 * @return the default LogX instance, intended to be used for global logging
	 *         information.
	 */
	public static LogX getDefaultLogX() {
		return _defaultLogX;
	}

	/**
	 * 
	 * @param logxName
	 *            the name of the LogX logger.
	 * @return the MDC key for the loggers passed in trace level.
	 */
	public static String getMDCTraceLevelKey(String logxName) {
		return logxName + MDC_KEY_SUFFIX_LOGX_TRACE_LEVEL;
	}

	/**
	 * 
	 * @param logxName
	 *            the name of the LogX logger.
	 * @return the MDC key for the loggers passed in profile information.
	 */
	public static String getMDCProfileInfoKey(String logxName) {
		return logxName + MDC_KEY_SUFFIX_LOGX_PROFILE_INFO;
	}
	
	/**
	 * 
	 * @param logxName
	 *            the name of the LogX logger.
	 * @return the MDC key for the loggers passed in root profile information.
	 */
	public static String getMDCRootProfileInfoKey(String logxName) {
		return logxName + MDC_KEY_SUFFIX_LOGX_ROOT_PROFILE_INFO;
	}

	/**
	 * 
	 * @param logxName
	 *            the name of the LogX logger.
	 * @return the trace level associated with the passed in logger. If no trace
	 *         level defined returns 0 by default.
	 */
	public static final Integer getTraceLevel(String logxName) {
		Integer level = 0;

		String slevel = MDC.get(getMDCTraceLevelKey(logxName));
		if (null != slevel) {
			level = Integer.parseInt(slevel);
		}

		return level;
	}

	/**
	 * @param logxName
	 *            the name of the LogX logger.
	 * @return optional profiler information, time in nanoseconds since the
	 *         process started, associated with the logger. null returned if no
	 *         profiler information registered.
	 */
	public static final Long getProfileInfo(String logxName) {
		Long profilerInfo = null;

		String sProfileInfo = MDC.get(getMDCProfileInfoKey(logxName));
		if (sProfileInfo != null) {
			profilerInfo = Long.parseLong(sProfileInfo);
		}

		return profilerInfo;
	}
	
	/**
	 * @param logxName
	 *            the name of the LogX logger.
	 * @return optional root profiler information, time in nanoseconds since the
	 *         process started, associated with the logger. null returned if no
	 *         profiler information registered.
	 */
	public static final Long getRootProfileInfo(String logxName) {
		Long rootProfilerInfo = null;

		String sRootProfileInfo = MDC.get(getMDCRootProfileInfoKey(logxName));
		if (sRootProfileInfo != null) {
			rootProfilerInfo = Long.parseLong(sRootProfileInfo);
		}

		return rootProfilerInfo;
	}

	/**
	 * Indent the trace output a level, for the default Execution Trace Logger.
	 * 
	 * @param msg
	 *            the message to be logged, optionally parameterized with args
	 *            (see slf4j APIs for details).
	 * @param args
	 *            the optional parameterized values for the message.
	 */
	public static void in(String msg, Object... args) {
		in(null, msg, args);
	}
	
	/**
	 * Indent the trace output a level, for the default Execution Trace Logger.
	 * 
	 * @param marker
	 * 	          the marker data specific to this log statement.
	 * @param msg
	 *            the message to be logged, optionally parameterized with args
	 *            (see slf4j APIs for details).
	 * @param args
	 *            the optional parameterized values for the message.
	 */
	public static void in(Marker marker, String msg, Object... args) {
		getDefaultLogX().indent(marker, msg, args);
	}

	/**
	 * Un-indent the trace output a level, for the default Execution Trace
	 * Logger.
	 * 
	 * @param msg
	 *            the message to be logged, optionally parameterized with args
	 *            (see slf4j APIs for details).
	 * @param args
	 *            the optional parameterized values for the message.
	 */
	public static void out(String msg, Object... args) {
		out(null, msg, args);
	}
	
	/**
	 * Un-indent the trace output a level, for the default Execution Trace
	 * Logger.
	 * 
	 * @param marker
	 *            the marker data specific to this log statement.
	 * @param msg
	 *            the message to be logged, optionally parameterized with args
	 *            (see slf4j APIs for details).
	 * @param args
	 *            the optional parameterized values for the message.
	 */
	public static void out(Marker marker, String msg, Object... args) {
		getDefaultLogX().outdent(marker, msg, args);
	}

	/**
	 * Output a log message at the current trace level to the default Execution
	 * Trace Logger.
	 * 
	 * @param msg
	 *            the message to be logged, optionally parameterized with args
	 *            (see slf4j APIs for details).
	 * @param args
	 *            the optional parameterized values for the message.
	 */
	public static void log(String msg, Object... args) {
		log(null, msg, args);
	}
	
	/**
	 * Output a log message at the current trace level to the default Execution
	 * Trace Logger.
	 * 
	 * @param marker
	 *            the marker data specific to this log statement. 
	 * @param msg
	 *            the message to be logged, optionally parameterized with args
	 *            (see slf4j APIs for details).
	 * @param args
	 *            the optional parameterized values for the message.
	 */
	public static void log(Marker marker, String msg, Object... args) {
		getDefaultLogX().trace(marker, msg, args);
	}

	//
	// PUBLIC METHODS
	//

	public LogX(Logger logger) {
		this(logger, LogX.class.getName());
	}

	public LogX(Logger logger, String fqcn) {
		super(logger, fqcn);

		setTraceLevel(0);
		MDC.remove(getMDCProfileInfoKey());
		MDC.remove(getMDCRootProfileInfoKey());
	}
	
	public void indent(String msg, Object... args) {
		indent(null, msg, args);
	}

	public void indent(Marker marker, String msg, Object... args) {
		if (logger.isTraceEnabled()) {
			pushActiveProfiler();
			setTraceLevel(getTraceLevel() + 1);
			trace(marker, msg, args);
		}
	}
	
	public void outdent(String msg, Object... args) {
		outdent(null, msg, args);
	}

	public void outdent(Marker marker, String msg, Object... args) {
		if (logger.isTraceEnabled()) {
			// Stop the nested profiler at this trace level
			popActiveProfiler();
			setTraceLevel(getTraceLevel() - 1);
			// Output the message
			trace(marker, msg, args);
			MDC.remove(getMDCProfileInfoKey());
			MDC.remove(getMDCRootProfileInfoKey());
		}
	}

	public int getTraceLevel() {
		int traceLevel = 0;
		String strTraceLevel = MDC.get(getMDCTraceLevelKey());
		if (strTraceLevel != null) {
			traceLevel = Integer.parseInt(strTraceLevel);
		}
		return traceLevel;
	}

	public void setTraceLevel(int level) {
		if (0 > level) {
			throw new IllegalArgumentException("Trace level must be >= 0.");
		}
		MDC.put(getMDCTraceLevelKey(), "" + level);
	}

	//
	// PACKAGE PROTECTED
	//
	String getMDCTraceLevelKey() {
		return getMDCTraceLevelKey(getName());
	}

	String getMDCProfileInfoKey() {
		return getMDCProfileInfoKey(getName());
	}
	
	String getMDCRootProfileInfoKey() {
		return getMDCRootProfileInfoKey(getName());
	}
	
	//
	//  PRIVATE
	//
	private void pushActiveProfiler() {
		List thisThreadsProfilers = activeProfilers.getIfPresent(Thread.currentThread());
		if (thisThreadsProfilers == null) {
			thisThreadsProfilers = new ArrayList();
			activeProfilers.put(Thread.currentThread(), thisThreadsProfilers);
		}
		Profiler cp = null;
		
		if (thisThreadsProfilers.size() == 0) {
			cp = new Profiler(""+getTraceLevel());
			activeRootProfilerStart.put(Thread.currentThread(), System.nanoTime());
		} 
		else {
			cp = thisThreadsProfilers.get(thisThreadsProfilers.size() - 1)
				.startNested("" + getTraceLevel());
		}
		thisThreadsProfilers.add(cp);
	}
	
	private void popActiveProfiler() {
		List thisThreadsProfilers = activeProfilers.getIfPresent(Thread.currentThread());
		if (thisThreadsProfilers != null) {
			Profiler cp = thisThreadsProfilers.remove(thisThreadsProfilers.size() - 1);
			cp.stop();
			MDC.put(getMDCProfileInfoKey(), "" + cp.elapsedTime());
			// If not at the root profiler, then track the root information too.
			if (thisThreadsProfilers.size() > 0) {
				MDC.put(getMDCRootProfileInfoKey(), "" + (System.nanoTime() - activeRootProfilerStart.getIfPresent(Thread.currentThread())));
			}
			// Ensure clean up fully.
			if (thisThreadsProfilers.size() == 0) {
				activeProfilers.invalidate(Thread.currentThread());
				activeRootProfilerStart.invalidate(Thread.currentThread());
			}
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy