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