From aaca6272d27a822e0d73c8ed8120c8b8c5995946 Mon Sep 17 00:00:00 2001 From: Jonathing Date: Fri, 24 Oct 2025 23:09:24 -0400 Subject: [PATCH] Log Utils 0.4 - Migrate from static to instance loggers (#15) --- .../util/logging/AbstractLogger.java | 148 +++++ .../util/logging/DefaultLogger.java | 62 ++ .../util/logging/DelegatePrintStream.java | 22 +- .../net/minecraftforge/util/logging/Log.java | 607 ------------------ .../util/logging/LogConsumer.java | 16 +- .../minecraftforge/util/logging/Logger.java | 562 ++++++++++++++++ log-utils/src/test/java/Main.java | 24 +- 7 files changed, 806 insertions(+), 635 deletions(-) create mode 100644 log-utils/src/main/java/net/minecraftforge/util/logging/AbstractLogger.java create mode 100644 log-utils/src/main/java/net/minecraftforge/util/logging/DefaultLogger.java delete mode 100644 log-utils/src/main/java/net/minecraftforge/util/logging/Log.java create mode 100644 log-utils/src/main/java/net/minecraftforge/util/logging/Logger.java diff --git a/log-utils/src/main/java/net/minecraftforge/util/logging/AbstractLogger.java b/log-utils/src/main/java/net/minecraftforge/util/logging/AbstractLogger.java new file mode 100644 index 0000000..b9624bc --- /dev/null +++ b/log-utils/src/main/java/net/minecraftforge/util/logging/AbstractLogger.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) Forge Development LLC + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.util.logging; + +import org.jetbrains.annotations.Nullable; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public abstract class AbstractLogger implements Logger { + /* CREATION */ + + protected AbstractLogger() { + this(Level.INFO, (byte) 0); + } + + protected AbstractLogger(byte indentLevel) { + this(Level.INFO, indentLevel); + } + + protected AbstractLogger(Level enabled) { + this(enabled, (byte) 0); + } + + protected AbstractLogger(Level enabled, byte indentLevel) { + this.enabled = enabled; + this.indentLevel = indentLevel; + } + + /* LOG LEVELS */ + + private @Nullable Level enabled; + + @Override + public final @Nullable Level getEnabled() { + return this.enabled; + } + + @Override + public final void setEnabled(@Nullable Level level) { + this.enabled = level; + } + + /* INDENTATIONS */ + + private static final String INDENT_STRING = " "; + private static final String[] INDENT_CACHE = new String[Byte.MAX_VALUE]; + private byte indentLevel; + + static { + INDENT_CACHE[0] = ""; + INDENT_CACHE[1] = INDENT_STRING; + } + + @Override + public final byte push() { + return indentLevel++; + } + + @Override + public final byte pop() { + if (--indentLevel < 0) + throw new IllegalStateException("Cannot pop Log below 0"); + + return indentLevel; + } + + @Override + public final byte pop(byte indent) { + if (indent < 0) + throw new IllegalArgumentException("Cannot pop Log below 0"); + + return indentLevel = indent; + } + + @Override + public final String getIndentation() { + String ret = INDENT_CACHE[indentLevel]; + //noinspection ConstantValue -- IntelliJ skill issue + return ret == null ? INDENT_CACHE[indentLevel] = getIndentation(indentLevel) : ret; + } + + private static String getIndentation(byte indent) { + StringBuilder builder = new StringBuilder(INDENT_STRING.length() * indent); + for (int i = 0; i < indent; i++) + builder.append(INDENT_STRING); + return builder.toString(); + } + + /* CAPTURING */ + + private @Nullable List> captured; + + @Override + public final boolean isCapturing() { + return captured != null; + } + + @Override + public final void capture() { + if (captured != null) return; + captured = new ArrayList<>(128); + } + + final void tryCapture(Consumer logger, Level level, String message) { + if (captured != null) + captured.add(new AbstractMap.SimpleImmutableEntry<>(level, message)); + else + logger.accept(message); + } + + @Override + public final void drop() { + captured = null; + } + + @Override + public final void release() { + release(this::logDirectly); + } + + @Override + public final void release(BiConsumer consumer) { + if (captured == null) return; + + Iterator> itor = captured.iterator(); + captured = null; + while (itor.hasNext()) { + Map.Entry capture = itor.next(); + consumer.accept(capture.getKey(), capture.getValue()); + } + } + + /* LOGGING */ + + static final DelegatePrintStream.Empty EMPTY = DelegatePrintStream.EMPTY; + + private void logDirectly(Level level, String message) { + ((DelegatePrintStream) this.getLog(level)).getDelegate().accept(message); + } +} diff --git a/log-utils/src/main/java/net/minecraftforge/util/logging/DefaultLogger.java b/log-utils/src/main/java/net/minecraftforge/util/logging/DefaultLogger.java new file mode 100644 index 0000000..bbbbae3 --- /dev/null +++ b/log-utils/src/main/java/net/minecraftforge/util/logging/DefaultLogger.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) Forge Development LLC + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.util.logging; + +import java.io.PrintStream; + +final class DefaultLogger extends AbstractLogger { + DefaultLogger() { + super(); + } + + DefaultLogger(byte indentLevel) { + super(indentLevel); + } + + DefaultLogger(Level enabled) { + super(enabled); + } + + DefaultLogger(Level enabled, byte indentLevel) { + super(enabled, indentLevel); + } + + private final DelegatePrintStream.Capturing debug = new DelegatePrintStream.Capturing(this, Level.DEBUG, System.out::println); + private final DelegatePrintStream.Capturing quiet = new DelegatePrintStream.Capturing(this, Level.QUIET, System.out::println); + private final DelegatePrintStream.Capturing info = new DelegatePrintStream.Capturing(this, Level.INFO, System.out::println); + private final DelegatePrintStream.Capturing warn = new DelegatePrintStream.Capturing(this, Level.WARN, System.out::println); + private final DelegatePrintStream.Capturing error = new DelegatePrintStream.Capturing(this, Level.ERROR, System.err::println); + private final DelegatePrintStream.Capturing fatal = new DelegatePrintStream.Capturing(this, Level.FATAL, System.err::println); + + @Override + public PrintStream getDebug() { + return this.debug; + } + + @Override + public PrintStream getQuiet() { + return this.quiet; + } + + @Override + public PrintStream getInfo() { + return this.info; + } + + @Override + public PrintStream getWarn() { + return this.warn; + } + + @Override + public PrintStream getError() { + return this.error; + } + + @Override + public PrintStream getFatal() { + return this.fatal; + } +} diff --git a/log-utils/src/main/java/net/minecraftforge/util/logging/DelegatePrintStream.java b/log-utils/src/main/java/net/minecraftforge/util/logging/DelegatePrintStream.java index 964a730..12cf7dd 100644 --- a/log-utils/src/main/java/net/minecraftforge/util/logging/DelegatePrintStream.java +++ b/log-utils/src/main/java/net/minecraftforge/util/logging/DelegatePrintStream.java @@ -9,16 +9,16 @@ import java.util.function.Consumer; interface DelegatePrintStream { - PrintStream EMPTY = new Empty(); + DelegatePrintStream.Empty EMPTY = new Empty(); - PrintStream getDelegate(); + Consumer getDelegate(); final class Capturing extends PrintStream implements DelegatePrintStream { - private final PrintStream raw; + private final Consumer delegate; - public Capturing(Log.Level level, PrintStream stream) { + public Capturing(AbstractLogger logger, Logger.Level level, Consumer output) { super(new OutputStream() { - private final Consumer logger = new LogConsumer(level, stream::println); + private final Consumer consumer = new LogConsumer(logger, level, output); private StringBuffer buffer = new StringBuffer(512); @Override @@ -29,7 +29,7 @@ public void write(int b) { private void write(char c) { if (c == '\n' || c == '\r') { if (this.buffer.length() != 0) { - logger.accept(this.buffer.insert(0, Log.getIndentation()).toString()); + consumer.accept(this.buffer.insert(0, logger.getIndentation()).toString()); this.buffer = new StringBuffer(512); } } else { @@ -38,12 +38,12 @@ private void write(char c) { } }); - this.raw = stream; + this.delegate = output; } @Override - public PrintStream getDelegate() { - return this.raw; + public Consumer getDelegate() { + return this.delegate; } } @@ -64,8 +64,8 @@ public boolean checkError() { protected void clearError() { } @Override - public PrintStream getDelegate() { - return this; + public Consumer getDelegate() { + return s -> {}; } } } diff --git a/log-utils/src/main/java/net/minecraftforge/util/logging/Log.java b/log-utils/src/main/java/net/minecraftforge/util/logging/Log.java deleted file mode 100644 index 6393732..0000000 --- a/log-utils/src/main/java/net/minecraftforge/util/logging/Log.java +++ /dev/null @@ -1,607 +0,0 @@ -/* - * Copyright (c) Forge Development LLC - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.util.logging; - -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.UnknownNullability; - -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -/** - * Logging Utils uses this single static logger to log messages. - * - *

Features

- *
    - *
  • A single, static logger to be used primarily by top-level CLIs/programs. - *
      - *
    • It is essentially a wrapper around {@link System#out} and {@link System#err}
    • - *
  • - *
  • The ability to add indentations using {@link #push()} and {@link #pop()}
  • - *
  • A simple level system using {@link Level}.
  • - *
  • Delegate to a {@link PrintStream} object (for things like {@link Throwable#printStackTrace(PrintStream)}) using {@link #getLog(Level)} or similar. - *
      - *
    • If a level is not being logged (i.e. lower than {@link Log#enabled}), an empty print stream is returned that will ignore all method calls.
    • - *
    • Each level's {@link PrintStream} also exists as a public static final field, such as {@link #WARN}.
    • - *
  • - *
  • No config files, no managers, no bullshit.
  • - *
- */ -public final class Log { - /** The lowest level that should be logged. If {@code null}, all logging is completely disabled. */ - public static @Nullable Level enabled = Level.INFO; - - public static final PrintStream EMPTY = DelegatePrintStream.EMPTY; - /** The stream used for {@link Level#DEBUG}. */ - public static final PrintStream DEBUG = new DelegatePrintStream.Capturing(Level.DEBUG, System.out); - /** The stream used for {@link Level#QUIET}. */ - public static final PrintStream QUIET = new DelegatePrintStream.Capturing(Level.QUIET, System.out); - /** The stream used for {@link Level#INFO}. */ - public static final PrintStream INFO = new DelegatePrintStream.Capturing(Level.INFO, System.out); - /** The stream used for {@link Level#WARN}. */ - public static final PrintStream WARN = new DelegatePrintStream.Capturing(Level.WARN, System.out); - /** The stream used for {@link Level#ERROR}. */ - public static final PrintStream ERROR = new DelegatePrintStream.Capturing(Level.ERROR, System.err); - /** The stream used for {@link Level#FATAL}. */ - public static final PrintStream FATAL = new DelegatePrintStream.Capturing(Level.FATAL, System.err); - - - /* INDENTATIONS */ - - private static final String INDENT_STRING = " "; - private static final String[] INDENT_CACHE = new String[Byte.MAX_VALUE]; - private static byte indentLevel = 0; - - static { - INDENT_CACHE[0] = ""; - INDENT_CACHE[1] = INDENT_STRING; - } - - /** Pushes the current indentation level up by one. */ - public static byte push() { - return indentLevel++; - } - - /** - * Pops the current indentation level down by one. - * - * @throws IllegalStateException If the indentation level is already 0 - */ - public static void pop() { - if (--indentLevel < 0) - throw new IllegalStateException("Cannot pop Log below 0"); - } - - public static void pop(byte indent) { - if (indent < 0) - throw new IllegalArgumentException("Cannot pop Log below 0"); - - indentLevel = indent; - } - - static String getIndentation() { - String ret = INDENT_CACHE[indentLevel]; - //noinspection ConstantValue -- IntelliJ skill issue - return ret == null ? INDENT_CACHE[indentLevel] = getIndentation(indentLevel) : ret; - } - - private static String getIndentation(byte indent) { - StringBuilder builder = new StringBuilder(INDENT_STRING.length() * indent); - for (int i = 0; i < indent; i++) - builder.append(INDENT_STRING); - return builder.toString(); - } - - - /* CAPTURING */ - - private static @UnknownNullability List CAPTURED; - - private static final class CapturedMessage { - private final Level level; - private final String message; - - private CapturedMessage(Level level, String message) { - this.level = level; - this.message = message; - } - } - - /** - * If the log is capturing log messages. - * - * @return The capturing state of the Log - * @see #capture() - */ - public static boolean isCapturing() { - return CAPTURED != null; - } - - /** - * Begins capturing log messages. - *

When capturing, all log messages are stored in memory and not printed to the screen. They can either be - * {@linkplain #release() released} to be printed on the screen or {@linkplain #drop() dropped} to be removed and - * never printed.

- */ - public static void capture() { - if (CAPTURED != null) return; - CAPTURED = new ArrayList<>(128); - } - - static void tryCapture(Consumer logger, Level level, String message) { - if (CAPTURED != null) - CAPTURED.add(new CapturedMessage(level, message)); - else - logger.accept(message); - } - - /** - * Drops all captured log messages and stops capturing. - * - * @see #capture() - */ - public static void drop() { - CAPTURED = null; - } - - /** - * Releases all captured log messages (using {@link Log#log(Level, Object)}), then stops capturing. - * - * @see #capture() - * @see #release(BiConsumer) - */ - public static void release() { - release(Log::logCaptured); - } - - /** - * Releases all captured log messages into the given consumer, then stops capturing. - * - * @param consumer The consumer to release the captured log messages to - * @see #capture() - */ - public static void release(BiConsumer consumer) { - if (CAPTURED == null) return; - - Iterator itor = CAPTURED.iterator(); - CAPTURED = null; - while (itor.hasNext()) { - CapturedMessage capture = itor.next(); - consumer.accept(capture.level, capture.message); - } - } - - // so we can use a method reference instead of allocate a lambda - private static void logCaptured(Level level, String message) { - ((DelegatePrintStream) Log.getLog(level)).getDelegate().println(message); - } - - - /** - * Gets the {@linkplain PrintStream print stream} for the given {@linkplain Level level}. If the given level is not - * {@linkplain #enabled enabled} or is {@code null}, then an empty print stream is returned that ignores all method - * calls. - * - * @param level The level to get the print stream for - * @return The print stream - */ - public static PrintStream getLog(@Nullable Level level) { - if (level == null) return EMPTY; - switch (level) { - case DEBUG: return DEBUG; - case QUIET: return QUIET; - case INFO: return INFO; - case WARN: return WARN; - case ERROR: return ERROR; - case FATAL: return FATAL; - } - - throw new IllegalStateException("Unexpected value: " + level); - } - - /** - * Prints an empty message to the given level, similar to {@link PrintStream#println()}. - * - * @deprecated Directly use the no-args method for the given level instead - e.g. {@link Log#info()} for the - * {@link Level#INFO INFO} level. - * - * @param level The level to log the message at - */ - @Deprecated - public static void log(Level level) { - getLog(level).println(); - } - - /** - * Logs a message for the given level. - * - * @deprecated Directly use the object arg method for the given level instead - e.g. {@link Log#info(Object)} for - * the {@link Level#INFO INFO} level. - * - * @param level The level to log the message at - * @param message The message to log - */ - @Deprecated - public static void log(Level level, Object message) { - getLog(level).println(message); - } - - /** - * Logs the stacktrace of a throwable for the given level. - * - * @deprecated Directly use the throwable arg method for the given level instead - e.g. {@link Log#error(Throwable)} - * for the {@link Level#ERROR ERROR} level. - * - * @param level The level to log the message at - * @param throwable The throwable to log - */ - @Deprecated - public static void log(Level level, Throwable throwable) { - throwable.printStackTrace(getLog(level)); - } - - /** - * Logs a message and THEN the stacktrace of a throwable for the given level. - * - * @deprecated Directly use the object and throwable args method for the given level instead - e.g. - * {@link Log#error(Object, Throwable)} for the {@link Level#ERROR ERROR} level. - * - * @param level The level to log the message at - * @param throwable The throwable to log - */ - @Deprecated - public static void log(Level level, Object message, Throwable throwable) { - PrintStream log = getLog(level); - log.println(message); - throwable.printStackTrace(log); - } - - /** Represents an individual logging level. */ - public enum Level { - DEBUG, QUIET, INFO, WARN, ERROR, FATAL; - } - - /** - * Prints an empty message to the {@link Level#DEBUG DEBUG} level, similar to {@link PrintStream#println()}. - */ - public static void debug() { - DEBUG.println(); - } - - /** - * Logs a message for the {@link Level#DEBUG DEBUG} level. - * - * @param message The message to log - */ - public static void debug(String message) { - DEBUG.println(message); - } - - /** - * Logs a message for the {@link Level#DEBUG DEBUG} level. - * - * @param message The message to log - */ - public static void debug(Object message) { - DEBUG.println(message); - } - - /** - * Logs the stacktrace of a throwable for the {@link Level#DEBUG DEBUG} level. - * - * @param throwable The throwable to log - */ - public static void debug(Throwable throwable) { - throwable.printStackTrace(DEBUG); - } - - /** - * Logs a message and THEN the stacktrace of a throwable for the {@link Level#DEBUG DEBUG} level. - * - * @param message The message to log - * @param throwable The throwable to log - */ - public static void debug(String message, Throwable throwable) { - debug(message); - debug(throwable); - } - - /** - * Logs a message and THEN the stacktrace of a throwable for the {@link Level#DEBUG DEBUG} level. - * - * @param message The message to log - * @param throwable The throwable to log - */ - public static void debug(Object message, Throwable throwable) { - debug(message); - debug(throwable); - } - - /** - * Prints an empty message to the {@link Level#QUIET QUIET} level, similar to {@link PrintStream#println()}. - */ - public static void quiet() { - QUIET.println(); - } - - /** - * Logs a message for the {@link Level#QUIET QUIET} level. - * - * @param message The message to log - */ - public static void quiet(String message) { - QUIET.println(message); - } - - /** - * Logs a message for the {@link Level#QUIET QUIET} level. - * - * @param message The message to log - */ - public static void quiet(Object message) { - QUIET.println(message); - } - - /** - * Logs the stacktrace of a throwable for the {@link Level#QUIET QUIET} level. - * - * @param throwable The throwable to log - */ - public static void quiet(Throwable throwable) { - throwable.printStackTrace(QUIET); - } - - /** - * Logs a message and THEN the stacktrace of a throwable for the {@link Level#QUIET QUIET} level. - * - * @param message The message to log - * @param throwable The throwable to log - */ - public static void quiet(String message, Throwable throwable) { - quiet(message); - quiet(throwable); - } - - /** - * Logs a message and THEN the stacktrace of a throwable for the {@link Level#QUIET QUIET} level. - * - * @param message The message to log - * @param throwable The throwable to log - */ - public static void quiet(Object message, Throwable throwable) { - quiet(message); - quiet(throwable); - } - - /** - * Prints an empty message to the {@link Level#INFO INFO} level, similar to {@link PrintStream#println()}. - */ - public static void info() { - INFO.println(); - } - - /** - * Logs a message for the {@link Level#INFO INFO} level. - * - * @param message The message to log - */ - public static void info(String message) { - INFO.println(message); - } - - /** - * Logs a message for the {@link Level#INFO INFO} level. - * - * @param message The message to log - */ - public static void info(Object message) { - INFO.println(message); - } - - /** - * Logs the stacktrace of a throwable for the {@link Level#INFO INFO} level. - * - * @param throwable The throwable to log - */ - public static void info(Throwable throwable) { - throwable.printStackTrace(INFO); - } - - /** - * Logs a message and THEN the stacktrace of a throwable for the {@link Level#INFO INFO} level. - * - * @param message The message to log - * @param throwable The throwable to log - */ - public static void info(String message, Throwable throwable) { - info(message); - info(throwable); - } - - /** - * Logs a message and THEN the stacktrace of a throwable for the {@link Level#INFO INFO} level. - * - * @param message The message to log - * @param throwable The throwable to log - */ - public static void info(Object message, Throwable throwable) { - info(message); - info(throwable); - } - - /** - * Prints an empty message to the {@link Level#WARN WARN} level, similar to {@link PrintStream#println()}. - */ - public static void warn() { - WARN.println(); - } - - /** - * Logs a message for the {@link Level#WARN WARN} level. - * - * @param message The message to log - */ - public static void warn(String message) { - WARN.println(message); - } - - /** - * Logs a message for the {@link Level#WARN WARN} level. - * - * @param message The message to log - */ - public static void warn(Object message) { - WARN.println(message); - } - - /** - * Logs the stacktrace of a throwable for the {@link Level#WARN WARN} level. - * - * @param throwable The throwable to log - */ - public static void warn(Throwable throwable) { - throwable.printStackTrace(WARN); - } - - /** - * Logs a message and THEN the stacktrace of a throwable for the {@link Level#WARN WARN} level. - * - * @param message The message to log - * @param throwable The throwable to log - */ - public static void warn(String message, Throwable throwable) { - warn(message); - warn(throwable); - } - - /** - * Logs a message and THEN the stacktrace of a throwable for the {@link Level#WARN WARN} level. - * - * @param message The message to log - * @param throwable The throwable to log - */ - public static void warn(Object message, Throwable throwable) { - warn(message); - warn(throwable); - } - - /** - * Prints an empty message to the {@link Level#ERROR ERROR} level, similar to {@link PrintStream#println()}. - */ - public static void error() { - ERROR.println(); - } - - /** - * Logs a message for the {@link Level#ERROR ERROR} level. - * - * @param message The message to log - */ - public static void error(String message) { - ERROR.println(message); - } - - /** - * Logs a message for the {@link Level#ERROR ERROR} level. - * - * @param message The message to log - */ - public static void error(Object message) { - ERROR.println(message); - } - - /** - * Logs the stacktrace of a throwable for the {@link Level#ERROR ERROR} level. - * - * @param throwable The throwable to log - */ - public static void error(Throwable throwable) { - throwable.printStackTrace(ERROR); - } - - /** - * Logs a message and THEN the stacktrace of a throwable for the {@link Level#ERROR ERROR} level. - * - * @param message The message to log - * @param throwable The throwable to log - */ - public static void error(String message, Throwable throwable) { - error(message); - error(throwable); - } - - /** - * Logs a message and THEN the stacktrace of a throwable for the {@link Level#ERROR ERROR} level. - * - * @param message The message to log - * @param throwable The throwable to log - */ - public static void error(Object message, Throwable throwable) { - error(message); - error(throwable); - } - - /** - * Prints an empty message to the {@link Level#FATAL FATAL} level, similar to {@link PrintStream#println()}. - */ - public static void fatal() { - FATAL.println(); - } - - /** - * Logs a message for the {@link Level#FATAL FATAL} level. - * - * @param message The message to log - */ - public static void fatal(String message) { - FATAL.println(message); - } - - /** - * Logs a message for the {@link Level#FATAL FATAL} level. - * - * @param message The message to log - */ - public static void fatal(Object message) { - FATAL.println(message); - } - - /** - * Logs the stacktrace of a throwable for the {@link Level#FATAL FATAL} level. - * - * @param throwable The throwable to log - */ - public static void fatal(Throwable throwable) { - throwable.printStackTrace(FATAL); - } - - /** - * Logs a message and THEN the stacktrace of a throwable for the {@link Level#FATAL FATAL} level. - * - * @param message The message to log - * @param throwable The throwable to log - */ - public static void fatal(String message, Throwable throwable) { - fatal(message); - fatal(throwable); - } - - /** - * Logs a message and THEN the stacktrace of a throwable for the {@link Level#FATAL FATAL} level. - * - * @param message The message to log - * @param throwable The throwable to log - */ - public static void fatal(Object message, Throwable throwable) { - fatal(message); - fatal(throwable); - } - - private Log() { } -} diff --git a/log-utils/src/main/java/net/minecraftforge/util/logging/LogConsumer.java b/log-utils/src/main/java/net/minecraftforge/util/logging/LogConsumer.java index 636a374..f018465 100644 --- a/log-utils/src/main/java/net/minecraftforge/util/logging/LogConsumer.java +++ b/log-utils/src/main/java/net/minecraftforge/util/logging/LogConsumer.java @@ -7,22 +7,24 @@ import java.util.function.Consumer; final class LogConsumer implements Consumer { - private final Log.Level level; - private final Consumer logger; + private final AbstractLogger logger; + private final Logger.Level level; + private final Consumer output; - LogConsumer(Log.Level level, Consumer logger) { - this.level = level; + LogConsumer(AbstractLogger logger, Logger.Level level, Consumer output) { this.logger = logger; + this.level = level; + this.output = output; } private boolean isEnabled() { - return Log.enabled != null && this.level.compareTo(Log.enabled) >= 0; + return this.logger.isEnabled(this.level); } @Override - public void accept(String s) { + public void accept(String message) { if (!this.isEnabled()) return; - Log.tryCapture(this.logger, this.level, s); + this.logger.tryCapture(this.output, this.level, message); } } diff --git a/log-utils/src/main/java/net/minecraftforge/util/logging/Logger.java b/log-utils/src/main/java/net/minecraftforge/util/logging/Logger.java new file mode 100644 index 0000000..4e6c57d --- /dev/null +++ b/log-utils/src/main/java/net/minecraftforge/util/logging/Logger.java @@ -0,0 +1,562 @@ +/* + * Copyright (c) Forge Development LLC + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.util.logging; + +import org.jetbrains.annotations.Nullable; + +import java.io.PrintStream; +import java.util.function.BiConsumer; + +public interface Logger { + /* CREATION */ + + static Logger create() { + return new DefaultLogger(); + } + + static Logger create(byte indentLevel) { + return new DefaultLogger(indentLevel); + } + + static Logger create(Level enabled) { + return new DefaultLogger(enabled); + } + + static Logger create(Level enabled, byte indentLevel) { + return new DefaultLogger(enabled, indentLevel); + } + + /* LOG LEVELS */ + + /** Represents an individual logging level. */ + enum Level { + DEBUG, QUIET, INFO, WARN, ERROR, FATAL + } + + @Nullable Level getEnabled(); + + void setEnabled(@Nullable Level level); + + default boolean isEnabled(Level level) { + Level enabled = this.getEnabled(); + return enabled != null && level.compareTo(enabled) >= 0; + } + + /* INDENTATIONS */ + + byte push(); + + byte pop(); + + byte pop(byte indent); + + String getIndentation(); + + /* CAPTURING */ + + /** + * If the log is capturing log messages. + * + * @return The capturing state of the Log + * @see #capture() + */ + boolean isCapturing(); + + /** + * Begins capturing log messages. + *

When capturing, all log messages are stored in memory and not printed to the screen. They can either be + * {@linkplain #release() released} to be printed on the screen or {@linkplain #drop() dropped} to be removed and + * never printed.

+ */ + void capture(); + + /** + * Drops all captured log messages and stops capturing. + * + * @see #capture() + */ + void drop(); + + /** + * Releases all captured log messages (using {@link #log(Level, Object)}), then stops capturing. + * + * @see #capture() + * @see #release(BiConsumer) + */ + void release(); + + /** + * Releases all captured log messages into the given consumer, then stops capturing. + * + * @param consumer The consumer to release the captured log messages to + * @see #capture() + */ + void release(BiConsumer consumer); + + /* LOGGING */ + + /** + * Gets the {@linkplain PrintStream print stream} for the given {@linkplain Level level}. If the given level is not + * {@linkplain #isEnabled(Level) enabled} or is {@code null}, then an empty print stream is returned that ignores + * all method calls. + * + * @param level The level to get the print stream for + * @return The print stream + */ + default PrintStream getLog(@Nullable Level level) { + if (level == null) return AbstractLogger.EMPTY; + switch (level) { + case DEBUG: return getDebug(); + case QUIET: return getQuiet(); + case INFO: return getInfo(); + case WARN: return getWarn(); + case ERROR: return getError(); + case FATAL: return getFatal(); + } + + throw new IllegalStateException("Unexpected value: " + level); + } + + /** + * Prints an empty message to the given level, similar to {@link PrintStream#println()}. + * + * @param level The level to log the message at + */ + default void log(Level level) { + getLog(level).println(); + } + + /** + * Logs a message for the given level. + * + * @param level The level to log the message at + * @param message The message to log + */ + default void log(Level level, String message) { + getLog(level).println(message); + } + + /** + * Logs a message for the given level. + * + * @param level The level to log the message at + * @param message The message to log + */ + default void log(Level level, Object message) { + getLog(level).println(message); + } + + /** + * Logs the stacktrace of a throwable for the given level. + * + * @param level The level to log the message at + * @param throwable The throwable to log + */ + default void log(Level level, Throwable throwable) { + throwable.printStackTrace(getLog(level)); + } + + /** + * Logs a message and THEN the stacktrace of a throwable for the given level. + * + * @param level The level to log the message at + * @param throwable The throwable to log + */ + default void log(Level level, Object message, Throwable throwable) { + PrintStream log = getLog(level); + log.println(message); + throwable.printStackTrace(log); + } + + /** + * Logs a message and THEN the stacktrace of a throwable for the given level. + * + * @param level The level to log the message at + * @param throwable The throwable to log + */ + default void log(Level level, String message, Throwable throwable) { + PrintStream log = getLog(level); + log.println(message); + throwable.printStackTrace(log); + } + + /** + * Gets the {@linkplain PrintStream print stream} for {@link Level#DEBUG}. + * + * @return The print stream + */ + PrintStream getDebug(); + + /** + * Prints an empty message to the {@link Level#DEBUG DEBUG} level, similar to {@link PrintStream#println()}. + */ + default void debug() { + this.getDebug().println(); + } + + /** + * Logs a message for the {@link Level#DEBUG DEBUG} level. + * + * @param message The message to log + */ + default void debug(String message) { + this.getDebug().println(message); + } + + /** + * Logs a message for the {@link Level#DEBUG DEBUG} level. + * + * @param message The message to log + */ + default void debug(Object message) { + this.getDebug().println(message); + } + + /** + * Logs the stacktrace of a throwable for the {@link Level#DEBUG DEBUG} level. + * + * @param throwable The throwable to log + */ + default void debug(Throwable throwable) { + throwable.printStackTrace(this.getDebug()); + } + + /** + * Logs a message and THEN the stacktrace of a throwable for the {@link Level#DEBUG DEBUG} level. + * + * @param message The message to log + * @param throwable The throwable to log + */ + default void debug(String message, Throwable throwable) { + debug(message); + debug(throwable); + } + + /** + * Logs a message and THEN the stacktrace of a throwable for the {@link Level#DEBUG DEBUG} level. + * + * @param message The message to log + * @param throwable The throwable to log + */ + default void debug(Object message, Throwable throwable) { + debug(message); + debug(throwable); + } + + /** + * Gets the {@linkplain PrintStream print stream} for {@link Level#QUIET}. + * + * @return The print stream + */ + PrintStream getQuiet(); + + /** + * Prints an empty message to the {@link Level#QUIET QUIET} level, similar to {@link PrintStream#println()}. + */ + default void quiet() { + this.getQuiet().println(); + } + + /** + * Logs a message for the {@link Level#QUIET QUIET} level. + * + * @param message The message to log + */ + default void quiet(String message) { + this.getQuiet().println(message); + } + + /** + * Logs a message for the {@link Level#QUIET QUIET} level. + * + * @param message The message to log + */ + default void quiet(Object message) { + this.getQuiet().println(message); + } + + /** + * Logs the stacktrace of a throwable for the {@link Level#QUIET QUIET} level. + * + * @param throwable The throwable to log + */ + default void quiet(Throwable throwable) { + throwable.printStackTrace(this.getQuiet()); + } + + /** + * Logs a message and THEN the stacktrace of a throwable for the {@link Level#QUIET QUIET} level. + * + * @param message The message to log + * @param throwable The throwable to log + */ + default void quiet(String message, Throwable throwable) { + quiet(message); + quiet(throwable); + } + + /** + * Logs a message and THEN the stacktrace of a throwable for the {@link Level#QUIET QUIET} level. + * + * @param message The message to log + * @param throwable The throwable to log + */ + default void quiet(Object message, Throwable throwable) { + quiet(message); + quiet(throwable); + } + + /** + * Gets the {@linkplain PrintStream print stream} for {@link Level#INFO}. + * + * @return The print stream + */ + PrintStream getInfo(); + + /** + * Prints an empty message to the {@link Level#INFO INFO} level, similar to {@link PrintStream#println()}. + */ + default void info() { + this.getInfo().println(); + } + + /** + * Logs a message for the {@link Level#INFO INFO} level. + * + * @param message The message to log + */ + default void info(String message) { + this.getInfo().println(message); + } + + /** + * Logs a message for the {@link Level#INFO INFO} level. + * + * @param message The message to log + */ + default void info(Object message) { + this.getInfo().println(message); + } + + /** + * Logs the stacktrace of a throwable for the {@link Level#INFO INFO} level. + * + * @param throwable The throwable to log + */ + default void info(Throwable throwable) { + throwable.printStackTrace(this.getInfo()); + } + + /** + * Logs a message and THEN the stacktrace of a throwable for the {@link Level#INFO INFO} level. + * + * @param message The message to log + * @param throwable The throwable to log + */ + default void info(String message, Throwable throwable) { + info(message); + info(throwable); + } + + /** + * Logs a message and THEN the stacktrace of a throwable for the {@link Level#INFO INFO} level. + * + * @param message The message to log + * @param throwable The throwable to log + */ + default void info(Object message, Throwable throwable) { + info(message); + info(throwable); + } + + /** + * Gets the {@linkplain PrintStream print stream} for {@link Level#WARN}. + * + * @return The print stream + */ + PrintStream getWarn(); + + /** + * Prints an empty message to the {@link Level#WARN WARN} level, similar to {@link PrintStream#println()}. + */ + default void warn() { + this.getWarn().println(); + } + + /** + * Logs a message for the {@link Level#WARN WARN} level. + * + * @param message The message to log + */ + default void warn(String message) { + this.getWarn().println(message); + } + + /** + * Logs a message for the {@link Level#WARN WARN} level. + * + * @param message The message to log + */ + default void warn(Object message) { + this.getWarn().println(message); + } + + /** + * Logs the stacktrace of a throwable for the {@link Level#WARN WARN} level. + * + * @param throwable The throwable to log + */ + default void warn(Throwable throwable) { + throwable.printStackTrace(this.getWarn()); + } + + /** + * Logs a message and THEN the stacktrace of a throwable for the {@link Level#WARN WARN} level. + * + * @param message The message to log + * @param throwable The throwable to log + */ + default void warn(String message, Throwable throwable) { + warn(message); + warn(throwable); + } + + /** + * Logs a message and THEN the stacktrace of a throwable for the {@link Level#WARN WARN} level. + * + * @param message The message to log + * @param throwable The throwable to log + */ + default void warn(Object message, Throwable throwable) { + warn(message); + warn(throwable); + } + + /** + * Gets the {@linkplain PrintStream print stream} for {@link Level#ERROR}. + * + * @return The print stream + */ + PrintStream getError(); + + /** + * Prints an empty message to the {@link Level#ERROR ERROR} level, similar to {@link PrintStream#println()}. + */ + default void error() { + this.getError().println(); + } + + /** + * Logs a message for the {@link Level#ERROR ERROR} level. + * + * @param message The message to log + */ + default void error(String message) { + this.getError().println(message); + } + + /** + * Logs a message for the {@link Level#ERROR ERROR} level. + * + * @param message The message to log + */ + default void error(Object message) { + this.getError().println(message); + } + + /** + * Logs the stacktrace of a throwable for the {@link Level#ERROR ERROR} level. + * + * @param throwable The throwable to log + */ + default void error(Throwable throwable) { + throwable.printStackTrace(this.getError()); + } + + /** + * Logs a message and THEN the stacktrace of a throwable for the {@link Level#ERROR ERROR} level. + * + * @param message The message to log + * @param throwable The throwable to log + */ + default void error(String message, Throwable throwable) { + error(message); + error(throwable); + } + + /** + * Logs a message and THEN the stacktrace of a throwable for the {@link Level#ERROR ERROR} level. + * + * @param message The message to log + * @param throwable The throwable to log + */ + default void error(Object message, Throwable throwable) { + error(message); + error(throwable); + } + + /** + * Gets the {@linkplain PrintStream print stream} for {@link Level#FATAL}. + * + * @return The print stream + */ + PrintStream getFatal(); + + /** + * Prints an empty message to the {@link Level#FATAL FATAL} level, similar to {@link PrintStream#println()}. + */ + default void fatal() { + this.getFatal().println(); + } + + /** + * Logs a message for the {@link Level#FATAL FATAL} level. + * + * @param message The message to log + */ + default void fatal(String message) { + this.getFatal().println(message); + } + + /** + * Logs a message for the {@link Level#FATAL FATAL} level. + * + * @param message The message to log + */ + default void fatal(Object message) { + this.getFatal().println(message); + } + + /** + * Logs the stacktrace of a throwable for the {@link Level#FATAL FATAL} level. + * + * @param throwable The throwable to log + */ + default void fatal(Throwable throwable) { + throwable.printStackTrace(this.getFatal()); + } + + /** + * Logs a message and THEN the stacktrace of a throwable for the {@link Level#FATAL FATAL} level. + * + * @param message The message to log + * @param throwable The throwable to log + */ + default void fatal(String message, Throwable throwable) { + fatal(message); + fatal(throwable); + } + + /** + * Logs a message and THEN the stacktrace of a throwable for the {@link Level#FATAL FATAL} level. + * + * @param message The message to log + * @param throwable The throwable to log + */ + default void fatal(Object message, Throwable throwable) { + fatal(message); + fatal(throwable); + } +} diff --git a/log-utils/src/test/java/Main.java b/log-utils/src/test/java/Main.java index 26fef6e..8be9b8f 100644 --- a/log-utils/src/test/java/Main.java +++ b/log-utils/src/test/java/Main.java @@ -2,18 +2,22 @@ * Copyright (c) Forge Development LLC * SPDX-License-Identifier: LGPL-2.1-only */ -import net.minecraftforge.util.logging.Log; +import net.minecraftforge.util.logging.Logger; public class Main { public static void main(String[] args) { - Log.info("Hello, World!"); - Log.push(); - Log.info("Hello, World!"); - Log.push(); - Log.info("Hello, World!"); - Log.pop(); - Log.info("Hello, World!"); - Log.pop(); - Log.info("Hello, World!"); + long start = System.currentTimeMillis(); + Logger log = Logger.create(); + + log.info("Hello, World!"); + log.push(); + log.info("Hello, World!"); + log.push(); + log.info("Hello, World!"); + log.pop(); + log.info("Hello, World!"); + log.pop(); + log.info("Hello, World!"); + System.out.println("Time: " + (System.currentTimeMillis() - start)); } }