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

org.echocat.jomon.runtime.logging.Slf4jUtils Maven / Gradle / Ivy

/*****************************************************************************************
 * *** BEGIN LICENSE BLOCK *****
 *
 * Version: MPL 2.0
 *
 * echocat Jomon, Copyright (c) 2012-2014 echocat
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * *** END LICENSE BLOCK *****
 ****************************************************************************************/

package org.echocat.jomon.runtime.logging;

import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
import org.slf4j.Marker;
import org.slf4j.spi.LocationAwareLogger;

import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Handler;
import java.util.logging.LogManager;

import static java.util.logging.LogManager.getLogManager;
import static org.echocat.jomon.runtime.CollectionUtils.asImmutableList;
import static org.echocat.jomon.runtime.logging.LogLevel.*;
import static org.echocat.jomon.runtime.logging.LogLevels.allLogLevels;
import static org.echocat.jomon.runtime.util.ResourceUtils.closeQuietlyIfAutoCloseable;
import static org.slf4j.spi.LocationAwareLogger.*;

public class Slf4jUtils {

    private static final String FQCN = Slf4jUtils.class.getName();
    private static final Map NORMALIZED_LEVEL_CACHE = new ConcurrentHashMap<>();

    public static void log(@Nonnull LocationAwareLogger logger, @Nonnull LogLevel level, @Nullable String fqcn, @Nullable Marker marker, @Nullable String message, @Nullable Object[] argArray, @Nullable Throwable t) {
        if (trace.equals(level)) {
            logger.log(marker, fqcn, TRACE_INT, message, argArray, t);
        } else if (debug.equals(level)) {
            logger.log(marker, fqcn, DEBUG_INT, message, argArray, t);
        } else if (info.equals(level)) {
            logger.log(marker, fqcn, INFO_INT, message, argArray, t);
        } else if (warning.equals(level)) {
            logger.log(marker, fqcn, WARN_INT, message, argArray, t);
        } else if (error.equals(level) || fatal.equals(level)) {
            logger.log(marker, fqcn, ERROR_INT, message, argArray, t);
        } else {
            log(logger, normalize(level), fqcn, marker, message, argArray, t);
        }
    }


    /**
     * Is the logger instance enabled for the given level?
     *
     * @return true if this Logger is enabled for the given level, false otherwise.
     */
    public static boolean isEnabled(@Nonnull Logger logger, @Nonnull LogLevel level) {
        final boolean result;
        if (trace.equals(level)) {
            result = logger.isTraceEnabled();
        } else if (debug.equals(level)) {
            result = logger.isDebugEnabled();
        } else if (info.equals(level)) {
            result = logger.isInfoEnabled();
        } else if (warning.equals(level)) {
            result = logger.isWarnEnabled();
        } else if (error.equals(level) || fatal.equals(level)) {
            result = logger.isErrorEnabled();
        } else {
            result = isEnabled(logger, normalize(level));
        }
        return result;
    }


    /**
     * Log a message at the given level.
     *
     * @param msg the message string to be logged
     */
    public static void log(@Nonnull Logger logger, @Nonnull LogLevel level, @Nullable String msg) {
        if (logger instanceof LocationAwareLogger) {
            log((LocationAwareLogger) logger, level, FQCN, null, msg, null, null);
        } else if (trace.equals(level)) {
            logger.trace(msg);
        } else if (debug.equals(level)) {
            logger.debug(msg);
        } else if (info.equals(level)) {
            logger.info(msg);
        } else if (warning.equals(level)) {
            logger.warn(msg);
        } else if (error.equals(level) || fatal.equals(level)) {
            logger.error(msg);
        } else {
            log(logger, normalize(level), msg);
        }
    }


