001/*
002 * Copyright 2023 the original author or authors.
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * https://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package de.cuioss.test.juli;
017
018import static de.cuioss.tools.string.MoreStrings.isEmpty;
019
020import java.util.Arrays;
021import java.util.List;
022import java.util.Map.Entry;
023import java.util.Optional;
024import java.util.logging.ConsoleHandler;
025import java.util.logging.Handler;
026import java.util.logging.LogManager;
027import java.util.logging.Logger;
028
029import lombok.experimental.UtilityClass;
030
031/**
032 * Central entry point for handling {@link TestLogHandler}
033 *
034 *
035 * @author Oliver Wolff
036 *
037 */
038@UtilityClass
039@SuppressWarnings("java:S4792") // owolff: Changing the logger is the actual idea of this type, not
040                                // a security issue
041public class TestLoggerFactory {
042
043    private static final StaticLoggerConfigurator configuration = new StaticLoggerConfigurator();
044
045    private static final ConsoleHandlerModifier CONSOLE_HANDLER = new ConsoleHandlerModifier();
046
047    /**
048     * Adds a {@link TestLogHandler} instance to jul's root logger. This method is
049     * reentrant, it ensures the {@link TestLogHandler} is installed only once
050     */
051    public static void install() {
052        if (getTestHandlerOption().isEmpty()) {
053            CONSOLE_HANDLER.saveLevel();
054            getRootLogger().addHandler(new TestLogHandler());
055        }
056    }
057
058    /**
059     * Removes previously installed {@link TestLogHandler} instance and restores the
060     * previously stored {@link ConsoleHandler#getLevel()}. See also
061     * {@link #install()}.
062     */
063    public static void uninstall() {
064        CONSOLE_HANDLER.restoreLevel();
065        var testHandlerOption = getTestHandlerOption();
066        testHandlerOption.ifPresent(testLogHandler -> getRootLogger().removeHandler(testLogHandler));
067    }
068
069    /**
070     * Configures the logger sub-system according to the configuration found within
071     * {@link System#getProperties()} and / or the file "cui_logger.properties"
072     * usually located directly in "src/test/resources".
073     */
074    public static void configureLogger() {
075        // Set Root logger
076        var rootLevel = configuration.getRootLevel();
077        rootLevel.setAsRootLevel();
078        CONSOLE_HANDLER.adjustLevel(rootLevel);
079        // Set concrete logger
080        for (Entry<String, TestLogLevel> entry : configuration.getConfiguredLogger().entrySet()) {
081            entry.getValue().addLogger(entry.getKey());
082        }
083    }
084
085    private static Logger getRootLogger() {
086        return LogManager.getLogManager().getLogger("");
087    }
088
089    private static List<Handler> getHandler() {
090        return Arrays.asList(getRootLogger().getHandlers());
091    }
092
093    /**
094     * @return the configured {@link TestLogHandler}
095     * @throws AssertionError in case no {@link TestLogHandler} could be found. This
096     *                        is usually the case if {@link #install()} was not
097     *                        called prior to this request
098     */
099    public static TestLogHandler getTestHandler() {
100        return getTestHandlerOption().orElseThrow(
101                () -> new AssertionError("Unable to access de.cuioss.test.juli.TestLogHandler. Used properly?"));
102    }
103
104    /**
105     * @return the configured {@link TestLogHandler} if present
106     */
107    public static Optional<TestLogHandler> getTestHandlerOption() {
108        for (Handler handler : getHandler()) {
109            if (handler instanceof TestLogHandler logHandler) {
110                return Optional.of(logHandler);
111            }
112        }
113        return Optional.empty();
114    }
115
116    /**
117     * Convenient method for setting a Log-Level in context of the given
118     * {@link TestLogLevel}
119     *
120     * @param logLevel   to be set
121     * @param loggerName if it is {@code null} or empty it will set the root-logger
122     *                   for the actual Log-Level
123     */
124    public static void addLogger(TestLogLevel logLevel, String loggerName) {
125        CONSOLE_HANDLER.adjustLevel(logLevel);
126        if (isEmpty(loggerName)) {
127            Logger.getLogger("").setLevel(logLevel.getJuliLevel());
128        }
129        Logger.getLogger(loggerName).setLevel(logLevel.getJuliLevel());
130    }
131}