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;
019import static java.util.Objects.requireNonNull;
020
021import java.util.EnumSet;
022import java.util.logging.Level;
023import java.util.logging.Logger;
024
025import lombok.AccessLevel;
026import lombok.Getter;
027import lombok.RequiredArgsConstructor;
028
029/**
030 * Defines the log-levels with implicit mapping to JUL log level according to:
031 * <a href=
032 * "https://www.slf4j.org/apidocs/org/slf4j/bridge/SLF4JBridgeHandler.html">SLF4JBridgeHandler</a>
033 *
034 * @author Oliver Wolff
035 *
036 */
037@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
038public enum TestLogLevel {
039
040    /** Trace Level, maps to {@link Level#FINER}. */
041    TRACE(Level.FINER),
042    /** Debug Level, maps to {@link Level#FINE}. */
043    DEBUG(Level.FINE),
044    /** Info Level, maps to {@link Level#INFO}. */
045    INFO(Level.INFO),
046    /** Warn Level, maps to {@link Level#WARNING}. */
047    WARN(Level.WARNING),
048    /** Error Level, maps to {@link Level#SEVERE}. */
049    ERROR(Level.SEVERE);
050
051    @Getter
052    private final Level juliLevel;
053
054    /**
055     *
056     * @param logger to be checked, must not be null
057     * @return {@code true} if the log-level is enabled on the logger, false
058     *         otherwise
059     */
060    boolean isEnabled(Logger logger) {
061        return logger.isLoggable(getJuliLevel());
062    }
063
064    /**
065     * Convenient method for setting the root-logger-level in the context of the
066     * current {@link TestLogLevel}
067     *
068     * @return the {@link TestLogLevel} itself in order to us is in a fluent way
069     */
070    public TestLogLevel setAsRootLevel() {
071        return addLogger("");
072    }
073
074    /**
075     * Convenient method for setting a Log-Level in the context of the current
076     * {@link TestLogLevel}
077     *
078     * @param className if it is {@code null} it will set the root-logger for the
079     *                  actual Log-Level
080     * @return the {@link TestLogLevel} itself in order to us is in a fluent way
081     */
082    public TestLogLevel addLogger(Class<?> className) {
083        if (null == className) {
084            return addLogger("");
085        }
086        return addLogger(className.getName());
087    }
088
089    /**
090     * Convenient method for setting a Log-Level in context of the current
091     * {@link TestLogLevel}
092     *
093     * @param loggerName if it is {@code null} or empty it will set the root-logger
094     *                   for the actual Log-Level
095     * @return the {@link TestLogLevel} itself in order to us is in a fluent way
096     */
097    public TestLogLevel addLogger(String loggerName) {
098        TestLoggerFactory.addLogger(this, loggerName);
099        return this;
100    }
101
102    /**
103     * @param level to be parsed, must not be null
104     * @return the mapped level or {@link TestLogLevel#INFO} if the level can not be
105     *         mapped
106     */
107    public static TestLogLevel parse(Level level) {
108        requireNonNull(level);
109        for (TestLogLevel testLogLevel : TestLogLevel.values()) {
110            if (testLogLevel.juliLevel.equals(level)) {
111                return testLogLevel;
112            }
113        }
114        return TestLogLevel.INFO;
115    }
116
117    /**
118     * Factory method for deriving a {@link TestLogLevel} from a given String.
119     *
120     * @param levelAsAString The String representation of the desired
121     *                       {@link TestLogLevel}
122     * @param defaultLevel   must not be null
123     * @return the desired {@link TestLogLevel} if levelAsAString is null or empty
124     *         or is not a defined {@link TestLogLevel} the method will return the
125     *         given defaultLevel
126     */
127    static TestLogLevel getLevelOrDefault(final String levelAsAString, final TestLogLevel defaultLevel) {
128        requireNonNull(defaultLevel);
129        if (isEmpty(levelAsAString)) {
130            return defaultLevel;
131        }
132        try {
133            return TestLogLevel.valueOf(levelAsAString.toUpperCase());
134        } catch (IllegalArgumentException e) {
135            var message = "Unable to determine logger, expected one of %s, but was %s"
136                    .formatted(EnumSet.allOf(TestLogLevel.class), levelAsAString);
137            Logger.getLogger(TestLogLevel.class.getName()).log(Level.FINE, message, e);
138            return defaultLevel;
139        }
140    }
141}