    /**
     * 

Log a message at the given level according to the specified format and argument.

* *

This form avoids superfluous object creation when the logger is disabled for the given level.

* * @param format the format string * @param arg the argument */ public static void log(@Nonnull Logger logger, @Nonnull LogLevel level, @Nonnull String format, @Nullable Object arg) { if (logger instanceof LocationAwareLogger) { log((LocationAwareLogger) logger, level, FQCN, null, format, new Object[]{arg}, null); } else if (trace.equals(level)) { logger.trace(format, arg); } else if (debug.equals(level)) { logger.debug(format, arg); } else if (info.equals(level)) { logger.info(format, arg); } else if (warning.equals(level)) { logger.warn(format, arg); } else if (error.equals(level) || fatal.equals(level)) { logger.error(format, arg); } else { log(logger, normalize(level), format, arg); } } /** *

Log a message at the given level according to the specified format and arguments.

* *

This form avoids superfluous object creation when the logger is disabled for the given level.

* * @param format the format string * @param arg1 the first argument * @param arg2 the second argument */ public static void log(@Nonnull Logger logger, @Nonnull LogLevel level, @Nonnull String format, @Nullable Object arg1, @Nullable Object arg2) { if (logger instanceof LocationAwareLogger) { log((LocationAwareLogger) logger, level, FQCN, null, format, new Object[]{arg1, arg2}, null); } else if (trace.equals(level)) { logger.trace(format, arg1, arg2); } else if (debug.equals(level)) { logger.debug(format, arg1, arg2); } else if (info.equals(level)) { logger.info(format, arg1, arg2); } else if (warning.equals(level)) { logger.warn(format, arg1, arg2); } else if (error.equals(level) || fatal.equals(level)) { logger.error(format, arg1, arg2); } else { log(logger, normalize(level), format, arg1, arg2); } } /** *

Log a message at the given level according to the specified format and arguments.

* *

This form avoids superfluous object creation when the logger is disabled for the given level.

* * @param format the format string * @param arguments a list of 3 or more arguments */ public static void log(@Nonnull Logger logger, @Nonnull LogLevel level, @Nonnull String format, @Nullable Object... arguments) { if (logger instanceof LocationAwareLogger) { log((LocationAwareLogger) logger, level, FQCN, null, format, arguments, null); } else if (trace.equals(level)) { logger.trace(format, arguments); } else if (debug.equals(level)) { logger.debug(format, arguments); } else if (info.equals(level)) { logger.info(format, arguments); } else if (warning.equals(level)) { logger.warn(format, arguments); } else if (error.equals(level) || fatal.equals(level)) { logger.error(format, arguments); } else { log(logger, normalize(level), format, arguments); } } /** * Log an exception (throwable) at the given level with an accompanying message. * * @param msg the message accompanying the exception * @param t the exception (throwable) to log */ public static void log(@Nonnull Logger logger, @Nonnull LogLevel level, @Nullable String msg, @Nullable Throwable t) { if (logger instanceof LocationAwareLogger) { log((LocationAwareLogger) logger, level, FQCN, null, msg, null, t); } else if (trace.equals(level)) { logger.trace(msg, t); } else if (debug.equals(level)) { logger.debug(msg, t); } else if (info.equals(level)) { logger.info(msg, t); } else if (warning.equals(level)) { logger.warn(msg, t); } else if (error.equals(level) || fatal.equals(level)) { logger.error(msg, t); } else { log(logger, normalize(level), msg, t); } } /** * Similar to {@link #isEnabled} method except that the marker data is also taken into account. * * @param marker The marker data to take into consideration * @return true if this Logger is enabled for the given level, false otherwise. */ public static boolean isEnabled(@Nonnull Logger logger, @Nonnull LogLevel level, @Nonnull Marker marker) { final boolean result; if (trace.equals(level)) { result = logger.isTraceEnabled(marker); } else if (debug.equals(level)) { result = logger.isDebugEnabled(marker); } else if (info.equals(level)) { result = logger.isInfoEnabled(marker); } else if (warning.equals(level)) { result = logger.isWarnEnabled(marker); } else if (error.equals(level) || fatal.equals(level)) { result = logger.isErrorEnabled(marker); } else { result = isEnabled(logger, normalize(level), marker); } return result; } /** * Log a message with the specific Marker at the given level. * * @param marker the marker data specific to this log statement * @param msg the message string to be logged */ public static void log(@Nonnull Logger logger, @Nonnull LogLevel level, @Nonnull Marker marker, @Nullable String msg) { if (logger instanceof LocationAwareLogger) { log((LocationAwareLogger) logger, level, FQCN, marker, msg, null, null); } else if (trace.equals(level)) { logger.trace(marker, msg); } else if (debug.equals(level)) { logger.debug(marker, msg); } else if (info.equals(level)) { logger.info(marker, msg); } else if (warning.equals(level)) { logger.warn(marker, msg); } else if (error.equals(level) || fatal.equals(level)) { logger.error(marker, msg); } else { log(logger, normalize(level), marker, msg); } } /** * This method is similar to {@link #log(Logger, LogLevel, String, Object)} method except that the marker data is also taken into consideration. * * @param marker the marker data specific to this log statement * @param format the format string * @param arg the argument */ public static void log(@Nonnull Logger logger, @Nonnull LogLevel level, @Nonnull Marker marker, @Nonnull String format, @Nullable Object arg) { if (logger instanceof LocationAwareLogger) { log((LocationAwareLogger) logger, level, FQCN, marker, format, new Object[]{arg}, null); } else if (trace.equals(level)) { logger.trace(marker, format, arg); } else if (debug.equals(level)) { logger.debug(marker, format, arg); } else if (info.equals(level)) { logger.info(marker, format, arg); } else if (warning.equals(level)) { logger.warn(marker, format, arg); } else if (error.equals(level) || fatal.equals(level)) { logger.error(marker, format, arg); } else { log(logger, normalize(level), marker, format, arg); } } /** * This method is similar to {@link #log(Logger, LogLevel, String, Object, Object)} method except that the marker data is also taken into consideration. * * @param marker the marker data specific to this log statement * @param format the format string * @param arg1 the first argument * @param arg2 the second argument */ public static void log(@Nonnull Logger logger, @Nonnull LogLevel level, @Nonnull Marker marker, @Nonnull String format, @Nullable Object arg1, @Nullable Object arg2) { if (logger instanceof LocationAwareLogger) { log((LocationAwareLogger) logger, level, FQCN, marker, format, new Object[]{arg1, arg2}, null); } else if (trace.equals(level)) { logger.trace(marker, format, arg1, arg2); } else if (debug.equals(level)) { logger.debug(marker, format, arg1, arg2); } else if (info.equals(level)) { logger.info(marker, format, arg1, arg2); } else if (warning.equals(level)) { logger.warn(marker, format, arg1, arg2); } else if (error.equals(level) || fatal.equals(level)) { logger.error(marker, format, arg1, arg2); } else { log(logger, normalize(level), marker, format, arg1, arg2); } } /** * This method is similar to {@link #log(Logger, LogLevel, String, Object...)} method except that the marker data is also taken into consideration. * * @param marker the marker data specific to this log statement * @param format the format string * @param arguments an array of arguments */ public static void log(@Nonnull Logger logger, @Nonnull LogLevel level, @Nonnull Marker marker, @Nonnull String format, @Nullable Object... arguments) { if (logger instanceof LocationAwareLogger) { log((LocationAwareLogger) logger, level, FQCN, marker, format, arguments, null); } else if (trace.equals(level)) { logger.trace(marker, format, arguments); } else if (debug.equals(level)) { logger.debug(marker, format, arguments); } else if (info.equals(level)) { logger.info(marker, format, arguments); } else if (warning.equals(level)) { logger.warn(marker, format, arguments); } else if (error.equals(level) || fatal.equals(level)) { logger.error(marker, format, arguments); } else { log(logger, normalize(level), marker, format, arguments); } } /** * This method is similar to {@link #log(Logger, LogLevel, String, Throwable)} method except that the marker data is also taken into consideration. * * @param marker the marker data specific to this log statement * @param msg the message accompanying the exception * @param t the exception (throwable) to log */ public static void log(@Nonnull Logger logger, @Nonnull LogLevel level, @Nonnull Marker marker, @Nullable String msg, @Nullable Throwable t) { if (logger instanceof LocationAwareLogger) { log((LocationAwareLogger) logger, level, FQCN, marker, msg, null, t); } else if (trace.equals(level)) { logger.trace(marker, msg, t); } else if (debug.equals(level)) { logger.debug(marker, msg, t); } else if (info.equals(level)) { logger.info(marker, msg, t); } else if (warning.equals(level)) { logger.warn(marker, msg, t); } else if (error.equals(level) || fatal.equals(level)) { logger.error(marker, msg, t); } else { log(logger, normalize(level), marker, msg, t); } } @Nonnull protected static LogLevel normalize(@Nonnull LogLevel level) { LogLevel result = NORMALIZED_LEVEL_CACHE.get(level); if (result == null) { result = debug; int lastDifference = differenceOf(debug, level); for (final LogLevel logLevel : allLogLevels()) { final int difference = differenceOf(logLevel, level); if (difference < lastDifference) { result = logLevel; lastDifference = difference; } } NORMALIZED_LEVEL_CACHE.put(level, result); } return result; } @Nonnegative protected static int differenceOf(@Nonnull LogLevel a, @Nonnull LogLevel b) { final int pa = a.getPriority(); final int pb = b.getPriority(); final int result; if (pa == pb) { result = 0; } else if (pa > pb) { result = pa - pb; } else { result = pb - pa; } return result; } private Slf4jUtils() {} @Nonnull public static Installation tryInstallSlf4jBridges(@Nullable ILoggerFactory loggerFactory) { return new CombinedInstallation( tryInstallClToSlf4jBridge(loggerFactory), tryInstallJulToSlf4jBridge(loggerFactory) ); } @Nonnull public static Installation tryInstallClToSlf4jBridge(@Nullable ILoggerFactory loggerFactory) { Installation result = new NoopInstallation(); try { final Class type = Slf4jUtils.class.getClassLoader().loadClass("org.apache.commons.logging.LogFactory"); final Field field = type.getDeclaredField("factories"); field.setAccessible(true); if (Map.class.isAssignableFrom(field.getType())) { // noinspection unchecked final Map factories = (Map) field.get(null); if (factories != null) { // noinspection SynchronizationOnLocalVariableOrMethodParameter synchronized (factories) { final ClassLoader classLoader = classLoader(); factories.put(classLoader, createClToSlf4jLoggerFactoryFor(loggerFactory)); result = new Cl2Slf4jInstallation(factories, classLoader); } } } } catch (ClassNotFoundException | InstantiationException | IllegalAccessException |InvocationTargetException | NoSuchMethodException | NoSuchFieldException ignored) {} return result; } @Nonnull private static Object createClToSlf4jLoggerFactoryFor(@Nullable ILoggerFactory loggerFactory) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, ClassNotFoundException { try { final Class type = classLoader().loadClass("org.echocat.jomon.runtime.logging.Cl2Slf4jLoggerFactory"); return type.getConstructor(ILoggerFactory.class).newInstance(loggerFactory); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException |InvocationTargetException | NoSuchMethodException e) { // noinspection UseOfSystemOutOrSystemErr final PrintStream stream = System.err; stream.print("WARN Could not initiate instance of org.echocat.jomon.runtime.logging.Cl2Slf4jLoggerFactory."); e.printStackTrace(stream); throw e; } } @Nonnull protected static ClassLoader classLoader() { return Thread.currentThread().getContextClassLoader(); } @Nonnull public static Installation tryInstallJulToSlf4jBridge() { return tryInstallJulToSlf4jBridge(null); } @Nonnull public static Installation tryInstallJulToSlf4jBridge(@Nullable ILoggerFactory loggerFactory) { return tryInstallJulToSlf4jBridge(loggerFactory, null); } @Nonnull public static Installation tryInstallJulToSlf4jBridge(@Nullable ILoggerFactory loggerFactory, @Nullable LogManager logManager) { final Jul2Slf4jHandler newHandler = new Jul2Slf4jHandler(loggerFactory); final LogManager manager = logManager != null ? logManager : getLogManager(); manager.reset(); final java.util.logging.Logger logger = manager.getLogger(""); final List originalHandlers = new ArrayList<>(); for (final Handler oldHandlers : logger.getHandlers()) { logger.removeHandler(oldHandlers); originalHandlers.add(newHandler); } logger.addHandler(newHandler); return new Jul2Slf4jInstallation(originalHandlers, newHandler, manager); } public static void tryFixMdcInSlf4j() { try { final ClassLoader classLoader = Log4JUtils.class.getClassLoader(); final Class mdc = classLoader.loadClass("org.slf4j.MDC"); final Class mdcAdapter = classLoader.loadClass("org.slf4j.spi.MDCAdapter"); final Field mdcAdapterField = mdc.getDeclaredField("mdcAdapter"); if (mdcAdapterField.getType().equals(mdcAdapter)) { mdcAdapterField.setAccessible(true); final Object delegate = mdcAdapterField.get(null); final Object fixed = classLoader.loadClass("org.echocat.jomon.runtime.logging.FixingSlf4jMDCAdapter").getConstructor(mdcAdapter).newInstance(delegate); mdcAdapterField.set(null, fixed); } } catch (final Exception ignored) {} } public static interface Installation extends AutoCloseable {} private static class NoopInstallation implements Installation { @Override public void close() throws Exception {} } private static class CombinedInstallation implements Installation { @Nonnull private final Iterable _installations; private CombinedInstallation(@Nullable Installation... installations) { this(asImmutableList(installations)); } private CombinedInstallation(@Nonnull Iterable installations) { _installations = installations; } @Override public void close() throws Exception { closeQuietlyIfAutoCloseable(_installations); } } private static class Cl2Slf4jInstallation implements Installation { @Nonnull private final Map _factories; @Nonnull private final ClassLoader _classLoader; private Cl2Slf4jInstallation(@Nonnull Map factories, @Nonnull ClassLoader classLoader) { _factories = factories; _classLoader = classLoader; } @Override public void close() throws Exception { synchronized (_factories) { _factories.remove(_classLoader); } } } private static class Jul2Slf4jInstallation implements Installation { @Nonnull private final Iterable _originalHandlers; @Nonnull private final Handler _installedHandler; @Nonnull private final LogManager _logManager; private Jul2Slf4jInstallation(@Nonnull Iterable originalHandlers, @Nonnull Handler installedHandler, @Nonnull LogManager logManager) { _originalHandlers = originalHandlers; _installedHandler = installedHandler; _logManager = logManager; } @Override public void close() throws Exception { final java.util.logging.Logger root = _logManager.getLogger(""); _logManager.reset(); root.removeHandler(_installedHandler); for (final Handler originalHandler : _originalHandlers) { root.addHandler(originalHandler); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy