AbstractSimpleLogger.java

package space.sunqian.fs.base.logging;

import space.sunqian.annotation.Nonnull;
import space.sunqian.annotation.Nullable;
import space.sunqian.fs.Fs;
import space.sunqian.fs.base.exception.UnreachablePointException;

import java.lang.reflect.Method;
import java.time.ZonedDateTime;

/**
 * This is a skeletal implementation of {@link SimpleLogger} to minimize the effort required to implement the interface.
 * The subclasses of this class only need to implement the {@link #log(SimpleLog, Method)} method.
 *
 * @author sunqian
 */
public abstract class AbstractSimpleLogger implements SimpleLogger {

    private final @Nonnull Method FATAL_METHOD = Fs.uncheck(
        () -> AbstractSimpleLogger.class.getMethod("fatal", Object[].class),
        UnreachablePointException::new);
    private final @Nonnull Method ERROR_METHOD = Fs.uncheck(
        () -> AbstractSimpleLogger.class.getMethod("error", Object[].class),
        UnreachablePointException::new);
    private final @Nonnull Method WARN_METHOD = Fs.uncheck(
        () -> AbstractSimpleLogger.class.getMethod("warn", Object[].class),
        UnreachablePointException::new);
    private final @Nonnull Method INFO_METHOD = Fs.uncheck(
        () -> AbstractSimpleLogger.class.getMethod("info", Object[].class),
        UnreachablePointException::new);
    private final @Nonnull Method DEBUG_METHOD = Fs.uncheck(
        () -> AbstractSimpleLogger.class.getMethod("debug", Object[].class),
        UnreachablePointException::new);
    private final @Nonnull Method TRACE_METHOD = Fs.uncheck(
        () -> AbstractSimpleLogger.class.getMethod("trace", Object[].class),
        UnreachablePointException::new);

    private final @Nonnull Level level;

    /**
     * Constructs a new abstract simple logger with the given level. The level can be accessed by {@link #level()}.
     *
     * @param level the level of this logger
     */
    protected AbstractSimpleLogger(@Nonnull Level level) {
        this.level = level;
    }

    @Override
    public void fatal(Object @Nonnull ... message) {
        log(Level.FATAL, FATAL_METHOD, message);
    }

    @Override
    public void error(Object @Nonnull ... message) {
        log(Level.ERROR, ERROR_METHOD, message);
    }

    @Override
    public void warn(Object @Nonnull ... message) {
        log(Level.WARN, WARN_METHOD, message);
    }

    @Override
    public void info(Object @Nonnull ... message) {
        log(Level.INFO, INFO_METHOD, message);
    }

    @Override
    public void debug(Object @Nonnull ... message) {
        log(Level.DEBUG, DEBUG_METHOD, message);
    }

    @Override
    public void trace(Object @Nonnull ... message) {
        log(Level.TRACE, TRACE_METHOD, message);
    }

    @Override
    public @Nonnull Level level() {
        return level;
    }

    private void log(@Nonnull Level level, @Nonnull Method method, Object @Nonnull ... message) {
        SimpleLog log = newLog(level, message);
        if (log == null) {
            return;
        }
        log(log, method);
    }

    /**
     * Logs the given log message with the log method which is the method be called to log the message.
     *
     * @param log    the log message
     * @param method the log method, which is the method be called to log the message, one of
     *               {@link SimpleLogger#fatal(Object...)}, {@link SimpleLogger#error(Object...)},
     *               {@link SimpleLogger#warn(Object...)}, {@link SimpleLogger#info(Object...)},
     *               {@link SimpleLogger#debug(Object...)}, and {@link SimpleLogger#trace(Object...)}.
     */
    protected abstract void log(@Nonnull SimpleLog log, @Nonnull Method method);

    private @Nullable SimpleLog newLog(@Nonnull Level level, Object @Nonnull ... message) {
        if (level.value() < this.level.value()) {
            return null;
        }
        ZonedDateTime now = ZonedDateTime.now();
        Thread currentThread = Thread.currentThread();
        StackTraceElement[] stackElements = currentThread.getStackTrace();
        return SimpleLog.newLog(now, level, message, stackElements, currentThread);
    }
}