/* * Copyright 2000-2013 JetBrains s.r.o. * Copyright 2014-2015 AS3Boyan * Copyright 2014-2014 Elias Ku * * 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.intellij.plugins.haxe.util; import org.apache.log4j.*; import org.apache.log4j.spi.LoggerFactory; import org.apache.log4j.spi.LoggerRepository; import org.apache.log4j.varia.NullAppender; import java.util.Enumeration; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * A logger that can be controlled independent of (and, more importantly, can suppress) * the underlying log4j logger that is used in the IDEA code. Loggers allocated through * this interface are not part of IDEA's Logger hierarchy; their hierarchy is independent. * * Created by ebishton on 6/12/15. */ public class HaxeDebugLogger extends org.apache.log4j.Logger { private static final HaxeDebugLoggerManager manager = new HaxeDebugLoggerManager(); public static interface Factory extends LoggerFactory { public HaxeDebugLogger makeNewRootLoggerInstance(); } public static class DefaultFactory implements Factory { public DefaultFactory() { } public HaxeDebugLogger makeNewLoggerInstance(String name) { HaxeDebugLogger logger = new HaxeDebugLogger(name); return logger; } public HaxeDebugLogger makeNewRootLoggerInstance() { HaxeDebugLogger logger = new HaxeDebugLogger("root"); return logger; } } public static interface HierarchyManipulator { public void restore() throws IllegalStateException; public void mute(LoggerRepository hierarchy) throws IllegalStateException; } /** * Simply configure the primary log4j Loggers to send all output to the bit bucket. */ public static void configurePrimaryLoggerToSwallowLogs() { org.apache.log4j.BasicConfigurator.configure(new NullAppender()); } /** * Simply configure the primary log4j Loggers to send all output to the console. */ public static void configurePrimaryLoggerToStdout() { org.apache.log4j.BasicConfigurator.configure(); } private static String getCallingClassName() { // Get the current call stack. Then walk it until we find the first // entry that isn't *this* class. String className = "<Unknown class>"; StackTraceElement[] stack = new Exception().getStackTrace(); for (StackTraceElement frame : stack) { String frameClassName = frame.getClassName(); if (!frameClassName.equals(HaxeDebugLogger.class.getName())) { className = frameClassName; break; } } return className; } // These deprecated versions remain here to override the default deprecated methods. @Deprecated public static HaxeDebugLogger getInstance() { return getLogger(getCallingClassName(), manager.getFactory()); } public static HaxeDebugLogger getInstance(String name) { return getLogger(name, manager.getFactory()); } public static HaxeDebugLogger getInstance(Class clazz) { return getLogger(clazz.getName(), manager.getFactory()); } public static HaxeDebugLogger getLogger() { return getLogger(getCallingClassName(), manager.getFactory()); } public static HaxeDebugLogger getLogger(String name) { return getLogger(name, manager.getFactory()); } public static HaxeDebugLogger getLogger(Class clazz) { return getLogger(clazz.getName(), manager.getFactory()); } public static HaxeDebugLogger getLogger(String name, LoggerFactory factory) { Logger logger = manager.getHierarchy().getLogger(name, factory); assert(logger instanceof HaxeDebugLogger); if (manager.getLogConfiguration().contains(name)) { logger.setLevel(manager.getLogConfiguration().getLevel(name)); } else { manager.getLogConfiguration().addConfiguration(name, manager.getDefaultLevel()); logger.setLevel(manager.getDefaultLevel()); } logger.addAppender(manager.getDefaultAppender()); return (HaxeDebugLogger)logger; } public static HierarchyManipulator mutePrimaryConfiguration() { HierarchyConfiguration config = new HierarchyConfiguration(); config.mute(Logger.getRootLogger().getLoggerRepository()); return config; } private HaxeDebugLogger(String name) { super(name); } public void setLevel(Level lvl) { configure(this.getName(), lvl); super.setLevel(lvl); } public static void configure(Class clazz, Level lvl) { configure(clazz.getName(), lvl); } public static void configure(String name, Level lvl) { manager.getLogConfiguration().addConfiguration(name, lvl); } } /** * Centralize global data for the HaxeDebugLogger. */ class HaxeDebugLoggerManager { private static final Level DEFAULT_LEVEL = Level.OFF; private Level defaultLevel = DEFAULT_LEVEL; private static final HaxeDebugLogger.Factory DEFAULT_FACTORY = new HaxeDebugLogger.DefaultFactory(); private HaxeDebugLogger.Factory configuredFactory = DEFAULT_FACTORY; private static final Appender DEFAULT_APPENDER = new ConsoleAppender(new PatternLayout("%p [%c{1}] %x - %m%n")); private static final Appender METHOD_APPENDER = new ConsoleAppender(new PatternLayout("%p [%C{1}.%M] %x - %m%n")); private Appender defaultAppender = METHOD_APPENDER; private org.apache.log4j.Hierarchy hierarchy; private LogConfiguration logConfiguration; public HaxeDebugLoggerManager() { logConfiguration = new LogConfiguration(); hierarchy = new Hierarchy(configuredFactory.makeNewRootLoggerInstance()); } public LoggerFactory getFactory() { return configuredFactory; } public LogConfiguration getLogConfiguration() { return logConfiguration; } public void setDefaultLevel(Level lvl) { // Null is a valid default. It means: check my parents. defaultLevel = lvl; } public Level getDefaultLevel() { return defaultLevel; } public Hierarchy getHierarchy() { return hierarchy; } /** * Gets the Appender that is to be set up on each logger, by default. */ public Appender getDefaultAppender() { return defaultAppender; } } // /////////////////////////////////////////////////////////////////////////// /** * Tracks the level configuration for loggers. Level configuration can be * set before the logger is allocated. */ class LogConfiguration { private Map<String, Level> map; public LogConfiguration() { map = new ConcurrentHashMap<String, Level>(); } public void addConfiguration(String name, Level lvl) { // It's not an error to add the same logger configuration again; update the level. // A null level is a valid configuration. if (null != name) { map.put(name, lvl); } } public boolean contains(String name) { return map.containsKey(name); } public Level getLevel(String name) { return map.get(name); } public void clear() { map.clear(); } public Set<String> getAllLogNames() { return map.keySet(); } } // /////////////////////////////////////////////////////////////////////////// /** * HierarchyConfiguration * * Stores the names and level configuration of an entire LoggerHierarchy. */ class HierarchyConfiguration implements HaxeDebugLogger.HierarchyManipulator { private LogConfiguration capturedConfiguration; private LoggerRepository capturedHierarchy; private Level threshold; public HierarchyConfiguration() { threshold = Level.ALL; capturedConfiguration = new LogConfiguration(); capturedHierarchy = null; } private synchronized void clear() { capturedConfiguration.clear(); capturedHierarchy = null; threshold = Level.ALL; } private synchronized void captureState(LoggerRepository hierarchy) throws IllegalStateException { if (null == hierarchy) { throw new IllegalArgumentException("Null Hierarchy is not allowed."); } if (null != capturedHierarchy && capturedHierarchy != hierarchy) { throw new IllegalStateException("Log configurations is already holding a captured hierarchy."); } capturedHierarchy = hierarchy; threshold = hierarchy.getThreshold(); Enumeration e = hierarchy.getCurrentLoggers(); while (e.hasMoreElements()) { Logger l = (Logger)e.nextElement(); Level v = l.getLevel(); // No point in capturing or restoring NULL loggers. if (null != v) { capturedConfiguration.addConfiguration(l.getName(), v); } } } public synchronized void restore() throws IllegalStateException { if (null == capturedHierarchy) { throw new IllegalStateException("No log configuration to restore."); } for(String key : capturedConfiguration.getAllLogNames()) { Logger l = capturedHierarchy.getLogger(key); l.setLevel(capturedConfiguration.getLevel(key)); } capturedHierarchy.setThreshold(threshold); clear(); } public synchronized void mute(LoggerRepository hierarchy) throws IllegalStateException { if (null == hierarchy) { throw new IllegalArgumentException("Null Hierarchy is not allowed."); } if (null != capturedHierarchy && capturedHierarchy != hierarchy) { throw new IllegalStateException("Trying to mute a hierarchy that is not the one previously captured."); } captureState(hierarchy); hierarchy.setThreshold(Level.OFF); } }