All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nl.altindag.log.util.LogbackUtils Maven / Gradle / Ivy

/*
 * Copyright 2019 Thunderberry.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package nl.altindag.log.util;

import nl.altindag.log.exception.LogCaptorException;
import org.slf4j.LoggerFactory;
import org.slf4j.helpers.SubstituteLogger;

import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.function.IntSupplier;
import java.util.regex.Pattern;

/**
 * NOTE:
 * Please don't use this class directly as it is part of the internal API. Class name and methods can be changed any time.
 *
 * @author Hakan Altindag
 */
public final class LogbackUtils {

    private static final int DEFAULT_POLL_COUNTER_LIMIT = 10;
    private static final int DEFAULT_POLL_DELAY_MILLISECONDS = 100;
    private static final Pattern IS_NUMBER_PATTERN = Pattern.compile("^\\d+$");
    private static final BiFunction INT_SYSTEM_PROPERTY_PROVIDER = (propertyName, defaultValue) -> Optional.ofNullable(System.getProperty(propertyName))
        .map(String::trim)
        .filter(value -> !value.isEmpty())
        .filter(value -> IS_NUMBER_PATTERN.matcher(value).matches())
        .map(Integer::parseInt)
        .orElse(defaultValue);

    private static final IntSupplier POLL_COUNTER_LIMIT = () -> INT_SYSTEM_PROPERTY_PROVIDER.apply("logcaptor.poll-counter-limit", DEFAULT_POLL_COUNTER_LIMIT);
    private static final IntSupplier POLL_DELAY_MILLISECONDS = () -> INT_SYSTEM_PROPERTY_PROVIDER.apply("logcaptor.poll-delay-milliseconds", DEFAULT_POLL_DELAY_MILLISECONDS);

    private LogbackUtils() {}

    public static ch.qos.logback.classic.Logger getLogger(String loggerName) {
        org.slf4j.Logger slf4jLogger = getSlf4jLogger(loggerName);
        ValidationUtils.requireLoggerOfType(slf4jLogger, ch.qos.logback.classic.Logger.class);
        return (ch.qos.logback.classic.Logger) slf4jLogger;
    }

    /**
     * Attempts to get the {@link org.slf4j.Logger}.
     * It might occur the {@link LoggerFactory} returns a {@link SubstituteLogger}. In that
     * case it will retry to get the correct logger within the given poll limit and poll delay.
     * SLF4J will provide the {@link SubstituteLogger} temporally when the underlying logger is not ready yet.
     * This will most likely happen when running the tests in parallel.
     */
    private static org.slf4j.Logger getSlf4jLogger(String loggerName) {
        int retryCounter = 0;
        org.slf4j.Logger slf4jLogger = LoggerFactory.getLogger(loggerName);

        if (!(slf4jLogger instanceof SubstituteLogger)) {
            return slf4jLogger;
        }

        int pollCounterLimit = POLL_COUNTER_LIMIT.getAsInt();
        int pollDelayMilliseconds = POLL_DELAY_MILLISECONDS.getAsInt();

        while (slf4jLogger instanceof SubstituteLogger && retryCounter++ < pollCounterLimit) {
            try {
                TimeUnit.MILLISECONDS.sleep(pollDelayMilliseconds);
                slf4jLogger = LoggerFactory.getLogger(loggerName);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new LogCaptorException(e);
            }
        }

        return slf4jLogger;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy