001/**
002 * Logback: the reliable, generic, fast and flexible logging framework. Copyright (C) 1999-2015, QOS.ch. All rights reserved.
003 * <p>
004 * This program and the accompanying materials are dual-licensed under either the terms of the Eclipse Public License v1.0 as published by the Eclipse
005 * Foundation
006 * <p>
007 * or (per the licensee's choosing)
008 * <p>
009 * under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation.
010 */
011package ch.qos.logback.classic.util;
012
013import ch.qos.logback.classic.ClassicConstants;
014import ch.qos.logback.classic.LoggerContext;
015import ch.qos.logback.classic.spi.Configurator;
016import ch.qos.logback.classic.spi.ConfiguratorRank;
017import ch.qos.logback.core.LogbackException;
018import ch.qos.logback.core.joran.spi.JoranException;
019import ch.qos.logback.core.spi.ContextAware;
020import ch.qos.logback.core.spi.ContextAwareImpl;
021import ch.qos.logback.core.util.CoreVersionUtil;
022import ch.qos.logback.core.util.Loader;
023import ch.qos.logback.core.util.StatusListenerConfigHelper;
024import ch.qos.logback.core.util.VersionUtil;
025
026import java.util.Comparator;
027import java.util.List;
028
029// contributors
030// Ted Graham, Matt Fowles, see also http://jira.qos.ch/browse/LBCORE-32
031
032/**
033 * This class contains logback's logic for automatic configuration
034 *
035 * @author Ceki Gulcu
036 */
037public class ContextInitializer {
038
039    /**
040     *  @deprecated Please use ClassicConstants.AUTOCONFIG_FILE instead
041     */
042    final public static String AUTOCONFIG_FILE = ClassicConstants.AUTOCONFIG_FILE;
043    /**
044     * @deprecated Please use ClassicConstants.TEST_AUTOCONFIG_FILE instead
045     */
046    final public static String TEST_AUTOCONFIG_FILE = ClassicConstants.TEST_AUTOCONFIG_FILE;
047    /**
048     * @deprecated Please use ClassicConstants.CONFIG_FILE_PROPERTY instead
049     */
050    final public static String CONFIG_FILE_PROPERTY = ClassicConstants.CONFIG_FILE_PROPERTY;
051
052    String[] INTERNAL_CONFIGURATOR_CLASSNAME_LIST = {"ch.qos.logback.classic.util.DefaultJoranConfigurator", "ch.qos.logback.classic.BasicConfigurator"};
053
054    final LoggerContext loggerContext;
055
056    final ContextAware contextAware;
057
058    public ContextInitializer(LoggerContext loggerContext) {
059        this.loggerContext = loggerContext;
060        this.contextAware = new ContextAwareImpl(loggerContext, this);
061    }
062
063    public void autoConfig() throws JoranException {
064        autoConfig(Configurator.class.getClassLoader());
065    }
066
067
068    public void autoConfig(ClassLoader classLoader) throws JoranException {
069
070        // see https://github.com/qos-ch/logback/issues/715
071        classLoader = Loader.systemClassloaderIfNull(classLoader);
072
073        checkVersions();
074
075        StatusListenerConfigHelper.installIfAsked(loggerContext);
076
077
078        // invoke custom configurators
079        List<Configurator> configuratorList = ClassicEnvUtil.loadFromServiceLoader(Configurator.class, classLoader);
080        configuratorList.sort(rankComparator);
081        if (configuratorList.isEmpty()) {
082            contextAware.addInfo("No custom configurators were discovered as a service.");
083        } else {
084            printConfiguratorOrder(configuratorList);
085        }
086
087        for (Configurator c : configuratorList) {
088            if (invokeConfigure(c) == Configurator.ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY)
089                return;
090        }
091
092        // invoke internal configurators
093        for (String configuratorClassName : INTERNAL_CONFIGURATOR_CLASSNAME_LIST) {
094            contextAware.addInfo("Trying to configure with "+configuratorClassName);
095            Configurator c = instantiateConfiguratorByClassName(configuratorClassName, classLoader);
096            if(c == null)
097                continue;
098            if (invokeConfigure(c) == Configurator.ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY)
099                return;
100        }
101    }
102
103    private void checkVersions() {
104        try {
105            String coreVersion = CoreVersionUtil.getCoreVersionBySelfDeclaredProperties();
106            String classicVersion = ClassicVersionUtil.getVersionBySelfDeclaredProperties();
107            VersionUtil.checkForVersionEquality(loggerContext, coreVersion, classicVersion, "logback-core", "logback-classic");
108        }  catch(NoClassDefFoundError e) {
109            contextAware.addWarn("Missing ch.logback.core.util.VersionUtil class on classpath. The version of logback-core is probably older than 1.5.26.");
110        } catch (NoSuchMethodError e) {
111            contextAware.addWarn(e.toString());
112            contextAware.addWarn("The version of logback-core is probably older than 1.5.26.");
113        }
114    }
115
116    private Configurator instantiateConfiguratorByClassName(String configuratorClassName, ClassLoader classLoader) {
117        try {
118            Class<?> classObj = classLoader.loadClass(configuratorClassName);
119            return (Configurator) classObj.getConstructor().newInstance();
120        } catch (ReflectiveOperationException  e) {
121            contextAware.addInfo("Instantiation failure: " + e.toString());
122            return null;
123        }
124    }
125
126    /**
127     *
128     * @param configurator
129     * @return ExecutionStatus
130     */
131    private Configurator.ExecutionStatus invokeConfigure(Configurator configurator) {
132        try {
133            long start = System.currentTimeMillis();
134            contextAware.addInfo("Constructed configurator of type " + configurator.getClass());
135            configurator.setContext(loggerContext);
136            Configurator.ExecutionStatus status = configurator.configure(loggerContext);
137            printDuration(start, configurator, status);
138            return status;
139
140        } catch (Exception e) {
141            throw new LogbackException(String.format("Failed to initialize or to run Configurator: %s",
142                    configurator != null ? configurator.getClass().getCanonicalName() : "null"), e);
143        }
144    }
145
146    private void printConfiguratorOrder(List<Configurator> configuratorList) {
147        contextAware.addInfo("Here is a list of configurators discovered as a service, by rank: ");
148        for(Configurator c: configuratorList) {
149            contextAware.addInfo("  "+c.getClass().getName());
150        }
151        contextAware.addInfo("They will be invoked in order until ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY is returned.");
152    }
153
154    private void printDuration(long start, Configurator configurator, Configurator.ExecutionStatus executionStatus) {
155        long end = System.currentTimeMillis();
156        long diff = end - start;
157        contextAware.addInfo( configurator.getClass().getName()+".configure() call lasted "+diff + " milliseconds. ExecutionStatus="+executionStatus);
158    }
159
160    private Configurator.ExecutionStatus attemptConfigurationUsingJoranUsingReflexion(ClassLoader classLoader) {
161
162        try {
163            Class<?> djcClass = classLoader.loadClass("ch.qos.logback.classic.util.DefaultJoranConfigurator");
164            Configurator c = (Configurator) djcClass.newInstance();
165            c.setContext(loggerContext);
166            return c.configure(loggerContext);
167        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
168            contextAware.addError("unexpected exception while instantiating DefaultJoranConfigurator", e);
169            return Configurator.ExecutionStatus.INVOKE_NEXT_IF_ANY;
170        }
171
172    }
173
174    Comparator<Configurator> rankComparator = new Comparator<Configurator>() {
175        @Override
176        public int compare(Configurator c1, Configurator c2) {
177
178            ConfiguratorRank r1 = c1.getClass().getAnnotation(ConfiguratorRank.class);
179            ConfiguratorRank r2 = c2.getClass().getAnnotation(ConfiguratorRank.class);
180
181            int value1 = r1 == null ? ConfiguratorRank.DEFAULT : r1.value();
182            int value2 = r2 == null ? ConfiguratorRank.DEFAULT : r2.value();
183
184            int result = compareRankValue(value1, value2);
185            // reverse the result for high to low sort
186            return (-result);
187        }
188    };
189
190    private int compareRankValue(int value1, int value2) {
191        if(value1 > value2)
192            return 1;
193        else if (value1 == value2)
194            return 0;
195        else return -1;
196
197    }
198}