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