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

org.spf4j.log.SLF4JBridgeHandler Maven / Gradle / Ivy

Go to download

A continuously growing collection of utilities to measure performance, get better diagnostics, improve performance, or do things more reliably, faster that other open source libraries...

There is a newer version: 8.10.0
Show newest version
/*
 * Copyright (c) 2001-2017, Zoltan Farkas All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * Additionally licensed with:
 *
 * 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 org.spf4j.log;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.spi.LocationAwareLogger;
import org.spf4j.base.Arrays;
import org.spf4j.base.Pair;
import org.spf4j.base.avro.Method;
import org.spf4j.text.MessageFormat;
import org.spf4j.text.Slf4jFormat;

/**
 * 

* Bridge/route all JUL log records to the SLF4J API.

*

* Implementation based on jul-to-slf4j bridge but at least 20% faster. Speed improvements come from using spf4j * improved MessageFormatter + code cleanup. * Unlike jul-to-slf4j the source class and source method information is not being dropped. * The MessageFormat is transformed into a parameterized Slf4jFormat. * *

*

* Implementation is interchangeable with the jul-to-slf4j implementation

* */ public final class SLF4JBridgeHandler extends Handler { private static final String FQCN = java.util.logging.Logger.class.getName(); private static final String UNKNOWN_LOGGER_NAME = "unknown.jul.logger"; private static final int TRACE_LEVEL_THRESHOLD = Level.FINEST.intValue(); private static final int DEBUG_LEVEL_THRESHOLD = Level.FINE.intValue(); private static final int INFO_LEVEL_THRESHOLD = Level.INFO.intValue(); private static final int WARN_LEVEL_THRESHOLD = Level.WARNING.intValue(); private static final int MAX_FORMAT_CACHE_SIZE = Integer.getInteger("spf4j.julBridge.MaxFormatCacheSize", 1024); private static final Slf4jFormat INVALID_FORMAT = new MessageFormat("SPF4J Invalid Message Format").subformatSlf4j(); private static class Lazy { private static final LoadingCache> LOCALIZED_FORMAT_CACHE = CacheBuilder.newBuilder() .build(new CacheLoader>() { @Override public LoadingCache load(final Locale locale) throws Exception { return CacheBuilder.newBuilder() .maximumSize(MAX_FORMAT_CACHE_SIZE).build(new CacheLoader() { @Override public Slf4jFormat load(final String key) { try { return new MessageFormat(key, locale).subformatSlf4j(); } catch (IllegalArgumentException ex) { // certain loggers extend LogMessage with printf syntax... LoggerFactory.getLogger(Lazy.class).trace("Unable to forrmat {}", key, ex); return INVALID_FORMAT; } } }); } } ); } private static final LoadingCache FORMAT_CACHE = CacheBuilder.newBuilder() .maximumSize(MAX_FORMAT_CACHE_SIZE).build(new CacheLoader() { @Override public Slf4jFormat load(final String key) { try { return new MessageFormat(key).subformatSlf4j(); } catch (IllegalArgumentException ex) { // certain loggers extend LogMessage with printf syntax... LoggerFactory.getLogger(SLF4JBridgeHandler.class).trace("Unable to forrmat {}", key, ex); return INVALID_FORMAT; } } }); private static final boolean ALWAYS_TRY_INFER = Boolean.getBoolean("spf4j.jul2slf4jBridge.alwaysTryInferSource"); /** * Need this to avoid source class and method inference. (which is expensive) * @see https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8244321 */ @Nullable private static final Field NEED_INFER; static { NEED_INFER = AccessController.doPrivileged(new PrivilegedAction() { @Override public Field run() { try { Field declaredField = LogRecord.class.getDeclaredField("needToInferCaller"); declaredField.setAccessible(true); return declaredField; } catch (NoSuchFieldException | SecurityException ex) { LoggerFactory.getLogger(SLF4JBridgeHandler.class) .debug("jul to slf4j bridge will not differentiate between computed caller info and provided", ex); return null; } } }); } /** * Adds a SLF4JBridgeHandler instance to jul's root logger. * * This handler will redirect j.u.l. logging to SLF4J. However, only logs enabled in j.u.l. will be redirected. For * example, if a log statement invoking a j.u.l. logger is disabled, then the corresponding non-event will * not * reach SLF4JBridgeHandler and cannot be redirected. */ public static void install() { getRootLogger().addHandler(new SLF4JBridgeHandler()); } private static java.util.logging.Logger getRootLogger() { return LogManager.getLogManager().getLogger(""); } /** * Removes previously installed SLF4JBridgeHandler instances. See also {@link #install()}. * * @throws SecurityException A SecurityException is thrown, if a security manager exists and if the * caller does not have LoggingPermission("control"). */ public static void uninstall() { java.util.logging.Logger rootLogger = getRootLogger(); Handler[] handlers = rootLogger.getHandlers(); for (int i = 0; i < handlers.length; i++) { if (handlers[i] instanceof SLF4JBridgeHandler) { rootLogger.removeHandler(handlers[i]); } } } /** * Returns true if SLF4JBridgeHandler has been previously installed, returns false otherwise. * * @return true if SLF4JBridgeHandler is already installed, false other wise * @throws SecurityException */ public static boolean isInstalled() { java.util.logging.Logger rootLogger = getRootLogger(); Handler[] handlers = rootLogger.getHandlers(); for (int i = 0; i < handlers.length; i++) { if (handlers[i] instanceof SLF4JBridgeHandler) { return true; } } return false; } /** * Invoking this method removes/unregisters/detaches all handlers currently attached to the root logger */ public static void removeHandlersForRootLogger() { java.util.logging.Logger rootLogger = getRootLogger(); Handler[] handlers = rootLogger.getHandlers(); for (int i = 0; i < handlers.length; i++) { rootLogger.removeHandler(handlers[i]); } } @Override public void close() { // NOOP } @Override public void flush() { // NOOP } /** * Return the Logger instance that will be used for logging. */ private static Logger getSLF4JLogger(final LogRecord record) { String name = record.getLoggerName(); if (name == null) { name = UNKNOWN_LOGGER_NAME; } return LoggerFactory.getLogger(name); } private static void callLocationAwareLogger(final LocationAwareLogger lal, final LogRecord record) { int julLevelValue = record.getLevel().intValue(); int slf4jLevel; if (julLevelValue <= TRACE_LEVEL_THRESHOLD) { if (!lal.isTraceEnabled()) { return; } slf4jLevel = LocationAwareLogger.TRACE_INT; } else if (julLevelValue <= DEBUG_LEVEL_THRESHOLD) { if (!lal.isDebugEnabled()) { return; } slf4jLevel = LocationAwareLogger.DEBUG_INT; } else if (julLevelValue <= INFO_LEVEL_THRESHOLD) { if (!lal.isInfoEnabled()) { return; } slf4jLevel = LocationAwareLogger.INFO_INT; } else if (julLevelValue <= WARN_LEVEL_THRESHOLD) { if (!lal.isWarnEnabled()) { return; } slf4jLevel = LocationAwareLogger.WARN_INT; } else { if (!lal.isErrorEnabled()) { return; } slf4jLevel = LocationAwareLogger.ERROR_INT; } Pair messageArgs = getMessageI18N(record); Method m = getSourceMethodInfo(record); if (m != null) { lal.log(null, m.toString(), slf4jLevel, messageArgs.getFirst(), messageArgs.getSecond(), record.getThrown()); } else { lal.log(null, FQCN, slf4jLevel, messageArgs.getFirst(), messageArgs.getSecond(), record.getThrown()); } } @SuppressFBWarnings("UCC_UNRELATED_COLLECTION_CONTENTS") // nature of log args private static void callPlainSLF4JLogger(final Logger slf4jLogger, final LogRecord record) { int julLevelValue = record.getLevel().intValue(); if (julLevelValue <= TRACE_LEVEL_THRESHOLD) { if (!slf4jLogger.isTraceEnabled()) { return; } } else if (julLevelValue <= DEBUG_LEVEL_THRESHOLD) { if (!slf4jLogger.isDebugEnabled()) { return; } } else if (julLevelValue <= INFO_LEVEL_THRESHOLD) { if (!slf4jLogger.isInfoEnabled()) { return; } } else if (julLevelValue <= WARN_LEVEL_THRESHOLD) { if (!slf4jLogger.isWarnEnabled()) { return; } } else { if (!slf4jLogger.isErrorEnabled()) { return; } } Pair message = getMessageI18N(record); Object[] args = message.getSecond(); if (args == null || args.length == 0) { logEfficient(message.getFirst(), record, slf4jLogger); } else { Throwable thrown = record.getThrown(); Method m = getSourceMethodInfo(record); Object[] pargs; if (m == null) { if (thrown == null) { pargs = args; } else { pargs = java.util.Arrays.copyOf(args, args.length + 1); pargs[args.length] = thrown; } } else { if (thrown == null) { pargs = java.util.Arrays.copyOf(args, args.length + 1); pargs[args.length] = m; } else { pargs = java.util.Arrays.copyOf(args, args.length + 2); pargs[args.length] = m; pargs[args.length + 1] = thrown; } } if (julLevelValue <= TRACE_LEVEL_THRESHOLD) { slf4jLogger.trace(message.getFirst(), pargs); } else if (julLevelValue <= DEBUG_LEVEL_THRESHOLD) { slf4jLogger.debug(message.getFirst(), pargs); } else if (julLevelValue <= INFO_LEVEL_THRESHOLD) { slf4jLogger.info(message.getFirst(), pargs); } else if (julLevelValue <= WARN_LEVEL_THRESHOLD) { slf4jLogger.warn(message.getFirst(), pargs); } else { slf4jLogger.error(message.getFirst(), pargs); } } } public static void logEfficient(final String i18nMessage, final LogRecord record, final Logger slf4jLogger) { int julLevelValue = record.getLevel().intValue(); Throwable thrown = record.getThrown(); Method m = getSourceMethodInfo(record); if (thrown != null) { if (julLevelValue <= TRACE_LEVEL_THRESHOLD) { if (m != null) { slf4jLogger.trace(i18nMessage, m, thrown); } else { slf4jLogger.trace(i18nMessage, thrown); } } else if (julLevelValue <= DEBUG_LEVEL_THRESHOLD) { if (m != null) { slf4jLogger.debug(i18nMessage, m, thrown); } else { slf4jLogger.debug(i18nMessage, thrown); } } else if (julLevelValue <= INFO_LEVEL_THRESHOLD) { if (m != null) { slf4jLogger.info(i18nMessage, m, thrown); } else { slf4jLogger.info(i18nMessage, thrown); } } else if (julLevelValue <= WARN_LEVEL_THRESHOLD) { if (m != null) { slf4jLogger.warn(i18nMessage, m, thrown); } else { slf4jLogger.warn(i18nMessage, thrown); } } else { if (m != null) { slf4jLogger.error(i18nMessage, m, thrown); } else { slf4jLogger.error(i18nMessage, thrown); } } } else { if (julLevelValue <= TRACE_LEVEL_THRESHOLD) { if (m != null) { slf4jLogger.trace(i18nMessage, m); } else { slf4jLogger.trace(i18nMessage); } } else if (julLevelValue <= DEBUG_LEVEL_THRESHOLD) { if (m != null) { slf4jLogger.debug(i18nMessage, m); } else { slf4jLogger.debug(i18nMessage); } } else if (julLevelValue <= INFO_LEVEL_THRESHOLD) { if (m != null) { slf4jLogger.info(i18nMessage, m); } else { slf4jLogger.info(i18nMessage); } } else if (julLevelValue <= WARN_LEVEL_THRESHOLD) { if (m != null) { slf4jLogger.warn(i18nMessage, m); } else { slf4jLogger.warn(i18nMessage); } } else { if (m != null) { slf4jLogger.error(i18nMessage, m); } else { slf4jLogger.error(i18nMessage); } } } } @Nullable public static Method getSourceMethodInfo(final LogRecord record) { Method m; try { if (ALWAYS_TRY_INFER || (NEED_INFER != null && !NEED_INFER.getBoolean(record))) { m = new Method(record.getSourceClassName(), record.getSourceMethodName()); } else { m = null; } } catch (IllegalArgumentException | IllegalAccessException ex) { throw new RuntimeException(ex); } return m; } /** * Get the record's message, possibly via a resource bundle. * * @param record * @return */ @Nonnull private static Pair getMessageI18N(final LogRecord record) { String message = record.getMessage(); if (message == null) { return Pair.of("", record.getParameters()); } ResourceBundle bundle = record.getResourceBundle(); if (bundle != null) { try { message = bundle.getString(message); } catch (MissingResourceException e) { } } Object[] params = record.getParameters(); if (params != null && params.length > 0) { try { Slf4jFormat msgFormat; if (bundle == null) { msgFormat = FORMAT_CACHE.getUnchecked(message); } else { msgFormat = Lazy.LOCALIZED_FORMAT_CACHE.getUnchecked(bundle.getLocale()).getUnchecked(message); } if (msgFormat != INVALID_FORMAT) { return Pair.of(msgFormat.getFormat(), msgFormat.convert(record.getParameters())); } else { // Some libraries like jboss log manager embeded do unkosher stuff, // like printf style formatting see ExtLogRecord... return Pair.of(record.getMessage(), record.getParameters()); } } catch (IllegalArgumentException e) { LoggerFactory.getLogger(SLF4JBridgeHandler.class).warn("Unable to format {} with {}", message, params, e); return Pair.of(message, Arrays.EMPTY_OBJ_ARRAY); } } else { return Pair.of(message, Arrays.EMPTY_OBJ_ARRAY); } } @Override public void publish(final LogRecord record) { try { Logger slf4jLogger = getSLF4JLogger(record); if (slf4jLogger instanceof LocationAwareLogger) { callLocationAwareLogger((LocationAwareLogger) slf4jLogger, record); } else { callPlainSLF4JLogger(slf4jLogger, record); } } catch (RuntimeException ex) { LoggerFactory.getLogger(SLF4JBridgeHandler.class).warn("Unable to publish {}", record, ex); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy