/******************************************************************************* * Copyright 2012 Geoscience Australia * * 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 au.gov.ga.earthsci.logging; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.logging.Handler; import java.util.logging.LogManager; import org.eclipse.core.internal.runtime.PlatformLogWriter; import org.eclipse.core.runtime.Platform; import org.eclipse.e4.core.services.log.ILoggerProvider; import org.eclipse.equinox.log.ExtendedLogService; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.osgi.service.log.LogService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.bridge.SLF4JBridgeHandler; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.classic.jul.LevelChangePropagator; import ch.qos.logback.core.joran.spi.JoranException; import ch.qos.logback.core.util.StatusPrinter; /** * A class used to bootstrap the configuration of logging. * <p/> * Searches the current workspace for a {@value #LOGBACK_XML} configuration * file. If it doesn't find one, it will use a default configuration that * outputs to the console. * * @author James Navin (james.navin@ga.gov.au) */ public class LoggingConfigurator { static { DevelopmentModeMultipleBindingsFix.apply(); } private static final String LOGBACK_XML = "logback.xml"; //$NON-NLS-1$ private static final Logger logger = LoggerFactory.getLogger(LoggingConfigurator.class); private static int defaultLoggingLevel; private LoggingConfigurator() { }; /** * Configure the logging to replace the standard workbench loggers with * SLF4J bridges. * * @param bundleContext * The current bundle context */ public static void configure(final BundleContext bundleContext) { configureLogback(); registerOSGiLogService(bundleContext); bypassRuntimeLog(bundleContext); logger.debug("Logging configuration initialised."); //$NON-NLS-1$ } /** * @return The current global logging level: * <ol> * <li>Trace * <li>Debug * <li>Info * <li>Warn * <li>Error * <li>Fatal * </ol> */ public static int getGlobalLoggingLevel() { ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME); return toLoggingLevel(root.getLevel()); } /** * @return The default global logging level as set in the configuration * files * * @see #getGlobalLoggingLevel() */ public static int getDefaultLoggingLevel() { return defaultLoggingLevel; } private static int toLoggingLevel(Level l) { switch (l.levelInt) { case Level.TRACE_INT: return 1; case Level.DEBUG_INT: return 2; case Level.INFO_INT: return 3; case Level.WARN_INT: return 4; case Level.ERROR_INT: return 5; default: return 5; } } /** * Set the global logging level, overriding any settings found in the * configuration files * * @param l * The level to set: * <ol> * <li>Trace * <li>Debug * <li>Info * <li>Warn * <li>Error * <li>Fatal * </ol> */ public static void setGlobalLoggingLevel(int l) { ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME); Level level = Level.DEBUG; switch (l) { case 1: level = Level.TRACE; break; case 2: level = Level.DEBUG; break; case 3: level = Level.INFO; break; case 4: level = Level.WARN; break; case 5: level = Level.ERROR; break; case 6: level = Level.ERROR; break; } root.setLevel(level); } private static void configureLogback() { JoranConfigurator configurator = new JoranConfigurator(); LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); configurator.setContext(loggerContext); loggerContext.reset(); configureJULBridge(loggerContext); try { InputStream configurationStream = getConfiguration(); configurator.doConfigure(configurationStream); } catch (JoranException e) { // Use status printer to show errors if needed } StatusPrinter.printInCaseOfErrorsOrWarnings(loggerContext); ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME); defaultLoggingLevel = toLoggingLevel(root.getLevel()); } private static void configureJULBridge(LoggerContext loggerContext) { // Remove existing handlers java.util.logging.Logger rootLogger = LogManager.getLogManager().getLogger(""); //$NON-NLS-1$ Handler[] handlers = rootLogger.getHandlers(); for (int i = 0; i < handlers.length; i++) { rootLogger.removeHandler(handlers[i]); } // Install the bridge LevelChangePropagator levelChangePropagator = new LevelChangePropagator(); levelChangePropagator.setContext(loggerContext); levelChangePropagator.setResetJUL(true); loggerContext.addListener(levelChangePropagator); SLF4JBridgeHandler.install(); } private static InputStream getConfiguration() { // Look in the current workspace for a logback XML try { URL workspaceConfig = Platform.getConfigurationLocation().getDataArea(LOGBACK_XML); return workspaceConfig.openStream(); } catch (IOException e) { } // Fallback to the default configuration return LoggingConfigurator.class.getResourceAsStream(LOGBACK_XML); } /** * Replace the OSGi LogService implementation, and the ILoggerProvider * * @param bundleContext */ private static void registerOSGiLogService(BundleContext bundleContext) { bundleContext.registerService(LogService.class, new SLF4JOSGiLogServiceBridge(), null); bundleContext.registerService(ILoggerProvider.class, new SLF4JE4BridgeLoggerProvider(), null); bundleContext.registerService(ExtendedLogService.class, new SLF4JExtendedLogService(), null); } @SuppressWarnings({ "rawtypes", "unchecked", "deprecation" }) private static void bypassRuntimeLog(BundleContext context) { ServiceReference packageAdminRef = context.getServiceReference(org.osgi.service.packageadmin.PackageAdmin.class.getName()); org.osgi.service.packageadmin.PackageAdmin packageAdmin = (org.osgi.service.packageadmin.PackageAdmin) context.getService(packageAdminRef); PlatformLogWriter writer = new PlatformLogWriter(new SLF4JExtendedLogService(), packageAdmin, context.getBundle()); RuntimeLogBypass.apply(writer); } }