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.CoreConstants;
018import ch.qos.logback.core.LogbackException;
019import ch.qos.logback.core.joran.spi.JoranException;
020import ch.qos.logback.core.spi.ContextAware;
021import ch.qos.logback.core.spi.ContextAwareImpl;
022import ch.qos.logback.core.status.InfoStatus;
023import ch.qos.logback.core.util.EnvUtil;
024import ch.qos.logback.core.util.Loader;
025import ch.qos.logback.core.util.StatusListenerConfigHelper;
026
027import java.util.Comparator;
028import java.util.List;
029
030// contributors
031// Ted Graham, Matt Fowles, see also http://jira.qos.ch/browse/LBCORE-32
032
033/**
034 * This class contains logback's logic for automatic configuration
035 *
036 * @author Ceki Gulcu
037 */
038public class ContextInitializer {
039
040    /**
041     *  @deprecated Please use ClassicConstants.AUTOCONFIG_FILE instead
042     */
043    final public static String AUTOCONFIG_FILE = ClassicConstants.AUTOCONFIG_FILE;
044    /**
045     * @deprecated Please use ClassicConstants.TEST_AUTOCONFIG_FILE instead
046     */
047    final public static String TEST_AUTOCONFIG_FILE = ClassicConstants.TEST_AUTOCONFIG_FILE;
048    /**
049     * @deprecated Please use ClassicConstants.CONFIG_FILE_PROPERTY instead
050     */
051    final public static String CONFIG_FILE_PROPERTY = ClassicConstants.CONFIG_FILE_PROPERTY;
052
053    String[] INTERNAL_CONFIGURATOR_CLASSNAME_LIST = {"ch.qos.logback.classic.joran.SerializedModelConfigurator",
054            "ch.qos.logback.classic.util.DefaultJoranConfigurator", "ch.qos.logback.classic.BasicConfigurator"};
055
056    final LoggerContext loggerContext;
057
058    final ContextAware contextAware;
059
060    public ContextInitializer(LoggerContext loggerContext) {
061        this.loggerContext = loggerContext;
062        this.contextAware = new ContextAwareImpl(loggerContext, this);
063    }
064
065    public void autoConfig() throws JoranException {
066        autoConfig(Configurator.class.getClassLoader());
067    }
068
069
070    public void autoConfig(ClassLoader classLoader) throws JoranException {
071
072        // see https://github.com/qos-ch/logback/issues/715
073        classLoader = Loader.systemClassloaderIfNull(classLoader);
074
075        String versionStr = EnvUtil.logbackVersion();
076        if (versionStr == null) {
077            versionStr = CoreConstants.NA;
078        }
079        loggerContext.getStatusManager().add(new InfoStatus(CoreConstants.LOGBACK_CLASSIC_VERSION_MESSAGE + versionStr, loggerContext));
080        StatusListenerConfigHelper.installIfAsked(loggerContext);
081
082
083        // invoke custom configurators
084        List<Configurator> configuratorList = ClassicEnvUtil.loadFromServiceLoader(Configurator.class, classLoader);
085        configuratorList.sort(rankComparator);
086        if (configuratorList.isEmpty()) {
087            contextAware.addInfo("No custom configurators were discovered as a service.");
088        } else {
089            printConfiguratorOrder(configuratorList);
090        }
091
092        for (Configurator c : configuratorList) {
093            if (invokeConfigure(c) == Configurator.ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY)
094                return;
095        }
096
097        // invoke internal configurators
098        for (String configuratorClassName : INTERNAL_CONFIGURATOR_CLASSNAME_LIST) {
099            contextAware.addInfo("Trying to configure with "+configuratorClassName);
100            Configurator c = instantiateConfiguratorByClassName(configuratorClassName, classLoader);
101            if(c == null)
102                continue;
103            if (invokeConfigure(c) == Configurator.ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY)
104                return;
105        }
106    }
107
108    private Configurator instantiateConfiguratorByClassName(String configuratorClassName, ClassLoader classLoader) {
109        try {
110            Class<?> classObj = classLoader.loadClass(configuratorClassName);
111            return (Configurator) classObj.getConstructor().newInstance();
112        } catch (ReflectiveOperationException  e) {
113            contextAware.addInfo("Instantiation failure: " + e.toString());
114            return null;
115        }
116    }
117
118    /**
119     *
120     * @param configurator
121     * @return ExecutionStatus
122     */
123    private Configurator.ExecutionStatus invokeConfigure(Configurator configurator) {
124        try {
125            long start = System.currentTimeMillis();
126            contextAware.addInfo("Constructed configurator of type " + configurator.getClass());
127            configurator.setContext(loggerContext);
128            Configurator.ExecutionStatus status = configurator.configure(loggerContext);
129            printDuration(start, configurator, status);
130            return status;
131
132        } catch (Exception e) {
133            throw new LogbackException(String.format("Failed to initialize or to run Configurator: %s",
134                    configurator != null ? configurator.getClass().getCanonicalName() : "null"), e);
135        }
136    }
137
138    private void printConfiguratorOrder(List<Configurator> configuratorList) {
139        contextAware.addInfo("Here is a list of configurators discovered as a service, by rank: ");
140        for(Configurator c: configuratorList) {
141            contextAware.addInfo("  "+c.getClass().getName());
142        }
143        contextAware.addInfo("They will be invoked in order until ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY is returned.");
144    }
145
146    private void printDuration(long start, Configurator configurator, Configurator.ExecutionStatus executionStatus) {
147        long end = System.currentTimeMillis();
148        long diff = end - start;
149        contextAware.addInfo( configurator.getClass().getName()+".configure() call lasted "+diff + " milliseconds. ExecutionStatus="+executionStatus);
150    }
151
152    private Configurator.ExecutionStatus attemptConfigurationUsingJoranUsingReflexion(ClassLoader classLoader) {
153
154        try {
155            Class<?> djcClass = classLoader.loadClass("ch.qos.logback.classic.util.DefaultJoranConfigurator");
156            Configurator c = (Configurator) djcClass.newInstance();
157            c.setContext(loggerContext);
158            return c.configure(loggerContext);
159        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
160            contextAware.addError("unexpected exception while instantiating DefaultJoranConfigurator", e);
161            return Configurator.ExecutionStatus.INVOKE_NEXT_IF_ANY;
162        }
163
164    }
165
166    Comparator<Configurator> rankComparator = new Comparator<Configurator>() {
167        @Override
168        public int compare(Configurator c1, Configurator c2) {
169
170            ConfiguratorRank r1 = c1.getClass().getAnnotation(ConfiguratorRank.class);
171            ConfiguratorRank r2 = c2.getClass().getAnnotation(ConfiguratorRank.class);
172
173            int value1 = r1 == null ? ConfiguratorRank.DEFAULT : r1.value();
174            int value2 = r2 == null ? ConfiguratorRank.DEFAULT : r2.value();
175
176            int result = compareRankValue(value1, value2);
177            // reverse the result for high to low sort
178            return (-result);
179        }
180    };
181
182    private int compareRankValue(int value1, int value2) {
183        if(value1 > value2)
184            return 1;
185        else if (value1 == value2)
186            return 0;
187        else return -1;
188
189    }
190}