From e8f928515be5bbb4a7dd76aae76c50bd2b86690f Mon Sep 17 00:00:00 2001 From: javasabr Date: Sun, 31 May 2026 15:38:19 +0200 Subject: [PATCH 1/8] add bridge slf4j to rlib logger --- .../java/javasabr/rlib/logger/api/Logger.java | 91 ++++- .../javasabr/rlib/logger/api/LoggerLevel.java | 3 +- .../rlib/logger/api/impl/NullLogger.java | 9 + .../javasabr/rlib/logger/api/LoggerTest.java | 79 +++++ .../rlib/logger/impl/DefaultLogger.java | 18 +- rlib-logger-slf4j-impl/build.gradle | 11 + .../slf4j/impl/SLF4JServiceProviderImpl.java | 47 +++ .../slf4j/impl/Slf4jLoggerFactoryImpl.java | 13 + .../logger/slf4j/impl/Slf4jLoggerImpl.java | 318 ++++++++++++++++++ .../rlib/logger/slf4j/impl/package-info.java | 4 + .../org.slf4j.spi.SLF4JServiceProvider | 1 + .../rlib/logger/slf4j/Slf4jLogger.java | 32 +- settings.gradle | 1 + test-coverage/build.gradle | 1 + 14 files changed, 614 insertions(+), 14 deletions(-) create mode 100644 rlib-logger-slf4j-impl/build.gradle create mode 100644 rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/SLF4JServiceProviderImpl.java create mode 100644 rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerFactoryImpl.java create mode 100644 rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImpl.java create mode 100644 rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/package-info.java create mode 100644 rlib-logger-slf4j-impl/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/Logger.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/Logger.java index 5fe3ecd8..f93f3284 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/Logger.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/Logger.java @@ -198,7 +198,36 @@ interface N4Factory { @NonNull String make(A arg1, B arg2, C arg3, D arg4); } + + /** + * Returns the name of this logger. + * + * @return the logger name + * @since 10.0.0 + */ + @NonNull String name(); + /** + * Prints a trace message. + * + * @param message the message to print + * @since 10.0.0 + */ + default void trace(@NonNull String message) { + print(LoggerLevel.TRACE, message); + } + + /** + * Prints a trace message with an exception. + * + * @param message the message to print + * @param exception the exception to print + * @since 10.0.0 + */ + default void trace(@NonNull String message, @NonNull Throwable exception) { + print(LoggerLevel.TRACE, message, exception); + } + /** * Prints a debug message. * @@ -209,6 +238,17 @@ default void debug(@NonNull String message) { print(LoggerLevel.DEBUG, message); } + /** + * Prints a debug message with an exception. + * + * @param message the message to print + * @param exception the exception to print + * @since 10.0.0 + */ + default void debug(@NonNull String message, @NonNull Throwable exception) { + print(LoggerLevel.DEBUG, message, exception); + } + default void debug(int arg1, @NonNull IntFactory factory) { print(LoggerLevel.DEBUG, arg1, factory); } @@ -261,6 +301,21 @@ default void debug(A arg1, int arg2, int arg3, @NonNull N1Int2Factory fac default void error(@NonNull String message) { print(LoggerLevel.ERROR, message); } + + default void error(@NonNull Throwable exception) { + print(LoggerLevel.ERROR, exception); + } + + /** + * Prints an error message with an exception. + * + * @param message the message to print + * @param exception the exception to print + * @since 10.0.0 + */ + default void error(@NonNull String message, @NonNull Throwable exception) { + print(LoggerLevel.ERROR, message, exception); + } default void error(A arg1, @NonNull N1Factory factory) { print(LoggerLevel.ERROR, arg1, factory); @@ -310,14 +365,21 @@ default void error(A arg1, B arg2, C arg3, D arg4, @NonNull N4Facto print(LoggerLevel.ERROR, arg1, arg2, arg3, arg4, factory); } - default void error(@NonNull Throwable exception) { - print(LoggerLevel.ERROR, exception); - } - default void info(@NonNull String message) { print(LoggerLevel.INFO, message); } + /** + * Prints an info message with an exception. + * + * @param message the message to print + * @param exception the exception to print + * @since 10.0.0 + */ + default void info(@NonNull String message, @NonNull Throwable exception) { + print(LoggerLevel.INFO, message, exception); + } + default void info(int arg1, @NonNull IntFactory factory) { print(LoggerLevel.INFO, arg1, factory); } @@ -399,6 +461,17 @@ default void warn(@NonNull String message) { print(LoggerLevel.WARNING, message); } + /** + * Prints a warning message with an exception. + * + * @param message the message to print + * @param exception the exception to print + * @since 10.0.0 + */ + default void warn(@NonNull String message, @NonNull Throwable exception) { + print(LoggerLevel.WARNING, message, exception); + } + @Deprecated(forRemoval = true) default void warning(@NonNull String message) { print(LoggerLevel.WARNING, message); @@ -582,6 +655,16 @@ default void warning( void print(@NonNull LoggerLevel level, @NonNull Throwable exception); + /** + * Prints a message with an exception at the specified level. + * + * @param level the logger level + * @param message the message to print + * @param exception the exception to print + * @since 10.0.0 + */ + void print(@NonNull LoggerLevel level, @NonNull String message, @NonNull Throwable exception); + default void print(@NonNull LoggerLevel level, A arg1, @NonNull N1Factory factory) { if (enabled(level)) { print(level, factory.make(arg1)); diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerLevel.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerLevel.java index 71863c28..22c94bd9 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerLevel.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerLevel.java @@ -16,8 +16,9 @@ @Accessors(fluent = true) @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) public enum LoggerLevel { - INFO("INFO", " ", true, true), + TRACE("TRACE", " ", false, false), DEBUG("DEBUG", " ", false, false), + INFO("INFO", " ", true, true), WARNING("WARN", " ", true, true), ERROR("ERROR", " ", true, true); diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NullLogger.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NullLogger.java index 3e50da07..ae1be7cc 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NullLogger.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NullLogger.java @@ -2,12 +2,21 @@ import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerLevel; +import org.jspecify.annotations.NonNull; public final class NullLogger implements Logger { + + @Override + public String name() { + return "null"; + } @Override public void print(LoggerLevel level, String message) {} @Override public void print(LoggerLevel level, Throwable exception) {} + + @Override + public void print(LoggerLevel level, String message, Throwable exception) {} } diff --git a/rlib-logger-api/src/test/java/javasabr/rlib/logger/api/LoggerTest.java b/rlib-logger-api/src/test/java/javasabr/rlib/logger/api/LoggerTest.java index 044e4ba4..74d72046 100644 --- a/rlib-logger-api/src/test/java/javasabr/rlib/logger/api/LoggerTest.java +++ b/rlib-logger-api/src/test/java/javasabr/rlib/logger/api/LoggerTest.java @@ -10,6 +10,49 @@ public class LoggerTest { + @Test + @DisplayName("should print trace message and trace message with exception") + void shouldPrintTraceMethods() { + // given: + var messages = new ArrayList(); + var exception = new RuntimeException("trace-error"); + + Logger logger = new Logger() { + + @Override + public String name() { + return "test"; + } + + @Override + public boolean enabled(@NonNull LoggerLevel level) { + return true; + } + + @Override + public void print(@NonNull LoggerLevel level, @NonNull String message) { + messages.add(level + "_" + message); + } + + @Override + public void print(@NonNull LoggerLevel level, @NonNull Throwable exception) { + } + + @Override + public void print(@NonNull LoggerLevel level, @NonNull String message, @NonNull Throwable exception) { + messages.add(level + "_" + message + "_" + exception.getMessage()); + } + }; + + // when: + logger.trace("msg1"); + logger.trace("msg2", exception); + + // then: + assertThat(messages) + .isEqualTo(List.of("TRACE_msg1", "TRACE_msg2_trace-error")); + } + @Test @DisplayName("should print all debug methods") void shouldPrintAllDebugMethods() { @@ -31,6 +74,11 @@ void shouldPrintAllDebugMethods() { Logger logger = new Logger() { + @Override + public String name() { + return "test"; + } + @Override public boolean enabled(@NonNull LoggerLevel level) { return true; @@ -44,6 +92,10 @@ public void print(@NonNull LoggerLevel level, @NonNull String message) { @Override public void print(@NonNull LoggerLevel level, @NonNull Throwable exception) { } + + @Override + public void print(@NonNull LoggerLevel level, @NonNull String message, @NonNull Throwable exception) { + } }; // when: @@ -85,6 +137,11 @@ void shouldPrintAllInfoMethods() { Logger logger = new Logger() { + @Override + public String name() { + return "test"; + } + @Override public boolean enabled(@NonNull LoggerLevel level) { return true; @@ -98,6 +155,10 @@ public void print(@NonNull LoggerLevel level, @NonNull String message) { @Override public void print(@NonNull LoggerLevel level, @NonNull Throwable exception) { } + + @Override + public void print(@NonNull LoggerLevel level, @NonNull String message, @NonNull Throwable exception) { + } }; // when: @@ -139,6 +200,11 @@ void shouldPrintAllWarningMethods() { Logger logger = new Logger() { + @Override + public String name() { + return "test"; + } + @Override public boolean enabled(@NonNull LoggerLevel level) { return true; @@ -152,6 +218,10 @@ public void print(@NonNull LoggerLevel level, @NonNull String message) { @Override public void print(@NonNull LoggerLevel level, @NonNull Throwable exception) { } + + @Override + public void print(@NonNull LoggerLevel level, @NonNull String message, @NonNull Throwable exception) { + } }; // when: @@ -193,6 +263,11 @@ void shouldPrintAllErrorMethods() { Logger logger = new Logger() { + @Override + public String name() { + return "test"; + } + @Override public boolean enabled(@NonNull LoggerLevel level) { return true; @@ -206,6 +281,10 @@ public void print(@NonNull LoggerLevel level, @NonNull String message) { @Override public void print(@NonNull LoggerLevel level, @NonNull Throwable exception) { } + + @Override + public void print(@NonNull LoggerLevel level, @NonNull String message, @NonNull Throwable exception) { + } }; // when: diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLogger.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLogger.java index 8b7d14e5..fbff2aae 100644 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLogger.java +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLogger.java @@ -7,15 +7,14 @@ import javasabr.rlib.logger.api.LoggerService; import lombok.AccessLevel; import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.NonNull; /** * @author JavaSaBr */ @FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) public final class DefaultLogger implements Logger { - - private static final LoggerLevel[] VALUES = LoggerLevel.values(); - + int[] override; String name; LoggerService loggerService; @@ -27,6 +26,11 @@ public DefaultLogger(String name, LoggerService loggerService) { Arrays.fill(override, LoggerService.NOT_CONFIGURE); } + @Override + public String name() { + return name; + } + @Override public boolean enabled(LoggerLevel level) { int value = override[level.ordinal()]; @@ -63,4 +67,12 @@ public void print(LoggerLevel level, Throwable exception) { loggerService.write(level, name, StringUtils.toString(exception)); } } + + @Override + public void print(LoggerLevel level, String message, Throwable exception) { + if (enabled(level)) { + String exceptionInfo = StringUtils.toString(exception); + loggerService.write(level, name, message + ": " + exceptionInfo); + } + } } diff --git a/rlib-logger-slf4j-impl/build.gradle b/rlib-logger-slf4j-impl/build.gradle new file mode 100644 index 00000000..08ad7c5e --- /dev/null +++ b/rlib-logger-slf4j-impl/build.gradle @@ -0,0 +1,11 @@ +plugins { + id("configure-java") + id("configure-publishing") +} + +dependencies { + api projects.rlibLoggerApi + api libs.slf4j.api + + testRuntime libs.slf4j.simple +} diff --git a/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/SLF4JServiceProviderImpl.java b/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/SLF4JServiceProviderImpl.java new file mode 100644 index 00000000..e3b1a1d0 --- /dev/null +++ b/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/SLF4JServiceProviderImpl.java @@ -0,0 +1,47 @@ +package javasabr.rlib.logger.slf4j.impl; + +import org.jspecify.annotations.Nullable; +import org.slf4j.ILoggerFactory; +import org.slf4j.IMarkerFactory; +import org.slf4j.helpers.BasicMarkerFactory; +import org.slf4j.helpers.NOPMDCAdapter; +import org.slf4j.spi.MDCAdapter; +import org.slf4j.spi.SLF4JServiceProvider; + +public class SLF4JServiceProviderImpl implements SLF4JServiceProvider { + + public static final String REQUESTED_API_VERSION = "2.0.17"; + + private final IMarkerFactory markerFactory = new BasicMarkerFactory(); + private final MDCAdapter mdcAdapter = new NOPMDCAdapter(); + @Nullable + private ILoggerFactory loggerFactory; + + @Override + public ILoggerFactory getLoggerFactory() { + if (loggerFactory == null) { + throw new IllegalStateException("SLF4JServiceProviderImpl is not initialized yet"); + } + return loggerFactory; + } + + @Override + public IMarkerFactory getMarkerFactory() { + return markerFactory; + } + + @Override + public MDCAdapter getMDCAdapter() { + return mdcAdapter; + } + + @Override + public String getRequestedApiVersion() { + return REQUESTED_API_VERSION; + } + + @Override + public void initialize() { + this.loggerFactory = new Slf4jLoggerFactoryImpl(); + } +} diff --git a/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerFactoryImpl.java b/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerFactoryImpl.java new file mode 100644 index 00000000..76abbc5a --- /dev/null +++ b/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerFactoryImpl.java @@ -0,0 +1,13 @@ +package javasabr.rlib.logger.slf4j.impl; + +import javasabr.rlib.logger.api.LoggerManager; +import org.slf4j.ILoggerFactory; +import org.slf4j.Logger; + +public class Slf4jLoggerFactoryImpl implements ILoggerFactory { + + @Override + public Logger getLogger(String name) { + return new Slf4jLoggerImpl(LoggerManager.getLogger(name)); + } +} diff --git a/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImpl.java b/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImpl.java new file mode 100644 index 00000000..e3c625de --- /dev/null +++ b/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImpl.java @@ -0,0 +1,318 @@ +package javasabr.rlib.logger.slf4j.impl; + +import javasabr.rlib.logger.api.Logger; +import javasabr.rlib.logger.api.LoggerLevel; +import lombok.RequiredArgsConstructor; +import org.slf4j.Marker; +import org.slf4j.helpers.MessageFormatter; + +@RequiredArgsConstructor +public class Slf4jLoggerImpl implements org.slf4j.Logger { + + private final Logger logger; + + @Override + public String getName() { + return logger.name(); + } + + @Override + public boolean isTraceEnabled() { + return logger.enabled(LoggerLevel.TRACE); + } + + @Override + public void trace(String msg) { + logger.trace(msg); + } + + @Override + public void trace(String format, Object arg) { + logger.trace(MessageFormatter.format(format, arg).getMessage()); + } + + @Override + public void trace(String format, Object arg1, Object arg2) { + logger.trace(MessageFormatter.format(format, arg1, arg2).getMessage()); + } + + @Override + public void trace(String format, Object... arguments) { + logger.trace(MessageFormatter.format(format, arguments).getMessage()); + } + + @Override + public void trace(String msg, Throwable exception) { + logger.trace(msg, exception); + } + + @Override + public boolean isTraceEnabled(Marker marker) { + return logger.enabled(LoggerLevel.TRACE); + } + + @Override + public void trace(Marker marker, String msg) { + logger.trace(msg); + } + + @Override + public void trace(Marker marker, String format, Object arg) { + logger.trace(MessageFormatter.format(format, arg).getMessage()); + } + + @Override + public void trace(Marker marker, String format, Object arg1, Object arg2) { + logger.trace(MessageFormatter.format(format, arg1, arg2).getMessage()); + } + + @Override + public void trace(Marker marker, String format, Object... arguments) { + logger.trace(MessageFormatter.format(format, arguments).getMessage()); + } + + @Override + public void trace(Marker marker, String msg, Throwable exception) { + logger.trace(msg, exception); + } + + @Override + public boolean isDebugEnabled() { + return logger.enabled(LoggerLevel.DEBUG); + } + + @Override + public void debug(String msg) { + logger.debug(msg); + } + + @Override + public void debug(String format, Object arg) { + logger.debug(MessageFormatter.format(format, arg).getMessage()); + } + + @Override + public void debug(String format, Object arg1, Object arg2) { + logger.debug(MessageFormatter.format(format, arg1, arg2).getMessage()); + } + + @Override + public void debug(String format, Object... arguments) { + logger.debug(MessageFormatter.format(format, arguments).getMessage()); + } + + @Override + public void debug(String msg, Throwable exception) { + logger.debug(msg, exception); + } + + @Override + public boolean isDebugEnabled(Marker marker) { + return logger.enabled(LoggerLevel.DEBUG); + } + + @Override + public void debug(Marker marker, String msg) { + logger.debug(msg); + } + + @Override + public void debug(Marker marker, String format, Object arg) { + logger.debug(MessageFormatter.format(format, arg).getMessage()); + } + + @Override + public void debug(Marker marker, String format, Object arg1, Object arg2) { + logger.debug(MessageFormatter.format(format, arg1, arg2).getMessage()); + } + + @Override + public void debug(Marker marker, String format, Object... arguments) { + logger.debug(MessageFormatter.format(format, arguments).getMessage()); + } + + @Override + public void debug(Marker marker, String msg, Throwable exception) { + logger.debug(msg, exception); + } + + @Override + public boolean isInfoEnabled() { + return logger.enabled(LoggerLevel.INFO); + } + + @Override + public void info(String msg) { + logger.info(msg); + } + + @Override + public void info(String format, Object arg) { + logger.info(MessageFormatter.format(format, arg).getMessage()); + } + + @Override + public void info(String format, Object arg1, Object arg2) { + logger.info(MessageFormatter.format(format, arg1, arg2).getMessage()); + } + + @Override + public void info(String format, Object... arguments) { + logger.info(MessageFormatter.format(format, arguments).getMessage()); + } + + @Override + public void info(String msg, Throwable exception) { + logger.info(msg, exception); + } + + @Override + public boolean isInfoEnabled(Marker marker) { + return logger.enabled(LoggerLevel.INFO); + } + + @Override + public void info(Marker marker, String msg) { + logger.info(msg); + } + + @Override + public void info(Marker marker, String format, Object arg) { + logger.info(MessageFormatter.format(format, arg).getMessage()); + } + + @Override + public void info(Marker marker, String format, Object arg1, Object arg2) { + logger.info(MessageFormatter.format(format, arg1, arg2).getMessage()); + } + + @Override + public void info(Marker marker, String format, Object... arguments) { + logger.info(MessageFormatter.format(format, arguments).getMessage()); + } + + @Override + public void info(Marker marker, String msg, Throwable exception) { + logger.info(msg, exception); + } + + @Override + public boolean isWarnEnabled() { + return logger.enabled(LoggerLevel.WARNING); + } + + @Override + public void warn(String msg) { + logger.warn(msg); + } + + @Override + public void warn(String format, Object arg) { + logger.warn(MessageFormatter.format(format, arg).getMessage()); + } + + @Override + public void warn(String format, Object arg1, Object arg2) { + logger.warn(MessageFormatter.format(format, arg1, arg2).getMessage()); + } + + @Override + public void warn(String format, Object... arguments) { + logger.warn(MessageFormatter.format(format, arguments).getMessage()); + } + + @Override + public void warn(String msg, Throwable exception) { + logger.warn(msg, exception); + } + + @Override + public boolean isWarnEnabled(Marker marker) { + return logger.enabled(LoggerLevel.WARNING); + } + + @Override + public void warn(Marker marker, String msg) { + logger.warn(msg); + } + + @Override + public void warn(Marker marker, String format, Object arg) { + logger.warn(MessageFormatter.format(format, arg).getMessage()); + } + + @Override + public void warn(Marker marker, String format, Object arg1, Object arg2) { + logger.warn(MessageFormatter.format(format, arg1, arg2).getMessage()); + } + + @Override + public void warn(Marker marker, String format, Object... arguments) { + logger.warn(MessageFormatter.format(format, arguments).getMessage()); + } + + @Override + public void warn(Marker marker, String msg, Throwable exception) { + logger.warn(msg, exception); + } + + @Override + public boolean isErrorEnabled() { + return logger.enabled(LoggerLevel.ERROR); + } + + @Override + public void error(String msg) { + logger.error(msg); + } + + @Override + public void error(String format, Object arg) { + logger.error(MessageFormatter.format(format, arg).getMessage()); + } + + @Override + public void error(String format, Object arg1, Object arg2) { + logger.error(MessageFormatter.format(format, arg1, arg2).getMessage()); + } + + @Override + public void error(String format, Object... arguments) { + logger.error(MessageFormatter.format(format, arguments).getMessage()); + } + + @Override + public void error(String msg, Throwable exception) { + logger.error(msg, exception); + } + + @Override + public boolean isErrorEnabled(Marker marker) { + return logger.enabled(LoggerLevel.ERROR); + } + + @Override + public void error(Marker marker, String msg) { + logger.error(msg); + } + + @Override + public void error(Marker marker, String format, Object arg) { + logger.error(MessageFormatter.format(format, arg).getMessage()); + } + + @Override + public void error(Marker marker, String format, Object arg1, Object arg2) { + logger.error(MessageFormatter.format(format, arg1, arg2).getMessage()); + } + + @Override + public void error(Marker marker, String format, Object... arguments) { + logger.error(MessageFormatter.format(format, arguments).getMessage()); + } + + @Override + public void error(Marker marker, String msg, Throwable exception) { + logger.error(msg, exception); + } +} diff --git a/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/package-info.java b/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/package-info.java new file mode 100644 index 00000000..0f43b229 --- /dev/null +++ b/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package javasabr.rlib.logger.slf4j.impl; + +import org.jspecify.annotations.NullMarked; diff --git a/rlib-logger-slf4j-impl/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider b/rlib-logger-slf4j-impl/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider new file mode 100644 index 00000000..6236bf9b --- /dev/null +++ b/rlib-logger-slf4j-impl/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider @@ -0,0 +1 @@ +javasabr.rlib.logger.slf4j.impl.SLF4JServiceProviderImpl diff --git a/rlib-logger-slf4j/src/main/java/javasabr/rlib/logger/slf4j/Slf4jLogger.java b/rlib-logger-slf4j/src/main/java/javasabr/rlib/logger/slf4j/Slf4jLogger.java index c3729313..02bafda0 100644 --- a/rlib-logger-slf4j/src/main/java/javasabr/rlib/logger/slf4j/Slf4jLogger.java +++ b/rlib-logger-slf4j/src/main/java/javasabr/rlib/logger/slf4j/Slf4jLogger.java @@ -3,39 +3,59 @@ import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerLevel; import lombok.RequiredArgsConstructor; +import org.jspecify.annotations.NonNull; @RequiredArgsConstructor public class Slf4jLogger implements Logger { private final org.slf4j.Logger logger; + @Override + public String name() { + return logger.getName(); + } + @Override public boolean enabled(LoggerLevel level) { return switch (level) { - case INFO -> logger.isInfoEnabled(); + case TRACE -> logger.isTraceEnabled(); case DEBUG -> logger.isDebugEnabled(); - case ERROR -> logger.isErrorEnabled(); + case INFO -> logger.isInfoEnabled(); case WARNING -> logger.isWarnEnabled(); + case ERROR -> logger.isErrorEnabled(); }; } @Override public void print(LoggerLevel level, String message) { switch (level) { - case INFO -> logger.info(message); + case TRACE -> logger.trace(message); case DEBUG -> logger.debug(message); - case ERROR -> logger.error(message); + case INFO -> logger.info(message); case WARNING -> logger.warn(message); + case ERROR -> logger.error(message); } } @Override public void print(LoggerLevel level, Throwable exception) { switch (level) { - case INFO -> logger.info(exception.getMessage(), exception); + case TRACE -> logger.trace(exception.getMessage(), exception); case DEBUG -> logger.debug(exception.getMessage(), exception); - case ERROR -> logger.error(exception.getMessage(), exception); + case INFO -> logger.info(exception.getMessage(), exception); case WARNING -> logger.warn(exception.getMessage(), exception); + case ERROR -> logger.error(exception.getMessage(), exception); + } + } + + @Override + public void print(LoggerLevel level, String message, Throwable exception) { + switch (level) { + case TRACE -> logger.trace(message, exception); + case DEBUG -> logger.debug(message, exception); + case INFO -> logger.info(message, exception); + case WARNING -> logger.warn(message, exception); + case ERROR -> logger.error(message, exception); } } } diff --git a/settings.gradle b/settings.gradle index b5affd2b..ed51c8af 100644 --- a/settings.gradle +++ b/settings.gradle @@ -21,4 +21,5 @@ include ':rlib-reusable' include ':rlib-reference' include ':rlib-concurrent' include ':rlib-eventbus' +include ':rlib-logger-slf4j-impl' include ':test-coverage' diff --git a/test-coverage/build.gradle b/test-coverage/build.gradle index ac2ca37b..307e3b7a 100644 --- a/test-coverage/build.gradle +++ b/test-coverage/build.gradle @@ -14,6 +14,7 @@ dependencies { jacocoAggregation projects.rlibLoggerApi jacocoAggregation projects.rlibLoggerImpl jacocoAggregation projects.rlibLoggerSlf4j + jacocoAggregation projects.rlibLoggerSlf4jImpl jacocoAggregation projects.rlibMail jacocoAggregation projects.rlibNetwork jacocoAggregation projects.rlibPluginSystem From caf386b3efb9e21213cc75dd0f33946ccb745978 Mon Sep 17 00:00:00 2001 From: javasabr Date: Sun, 31 May 2026 15:48:13 +0200 Subject: [PATCH 2/8] start working on customizing logger --- .../rlib/logger/api/LoggerMessageRender.java | 6 +++++ .../api/impl/SimpleLoggerMessageRender.java | 22 +++++++++++++++++++ .../impl/config/LoggerConfiguration.java | 6 +++++ 3 files changed, 34 insertions(+) create mode 100644 rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerMessageRender.java create mode 100644 rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/SimpleLoggerMessageRender.java create mode 100644 rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfiguration.java diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerMessageRender.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerMessageRender.java new file mode 100644 index 00000000..1926922a --- /dev/null +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerMessageRender.java @@ -0,0 +1,6 @@ +package javasabr.rlib.logger.api; + +public interface LoggerMessageRender { + + String render(LoggerLevel level, String loggerName, String logMessage); +} diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/SimpleLoggerMessageRender.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/SimpleLoggerMessageRender.java new file mode 100644 index 00000000..40fcdc82 --- /dev/null +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/SimpleLoggerMessageRender.java @@ -0,0 +1,22 @@ +package javasabr.rlib.logger.api.impl; + +import java.time.format.DateTimeFormatter; +import javasabr.rlib.logger.api.LoggerLevel; +import javasabr.rlib.logger.api.LoggerMessageRender; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; + +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class SimpleLoggerMessageRender implements LoggerMessageRender { + + DateTimeFormatter timeFormatter; + + private SimpleLoggerMessageRender() { + this.timeFormatter = DateTimeFormatter.ofPattern("d.MM.yyyy HH:mm:ss:SSS"); + } + + @Override + public String render(LoggerLevel level, String loggerName, String logMessage) { + return ""; + } +} diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfiguration.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfiguration.java new file mode 100644 index 00000000..05d428c8 --- /dev/null +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfiguration.java @@ -0,0 +1,6 @@ +package javasabr.rlib.logger.impl.config; + +public class LoggerConfiguration { + + +} From b7a6dc6362113ba33862baf1b30546c4166b1590 Mon Sep 17 00:00:00 2001 From: javasabr Date: Sun, 31 May 2026 16:03:38 +0200 Subject: [PATCH 3/8] resolve CR comments --- .../rlib/logger/api/impl/NullLogger.java | 1 - .../rlib/logger/impl/DefaultLogger.java | 1 - rlib-logger-slf4j-impl/build.gradle | 2 +- .../slf4j/impl/Slf4jLoggerFactoryImpl.java | 7 +- .../logger/slf4j/impl/Slf4jLoggerImpl.java | 120 ++++++++++++---- .../slf4j/impl/Slf4jLoggerImplTest.java | 130 ++++++++++++++++++ .../rlib/logger/slf4j/Slf4jLogger.java | 1 - 7 files changed, 227 insertions(+), 35 deletions(-) create mode 100644 rlib-logger-slf4j-impl/src/test/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImplTest.java diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NullLogger.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NullLogger.java index ae1be7cc..d6e51582 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NullLogger.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NullLogger.java @@ -2,7 +2,6 @@ import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerLevel; -import org.jspecify.annotations.NonNull; public final class NullLogger implements Logger { diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLogger.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLogger.java index fbff2aae..48d59177 100644 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLogger.java +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLogger.java @@ -7,7 +7,6 @@ import javasabr.rlib.logger.api.LoggerService; import lombok.AccessLevel; import lombok.experimental.FieldDefaults; -import org.jspecify.annotations.NonNull; /** * @author JavaSaBr diff --git a/rlib-logger-slf4j-impl/build.gradle b/rlib-logger-slf4j-impl/build.gradle index 08ad7c5e..b809f887 100644 --- a/rlib-logger-slf4j-impl/build.gradle +++ b/rlib-logger-slf4j-impl/build.gradle @@ -7,5 +7,5 @@ dependencies { api projects.rlibLoggerApi api libs.slf4j.api - testRuntime libs.slf4j.simple + testImplementation projects.rlibLoggerImpl } diff --git a/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerFactoryImpl.java b/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerFactoryImpl.java index 76abbc5a..dd2843ef 100644 --- a/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerFactoryImpl.java +++ b/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerFactoryImpl.java @@ -1,13 +1,18 @@ package javasabr.rlib.logger.slf4j.impl; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import javasabr.rlib.logger.api.LoggerManager; import org.slf4j.ILoggerFactory; import org.slf4j.Logger; public class Slf4jLoggerFactoryImpl implements ILoggerFactory { + private final ConcurrentMap loggers = new ConcurrentHashMap<>(); + @Override public Logger getLogger(String name) { - return new Slf4jLoggerImpl(LoggerManager.getLogger(name)); + return loggers + .computeIfAbsent(name, requested -> new Slf4jLoggerImpl(LoggerManager.getLogger(requested))); } } diff --git a/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImpl.java b/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImpl.java index e3c625de..d11d03d5 100644 --- a/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImpl.java +++ b/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImpl.java @@ -28,17 +28,23 @@ public void trace(String msg) { @Override public void trace(String format, Object arg) { - logger.trace(MessageFormatter.format(format, arg).getMessage()); + if (isTraceEnabled()) { + logger.trace(MessageFormatter.format(format, arg).getMessage()); + } } @Override public void trace(String format, Object arg1, Object arg2) { - logger.trace(MessageFormatter.format(format, arg1, arg2).getMessage()); + if (isTraceEnabled()) { + logger.trace(MessageFormatter.format(format, arg1, arg2).getMessage()); + } } @Override public void trace(String format, Object... arguments) { - logger.trace(MessageFormatter.format(format, arguments).getMessage()); + if (isTraceEnabled()) { + logger.trace(MessageFormatter.arrayFormat(format, arguments).getMessage()); + } } @Override @@ -58,17 +64,23 @@ public void trace(Marker marker, String msg) { @Override public void trace(Marker marker, String format, Object arg) { - logger.trace(MessageFormatter.format(format, arg).getMessage()); + if (isTraceEnabled()) { + logger.trace(MessageFormatter.format(format, arg).getMessage()); + } } @Override public void trace(Marker marker, String format, Object arg1, Object arg2) { - logger.trace(MessageFormatter.format(format, arg1, arg2).getMessage()); + if (isTraceEnabled()) { + logger.trace(MessageFormatter.format(format, arg1, arg2).getMessage()); + } } @Override public void trace(Marker marker, String format, Object... arguments) { - logger.trace(MessageFormatter.format(format, arguments).getMessage()); + if (isTraceEnabled()) { + logger.trace(MessageFormatter.arrayFormat(format, arguments).getMessage()); + } } @Override @@ -88,17 +100,23 @@ public void debug(String msg) { @Override public void debug(String format, Object arg) { - logger.debug(MessageFormatter.format(format, arg).getMessage()); + if (isDebugEnabled()) { + logger.debug(MessageFormatter.format(format, arg).getMessage()); + } } @Override public void debug(String format, Object arg1, Object arg2) { - logger.debug(MessageFormatter.format(format, arg1, arg2).getMessage()); + if (isDebugEnabled()) { + logger.debug(MessageFormatter.format(format, arg1, arg2).getMessage()); + } } @Override public void debug(String format, Object... arguments) { - logger.debug(MessageFormatter.format(format, arguments).getMessage()); + if (isDebugEnabled()) { + logger.debug(MessageFormatter.arrayFormat(format, arguments).getMessage()); + } } @Override @@ -118,17 +136,23 @@ public void debug(Marker marker, String msg) { @Override public void debug(Marker marker, String format, Object arg) { - logger.debug(MessageFormatter.format(format, arg).getMessage()); + if (isDebugEnabled()) { + logger.debug(MessageFormatter.format(format, arg).getMessage()); + } } @Override public void debug(Marker marker, String format, Object arg1, Object arg2) { - logger.debug(MessageFormatter.format(format, arg1, arg2).getMessage()); + if (isDebugEnabled()) { + logger.debug(MessageFormatter.format(format, arg1, arg2).getMessage()); + } } @Override public void debug(Marker marker, String format, Object... arguments) { - logger.debug(MessageFormatter.format(format, arguments).getMessage()); + if (isDebugEnabled()) { + logger.debug(MessageFormatter.arrayFormat(format, arguments).getMessage()); + } } @Override @@ -148,17 +172,23 @@ public void info(String msg) { @Override public void info(String format, Object arg) { - logger.info(MessageFormatter.format(format, arg).getMessage()); + if (isInfoEnabled()) { + logger.info(MessageFormatter.format(format, arg).getMessage()); + } } @Override public void info(String format, Object arg1, Object arg2) { - logger.info(MessageFormatter.format(format, arg1, arg2).getMessage()); + if (isInfoEnabled()) { + logger.info(MessageFormatter.format(format, arg1, arg2).getMessage()); + } } @Override public void info(String format, Object... arguments) { - logger.info(MessageFormatter.format(format, arguments).getMessage()); + if (isInfoEnabled()) { + logger.info(MessageFormatter.arrayFormat(format, arguments).getMessage()); + } } @Override @@ -178,17 +208,23 @@ public void info(Marker marker, String msg) { @Override public void info(Marker marker, String format, Object arg) { - logger.info(MessageFormatter.format(format, arg).getMessage()); + if (isInfoEnabled()) { + logger.info(MessageFormatter.format(format, arg).getMessage()); + } } @Override public void info(Marker marker, String format, Object arg1, Object arg2) { - logger.info(MessageFormatter.format(format, arg1, arg2).getMessage()); + if (isInfoEnabled()) { + logger.info(MessageFormatter.format(format, arg1, arg2).getMessage()); + } } @Override public void info(Marker marker, String format, Object... arguments) { - logger.info(MessageFormatter.format(format, arguments).getMessage()); + if (isInfoEnabled()) { + logger.info(MessageFormatter.arrayFormat(format, arguments).getMessage()); + } } @Override @@ -208,17 +244,23 @@ public void warn(String msg) { @Override public void warn(String format, Object arg) { - logger.warn(MessageFormatter.format(format, arg).getMessage()); + if (isWarnEnabled()) { + logger.warn(MessageFormatter.format(format, arg).getMessage()); + } } @Override public void warn(String format, Object arg1, Object arg2) { - logger.warn(MessageFormatter.format(format, arg1, arg2).getMessage()); + if (isWarnEnabled()) { + logger.warn(MessageFormatter.format(format, arg1, arg2).getMessage()); + } } @Override public void warn(String format, Object... arguments) { - logger.warn(MessageFormatter.format(format, arguments).getMessage()); + if (isWarnEnabled()) { + logger.warn(MessageFormatter.arrayFormat(format, arguments).getMessage()); + } } @Override @@ -238,17 +280,23 @@ public void warn(Marker marker, String msg) { @Override public void warn(Marker marker, String format, Object arg) { - logger.warn(MessageFormatter.format(format, arg).getMessage()); + if (isWarnEnabled()) { + logger.warn(MessageFormatter.format(format, arg).getMessage()); + } } @Override public void warn(Marker marker, String format, Object arg1, Object arg2) { - logger.warn(MessageFormatter.format(format, arg1, arg2).getMessage()); + if (isWarnEnabled()) { + logger.warn(MessageFormatter.format(format, arg1, arg2).getMessage()); + } } @Override public void warn(Marker marker, String format, Object... arguments) { - logger.warn(MessageFormatter.format(format, arguments).getMessage()); + if (isWarnEnabled()) { + logger.warn(MessageFormatter.arrayFormat(format, arguments).getMessage()); + } } @Override @@ -268,17 +316,23 @@ public void error(String msg) { @Override public void error(String format, Object arg) { - logger.error(MessageFormatter.format(format, arg).getMessage()); + if (isErrorEnabled()) { + logger.error(MessageFormatter.format(format, arg).getMessage()); + } } @Override public void error(String format, Object arg1, Object arg2) { - logger.error(MessageFormatter.format(format, arg1, arg2).getMessage()); + if (isErrorEnabled()) { + logger.error(MessageFormatter.format(format, arg1, arg2).getMessage()); + } } @Override public void error(String format, Object... arguments) { - logger.error(MessageFormatter.format(format, arguments).getMessage()); + if (isErrorEnabled()) { + logger.error(MessageFormatter.arrayFormat(format, arguments).getMessage()); + } } @Override @@ -298,17 +352,23 @@ public void error(Marker marker, String msg) { @Override public void error(Marker marker, String format, Object arg) { - logger.error(MessageFormatter.format(format, arg).getMessage()); + if (isErrorEnabled()) { + logger.error(MessageFormatter.format(format, arg).getMessage()); + } } @Override public void error(Marker marker, String format, Object arg1, Object arg2) { - logger.error(MessageFormatter.format(format, arg1, arg2).getMessage()); + if (isErrorEnabled()) { + logger.error(MessageFormatter.format(format, arg1, arg2).getMessage()); + } } @Override public void error(Marker marker, String format, Object... arguments) { - logger.error(MessageFormatter.format(format, arguments).getMessage()); + if (isErrorEnabled()) { + logger.error(MessageFormatter.arrayFormat(format, arguments).getMessage()); + } } @Override diff --git a/rlib-logger-slf4j-impl/src/test/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImplTest.java b/rlib-logger-slf4j-impl/src/test/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImplTest.java new file mode 100644 index 00000000..e419dec2 --- /dev/null +++ b/rlib-logger-slf4j-impl/src/test/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImplTest.java @@ -0,0 +1,130 @@ +package javasabr.rlib.logger.slf4j.impl; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Collection; +import javasabr.rlib.collections.array.ArrayFactory; +import javasabr.rlib.collections.array.LockableArray; +import javasabr.rlib.collections.operation.LockableOperations; +import javasabr.rlib.logger.api.Logger; +import javasabr.rlib.logger.api.LoggerLevel; +import javasabr.rlib.logger.api.LoggerListener; +import javasabr.rlib.logger.api.LoggerManager; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.ResourceAccessMode; +import org.junit.jupiter.api.parallel.ResourceLock; +import org.slf4j.LoggerFactory; + +@ResourceLock("LoggerListeners") +@ResourceLock(value = "RLibLoggerOverrides", mode = ResourceAccessMode.READ_WRITE) +class Slf4jLoggerImplTest { + + private static final LockableArray LOGS_DATA = ArrayFactory + .stampedLockBasedArray(String.class); + private static final LockableOperations> LOGS_DATA_OPERATIONS = + LOGS_DATA.operations(); + private static final LoggerListener LOGGER_LISTENER = text -> LOGS_DATA_OPERATIONS + .inWriteLock(text, Collection::add); + + private final Logger rlibLogger = LoggerManager.getLogger(Slf4jLoggerImplTest.class); + + @BeforeEach + void prepare() { + LoggerManager.addListener(LOGGER_LISTENER); + LOGS_DATA_OPERATIONS.inWriteLock(Collection::clear); + } + + @AfterEach + void cleanup() { + for (var level : LoggerLevel.values()) { + rlibLogger.resetToDefault(level); + } + LOGS_DATA_OPERATIONS.inWriteLock(Collection::clear); + LoggerManager.removeListener(LOGGER_LISTENER); + } + + @Test + void shouldReturnSlf4jLoggerImplFromLoggerFactory() { + // when: + var logger = LoggerFactory.getLogger(Slf4jLoggerImplTest.class); + + // then: + assertThat(logger) + .isInstanceOf(Slf4jLoggerImpl.class); + } + + @Test + void shouldReturnLoggerWithMatchingName() { + // when: + var logger = LoggerFactory.getLogger("my.logger.name"); + + // then: + assertThat(logger.getName()) + .isEqualTo("my.logger.name"); + } + + @Test + void shouldDelegateInfoMessageToRlibLogger() { + // given: + rlibLogger.overrideEnabled(LoggerLevel.INFO, true); + var slf4jLogger = LoggerFactory.getLogger(Slf4jLoggerImplTest.class); + + // when: + slf4jLogger.info("hello from slf4j"); + + // then: + assertThat(LOGS_DATA.size()) + .isEqualTo(1); + assertThat(LOGS_DATA.get(0)) + .endsWith("Slf4jLoggerImplTest: hello from slf4j"); + } + + @Test + void shouldDelegateErrorWithExceptionToRlibLogger() { + // given: + rlibLogger.overrideEnabled(LoggerLevel.ERROR, true); + var slf4jLogger = LoggerFactory.getLogger(Slf4jLoggerImplTest.class); + var exception = new RuntimeException("boom"); + + // when: + slf4jLogger.error("error occurred", exception); + + // then: + assertThat(LOGS_DATA.size()) + .isEqualTo(1); + assertThat(LOGS_DATA.get(0)) + .contains("Slf4jLoggerImplTest: error occurred") + .contains("RuntimeException: boom"); + } + + @Test + void shouldNotDelegateDebugWhenDisabledByDefault() { + // given: + var slf4jLogger = LoggerFactory.getLogger(Slf4jLoggerImplTest.class); + + // when: + slf4jLogger.debug("should not appear"); + + // then: + assertThat(LOGS_DATA.size()) + .isEqualTo(0); + } + + @Test + void shouldDelegateFormattedMessageToRlibLogger() { + // given: + rlibLogger.overrideEnabled(LoggerLevel.INFO, true); + var slf4jLogger = LoggerFactory.getLogger(Slf4jLoggerImplTest.class); + + // when: + slf4jLogger.info("value is {}", 42); + + // then: + assertThat(LOGS_DATA.size()) + .isEqualTo(1); + assertThat(LOGS_DATA.get(0)) + .endsWith("Slf4jLoggerImplTest: value is 42"); + } +} diff --git a/rlib-logger-slf4j/src/main/java/javasabr/rlib/logger/slf4j/Slf4jLogger.java b/rlib-logger-slf4j/src/main/java/javasabr/rlib/logger/slf4j/Slf4jLogger.java index 02bafda0..aca1e099 100644 --- a/rlib-logger-slf4j/src/main/java/javasabr/rlib/logger/slf4j/Slf4jLogger.java +++ b/rlib-logger-slf4j/src/main/java/javasabr/rlib/logger/slf4j/Slf4jLogger.java @@ -3,7 +3,6 @@ import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerLevel; import lombok.RequiredArgsConstructor; -import org.jspecify.annotations.NonNull; @RequiredArgsConstructor public class Slf4jLogger implements Logger { From ab4fafa5282249b4027fc0591936fec018c5066c Mon Sep 17 00:00:00 2001 From: javasabr Date: Sun, 31 May 2026 16:47:11 +0200 Subject: [PATCH 4/8] resolve CR comments --- .../logger/slf4j/impl/Slf4jLoggerImpl.java | 145 +++++++++++------- .../slf4j/impl/Slf4jLoggerImplTest.java | 19 +++ 2 files changed, 112 insertions(+), 52 deletions(-) diff --git a/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImpl.java b/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImpl.java index d11d03d5..d2169d07 100644 --- a/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImpl.java +++ b/rlib-logger-slf4j-impl/src/main/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImpl.java @@ -4,6 +4,7 @@ import javasabr.rlib.logger.api.LoggerLevel; import lombok.RequiredArgsConstructor; import org.slf4j.Marker; +import org.slf4j.helpers.FormattingTuple; import org.slf4j.helpers.MessageFormatter; @RequiredArgsConstructor @@ -23,33 +24,33 @@ public boolean isTraceEnabled() { @Override public void trace(String msg) { - logger.trace(msg); + logger.print(LoggerLevel.TRACE, msg); } @Override public void trace(String format, Object arg) { if (isTraceEnabled()) { - logger.trace(MessageFormatter.format(format, arg).getMessage()); + traceImpl(MessageFormatter.format(format, arg)); } } @Override public void trace(String format, Object arg1, Object arg2) { if (isTraceEnabled()) { - logger.trace(MessageFormatter.format(format, arg1, arg2).getMessage()); + traceImpl(MessageFormatter.format(format, arg1, arg2)); } } @Override public void trace(String format, Object... arguments) { if (isTraceEnabled()) { - logger.trace(MessageFormatter.arrayFormat(format, arguments).getMessage()); + traceImpl(MessageFormatter.arrayFormat(format, arguments)); } } @Override public void trace(String msg, Throwable exception) { - logger.trace(msg, exception); + logger.print(LoggerLevel.TRACE, msg, exception); } @Override @@ -59,27 +60,27 @@ public boolean isTraceEnabled(Marker marker) { @Override public void trace(Marker marker, String msg) { - logger.trace(msg); + logger.print(LoggerLevel.TRACE, msg); } @Override public void trace(Marker marker, String format, Object arg) { if (isTraceEnabled()) { - logger.trace(MessageFormatter.format(format, arg).getMessage()); + traceImpl(MessageFormatter.format(format, arg)); } } @Override public void trace(Marker marker, String format, Object arg1, Object arg2) { if (isTraceEnabled()) { - logger.trace(MessageFormatter.format(format, arg1, arg2).getMessage()); + traceImpl(MessageFormatter.format(format, arg1, arg2)); } } @Override public void trace(Marker marker, String format, Object... arguments) { if (isTraceEnabled()) { - logger.trace(MessageFormatter.arrayFormat(format, arguments).getMessage()); + traceImpl(MessageFormatter.arrayFormat(format, arguments)); } } @@ -87,7 +88,15 @@ public void trace(Marker marker, String format, Object... arguments) { public void trace(Marker marker, String msg, Throwable exception) { logger.trace(msg, exception); } - + + private void traceImpl(FormattingTuple formatted) { + if (formatted.getThrowable() != null) { + logger.print(LoggerLevel.TRACE, formatted.getMessage(), formatted.getThrowable()); + } else { + logger.print(LoggerLevel.TRACE, formatted.getMessage()); + } + } + @Override public boolean isDebugEnabled() { return logger.enabled(LoggerLevel.DEBUG); @@ -95,33 +104,33 @@ public boolean isDebugEnabled() { @Override public void debug(String msg) { - logger.debug(msg); + logger.print(LoggerLevel.DEBUG, msg); } @Override public void debug(String format, Object arg) { if (isDebugEnabled()) { - logger.debug(MessageFormatter.format(format, arg).getMessage()); + debugImpl(MessageFormatter.format(format, arg)); } } @Override public void debug(String format, Object arg1, Object arg2) { if (isDebugEnabled()) { - logger.debug(MessageFormatter.format(format, arg1, arg2).getMessage()); + debugImpl(MessageFormatter.format(format, arg1, arg2)); } } @Override public void debug(String format, Object... arguments) { if (isDebugEnabled()) { - logger.debug(MessageFormatter.arrayFormat(format, arguments).getMessage()); + debugImpl(MessageFormatter.arrayFormat(format, arguments)); } } @Override public void debug(String msg, Throwable exception) { - logger.debug(msg, exception); + logger.print(LoggerLevel.DEBUG, msg, exception); } @Override @@ -131,35 +140,43 @@ public boolean isDebugEnabled(Marker marker) { @Override public void debug(Marker marker, String msg) { - logger.debug(msg); + logger.print(LoggerLevel.DEBUG, msg); } @Override public void debug(Marker marker, String format, Object arg) { if (isDebugEnabled()) { - logger.debug(MessageFormatter.format(format, arg).getMessage()); + debugImpl(MessageFormatter.format(format, arg)); } } @Override public void debug(Marker marker, String format, Object arg1, Object arg2) { if (isDebugEnabled()) { - logger.debug(MessageFormatter.format(format, arg1, arg2).getMessage()); + debugImpl(MessageFormatter.format(format, arg1, arg2)); } } @Override public void debug(Marker marker, String format, Object... arguments) { if (isDebugEnabled()) { - logger.debug(MessageFormatter.arrayFormat(format, arguments).getMessage()); + debugImpl(MessageFormatter.arrayFormat(format, arguments)); } } @Override public void debug(Marker marker, String msg, Throwable exception) { - logger.debug(msg, exception); + logger.print(LoggerLevel.DEBUG, msg, exception); } - + + private void debugImpl(FormattingTuple formatted) { + if (formatted.getThrowable() != null) { + logger.print(LoggerLevel.DEBUG, formatted.getMessage(), formatted.getThrowable()); + } else { + logger.print(LoggerLevel.DEBUG, formatted.getMessage()); + } + } + @Override public boolean isInfoEnabled() { return logger.enabled(LoggerLevel.INFO); @@ -167,33 +184,33 @@ public boolean isInfoEnabled() { @Override public void info(String msg) { - logger.info(msg); + logger.print(LoggerLevel.INFO, msg); } @Override public void info(String format, Object arg) { if (isInfoEnabled()) { - logger.info(MessageFormatter.format(format, arg).getMessage()); + infoImpl(MessageFormatter.format(format, arg)); } } @Override public void info(String format, Object arg1, Object arg2) { if (isInfoEnabled()) { - logger.info(MessageFormatter.format(format, arg1, arg2).getMessage()); + infoImpl(MessageFormatter.format(format, arg1, arg2)); } } @Override public void info(String format, Object... arguments) { if (isInfoEnabled()) { - logger.info(MessageFormatter.arrayFormat(format, arguments).getMessage()); + infoImpl(MessageFormatter.arrayFormat(format, arguments)); } } @Override public void info(String msg, Throwable exception) { - logger.info(msg, exception); + logger.print(LoggerLevel.INFO, msg, exception); } @Override @@ -203,33 +220,41 @@ public boolean isInfoEnabled(Marker marker) { @Override public void info(Marker marker, String msg) { - logger.info(msg); + logger.print(LoggerLevel.INFO, msg); } @Override public void info(Marker marker, String format, Object arg) { if (isInfoEnabled()) { - logger.info(MessageFormatter.format(format, arg).getMessage()); + infoImpl(MessageFormatter.format(format, arg)); } } @Override public void info(Marker marker, String format, Object arg1, Object arg2) { if (isInfoEnabled()) { - logger.info(MessageFormatter.format(format, arg1, arg2).getMessage()); + infoImpl(MessageFormatter.format(format, arg1, arg2)); } } @Override public void info(Marker marker, String format, Object... arguments) { if (isInfoEnabled()) { - logger.info(MessageFormatter.arrayFormat(format, arguments).getMessage()); + infoImpl(MessageFormatter.arrayFormat(format, arguments)); } } @Override public void info(Marker marker, String msg, Throwable exception) { - logger.info(msg, exception); + logger.print(LoggerLevel.INFO, msg, exception); + } + + private void infoImpl(FormattingTuple formatted) { + if (formatted.getThrowable() != null) { + logger.print(LoggerLevel.INFO, formatted.getMessage(), formatted.getThrowable()); + } else { + logger.print(LoggerLevel.INFO, formatted.getMessage()); + } } @Override @@ -239,33 +264,33 @@ public boolean isWarnEnabled() { @Override public void warn(String msg) { - logger.warn(msg); + logger.print(LoggerLevel.WARNING, msg); } @Override public void warn(String format, Object arg) { if (isWarnEnabled()) { - logger.warn(MessageFormatter.format(format, arg).getMessage()); + warnImpl(MessageFormatter.format(format, arg)); } } @Override public void warn(String format, Object arg1, Object arg2) { if (isWarnEnabled()) { - logger.warn(MessageFormatter.format(format, arg1, arg2).getMessage()); + warnImpl(MessageFormatter.format(format, arg1, arg2)); } } @Override public void warn(String format, Object... arguments) { if (isWarnEnabled()) { - logger.warn(MessageFormatter.arrayFormat(format, arguments).getMessage()); + warnImpl(MessageFormatter.arrayFormat(format, arguments)); } } @Override public void warn(String msg, Throwable exception) { - logger.warn(msg, exception); + logger.print(LoggerLevel.WARNING, msg, exception); } @Override @@ -275,35 +300,43 @@ public boolean isWarnEnabled(Marker marker) { @Override public void warn(Marker marker, String msg) { - logger.warn(msg); + logger.print(LoggerLevel.WARNING, msg); } @Override public void warn(Marker marker, String format, Object arg) { if (isWarnEnabled()) { - logger.warn(MessageFormatter.format(format, arg).getMessage()); + warnImpl(MessageFormatter.format(format, arg)); } } @Override public void warn(Marker marker, String format, Object arg1, Object arg2) { if (isWarnEnabled()) { - logger.warn(MessageFormatter.format(format, arg1, arg2).getMessage()); + warnImpl(MessageFormatter.format(format, arg1, arg2)); } } @Override public void warn(Marker marker, String format, Object... arguments) { if (isWarnEnabled()) { - logger.warn(MessageFormatter.arrayFormat(format, arguments).getMessage()); + warnImpl(MessageFormatter.arrayFormat(format, arguments)); } } - + @Override public void warn(Marker marker, String msg, Throwable exception) { - logger.warn(msg, exception); + logger.print(LoggerLevel.WARNING, msg, exception); } + private void warnImpl(FormattingTuple formatted) { + if (formatted.getThrowable() != null) { + logger.print(LoggerLevel.WARNING, formatted.getMessage(), formatted.getThrowable()); + } else { + logger.print(LoggerLevel.WARNING, formatted.getMessage()); + } + } + @Override public boolean isErrorEnabled() { return logger.enabled(LoggerLevel.ERROR); @@ -311,33 +344,33 @@ public boolean isErrorEnabled() { @Override public void error(String msg) { - logger.error(msg); + logger.print(LoggerLevel.ERROR, msg); } @Override public void error(String format, Object arg) { if (isErrorEnabled()) { - logger.error(MessageFormatter.format(format, arg).getMessage()); + errorImpl(MessageFormatter.format(format, arg)); } } @Override public void error(String format, Object arg1, Object arg2) { if (isErrorEnabled()) { - logger.error(MessageFormatter.format(format, arg1, arg2).getMessage()); + errorImpl(MessageFormatter.format(format, arg1, arg2)); } } @Override public void error(String format, Object... arguments) { if (isErrorEnabled()) { - logger.error(MessageFormatter.arrayFormat(format, arguments).getMessage()); + errorImpl(MessageFormatter.arrayFormat(format, arguments)); } } @Override public void error(String msg, Throwable exception) { - logger.error(msg, exception); + logger.print(LoggerLevel.ERROR, msg, exception); } @Override @@ -347,32 +380,40 @@ public boolean isErrorEnabled(Marker marker) { @Override public void error(Marker marker, String msg) { - logger.error(msg); + logger.print(LoggerLevel.ERROR, msg); } @Override public void error(Marker marker, String format, Object arg) { if (isErrorEnabled()) { - logger.error(MessageFormatter.format(format, arg).getMessage()); + errorImpl(MessageFormatter.format(format, arg)); } } @Override public void error(Marker marker, String format, Object arg1, Object arg2) { if (isErrorEnabled()) { - logger.error(MessageFormatter.format(format, arg1, arg2).getMessage()); + errorImpl(MessageFormatter.format(format, arg1, arg2)); } } @Override public void error(Marker marker, String format, Object... arguments) { if (isErrorEnabled()) { - logger.error(MessageFormatter.arrayFormat(format, arguments).getMessage()); + errorImpl(MessageFormatter.arrayFormat(format, arguments)); } } @Override public void error(Marker marker, String msg, Throwable exception) { - logger.error(msg, exception); + logger.print(LoggerLevel.ERROR, msg, exception); + } + + private void errorImpl(FormattingTuple formatted) { + if (formatted.getThrowable() != null) { + logger.print(LoggerLevel.ERROR, formatted.getMessage(), formatted.getThrowable()); + } else { + logger.print(LoggerLevel.ERROR, formatted.getMessage()); + } } } diff --git a/rlib-logger-slf4j-impl/src/test/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImplTest.java b/rlib-logger-slf4j-impl/src/test/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImplTest.java index e419dec2..a9bdbc6a 100644 --- a/rlib-logger-slf4j-impl/src/test/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImplTest.java +++ b/rlib-logger-slf4j-impl/src/test/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImplTest.java @@ -127,4 +127,23 @@ void shouldDelegateFormattedMessageToRlibLogger() { assertThat(LOGS_DATA.get(0)) .endsWith("Slf4jLoggerImplTest: value is 42"); } + + @Test + void shouldDelegateFormattedMessageWithTrailingExceptionToRlibLogger() { + // given: + rlibLogger.overrideEnabled(LoggerLevel.ERROR, true); + + var slf4jLogger = LoggerFactory.getLogger(Slf4jLoggerImplTest.class); + var exception = new RuntimeException("oops"); + + // when: + slf4jLogger.error("failed with code {}", 500, exception); + + // then: + assertThat(LOGS_DATA.size()) + .isEqualTo(1); + assertThat(LOGS_DATA.get(0)) + .contains("Slf4jLoggerImplTest: failed with code 500") + .contains("RuntimeException: oops"); + } } From e5b063458c87d9251276cf694049abb7b55d6416 Mon Sep 17 00:00:00 2001 From: javasabr Date: Sun, 31 May 2026 19:18:43 +0200 Subject: [PATCH 5/8] working on customization support for logger system --- .../rlib/logger/api/LoggerFactory.java | 44 ++------------ .../rlib/logger/api/LoggerManager.java | 60 ++++++++++--------- .../rlib/logger/api/LoggerMessageRender.java | 6 -- .../rlib/logger/api/LoggerService.java | 6 +- .../{NullLogger.java => NoOpsLogger.java} | 2 +- .../logger/api/impl/NoOpsLoggerFactory.java | 31 ++++++++++ .../logger/api/impl/NoOpsLoggerService.java | 42 +++++++++++++ .../logger/api/impl/NullLoggerFactory.java | 38 ------------ .../rlib/logger/impl/DefaultLogger.java | 22 ++++--- .../logger/impl/DefaultLoggerFactory.java | 34 +++++++++++ .../logger/impl/DefaultLoggerService.java | 55 +++++++++-------- .../rlib/logger/impl/config/LogConsumer.java | 8 +++ .../logger/impl/config/LogMessageRender.java | 8 +++ .../rlib/logger/impl/config/LoggerConfig.java | 11 ++++ .../impl/config/LoggerConfigLoader.java | 10 ++++ .../impl/config/LoggerConfiguration.java | 6 -- .../config/impl/SimpleLogMessageRender.java | 10 ++-- .../javasabr.rlib.logger.api.LoggerFactory | 2 +- .../rlib/logger/slf4j/Slf4jLoggerFactory.java | 44 +++++++------- 19 files changed, 256 insertions(+), 183 deletions(-) delete mode 100644 rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerMessageRender.java rename rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/{NullLogger.java => NoOpsLogger.java} (89%) create mode 100644 rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLoggerFactory.java create mode 100644 rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLoggerService.java delete mode 100644 rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NullLoggerFactory.java create mode 100644 rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerFactory.java create mode 100644 rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogConsumer.java create mode 100644 rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogMessageRender.java create mode 100644 rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfig.java create mode 100644 rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfigLoader.java delete mode 100644 rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfiguration.java rename rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/SimpleLoggerMessageRender.java => rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/SimpleLogMessageRender.java (66%) diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerFactory.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerFactory.java index 6f50649d..61a61fcb 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerFactory.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerFactory.java @@ -1,7 +1,5 @@ package javasabr.rlib.logger.api; -import java.io.Writer; - /** * Factory for creating {@link Logger} instances. * @@ -10,22 +8,22 @@ public interface LoggerFactory { /** - * Creates a logger with the specified name. + * Creates or gets a logger with the specified name. * * @param name the logger name * @return the logger instance * @since 10.0.0 */ - Logger make(String name); + Logger getLogger(String name); /** - * Creates a logger for the specified class. + * Creates or gets a logger for the specified class. * * @param type the class to create a logger for * @return the logger instance * @since 10.0.0 */ - Logger make(Class type); + Logger getLogger(Class type); /** * Returns the default logger. @@ -34,36 +32,6 @@ public interface LoggerFactory { * @since 10.0.0 */ Logger getDefault(); - - /** - * Adds a listener to receive log output. - * - * @param listener the listener to add - * @since 10.0.0 - */ - void addListener(LoggerListener listener); - - /** - * Removes a previously added listener. - * - * @param listener the listener to remove - * @since 10.0.0 - */ - void removeListener(LoggerListener listener); - - /** - * Adds a writer to receive log output. - * - * @param writer the writer to add - * @since 10.0.0 - */ - void addWriter(Writer writer); - - /** - * Removes a previously added writer. - * - * @param writer the writer to remove - * @since 10.0.0 - */ - void removeWriter(Writer writer); + + LoggerService getLoggerService(); } diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerManager.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerManager.java index 86b0c745..609fa988 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerManager.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerManager.java @@ -4,7 +4,7 @@ import java.lang.reflect.InvocationTargetException; import java.util.Iterator; import java.util.ServiceLoader; -import javasabr.rlib.logger.api.impl.NullLoggerFactory; +import javasabr.rlib.logger.api.impl.NoOpsLoggerFactory; /** * Central manager for obtaining and configuring loggers. @@ -41,7 +41,7 @@ public class LoggerManager { System.err.printf( "ERROR: No any exist implementation of [%s], will be used null logger%n", LoggerFactory.class); - LOGGER_FACTORY = new NullLoggerFactory(); + LOGGER_FACTORY = new NoOpsLoggerFactory(); } else { try { LOGGER_FACTORY = implementation @@ -71,7 +71,7 @@ public static Logger getDefaultLogger() { * @since 10.0.0 */ public static Logger getLogger(Class cs) { - return LOGGER_FACTORY.make(cs); + return LOGGER_FACTORY.getLogger(cs); } /** @@ -82,9 +82,13 @@ public static Logger getLogger(Class cs) { * @since 10.0.0 */ public static Logger getLogger(String id) { - return LOGGER_FACTORY.make(id); + return LOGGER_FACTORY.getLogger(id); } + public static LoggerService getLoggerService() { + return LOGGER_FACTORY.getLoggerService(); + } + /** * Adds a listener to receive log output. * @@ -92,9 +96,9 @@ public static Logger getLogger(String id) { * @since 10.0.0 */ public static void addListener(LoggerListener listener) { - if (LOGGER_FACTORY instanceof LoggerService ls) { - ls.addListener(listener); - } + LOGGER_FACTORY + .getLoggerService() + .addListener(listener); } /** @@ -104,9 +108,9 @@ public static void addListener(LoggerListener listener) { * @since 10.0.0 */ public static void removeListener(LoggerListener listener) { - if (LOGGER_FACTORY instanceof LoggerService ls) { - ls.removeListener(listener); - } + LOGGER_FACTORY + .getLoggerService() + .removeListener(listener); } /** @@ -116,9 +120,9 @@ public static void removeListener(LoggerListener listener) { * @since 10.0.0 */ public static void addWriter(Writer writer) { - if (LOGGER_FACTORY instanceof LoggerService ls) { - ls.addWriter(writer); - } + LOGGER_FACTORY + .getLoggerService() + .addWriter(writer); } /** @@ -128,9 +132,9 @@ public static void addWriter(Writer writer) { * @since 10.0.0 */ public static void removeWriter(Writer writer) { - if (LOGGER_FACTORY instanceof LoggerService ls) { - ls.removeWriter(writer); - } + LOGGER_FACTORY + .getLoggerService() + .removeWriter(writer); } /** @@ -141,9 +145,9 @@ public static void removeWriter(Writer writer) { * @since 10.0.0 */ public static void configureDefault(LoggerLevel level, boolean def) { - if (LOGGER_FACTORY instanceof LoggerService ls) { - ls.configureDefault(level, def); - } + LOGGER_FACTORY + .getLoggerService() + .configureDefault(level, def); } /** @@ -153,9 +157,9 @@ public static void configureDefault(LoggerLevel level, boolean def) { * @since 10.0.0 */ public static void removeDefault(LoggerLevel level) { - if (LOGGER_FACTORY instanceof LoggerService ls) { - ls.removeDefault(level); - } + LOGGER_FACTORY + .getLoggerService() + .removeDefault(level); } /** @@ -166,9 +170,9 @@ public static void removeDefault(LoggerLevel level) { * @since 10.0.0 */ public static void enable(Class cs, LoggerLevel level) { - if (LOGGER_FACTORY instanceof LoggerService ls) { - ls.enable(cs, level); - } + LOGGER_FACTORY + .getLoggerService() + .enable(cs, level); } /** @@ -179,8 +183,8 @@ public static void enable(Class cs, LoggerLevel level) { * @since 10.0.0 */ public static void disable(Class cs, LoggerLevel level) { - if (LOGGER_FACTORY instanceof LoggerService ls) { - ls.disable(cs, level); - } + LOGGER_FACTORY + .getLoggerService() + .disable(cs, level); } } diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerMessageRender.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerMessageRender.java deleted file mode 100644 index 1926922a..00000000 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerMessageRender.java +++ /dev/null @@ -1,6 +0,0 @@ -package javasabr.rlib.logger.api; - -public interface LoggerMessageRender { - - String render(LoggerLevel level, String loggerName, String logMessage); -} diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerService.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerService.java index f98f0582..a7460527 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerService.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerService.java @@ -103,10 +103,10 @@ public interface LoggerService { /** * Writes a log message. * + * @param logger the logger * @param level the log level - * @param loggerName the logger name - * @param logMessage the message to write + * @param message the message to write * @since 10.0.0 */ - void write(LoggerLevel level, String loggerName, String logMessage); + void write(Logger logger, LoggerLevel level, String message); } diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NullLogger.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLogger.java similarity index 89% rename from rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NullLogger.java rename to rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLogger.java index d6e51582..4b24fce0 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NullLogger.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLogger.java @@ -3,7 +3,7 @@ import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerLevel; -public final class NullLogger implements Logger { +public final class NoOpsLogger implements Logger { @Override public String name() { diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLoggerFactory.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLoggerFactory.java new file mode 100644 index 00000000..1d5cb8e7 --- /dev/null +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLoggerFactory.java @@ -0,0 +1,31 @@ +package javasabr.rlib.logger.api.impl; + +import javasabr.rlib.logger.api.Logger; +import javasabr.rlib.logger.api.LoggerFactory; +import javasabr.rlib.logger.api.LoggerService; + +public class NoOpsLoggerFactory implements LoggerFactory { + + private static final NoOpsLogger NULL_LOGGER = new NoOpsLogger(); + private static final LoggerService NO_OPS_LOGGER_SERVICE = new NoOpsLoggerService(); + + @Override + public Logger getLogger(String name) { + return NULL_LOGGER; + } + + @Override + public Logger getLogger(Class type) { + return NULL_LOGGER; + } + + @Override + public Logger getDefault() { + return NULL_LOGGER; + } + + @Override + public LoggerService getLoggerService() { + return NO_OPS_LOGGER_SERVICE; + } +} diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLoggerService.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLoggerService.java new file mode 100644 index 00000000..79a640f6 --- /dev/null +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLoggerService.java @@ -0,0 +1,42 @@ +package javasabr.rlib.logger.api.impl; + +import java.io.Writer; +import javasabr.rlib.logger.api.Logger; +import javasabr.rlib.logger.api.LoggerLevel; +import javasabr.rlib.logger.api.LoggerListener; +import javasabr.rlib.logger.api.LoggerService; + +public class NoOpsLoggerService implements LoggerService { + + @Override + public void addListener(LoggerListener listener) {} + + @Override + public void removeListener(LoggerListener listener) {} + + @Override + public void addWriter(Writer writer) {} + + @Override + public void removeWriter(Writer writer) {} + + @Override + public void enable(Class cs, LoggerLevel level) {} + + @Override + public void disable(Class cs, LoggerLevel level) {} + + @Override + public void configureDefault(LoggerLevel level, boolean def) {} + + @Override + public void removeDefault(LoggerLevel level) {} + + @Override + public int enabled(LoggerLevel level) { + return 0; + } + + @Override + public void write(Logger logger, LoggerLevel level, String message) {} +} diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NullLoggerFactory.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NullLoggerFactory.java deleted file mode 100644 index 686665b9..00000000 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NullLoggerFactory.java +++ /dev/null @@ -1,38 +0,0 @@ -package javasabr.rlib.logger.api.impl; - -import java.io.Writer; -import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerFactory; -import javasabr.rlib.logger.api.LoggerListener; - -public class NullLoggerFactory implements LoggerFactory { - - private static final NullLogger NULL_LOGGER = new NullLogger(); - - @Override - public Logger make(String name) { - return NULL_LOGGER; - } - - @Override - public Logger make(Class type) { - return NULL_LOGGER; - } - - @Override - public Logger getDefault() { - return NULL_LOGGER; - } - - @Override - public void addListener(LoggerListener listener) {} - - @Override - public void addWriter(Writer writer) {} - - @Override - public void removeListener(LoggerListener listener) {} - - @Override - public void removeWriter(Writer writer) {} -} diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLogger.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLogger.java index 48d59177..8a627509 100644 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLogger.java +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLogger.java @@ -1,24 +1,28 @@ package javasabr.rlib.logger.impl; import java.util.Arrays; +import javasabr.rlib.collections.array.Array; import javasabr.rlib.common.util.StringUtils; import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerLevel; import javasabr.rlib.logger.api.LoggerService; +import javasabr.rlib.logger.impl.config.LogConsumer; import lombok.AccessLevel; import lombok.experimental.FieldDefaults; /** * @author JavaSaBr */ -@FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) +@FieldDefaults(level = AccessLevel.PROTECTED) public final class DefaultLogger implements Logger { - - int[] override; - String name; - LoggerService loggerService; - public DefaultLogger(String name, LoggerService loggerService) { + final int[] override; + final String name; + final DefaultLoggerService loggerService; + + Array resolvedConsumers; + + public DefaultLogger(String name, DefaultLoggerService loggerService) { this.name = name; this.loggerService = loggerService; this.override = new int[DefaultLoggerService.LOGGER_LEVELS.length]; @@ -56,14 +60,14 @@ public void resetToDefault(LoggerLevel level) { @Override public void print(LoggerLevel level, String logMessage) { if (enabled(level)) { - loggerService.write(level, name, logMessage); + loggerService.write(this, level, logMessage); } } @Override public void print(LoggerLevel level, Throwable exception) { if (enabled(level)) { - loggerService.write(level, name, StringUtils.toString(exception)); + loggerService.write(this, level, StringUtils.toString(exception)); } } @@ -71,7 +75,7 @@ public void print(LoggerLevel level, Throwable exception) { public void print(LoggerLevel level, String message, Throwable exception) { if (enabled(level)) { String exceptionInfo = StringUtils.toString(exception); - loggerService.write(level, name, message + ": " + exceptionInfo); + loggerService.write(this, level, message + ": " + exceptionInfo); } } } diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerFactory.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerFactory.java new file mode 100644 index 00000000..2997997c --- /dev/null +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerFactory.java @@ -0,0 +1,34 @@ +package javasabr.rlib.logger.impl; + +import javasabr.rlib.logger.api.Logger; +import javasabr.rlib.logger.api.LoggerFactory; +import javasabr.rlib.logger.api.LoggerService; + +public class DefaultLoggerFactory implements LoggerFactory { + + DefaultLoggerService defaultLoggerService; + + public DefaultLoggerFactory() { + this.defaultLoggerService = new DefaultLoggerService(); + } + + @Override + public Logger getLogger(String name) { + return defaultLoggerService.getLogger(name); + } + + @Override + public Logger getLogger(Class type) { + return defaultLoggerService.getLogger(type); + } + + @Override + public Logger getDefault() { + return defaultLoggerService.getDefault(); + } + + @Override + public LoggerService getLoggerService() { + return defaultLoggerService; + } +} diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java index 50b7115d..ba6c40e0 100644 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java @@ -1,7 +1,5 @@ package javasabr.rlib.logger.impl; -import static javasabr.rlib.common.util.ObjectUtils.notNull; - import java.io.IOException; import java.io.Writer; import java.time.LocalDateTime; @@ -13,7 +11,6 @@ import javasabr.rlib.collections.array.ArrayIterationFunctions; import javasabr.rlib.collections.array.MutableArray; import javasabr.rlib.logger.api.Logger; -import javasabr.rlib.logger.api.LoggerFactory; import javasabr.rlib.logger.api.LoggerLevel; import javasabr.rlib.logger.api.LoggerListener; import javasabr.rlib.logger.api.LoggerService; @@ -26,11 +23,11 @@ * @author JavaSaBr */ @FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) -public class DefaultLoggerService implements LoggerFactory, LoggerService { +public class DefaultLoggerService implements LoggerService { static final LoggerLevel[] LOGGER_LEVELS = LoggerLevel.values(); - ConcurrentMap loggers; + ConcurrentMap loggers; MutableArray listeners; ArrayIterationFunctions listenerIterations; MutableArray writers; @@ -42,7 +39,7 @@ public class DefaultLoggerService implements LoggerFactory, LoggerService { public DefaultLoggerService() { this.loggers = new ConcurrentHashMap<>(); - this.logger = new DefaultLogger("", this); + this.logger = getLogger(""); this.timeFormatter = DateTimeFormatter.ofPattern("d.MM.yyyy HH:mm:ss:SSS"); this.listeners = ArrayFactory.copyOnModifyArray(LoggerListener.class); this.listenerIterations = listeners.iterations(); @@ -62,24 +59,23 @@ public void addWriter(Writer writer) { writers.add(writer); } - @Override public Logger getDefault() { return logger; } - @Override - public Logger make(Class type) { - String simpleName = type.getSimpleName(); - Logger logger = loggers - .computeIfAbsent(simpleName, name -> new DefaultLogger(name, this)); - return notNull(logger); + public DefaultLogger getLogger(Class type) { + return loggers.computeIfAbsent( + type, + key -> { + var clazz = (Class) key; + return new DefaultLogger(clazz.getSimpleName(), this); + }); } - @Override - public Logger make(String name) { - Logger logger = loggers - .computeIfAbsent(name, str -> new DefaultLogger(str, this)); - return notNull(logger); + public DefaultLogger getLogger(String name) { + return loggers.computeIfAbsent( + name, + key -> new DefaultLogger(key.toString(), this)); } @Override @@ -94,12 +90,12 @@ public void removeWriter(Writer writer) { @Override public void enable(Class cs, LoggerLevel level) { - make(cs).overrideEnabled(level, true); + getLogger(cs).overrideEnabled(level, true); } @Override public void disable(Class cs, LoggerLevel level) { - make(cs).overrideEnabled(level, false); + getLogger(cs).overrideEnabled(level, false); } @Override @@ -118,13 +114,22 @@ public int enabled(LoggerLevel level) { } @Override - public void write(LoggerLevel level, String loggerName, String logMessage) { + public void write(Logger logger, LoggerLevel level, String message) { + if (logger instanceof DefaultLogger defaultLogger) { + write(defaultLogger, level, message); + } else { + throw new UnsupportedOperationException("Unsupported logger type: " + logger.getClass()); + } + } + + void write(DefaultLogger logger, LoggerLevel level, String message) { + String name = logger.name(); var timestamp = timeFormatter.format(LocalDateTime.now()); - var resultMessage = level.title() - + level.offset() + ' ' + var resultMessage = level.title() + + level.offset() + ' ' + timestamp + ' ' - + loggerName + ": " - + logMessage; + + name + ": " + + message; write(level, resultMessage); } diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogConsumer.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogConsumer.java new file mode 100644 index 00000000..9de0a18b --- /dev/null +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogConsumer.java @@ -0,0 +1,8 @@ +package javasabr.rlib.logger.impl.config; + +import javasabr.rlib.logger.api.LoggerLevel; + +public interface LogConsumer { + + void consume(LoggerLevel level, String loggerName, String message); +} diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogMessageRender.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogMessageRender.java new file mode 100644 index 00000000..fb59a86c --- /dev/null +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogMessageRender.java @@ -0,0 +1,8 @@ +package javasabr.rlib.logger.impl.config; + +import javasabr.rlib.logger.api.LoggerLevel; + +public interface LogMessageRender { + + String render(LoggerLevel level, String loggerName, String message); +} diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfig.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfig.java new file mode 100644 index 00000000..dbca5d3f --- /dev/null +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfig.java @@ -0,0 +1,11 @@ +package javasabr.rlib.logger.impl.config; + +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.logger.api.Logger; + +public interface LoggerConfig { + + void configure(Logger logger); + + Array resolveConsumers(Logger logger); +} diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfigLoader.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfigLoader.java new file mode 100644 index 00000000..076d1857 --- /dev/null +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfigLoader.java @@ -0,0 +1,10 @@ +package javasabr.rlib.logger.impl.config; + +import java.util.Optional; + +public interface LoggerConfigLoader { + + Optional tryToLoad(); + + int order(); +} diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfiguration.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfiguration.java deleted file mode 100644 index 05d428c8..00000000 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfiguration.java +++ /dev/null @@ -1,6 +0,0 @@ -package javasabr.rlib.logger.impl.config; - -public class LoggerConfiguration { - - -} diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/SimpleLoggerMessageRender.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/SimpleLogMessageRender.java similarity index 66% rename from rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/SimpleLoggerMessageRender.java rename to rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/SimpleLogMessageRender.java index 40fcdc82..d1e81cb7 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/SimpleLoggerMessageRender.java +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/SimpleLogMessageRender.java @@ -1,22 +1,22 @@ -package javasabr.rlib.logger.api.impl; +package javasabr.rlib.logger.impl.config.impl; import java.time.format.DateTimeFormatter; import javasabr.rlib.logger.api.LoggerLevel; -import javasabr.rlib.logger.api.LoggerMessageRender; +import javasabr.rlib.logger.impl.config.LogMessageRender; import lombok.AccessLevel; import lombok.experimental.FieldDefaults; @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -public class SimpleLoggerMessageRender implements LoggerMessageRender { +public class SimpleLogMessageRender implements LogMessageRender { DateTimeFormatter timeFormatter; - private SimpleLoggerMessageRender() { + private SimpleLogMessageRender() { this.timeFormatter = DateTimeFormatter.ofPattern("d.MM.yyyy HH:mm:ss:SSS"); } @Override - public String render(LoggerLevel level, String loggerName, String logMessage) { + public String render(LoggerLevel level, String loggerName, String message) { return ""; } } diff --git a/rlib-logger-impl/src/main/resources/META-INF/services/javasabr.rlib.logger.api.LoggerFactory b/rlib-logger-impl/src/main/resources/META-INF/services/javasabr.rlib.logger.api.LoggerFactory index 05a43c4d..9746aa58 100644 --- a/rlib-logger-impl/src/main/resources/META-INF/services/javasabr.rlib.logger.api.LoggerFactory +++ b/rlib-logger-impl/src/main/resources/META-INF/services/javasabr.rlib.logger.api.LoggerFactory @@ -1 +1 @@ -javasabr.rlib.logger.impl.DefaultLoggerService \ No newline at end of file +javasabr.rlib.logger.impl.DefaultLoggerFactory diff --git a/rlib-logger-slf4j/src/main/java/javasabr/rlib/logger/slf4j/Slf4jLoggerFactory.java b/rlib-logger-slf4j/src/main/java/javasabr/rlib/logger/slf4j/Slf4jLoggerFactory.java index 97a49704..2920dfce 100644 --- a/rlib-logger-slf4j/src/main/java/javasabr/rlib/logger/slf4j/Slf4jLoggerFactory.java +++ b/rlib-logger-slf4j/src/main/java/javasabr/rlib/logger/slf4j/Slf4jLoggerFactory.java @@ -1,26 +1,39 @@ package javasabr.rlib.logger.slf4j; -import java.io.Writer; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerFactory; -import javasabr.rlib.logger.api.LoggerListener; +import javasabr.rlib.logger.api.LoggerService; +import javasabr.rlib.logger.api.impl.NoOpsLoggerService; public class Slf4jLoggerFactory implements LoggerFactory { + private static final LoggerService NO_OPS_LOGGER_SERVICE = new NoOpsLoggerService(); + + private final ConcurrentMap loggers; private final Logger logger; public Slf4jLoggerFactory() { this.logger = new Slf4jLogger(org.slf4j.LoggerFactory.getLogger("")); + this.loggers = new ConcurrentHashMap<>(); } @Override - public Logger make(String name) { - return new Slf4jLogger(org.slf4j.LoggerFactory.getLogger(name)); + public Logger getLogger(String name) { + return loggers.computeIfAbsent( + name, + key -> new Slf4jLogger(org.slf4j.LoggerFactory.getLogger(key.toString()))); } @Override - public Logger make(Class type) { - return new Slf4jLogger(org.slf4j.LoggerFactory.getLogger(type)); + public Logger getLogger(Class type) { + return loggers.computeIfAbsent( + type, + key -> { + var clazz = (Class) key; + return new Slf4jLogger(org.slf4j.LoggerFactory.getLogger(clazz)); + }); } @Override @@ -29,22 +42,7 @@ public Logger getDefault() { } @Override - public void addListener(LoggerListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void addWriter(Writer writer) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeListener(LoggerListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeWriter(Writer writer) { - throw new UnsupportedOperationException(); + public LoggerService getLoggerService() { + return NO_OPS_LOGGER_SERVICE; } } From fe44dd2330c9ba6cf01e2750d9991e661d95ae45 Mon Sep 17 00:00:00 2001 From: javasabr Date: Fri, 5 Jun 2026 14:35:26 +0200 Subject: [PATCH 6/8] implement base API for customization support of logger engine --- .../java/javasabr/rlib/logger/api/Logger.java | 6 +- .../javasabr/rlib/logger/api/LoggerLevel.java | 11 +- .../rlib/logger/api/LoggerManager.java | 49 --- .../rlib/logger/api/LoggerService.java | 36 +- .../rlib/logger/api/impl/NoOpsLogger.java | 7 +- .../logger/api/impl/NoOpsLoggerService.java | 14 - .../javasabr/rlib/logger/api/LoggerTest.java | 25 ++ .../rlib/logger/impl/DefaultLogger.java | 46 ++- .../logger/impl/DefaultLoggerFactory.java | 3 +- .../logger/impl/DefaultLoggerService.java | 156 ++++---- .../rlib/logger/impl/FolderFileListener.java | 72 ---- .../rlib/logger/impl/config/LogConsumer.java | 8 - .../impl/config/LogMessageConsumer.java | 9 + .../logger/impl/config/LogMessageRender.java | 3 +- .../rlib/logger/impl/config/LoggerConfig.java | 17 +- .../impl/config/LoggerConfigResolver.java | 25 ++ .../config/impl/ConsoleMessageConsumer.java | 25 ++ .../impl/config/impl/DefaultLoggerConfig.java | 160 ++++++++ .../impl/DefaultLoggerConfigLoader.java | 32 ++ .../config/impl/SimpleLogMessageRender.java | 13 +- .../logger/impl/config/impl/package-info.java | 4 + .../rlib/logger/impl/DefaultLoggerTest.java | 341 +++++++++++++++--- .../slf4j/impl/Slf4jLoggerImplTest.java | 91 ++--- .../rlib/logger/slf4j/Slf4jLogger.java | 6 + 24 files changed, 772 insertions(+), 387 deletions(-) delete mode 100644 rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/FolderFileListener.java delete mode 100644 rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogConsumer.java create mode 100644 rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogMessageConsumer.java create mode 100644 rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfigResolver.java create mode 100644 rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/ConsoleMessageConsumer.java create mode 100644 rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/DefaultLoggerConfig.java create mode 100644 rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/DefaultLoggerConfigLoader.java create mode 100644 rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/package-info.java diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/Logger.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/Logger.java index f93f3284..5ee4ee9b 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/Logger.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/Logger.java @@ -205,8 +205,12 @@ interface N4Factory { * @return the logger name * @since 10.0.0 */ - @NonNull String name(); + @NonNull + String name(); + @NonNull + String shortName(); + /** * Prints a trace message. * diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerLevel.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerLevel.java index 22c94bd9..b019257d 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerLevel.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerLevel.java @@ -16,11 +16,11 @@ @Accessors(fluent = true) @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) public enum LoggerLevel { - TRACE("TRACE", " ", false, false), - DEBUG("DEBUG", " ", false, false), - INFO("INFO", " ", true, true), - WARNING("WARN", " ", true, true), - ERROR("ERROR", " ", true, true); + TRACE("TRACE", " ", 0, false, false), + DEBUG("DEBUG", " ", 1, false, false), + INFO("INFO", " ", 2, true, true), + WARNING("WARN", " ", 3, true, true), + ERROR("ERROR", " ", 4, true, true); /** * The number of log levels. @@ -30,6 +30,7 @@ public enum LoggerLevel { String title; String offset; + int severity; boolean enabled; boolean forceFlush; diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerManager.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerManager.java index 609fa988..cc5510c3 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerManager.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerManager.java @@ -1,6 +1,5 @@ package javasabr.rlib.logger.api; -import java.io.Writer; import java.lang.reflect.InvocationTargetException; import java.util.Iterator; import java.util.ServiceLoader; @@ -89,54 +88,6 @@ public static LoggerService getLoggerService() { return LOGGER_FACTORY.getLoggerService(); } - /** - * Adds a listener to receive log output. - * - * @param listener the listener to add - * @since 10.0.0 - */ - public static void addListener(LoggerListener listener) { - LOGGER_FACTORY - .getLoggerService() - .addListener(listener); - } - - /** - * Removes a previously added listener. - * - * @param listener the listener to remove - * @since 10.0.0 - */ - public static void removeListener(LoggerListener listener) { - LOGGER_FACTORY - .getLoggerService() - .removeListener(listener); - } - - /** - * Adds a writer to receive log output. - * - * @param writer the writer to add - * @since 10.0.0 - */ - public static void addWriter(Writer writer) { - LOGGER_FACTORY - .getLoggerService() - .addWriter(writer); - } - - /** - * Removes a previously added writer. - * - * @param writer the writer to remove - * @since 10.0.0 - */ - public static void removeWriter(Writer writer) { - LOGGER_FACTORY - .getLoggerService() - .removeWriter(writer); - } - /** * Configures the default setting for a log level. * diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerService.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerService.java index a7460527..8b5d4595 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerService.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerService.java @@ -1,7 +1,5 @@ package javasabr.rlib.logger.api; -import java.io.Writer; - /** * Service interface for configuring logger behavior. * @@ -23,39 +21,7 @@ public interface LoggerService { * Indicates that the level is enabled. */ int ENABLED = 1; - - /** - * Adds a listener to receive log output. - * - * @param listener the listener to add - * @since 10.0.0 - */ - void addListener(LoggerListener listener); - - /** - * Removes a previously added listener. - * - * @param listener the listener to remove - * @since 10.0.0 - */ - void removeListener(LoggerListener listener); - - /** - * Adds a writer to receive log output. - * - * @param writer the writer to add - * @since 10.0.0 - */ - void addWriter(Writer writer); - - /** - * Removes a previously added writer. - * - * @param writer the writer to remove - * @since 10.0.0 - */ - void removeWriter(Writer writer); - + /** * Enables logging at the specified level for the class. * diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLogger.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLogger.java index 4b24fce0..b26a685e 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLogger.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLogger.java @@ -7,7 +7,12 @@ public final class NoOpsLogger implements Logger { @Override public String name() { - return "null"; + return "noops"; + } + + @Override + public String shortName() { + return "noops"; } @Override diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLoggerService.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLoggerService.java index 79a640f6..4af091e4 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLoggerService.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLoggerService.java @@ -1,24 +1,10 @@ package javasabr.rlib.logger.api.impl; -import java.io.Writer; import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerLevel; -import javasabr.rlib.logger.api.LoggerListener; import javasabr.rlib.logger.api.LoggerService; public class NoOpsLoggerService implements LoggerService { - - @Override - public void addListener(LoggerListener listener) {} - - @Override - public void removeListener(LoggerListener listener) {} - - @Override - public void addWriter(Writer writer) {} - - @Override - public void removeWriter(Writer writer) {} @Override public void enable(Class cs, LoggerLevel level) {} diff --git a/rlib-logger-api/src/test/java/javasabr/rlib/logger/api/LoggerTest.java b/rlib-logger-api/src/test/java/javasabr/rlib/logger/api/LoggerTest.java index 74d72046..1a696ad0 100644 --- a/rlib-logger-api/src/test/java/javasabr/rlib/logger/api/LoggerTest.java +++ b/rlib-logger-api/src/test/java/javasabr/rlib/logger/api/LoggerTest.java @@ -24,6 +24,11 @@ public String name() { return "test"; } + @Override + public String shortName() { + return "test"; + } + @Override public boolean enabled(@NonNull LoggerLevel level) { return true; @@ -79,6 +84,11 @@ public String name() { return "test"; } + @Override + public String shortName() { + return "test"; + } + @Override public boolean enabled(@NonNull LoggerLevel level) { return true; @@ -142,6 +152,11 @@ public String name() { return "test"; } + @Override + public String shortName() { + return "test"; + } + @Override public boolean enabled(@NonNull LoggerLevel level) { return true; @@ -205,6 +220,11 @@ public String name() { return "test"; } + @Override + public String shortName() { + return "test"; + } + @Override public boolean enabled(@NonNull LoggerLevel level) { return true; @@ -268,6 +288,11 @@ public String name() { return "test"; } + @Override + public String shortName() { + return "test"; + } + @Override public boolean enabled(@NonNull LoggerLevel level) { return true; diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLogger.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLogger.java index 8a627509..5e1a75c3 100644 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLogger.java +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLogger.java @@ -1,14 +1,15 @@ package javasabr.rlib.logger.impl; import java.util.Arrays; -import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.array.UnsafeArray; import javasabr.rlib.common.util.StringUtils; import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerLevel; import javasabr.rlib.logger.api.LoggerService; -import javasabr.rlib.logger.impl.config.LogConsumer; +import javasabr.rlib.logger.impl.config.LogMessageConsumer; import lombok.AccessLevel; import lombok.experimental.FieldDefaults; +import org.jspecify.annotations.Nullable; /** * @author JavaSaBr @@ -18,12 +19,23 @@ public final class DefaultLogger implements Logger { final int[] override; final String name; + final String shortName; final DefaultLoggerService loggerService; - Array resolvedConsumers; + @Nullable + UnsafeArray traceConsumers; + @Nullable + UnsafeArray debugConsumers; + @Nullable + UnsafeArray infoConsumers; + @Nullable + UnsafeArray warnConsumers; + @Nullable + UnsafeArray errorConsumers; - public DefaultLogger(String name, DefaultLoggerService loggerService) { + public DefaultLogger(String name, String shortName, DefaultLoggerService loggerService) { this.name = name; + this.shortName = shortName; this.loggerService = loggerService; this.override = new int[DefaultLoggerService.LOGGER_LEVELS.length]; Arrays.fill(override, LoggerService.NOT_CONFIGURE); @@ -34,6 +46,11 @@ public String name() { return name; } + @Override + public String shortName() { + return shortName; + } + @Override public boolean enabled(LoggerLevel level) { int value = override[level.ordinal()]; @@ -78,4 +95,25 @@ public void print(LoggerLevel level, String message, Throwable exception) { loggerService.write(this, level, message + ": " + exceptionInfo); } } + + @Nullable + UnsafeArray resolvedConsumers(LoggerLevel level) { + return switch (level) { + case TRACE -> traceConsumers; + case DEBUG -> debugConsumers; + case INFO -> infoConsumers; + case WARNING -> warnConsumers; + case ERROR -> errorConsumers; + }; + } + + void saveResolvedConsumers(LoggerLevel level, UnsafeArray consumers) { + switch (level) { + case TRACE -> traceConsumers = consumers; + case DEBUG -> debugConsumers = consumers; + case INFO -> infoConsumers = consumers; + case WARNING -> warnConsumers = consumers; + case ERROR -> errorConsumers = consumers; + } + } } diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerFactory.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerFactory.java index 2997997c..f065d957 100644 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerFactory.java +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerFactory.java @@ -3,13 +3,14 @@ import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerFactory; import javasabr.rlib.logger.api.LoggerService; +import javasabr.rlib.logger.impl.config.LoggerConfigResolver; public class DefaultLoggerFactory implements LoggerFactory { DefaultLoggerService defaultLoggerService; public DefaultLoggerFactory() { - this.defaultLoggerService = new DefaultLoggerService(); + this.defaultLoggerService = new DefaultLoggerService(LoggerConfigResolver.load()); } @Override diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java index ba6c40e0..95f48068 100644 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java @@ -1,19 +1,15 @@ package javasabr.rlib.logger.impl; -import java.io.IOException; -import java.io.Writer; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; import java.util.Arrays; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import javasabr.rlib.collections.array.ArrayFactory; -import javasabr.rlib.collections.array.ArrayIterationFunctions; -import javasabr.rlib.collections.array.MutableArray; +import java.util.function.Function; +import javasabr.rlib.collections.array.UnsafeArray; +import javasabr.rlib.collections.dictionary.DictionaryFactory; +import javasabr.rlib.collections.dictionary.LockableRefToRefDictionary; import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerLevel; -import javasabr.rlib.logger.api.LoggerListener; import javasabr.rlib.logger.api.LoggerService; +import javasabr.rlib.logger.impl.config.LogMessageConsumer; +import javasabr.rlib.logger.impl.config.LoggerConfig; import lombok.AccessLevel; import lombok.experimental.FieldDefaults; @@ -22,41 +18,26 @@ * * @author JavaSaBr */ -@FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) +@FieldDefaults(level = AccessLevel.PROTECTED) public class DefaultLoggerService implements LoggerService { - static final LoggerLevel[] LOGGER_LEVELS = LoggerLevel.values(); - - ConcurrentMap loggers; - MutableArray listeners; - ArrayIterationFunctions listenerIterations; - MutableArray writers; - ArrayIterationFunctions writerIterations; - - Logger logger; - DateTimeFormatter timeFormatter; - int[] override; - - public DefaultLoggerService() { - this.loggers = new ConcurrentHashMap<>(); - this.logger = getLogger(""); - this.timeFormatter = DateTimeFormatter.ofPattern("d.MM.yyyy HH:mm:ss:SSS"); - this.listeners = ArrayFactory.copyOnModifyArray(LoggerListener.class); - this.listenerIterations = listeners.iterations(); - this.writers = ArrayFactory.copyOnModifyArray(Writer.class); - this.writerIterations = writers.iterations(); - this.override = new int[LOGGER_LEVELS.length]; - Arrays.fill(override, NOT_CONFIGURE); - } + public static final LoggerLevel[] LOGGER_LEVELS = LoggerLevel.values(); + public static final String ROOT_LOGGER_NAME = "ROOT"; - @Override - public void addListener(LoggerListener listener) { - listeners.add(listener); - } + final LockableRefToRefDictionary loggers; + final Function loggerFactory = this::createNew; - @Override - public void addWriter(Writer writer) { - writers.add(writer); + final Logger logger; + final int[] override; + + volatile LoggerConfig config; + + public DefaultLoggerService(LoggerConfig config) { + this.config = config; + this.loggers = DictionaryFactory.stampedLockBasedRefToRefDictionary(); + this.logger = getLogger(ROOT_LOGGER_NAME); + this.override = new int[LOGGER_LEVELS.length]; + Arrays.fill(override, NOT_CONFIGURE); } public Logger getDefault() { @@ -64,28 +45,38 @@ public Logger getDefault() { } public DefaultLogger getLogger(Class type) { - return loggers.computeIfAbsent( - type, - key -> { - var clazz = (Class) key; - return new DefaultLogger(clazz.getSimpleName(), this); - }); + long lock = loggers.writeLock(); + try { + return loggers.getOrCompute(type.getName(), loggerFactory); + } finally { + loggers.writeUnlock(lock); + } } public DefaultLogger getLogger(String name) { - return loggers.computeIfAbsent( - name, - key -> new DefaultLogger(key.toString(), this)); - } - - @Override - public void removeListener(LoggerListener listener) { - listeners.remove(listener); + long lock = loggers.writeLock(); + try { + return loggers.getOrCompute(name, loggerFactory); + } finally { + loggers.writeUnlock(lock); + } } - @Override - public void removeWriter(Writer writer) { - writers.remove(writer); + private DefaultLogger createNew(String name) { + DefaultLogger created; + String shortName = name; + int cutUntil = name.lastIndexOf('.'); + boolean isDotLastChar = cutUntil != -1 && cutUntil == shortName.length() - 1; + if (isDotLastChar && shortName.length() > 1) { + shortName = shortName.substring(0, shortName.length() - 1); + cutUntil = shortName.lastIndexOf('.'); + } + if (cutUntil != -1) { + shortName = shortName.substring(cutUntil + 1); + } + created = new DefaultLogger(name, shortName, this); + config.configureLevels(created); + return created; } @Override @@ -123,46 +114,21 @@ public void write(Logger logger, LoggerLevel level, String message) { } void write(DefaultLogger logger, LoggerLevel level, String message) { - String name = logger.name(); - var timestamp = timeFormatter.format(LocalDateTime.now()); - var resultMessage = level.title() - + level.offset() + ' ' - + timestamp + ' ' - + name + ": " - + message; - write(level, resultMessage); - } - - private void write(LoggerLevel level, String resultMessage) { - listenerIterations.forEach(resultMessage, LoggerListener::println); - writerIterations.forEach(resultMessage, DefaultLoggerService::append); - switch (level) { - case INFO, DEBUG -> System.out.println(resultMessage); - case ERROR, WARNING -> System.err.println(resultMessage); - } - if (!level.forceFlush()) { - return; - } - listeners.forEach(LoggerListener::flush); - writers.forEach(DefaultLoggerService::flush); - } - - private static void append(Writer writer, String toWrite) { - try { - writer.append(toWrite); - writer.append('\n'); - } catch (IOException exception) { - //noinspection CallToPrintStackTrace - exception.printStackTrace(); + UnsafeArray consumers = resolveConsumers(logger, level); + for (LogMessageConsumer consumer : consumers.wrapped()) { + //noinspection DataFlowIssue it's safe + consumer.consume(level, logger, message); } } - private static void flush(Writer writer) { - try { - writer.flush(); - } catch (IOException exception) { - //noinspection CallToPrintStackTrace - exception.printStackTrace(); + private UnsafeArray resolveConsumers(DefaultLogger logger, LoggerLevel level) { + UnsafeArray consumers = logger.resolvedConsumers(level); + if (consumers == null) { + consumers = config + .resolveConsumers(logger, level) + .asUnsafe(); + logger.saveResolvedConsumers(level, consumers); } + return consumers; } } diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/FolderFileListener.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/FolderFileListener.java deleted file mode 100644 index fecd0269..00000000 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/FolderFileListener.java +++ /dev/null @@ -1,72 +0,0 @@ -package javasabr.rlib.logger.impl; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.io.Writer; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import javasabr.rlib.logger.api.LoggerListener; -import lombok.AccessLevel; -import lombok.experimental.FieldDefaults; -import org.jspecify.annotations.Nullable; - -/** - * @author JavaSaBr - */ -@FieldDefaults(level = AccessLevel.PROTECTED) -public class FolderFileListener implements LoggerListener { - - private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyy-MM-dd_HH-mm-ss"); - - final Path folder; - - @Nullable - volatile Writer writer; - - public FolderFileListener(Path folder) { - if (!Files.isDirectory(folder)) { - throw new IllegalArgumentException("File:[%s] is not directory".formatted(folder)); - } - if (!Files.exists(folder)) { - try { - Files.createDirectories(folder); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - this.folder = folder; - } - - public Writer getOrCreateWriter() throws IOException { - var local = writer; - if (local == null) { - synchronized (this) { - local = writer; - if (local == null) { - var dateTime = LocalDateTime.now(); - var filename = TIME_FORMATTER.format(dateTime) + ".log"; - local = Files.newBufferedWriter(folder.resolve(filename), StandardCharsets.UTF_8); - this.writer = local; - return local; - } - } - } - return local; - } - - @Override - public void println(String text) { - try { - var writer = getOrCreateWriter(); - writer.append(text); - writer.append('\n'); - writer.flush(); - } catch (IOException exception) { - //noinspection CallToPrintStackTrace - exception.printStackTrace(); - } - } -} diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogConsumer.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogConsumer.java deleted file mode 100644 index 9de0a18b..00000000 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogConsumer.java +++ /dev/null @@ -1,8 +0,0 @@ -package javasabr.rlib.logger.impl.config; - -import javasabr.rlib.logger.api.LoggerLevel; - -public interface LogConsumer { - - void consume(LoggerLevel level, String loggerName, String message); -} diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogMessageConsumer.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogMessageConsumer.java new file mode 100644 index 00000000..e49ead6d --- /dev/null +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogMessageConsumer.java @@ -0,0 +1,9 @@ +package javasabr.rlib.logger.impl.config; + +import javasabr.rlib.logger.api.Logger; +import javasabr.rlib.logger.api.LoggerLevel; + +public interface LogMessageConsumer { + + void consume(LoggerLevel level, Logger logger, String message); +} diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogMessageRender.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogMessageRender.java index fb59a86c..385dfb73 100644 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogMessageRender.java +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogMessageRender.java @@ -1,8 +1,9 @@ package javasabr.rlib.logger.impl.config; +import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerLevel; public interface LogMessageRender { - String render(LoggerLevel level, String loggerName, String message); + String render(LoggerLevel level, Logger logger, String message); } diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfig.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfig.java index dbca5d3f..995d3d37 100644 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfig.java +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfig.java @@ -1,11 +1,18 @@ package javasabr.rlib.logger.impl.config; -import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.array.UnsafeArray; import javasabr.rlib.logger.api.Logger; +import javasabr.rlib.logger.api.LoggerLevel; public interface LoggerConfig { - - void configure(Logger logger); - - Array resolveConsumers(Logger logger); + + /** + * Should configure enable state of all levels. + */ + void configureLevels(Logger logger); + + /** + * Should return trimmed unsafe array for efficient access. + */ + UnsafeArray resolveConsumers(Logger logger, LoggerLevel level); } diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfigResolver.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfigResolver.java new file mode 100644 index 00000000..860ca777 --- /dev/null +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfigResolver.java @@ -0,0 +1,25 @@ +package javasabr.rlib.logger.impl.config; + +import java.util.Comparator; +import java.util.Optional; +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.array.ArrayCollectors; +import javasabr.rlib.logger.impl.config.impl.DefaultLoggerConfigLoader; + +public class LoggerConfigResolver { + + private static final Array LOADERS = Array + .of(new DefaultLoggerConfigLoader()) + .stream() + .sorted(Comparator.comparingInt(LoggerConfigLoader::order)) + .collect(ArrayCollectors.toArray(LoggerConfigLoader.class)); + + public static LoggerConfig load() { + return LOADERS + .stream() + .map(LoggerConfigLoader::tryToLoad) + .flatMap(Optional::stream) + .findFirst() + .orElseThrow(); + } +} diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/ConsoleMessageConsumer.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/ConsoleMessageConsumer.java new file mode 100644 index 00000000..08780274 --- /dev/null +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/ConsoleMessageConsumer.java @@ -0,0 +1,25 @@ +package javasabr.rlib.logger.impl.config.impl; + +import javasabr.rlib.logger.api.Logger; +import javasabr.rlib.logger.api.LoggerLevel; +import javasabr.rlib.logger.impl.config.LogMessageConsumer; +import javasabr.rlib.logger.impl.config.LogMessageRender; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; + +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class ConsoleMessageConsumer implements LogMessageConsumer { + + LogMessageRender logMessageRender; + + @Override + public void consume(LoggerLevel level, Logger logger, String message) { + String rendered = logMessageRender.render(level, logger, message); + switch (level) { + case INFO, DEBUG -> System.out.println(rendered); + case ERROR, WARNING -> System.err.println(rendered); + } + } +} diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/DefaultLoggerConfig.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/DefaultLoggerConfig.java new file mode 100644 index 00000000..36b7890f --- /dev/null +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/DefaultLoggerConfig.java @@ -0,0 +1,160 @@ +package javasabr.rlib.logger.impl.config.impl; + +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.array.UnsafeArray; +import javasabr.rlib.collections.dictionary.DictionaryFactory; +import javasabr.rlib.collections.dictionary.RefToRefDictionary; +import javasabr.rlib.logger.api.Logger; +import javasabr.rlib.logger.api.LoggerLevel; +import javasabr.rlib.logger.impl.DefaultLoggerService; +import javasabr.rlib.logger.impl.config.LogMessageConsumer; +import javasabr.rlib.logger.impl.config.LoggerConfig; + +public class DefaultLoggerConfig implements LoggerConfig { + + static final UnsafeArray EMPTY_CONSUMERS = Array + .empty(LogMessageConsumer.class) + .asUnsafe(); + + static final UnsafeArray TRACE_AND_LOWER_LEVELS = Array + .of(LoggerLevel.TRACE) + .asUnsafe(); + static final UnsafeArray DEBUG_AND_LOWER_LEVELS = Array + .of(LoggerLevel.DEBUG, LoggerLevel.TRACE) + .asUnsafe(); + static final UnsafeArray INFO_AND_LOWER_LEVELS = Array + .of(LoggerLevel.INFO, LoggerLevel.DEBUG, LoggerLevel.TRACE) + .asUnsafe(); + static final UnsafeArray WARN_AND_LOWER_LEVELS = Array + .of(LoggerLevel.WARNING, LoggerLevel.INFO, LoggerLevel.DEBUG, LoggerLevel.TRACE) + .asUnsafe(); + static final UnsafeArray ERROR_AND_LOWER_LEVELS = Array + .of(LoggerLevel.ERROR, LoggerLevel.WARNING, LoggerLevel.INFO, LoggerLevel.DEBUG, LoggerLevel.TRACE) + .asUnsafe(); + + static final UnsafeArray TRACE_AND_HIGHER_LEVELS = Array + .of(LoggerLevel.TRACE, LoggerLevel.DEBUG, LoggerLevel.INFO, LoggerLevel.WARNING, LoggerLevel.ERROR) + .asUnsafe(); + static final UnsafeArray DEBUG_AND_HIGHER_LEVELS = Array + .of(LoggerLevel.DEBUG, LoggerLevel.INFO, LoggerLevel.WARNING, LoggerLevel.ERROR) + .asUnsafe(); + static final UnsafeArray INFO_AND_HIGHER_LEVELS = Array + .of(LoggerLevel.INFO, LoggerLevel.WARNING, LoggerLevel.ERROR) + .asUnsafe(); + static final UnsafeArray WARN_AND_HIGHER_LEVELS = Array + .of(LoggerLevel.WARNING, LoggerLevel.ERROR) + .asUnsafe(); + static final UnsafeArray ERROR_AND_HIGHER_LEVELS = Array + .of(LoggerLevel.ERROR) + .asUnsafe(); + + public static final LoggerConsumersKey ROOT_TRACE_CONSUMERS_KEY = + new LoggerConsumersKey(DefaultLoggerService.ROOT_LOGGER_NAME, LoggerLevel.TRACE); + public static final LoggerConsumersKey ROOT_DEBUG_CONSUMERS_KEY = + new LoggerConsumersKey(DefaultLoggerService.ROOT_LOGGER_NAME, LoggerLevel.DEBUG); + public static final LoggerConsumersKey ROOT_INFO_CONSUMERS_KEY = + new LoggerConsumersKey(DefaultLoggerService.ROOT_LOGGER_NAME, LoggerLevel.INFO); + public static final LoggerConsumersKey ROOT_WARN_CONSUMERS_KEY = + new LoggerConsumersKey(DefaultLoggerService.ROOT_LOGGER_NAME, LoggerLevel.WARNING); + public static final LoggerConsumersKey ROOT_ERROR_CONSUMERS_KEY = + new LoggerConsumersKey(DefaultLoggerService.ROOT_LOGGER_NAME, LoggerLevel.ERROR); + + public static final RefToRefDictionary ENABLE_ALL_LEVELS = RefToRefDictionary.of( + DefaultLoggerService.ROOT_LOGGER_NAME, + LoggerLevel.TRACE); + + final RefToRefDictionary loggerLevels; + final RefToRefDictionary> loggerConsumers; + + public DefaultLoggerConfig( + RefToRefDictionary loggerLevels, + RefToRefDictionary> loggerConsumers) { + this.loggerLevels = loggerLevels; + this.loggerConsumers = prepareLoggersConsumers(loggerConsumers); + } + + @Override + public void configureLevels(Logger logger) { + LoggerLevel targetLevel = loggerLevels.get(logger.name()); + if (targetLevel == null) { + targetLevel = loggerLevels.get(DefaultLoggerService.ROOT_LOGGER_NAME); + } + if (targetLevel == null) { + return; + } + for (LoggerLevel level : DefaultLoggerService.LOGGER_LEVELS) { + logger.overrideEnabled(level, false); + } + for (LoggerLevel level : resolveConfiguringLevels(targetLevel)) { + logger.overrideEnabled(level, true); + } + } + + @Override + public UnsafeArray resolveConsumers(Logger logger, LoggerLevel level) { + String loggerName = logger.name(); + UnsafeArray applicableLevels = resolveConsumerLevels(level); + for (LoggerLevel lookupLevel : applicableLevels.wrapped()) { + @SuppressWarnings("DataFlowIssue") + UnsafeArray consumers = loggerConsumers + .get(new LoggerConsumersKey(loggerName, lookupLevel)); + if (consumers != null) { + return consumers; + } + } + for (LoggerLevel lookupLevel : applicableLevels.wrapped()) { + @SuppressWarnings("DataFlowIssue") + UnsafeArray consumers = loggerConsumers + .get(resolveRootConsumersKey(lookupLevel)); + if (consumers != null) { + return consumers; + } + } + return EMPTY_CONSUMERS; + } + + public record LoggerConsumersKey(String loggerName, LoggerLevel level) {} + + private static UnsafeArray resolveConsumerLevels(LoggerLevel level) { + return switch (level) { + case TRACE -> TRACE_AND_LOWER_LEVELS; + case DEBUG -> DEBUG_AND_LOWER_LEVELS; + case INFO -> INFO_AND_LOWER_LEVELS; + case WARNING -> WARN_AND_LOWER_LEVELS; + case ERROR -> ERROR_AND_LOWER_LEVELS; + }; + } + + private static UnsafeArray resolveConfiguringLevels(LoggerLevel level) { + return switch (level) { + case TRACE -> TRACE_AND_HIGHER_LEVELS; + case DEBUG -> DEBUG_AND_HIGHER_LEVELS; + case INFO -> INFO_AND_HIGHER_LEVELS; + case WARNING -> WARN_AND_HIGHER_LEVELS; + case ERROR -> ERROR_AND_HIGHER_LEVELS; + }; + } + + private static LoggerConsumersKey resolveRootConsumersKey(LoggerLevel level) { + return switch (level) { + case TRACE -> ROOT_TRACE_CONSUMERS_KEY; + case DEBUG -> ROOT_DEBUG_CONSUMERS_KEY; + case INFO -> ROOT_INFO_CONSUMERS_KEY; + case WARNING -> ROOT_WARN_CONSUMERS_KEY; + case ERROR -> ROOT_ERROR_CONSUMERS_KEY; + }; + } + + private static RefToRefDictionary> prepareLoggersConsumers( + RefToRefDictionary> loggerConsumers) { + var tempDictionary = DictionaryFactory + .>mutableRefToRefDictionary(); + loggerConsumers.forEach((key, consumers) -> { + UnsafeArray trimmedCopy = Array + .copyOf(consumers) + .asUnsafe(); + tempDictionary.put(key, trimmedCopy); + }); + return tempDictionary.toReadOnly(); + } +} diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/DefaultLoggerConfigLoader.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/DefaultLoggerConfigLoader.java new file mode 100644 index 00000000..659f7e48 --- /dev/null +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/DefaultLoggerConfigLoader.java @@ -0,0 +1,32 @@ +package javasabr.rlib.logger.impl.config.impl; + +import java.util.Optional; +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.dictionary.RefToRefDictionary; +import javasabr.rlib.logger.api.LoggerLevel; +import javasabr.rlib.logger.impl.DefaultLoggerService; +import javasabr.rlib.logger.impl.config.LogMessageConsumer; +import javasabr.rlib.logger.impl.config.LoggerConfig; +import javasabr.rlib.logger.impl.config.LoggerConfigLoader; +import javasabr.rlib.logger.impl.config.impl.DefaultLoggerConfig.LoggerConsumersKey; + +public class DefaultLoggerConfigLoader implements LoggerConfigLoader { + + @Override + public Optional tryToLoad() { + RefToRefDictionary loggerLevels = RefToRefDictionary.of( + DefaultLoggerService.ROOT_LOGGER_NAME, + LoggerLevel.INFO); + + RefToRefDictionary> loggerConsumers = RefToRefDictionary.of( + DefaultLoggerConfig.ROOT_INFO_CONSUMERS_KEY, + Array.of(new ConsoleMessageConsumer(new SimpleLogMessageRender()))); + + return Optional.of(new DefaultLoggerConfig(loggerLevels, loggerConsumers)); + } + + @Override + public int order() { + return Integer.MAX_VALUE; + } +} diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/SimpleLogMessageRender.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/SimpleLogMessageRender.java index d1e81cb7..ea5705a7 100644 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/SimpleLogMessageRender.java +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/SimpleLogMessageRender.java @@ -1,6 +1,8 @@ package javasabr.rlib.logger.impl.config.impl; +import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerLevel; import javasabr.rlib.logger.impl.config.LogMessageRender; import lombok.AccessLevel; @@ -11,12 +13,17 @@ public class SimpleLogMessageRender implements LogMessageRender { DateTimeFormatter timeFormatter; - private SimpleLogMessageRender() { + public SimpleLogMessageRender() { this.timeFormatter = DateTimeFormatter.ofPattern("d.MM.yyyy HH:mm:ss:SSS"); } @Override - public String render(LoggerLevel level, String loggerName, String message) { - return ""; + public String render(LoggerLevel level, Logger logger, String message) { + var timestamp = timeFormatter.format(LocalDateTime.now()); + return level.title() + + level.offset() + ' ' + + timestamp + ' ' + + logger.shortName() + ": " + + message; } } diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/package-info.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/package-info.java new file mode 100644 index 00000000..460658b0 --- /dev/null +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/impl/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package javasabr.rlib.logger.impl.config.impl; + +import org.jspecify.annotations.NullMarked; diff --git a/rlib-logger-impl/src/test/java/javasabr/rlib/logger/impl/DefaultLoggerTest.java b/rlib-logger-impl/src/test/java/javasabr/rlib/logger/impl/DefaultLoggerTest.java index 7ae43cc9..be0be026 100644 --- a/rlib-logger-impl/src/test/java/javasabr/rlib/logger/impl/DefaultLoggerTest.java +++ b/rlib-logger-impl/src/test/java/javasabr/rlib/logger/impl/DefaultLoggerTest.java @@ -2,40 +2,27 @@ import static org.assertj.core.api.Assertions.assertThat; -import java.util.Collection; -import javasabr.rlib.collections.array.ArrayFactory; -import javasabr.rlib.collections.array.LockableArray; -import javasabr.rlib.collections.operation.LockableOperations; +import java.util.ArrayList; +import java.util.List; +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.dictionary.RefToRefDictionary; +import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerLevel; -import javasabr.rlib.logger.api.LoggerListener; import javasabr.rlib.logger.api.LoggerManager; +import javasabr.rlib.logger.impl.config.LogMessageConsumer; +import javasabr.rlib.logger.impl.config.impl.DefaultLoggerConfig; +import javasabr.rlib.logger.impl.config.impl.DefaultLoggerConfig.LoggerConsumersKey; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.parallel.ResourceLock; -@ResourceLock("LoggerListeners") class DefaultLoggerTest { - - private static final LockableArray LOGS_DATA = ArrayFactory - .stampedLockBasedArray(String.class); - private static final LockableOperations> LOGS_DATA_OPERATIONS = - LOGS_DATA.operations(); - private static final LoggerListener LOGGER_LISTENER = text -> LOGS_DATA_OPERATIONS - .inWriteLock(text, Collection::add); - - @BeforeEach - void prepare() { - LoggerManager.addListener(LOGGER_LISTENER); - LOGS_DATA_OPERATIONS - .inWriteLock(Collection::clear); - } + private final List receivedLogs = new ArrayList<>(); @AfterEach void cleanup() { - LOGS_DATA_OPERATIONS.inWriteLock(Collection::clear); - LoggerManager.removeListener(LOGGER_LISTENER); + receivedLogs.clear(); } @Test @@ -45,44 +32,316 @@ void shouldCreateDefaultLoggerImplementation() { } @Test - void shouldWriteDataToDefaultLoggerImplementation() { + void shouldCreateLoggerWithCorrectFullNameAndShortName() { + // when: + Logger logger1 = LoggerManager.getLogger(DefaultLoggerTest.class); + Logger logger2 = LoggerManager.getLogger("javasabr.rlib.logger.impl.DefaultLoggerTest2"); + + // then + assertThat(logger1.name()).isEqualTo("javasabr.rlib.logger.impl.DefaultLoggerTest"); + assertThat(logger1.shortName()).isEqualTo("DefaultLoggerTest"); + assertThat(logger2.name()).isEqualTo("javasabr.rlib.logger.impl.DefaultLoggerTest2"); + assertThat(logger2.shortName()).isEqualTo("DefaultLoggerTest2"); + + // when: + Logger logger3 = LoggerManager.getLogger("javasabr.rlib.logger.impl.DefaultLoggerTest3."); + + // then: + assertThat(logger3.name()).isEqualTo("javasabr.rlib.logger.impl.DefaultLoggerTest3."); + assertThat(logger3.shortName()).isEqualTo("DefaultLoggerTest3"); + } + + @Test + void shouldSendLogMessagesForAllLevels() { // given: - var logger = LoggerManager.getLogger(DefaultLoggerTest.class); - logger.overrideEnabled(LoggerLevel.DEBUG, true); - logger.overrideEnabled(LoggerLevel.WARNING, true); - logger.overrideEnabled(LoggerLevel.ERROR, true); - logger.overrideEnabled(LoggerLevel.INFO, true); + RefToRefDictionary> loggerConsumers = RefToRefDictionary.of( + DefaultLoggerConfig.ROOT_TRACE_CONSUMERS_KEY, + Array.of((level, logger, message) -> receivedLogs.add(level + " " + logger.name() + " " + message))); + var loggerService = new DefaultLoggerService(new DefaultLoggerConfig( + DefaultLoggerConfig.ENABLE_ALL_LEVELS, + loggerConsumers)); + var logger = loggerService.getLogger(DefaultLoggerTest.class); // when: logger.print(LoggerLevel.ERROR, "test error data"); // then: - assertThat(LOGS_DATA.size()).isEqualTo(1); - assertThat(LOGS_DATA.get(0)).startsWith("ERROR "); - assertThat(LOGS_DATA.get(0)).endsWith("DefaultLoggerTest: test error data"); + assertThat(receivedLogs.size()).isEqualTo(1); + assertThat(receivedLogs.get(0)).isEqualTo("ERROR javasabr.rlib.logger.impl.DefaultLoggerTest test error data"); // when: logger.print(LoggerLevel.WARNING, "test warn data 2"); // then: - assertThat(LOGS_DATA.size()).isEqualTo(2); - assertThat(LOGS_DATA.get(1)).startsWith("WARN "); - assertThat(LOGS_DATA.get(1)).endsWith("DefaultLoggerTest: test warn data 2"); + assertThat(receivedLogs.size()).isEqualTo(2); + assertThat(receivedLogs.get(1)).isEqualTo("WARN javasabr.rlib.logger.impl.DefaultLoggerTest test warn data 2"); // when: logger.print(LoggerLevel.DEBUG, "test debug data 3"); // then: - assertThat(LOGS_DATA.size()).isEqualTo(3); - assertThat(LOGS_DATA.get(2)).startsWith("DEBUG "); - assertThat(LOGS_DATA.get(2)).endsWith("DefaultLoggerTest: test debug data 3"); + assertThat(receivedLogs.size()).isEqualTo(3); + assertThat(receivedLogs.get(2)).isEqualTo("DEBUG javasabr.rlib.logger.impl.DefaultLoggerTest test debug data 3"); // when: logger.print(LoggerLevel.INFO, "test info data 4"); // then: - assertThat(LOGS_DATA.size()).isEqualTo(4); - assertThat(LOGS_DATA.get(3)).startsWith("INFO "); - assertThat(LOGS_DATA.get(3)).endsWith("DefaultLoggerTest: test info data 4"); + assertThat(receivedLogs.size()).isEqualTo(4); + assertThat(receivedLogs.get(3)).isEqualTo("INFO javasabr.rlib.logger.impl.DefaultLoggerTest test info data 4"); + + // when: + logger.print(LoggerLevel.TRACE, "test info data 5"); + + // then: + assertThat(receivedLogs.size()).isEqualTo(5); + assertThat(receivedLogs.get(4)).isEqualTo("TRACE javasabr.rlib.logger.impl.DefaultLoggerTest test info data 5"); + } + + @Test + void shouldSendOnlyErrorLogMessages() { + // given: + var enabledLevels = RefToRefDictionary.of( + DefaultLoggerService.ROOT_LOGGER_NAME, + LoggerLevel.ERROR); + RefToRefDictionary> loggerConsumers = RefToRefDictionary.of( + DefaultLoggerConfig.ROOT_TRACE_CONSUMERS_KEY, + Array.of((level, logger, message) -> receivedLogs.add(level + " " + logger.name() + " " + message))); + var loggerService = new DefaultLoggerService(new DefaultLoggerConfig(enabledLevels, loggerConsumers)); + var logger = loggerService.getLogger(DefaultLoggerTest.class); + + // when: + logger.print(LoggerLevel.ERROR, "test error data"); + + // then: + assertThat(receivedLogs.size()).isEqualTo(1); + assertThat(receivedLogs.get(0)).isEqualTo("ERROR javasabr.rlib.logger.impl.DefaultLoggerTest test error data"); + + // when: + logger.print(LoggerLevel.WARNING, "test warn data 2"); + logger.print(LoggerLevel.INFO, "test info data 3"); + logger.print(LoggerLevel.DEBUG, "test debug data 4"); + logger.print(LoggerLevel.TRACE, "test info data 5"); + + // then: + assertThat(receivedLogs.size()).isEqualTo(1); + } + + @Test + void shouldSendOnlyWarnAndHigherLogMessages() { + // given: + var enabledLevels = RefToRefDictionary.of( + DefaultLoggerService.ROOT_LOGGER_NAME, + LoggerLevel.WARNING); + RefToRefDictionary> loggerConsumers = RefToRefDictionary.of( + DefaultLoggerConfig.ROOT_TRACE_CONSUMERS_KEY, + Array.of((level, logger, message) -> receivedLogs.add(level + " " + logger.name() + " " + message))); + var loggerService = new DefaultLoggerService(new DefaultLoggerConfig(enabledLevels, loggerConsumers)); + var logger = loggerService.getLogger(DefaultLoggerTest.class); + + // when: + logger.print(LoggerLevel.ERROR, "test error data"); + + // then: + assertThat(receivedLogs.size()).isEqualTo(1); + assertThat(receivedLogs.get(0)).isEqualTo("ERROR javasabr.rlib.logger.impl.DefaultLoggerTest test error data"); + + // when: + logger.print(LoggerLevel.WARNING, "test warn data 2"); + + // then: + assertThat(receivedLogs.size()).isEqualTo(2); + assertThat(receivedLogs.get(1)).isEqualTo("WARN javasabr.rlib.logger.impl.DefaultLoggerTest test warn data 2"); + + // when: + logger.print(LoggerLevel.INFO, "test info data 3"); + logger.print(LoggerLevel.DEBUG, "test debug data 4"); + logger.print(LoggerLevel.TRACE, "test info data 5"); + + // then: + assertThat(receivedLogs.size()).isEqualTo(2); + } + + @Test + void shouldSendOnlyInfoAndHigherLogMessages() { + // given: + var enabledLevels = RefToRefDictionary.of( + DefaultLoggerService.ROOT_LOGGER_NAME, + LoggerLevel.INFO); + RefToRefDictionary> loggerConsumers = RefToRefDictionary.of( + DefaultLoggerConfig.ROOT_TRACE_CONSUMERS_KEY, + Array.of((level, logger, message) -> receivedLogs.add(level + " " + logger.name() + " " + message))); + var loggerService = new DefaultLoggerService(new DefaultLoggerConfig(enabledLevels, loggerConsumers)); + var logger = loggerService.getLogger(DefaultLoggerTest.class); + + // when: + logger.print(LoggerLevel.ERROR, "test error data"); + + // then: + assertThat(receivedLogs.size()).isEqualTo(1); + assertThat(receivedLogs.get(0)).isEqualTo("ERROR javasabr.rlib.logger.impl.DefaultLoggerTest test error data"); + + // when: + logger.print(LoggerLevel.WARNING, "test warn data 2"); + + // then: + assertThat(receivedLogs.size()).isEqualTo(2); + assertThat(receivedLogs.get(1)).isEqualTo("WARN javasabr.rlib.logger.impl.DefaultLoggerTest test warn data 2"); + + // when: + logger.print(LoggerLevel.INFO, "test info data 3"); + + // then: + assertThat(receivedLogs.size()).isEqualTo(3); + assertThat(receivedLogs.get(2)).isEqualTo("INFO javasabr.rlib.logger.impl.DefaultLoggerTest test info data 3"); + + // when: + logger.print(LoggerLevel.DEBUG, "test debug data 4"); + logger.print(LoggerLevel.TRACE, "test info data 5"); + + // then: + assertThat(receivedLogs.size()).isEqualTo(3); + } + + @Test + void shouldSendOnlyDebugAndHigherLogMessages() { + // given: + var enabledLevels = RefToRefDictionary.of( + DefaultLoggerService.ROOT_LOGGER_NAME, + LoggerLevel.DEBUG); + RefToRefDictionary> loggerConsumers = RefToRefDictionary.of( + DefaultLoggerConfig.ROOT_TRACE_CONSUMERS_KEY, + Array.of((level, logger, message) -> receivedLogs.add(level + " " + logger.name() + " " + message))); + var loggerService = new DefaultLoggerService(new DefaultLoggerConfig(enabledLevels, loggerConsumers)); + var logger = loggerService.getLogger(DefaultLoggerTest.class); + + // when: + logger.print(LoggerLevel.ERROR, "test error data"); + + // then: + assertThat(receivedLogs.size()).isEqualTo(1); + assertThat(receivedLogs.get(0)).isEqualTo("ERROR javasabr.rlib.logger.impl.DefaultLoggerTest test error data"); + + // when: + logger.print(LoggerLevel.WARNING, "test warn data 2"); + + // then: + assertThat(receivedLogs.size()).isEqualTo(2); + assertThat(receivedLogs.get(1)).isEqualTo("WARN javasabr.rlib.logger.impl.DefaultLoggerTest test warn data 2"); + + // when: + logger.print(LoggerLevel.INFO, "test info data 3"); + + // then: + assertThat(receivedLogs.size()).isEqualTo(3); + assertThat(receivedLogs.get(2)).isEqualTo("INFO javasabr.rlib.logger.impl.DefaultLoggerTest test info data 3"); + + // when: + logger.print(LoggerLevel.DEBUG, "test debug data 4"); + + // then: + assertThat(receivedLogs.size()).isEqualTo(4); + assertThat(receivedLogs.get(3)).isEqualTo("DEBUG javasabr.rlib.logger.impl.DefaultLoggerTest test debug data 4"); + + // when: + logger.print(LoggerLevel.TRACE, "test info data 5"); + + // then: + assertThat(receivedLogs.size()).isEqualTo(4); + } + + @Test + void shouldSendLogMessageToCorrectConsumer1() { + // given: + List traceReceivedLogs = new ArrayList<>(); + List warnReceivedLogs = new ArrayList<>(); + RefToRefDictionary> loggerConsumers = RefToRefDictionary.of( + DefaultLoggerConfig.ROOT_TRACE_CONSUMERS_KEY, + Array.of((level, logger, message) -> traceReceivedLogs.add(level + " " + logger.name() + " " + message)), + DefaultLoggerConfig.ROOT_WARN_CONSUMERS_KEY, + Array.of((level, logger, message) -> warnReceivedLogs.add(level + " " + logger.name() + " " + message))); + var loggerService = new DefaultLoggerService(new DefaultLoggerConfig( + DefaultLoggerConfig.ENABLE_ALL_LEVELS, + loggerConsumers)); + var logger = loggerService.getLogger(DefaultLoggerTest.class); + + // when: + logger.print(LoggerLevel.ERROR, "test error data"); + logger.print(LoggerLevel.WARNING, "test warn data 2"); + logger.print(LoggerLevel.INFO, "test info data 3"); + logger.print(LoggerLevel.DEBUG, "test debug data 4"); + logger.print(LoggerLevel.TRACE, "test info data 5"); + + // then: + assertThat(warnReceivedLogs).hasSize(2); + assertThat(traceReceivedLogs).hasSize(3); + assertThat(warnReceivedLogs.get(0)).isEqualTo("ERROR javasabr.rlib.logger.impl.DefaultLoggerTest test error data"); + assertThat(warnReceivedLogs.get(1)).isEqualTo("WARN javasabr.rlib.logger.impl.DefaultLoggerTest test warn data 2"); + assertThat(traceReceivedLogs.get(0)).isEqualTo("INFO javasabr.rlib.logger.impl.DefaultLoggerTest test info data 3"); + assertThat(traceReceivedLogs.get(1)).isEqualTo("DEBUG javasabr.rlib.logger.impl.DefaultLoggerTest test debug data 4"); + assertThat(traceReceivedLogs.get(2)).isEqualTo("TRACE javasabr.rlib.logger.impl.DefaultLoggerTest test info data 5"); + } + + @Test + void shouldSendLogMessageToCorrectConsumer2() { + // given: + List debugReceivedLogs = new ArrayList<>(); + List errorReceivedLogs = new ArrayList<>(); + RefToRefDictionary> loggerConsumers = RefToRefDictionary.of( + DefaultLoggerConfig.ROOT_DEBUG_CONSUMERS_KEY, + Array.of((level, logger, message) -> debugReceivedLogs.add(level + " " + logger.name() + " " + message)), + DefaultLoggerConfig.ROOT_ERROR_CONSUMERS_KEY, + Array.of((level, logger, message) -> errorReceivedLogs.add(level + " " + logger.name() + " " + message))); + var loggerService = new DefaultLoggerService(new DefaultLoggerConfig( + DefaultLoggerConfig.ENABLE_ALL_LEVELS, + loggerConsumers)); + var logger = loggerService.getLogger(DefaultLoggerTest.class); + + // when: + logger.print(LoggerLevel.ERROR, "test error data"); + logger.print(LoggerLevel.WARNING, "test warn data 2"); + logger.print(LoggerLevel.INFO, "test info data 3"); + logger.print(LoggerLevel.DEBUG, "test debug data 4"); + logger.print(LoggerLevel.TRACE, "test info data 5"); + + // then: + assertThat(errorReceivedLogs).hasSize(1); + assertThat(debugReceivedLogs).hasSize(3); + assertThat(errorReceivedLogs.get(0)).isEqualTo("ERROR javasabr.rlib.logger.impl.DefaultLoggerTest test error data"); + assertThat(debugReceivedLogs.get(0)).isEqualTo("WARN javasabr.rlib.logger.impl.DefaultLoggerTest test warn data 2"); + assertThat(debugReceivedLogs.get(1)).isEqualTo("INFO javasabr.rlib.logger.impl.DefaultLoggerTest test info data 3"); + assertThat(debugReceivedLogs.get(2)).isEqualTo("DEBUG javasabr.rlib.logger.impl.DefaultLoggerTest test debug data 4"); + } + + @Test + void shouldSendLogMessageToCorrectConsumer3() { + // given: + List traceReceivedLogs = new ArrayList<>(); + List infoReceivedLogs = new ArrayList<>(); + RefToRefDictionary> loggerConsumers = RefToRefDictionary.of( + DefaultLoggerConfig.ROOT_TRACE_CONSUMERS_KEY, + Array.of((level, logger, message) -> traceReceivedLogs.add(level + " " + logger.name() + " " + message)), + DefaultLoggerConfig.ROOT_INFO_CONSUMERS_KEY, + Array.of((level, logger, message) -> infoReceivedLogs.add(level + " " + logger.name() + " " + message))); + var loggerService = new DefaultLoggerService(new DefaultLoggerConfig( + DefaultLoggerConfig.ENABLE_ALL_LEVELS, + loggerConsumers)); + var logger = loggerService.getLogger(DefaultLoggerTest.class); + + // when: + logger.print(LoggerLevel.ERROR, "test error data"); + logger.print(LoggerLevel.WARNING, "test warn data 2"); + logger.print(LoggerLevel.INFO, "test info data 3"); + logger.print(LoggerLevel.DEBUG, "test debug data 4"); + logger.print(LoggerLevel.TRACE, "test info data 5"); + + // then: + assertThat(infoReceivedLogs).hasSize(3); + assertThat(traceReceivedLogs).hasSize(2); + assertThat(infoReceivedLogs.get(0)).isEqualTo("ERROR javasabr.rlib.logger.impl.DefaultLoggerTest test error data"); + assertThat(infoReceivedLogs.get(1)).isEqualTo("WARN javasabr.rlib.logger.impl.DefaultLoggerTest test warn data 2"); + assertThat(infoReceivedLogs.get(2)).isEqualTo("INFO javasabr.rlib.logger.impl.DefaultLoggerTest test info data 3"); + assertThat(traceReceivedLogs.get(0)).isEqualTo("DEBUG javasabr.rlib.logger.impl.DefaultLoggerTest test debug data 4"); + assertThat(traceReceivedLogs.get(1)).isEqualTo("TRACE javasabr.rlib.logger.impl.DefaultLoggerTest test info data 5"); } } diff --git a/rlib-logger-slf4j-impl/src/test/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImplTest.java b/rlib-logger-slf4j-impl/src/test/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImplTest.java index a9bdbc6a..412538d2 100644 --- a/rlib-logger-slf4j-impl/src/test/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImplTest.java +++ b/rlib-logger-slf4j-impl/src/test/java/javasabr/rlib/logger/slf4j/impl/Slf4jLoggerImplTest.java @@ -2,15 +2,15 @@ import static org.assertj.core.api.Assertions.assertThat; -import java.util.Collection; -import javasabr.rlib.collections.array.ArrayFactory; -import javasabr.rlib.collections.array.LockableArray; -import javasabr.rlib.collections.operation.LockableOperations; +import java.util.ArrayList; +import java.util.List; +import javasabr.rlib.collections.array.Array; +import javasabr.rlib.collections.dictionary.RefToRefDictionary; import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerLevel; -import javasabr.rlib.logger.api.LoggerListener; -import javasabr.rlib.logger.api.LoggerManager; -import org.junit.jupiter.api.AfterEach; +import javasabr.rlib.logger.impl.DefaultLoggerService; +import javasabr.rlib.logger.impl.config.LogMessageConsumer; +import javasabr.rlib.logger.impl.config.impl.DefaultLoggerConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.parallel.ResourceAccessMode; @@ -21,30 +21,20 @@ @ResourceLock(value = "RLibLoggerOverrides", mode = ResourceAccessMode.READ_WRITE) class Slf4jLoggerImplTest { - private static final LockableArray LOGS_DATA = ArrayFactory - .stampedLockBasedArray(String.class); - private static final LockableOperations> LOGS_DATA_OPERATIONS = - LOGS_DATA.operations(); - private static final LoggerListener LOGGER_LISTENER = text -> LOGS_DATA_OPERATIONS - .inWriteLock(text, Collection::add); - - private final Logger rlibLogger = LoggerManager.getLogger(Slf4jLoggerImplTest.class); + private final List receivedLogs = new ArrayList<>(); + private Logger logger; @BeforeEach void prepare() { - LoggerManager.addListener(LOGGER_LISTENER); - LOGS_DATA_OPERATIONS.inWriteLock(Collection::clear); - } - - @AfterEach - void cleanup() { - for (var level : LoggerLevel.values()) { - rlibLogger.resetToDefault(level); - } - LOGS_DATA_OPERATIONS.inWriteLock(Collection::clear); - LoggerManager.removeListener(LOGGER_LISTENER); + RefToRefDictionary> loggerConsumers = RefToRefDictionary.of( + DefaultLoggerConfig.ROOT_TRACE_CONSUMERS_KEY, + Array.of((level, logger, message) -> receivedLogs.add(level + " " + logger.name() + " " + message))); + var defaultLoggerService = new DefaultLoggerService(new DefaultLoggerConfig( + DefaultLoggerConfig.ENABLE_ALL_LEVELS, + loggerConsumers)); + logger = defaultLoggerService.getLogger(Slf4jLoggerImplTest.class); } - + @Test void shouldReturnSlf4jLoggerImplFromLoggerFactory() { // when: @@ -68,82 +58,79 @@ void shouldReturnLoggerWithMatchingName() { @Test void shouldDelegateInfoMessageToRlibLogger() { // given: - rlibLogger.overrideEnabled(LoggerLevel.INFO, true); - var slf4jLogger = LoggerFactory.getLogger(Slf4jLoggerImplTest.class); + var slf4jLogger = new Slf4jLoggerImpl(logger); // when: slf4jLogger.info("hello from slf4j"); // then: - assertThat(LOGS_DATA.size()) + assertThat(receivedLogs.size()) .isEqualTo(1); - assertThat(LOGS_DATA.get(0)) - .endsWith("Slf4jLoggerImplTest: hello from slf4j"); + assertThat(receivedLogs.getFirst()) + .isEqualTo("INFO javasabr.rlib.logger.slf4j.impl.Slf4jLoggerImplTest hello from slf4j"); } @Test void shouldDelegateErrorWithExceptionToRlibLogger() { // given: - rlibLogger.overrideEnabled(LoggerLevel.ERROR, true); - var slf4jLogger = LoggerFactory.getLogger(Slf4jLoggerImplTest.class); + var slf4jLogger = new Slf4jLoggerImpl(logger); var exception = new RuntimeException("boom"); // when: slf4jLogger.error("error occurred", exception); // then: - assertThat(LOGS_DATA.size()) + assertThat(receivedLogs.size()) .isEqualTo(1); - assertThat(LOGS_DATA.get(0)) - .contains("Slf4jLoggerImplTest: error occurred") - .contains("RuntimeException: boom"); + assertThat(receivedLogs.getFirst()) + .startsWith("ERROR javasabr.rlib.logger.slf4j.impl.Slf4jLoggerImplTest error occurred: java.lang.RuntimeException: boom"); } @Test - void shouldNotDelegateDebugWhenDisabledByDefault() { + void shouldNotDelegateDebugWhenDisabled() { // given: - var slf4jLogger = LoggerFactory.getLogger(Slf4jLoggerImplTest.class); + logger.overrideEnabled(LoggerLevel.DEBUG, false); + var slf4jLogger = new Slf4jLoggerImpl(logger); // when: slf4jLogger.debug("should not appear"); // then: - assertThat(LOGS_DATA.size()) + assertThat(receivedLogs.size()) .isEqualTo(0); } @Test void shouldDelegateFormattedMessageToRlibLogger() { // given: - rlibLogger.overrideEnabled(LoggerLevel.INFO, true); - var slf4jLogger = LoggerFactory.getLogger(Slf4jLoggerImplTest.class); + logger.overrideEnabled(LoggerLevel.INFO, true); + var slf4jLogger = new Slf4jLoggerImpl(logger); // when: slf4jLogger.info("value is {}", 42); // then: - assertThat(LOGS_DATA.size()) + assertThat(receivedLogs.size()) .isEqualTo(1); - assertThat(LOGS_DATA.get(0)) - .endsWith("Slf4jLoggerImplTest: value is 42"); + assertThat(receivedLogs.getFirst()) + .endsWith("INFO javasabr.rlib.logger.slf4j.impl.Slf4jLoggerImplTest value is 42"); } @Test void shouldDelegateFormattedMessageWithTrailingExceptionToRlibLogger() { // given: - rlibLogger.overrideEnabled(LoggerLevel.ERROR, true); - - var slf4jLogger = LoggerFactory.getLogger(Slf4jLoggerImplTest.class); + logger.overrideEnabled(LoggerLevel.ERROR, true); + var slf4jLogger = new Slf4jLoggerImpl(logger); var exception = new RuntimeException("oops"); // when: slf4jLogger.error("failed with code {}", 500, exception); // then: - assertThat(LOGS_DATA.size()) + assertThat(receivedLogs.size()) .isEqualTo(1); - assertThat(LOGS_DATA.get(0)) - .contains("Slf4jLoggerImplTest: failed with code 500") + assertThat(receivedLogs.getFirst()) + .contains("ERROR javasabr.rlib.logger.slf4j.impl.Slf4jLoggerImplTest failed with code 500: java.lang.RuntimeException: oops") .contains("RuntimeException: oops"); } } diff --git a/rlib-logger-slf4j/src/main/java/javasabr/rlib/logger/slf4j/Slf4jLogger.java b/rlib-logger-slf4j/src/main/java/javasabr/rlib/logger/slf4j/Slf4jLogger.java index aca1e099..96b509a7 100644 --- a/rlib-logger-slf4j/src/main/java/javasabr/rlib/logger/slf4j/Slf4jLogger.java +++ b/rlib-logger-slf4j/src/main/java/javasabr/rlib/logger/slf4j/Slf4jLogger.java @@ -3,6 +3,7 @@ import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerLevel; import lombok.RequiredArgsConstructor; +import org.jspecify.annotations.NonNull; @RequiredArgsConstructor public class Slf4jLogger implements Logger { @@ -14,6 +15,11 @@ public String name() { return logger.getName(); } + @Override + public String shortName() { + return logger.getName(); + } + @Override public boolean enabled(LoggerLevel level) { return switch (level) { From 1ccf4bd7d64f6dafb9803433df4720576bf7273a Mon Sep 17 00:00:00 2001 From: javasabr Date: Fri, 5 Jun 2026 14:59:03 +0200 Subject: [PATCH 7/8] add missed javadoc --- .../java/javasabr/rlib/logger/api/Logger.java | 6 ++++++ .../javasabr/rlib/logger/api/LoggerFactory.java | 6 ++++++ .../javasabr/rlib/logger/api/LoggerManager.java | 6 ++++++ .../logger/api/impl/NoOpsLoggerFactory.java | 8 ++++---- .../logger/impl/config/LogMessageConsumer.java | 13 +++++++++++++ .../logger/impl/config/LogMessageRender.java | 14 ++++++++++++++ .../rlib/logger/impl/config/LoggerConfig.java | 17 +++++++++++++++-- .../logger/impl/config/LoggerConfigLoader.java | 17 +++++++++++++++++ .../impl/config/LoggerConfigResolver.java | 11 +++++++++++ 9 files changed, 92 insertions(+), 6 deletions(-) diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/Logger.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/Logger.java index 5ee4ee9b..7dacd5ec 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/Logger.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/Logger.java @@ -208,6 +208,12 @@ interface N4Factory { @NonNull String name(); + /** + * Returns the short name of this logger. + * + * @return the short logger name + * @since 10.0.0 + */ @NonNull String shortName(); diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerFactory.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerFactory.java index 61a61fcb..c865f165 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerFactory.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerFactory.java @@ -33,5 +33,11 @@ public interface LoggerFactory { */ Logger getDefault(); + /** + * Returns the logger service. + * + * @return the logger service + * @since 10.0.0 + */ LoggerService getLoggerService(); } diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerManager.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerManager.java index cc5510c3..27529683 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerManager.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerManager.java @@ -84,6 +84,12 @@ public static Logger getLogger(String id) { return LOGGER_FACTORY.getLogger(id); } + /** + * Returns the logger service. + * + * @return the logger service + * @since 10.0.0 + */ public static LoggerService getLoggerService() { return LOGGER_FACTORY.getLoggerService(); } diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLoggerFactory.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLoggerFactory.java index 1d5cb8e7..ce94ea12 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLoggerFactory.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/impl/NoOpsLoggerFactory.java @@ -6,22 +6,22 @@ public class NoOpsLoggerFactory implements LoggerFactory { - private static final NoOpsLogger NULL_LOGGER = new NoOpsLogger(); + private static final NoOpsLogger NO_OPS_LOGGER = new NoOpsLogger(); private static final LoggerService NO_OPS_LOGGER_SERVICE = new NoOpsLoggerService(); @Override public Logger getLogger(String name) { - return NULL_LOGGER; + return NO_OPS_LOGGER; } @Override public Logger getLogger(Class type) { - return NULL_LOGGER; + return NO_OPS_LOGGER; } @Override public Logger getDefault() { - return NULL_LOGGER; + return NO_OPS_LOGGER; } @Override diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogMessageConsumer.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogMessageConsumer.java index e49ead6d..c29c8f3c 100644 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogMessageConsumer.java +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogMessageConsumer.java @@ -3,7 +3,20 @@ import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerLevel; +/** + * Consumer for processed log messages. + * + * @since 10.0.0 + */ public interface LogMessageConsumer { + /** + * Consumes a log message. + * + * @param level the log level + * @param logger the logger + * @param message the message + * @since 10.0.0 + */ void consume(LoggerLevel level, Logger logger, String message); } diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogMessageRender.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogMessageRender.java index 385dfb73..285430e9 100644 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogMessageRender.java +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LogMessageRender.java @@ -3,7 +3,21 @@ import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerLevel; +/** + * Renderer for log messages. + * + * @since 10.0.0 + */ public interface LogMessageRender { + /** + * Renders a log message. + * + * @param level the log level + * @param logger the logger + * @param message the message + * @return the rendered message + * @since 10.0.0 + */ String render(LoggerLevel level, Logger logger, String message); } diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfig.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfig.java index 995d3d37..833e8d43 100644 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfig.java +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfig.java @@ -4,15 +4,28 @@ import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerLevel; +/** + * Configuration contract for logger levels and message consumers. + * + * @since 10.0.0 + */ public interface LoggerConfig { /** - * Should configure enable state of all levels. + * Configures enabled state for logger levels. + * + * @param logger the logger + * @since 10.0.0 */ void configureLevels(Logger logger); /** - * Should return trimmed unsafe array for efficient access. + * Resolves consumers for the logger and level. + * + * @param logger the logger + * @param level the log level + * @return the resolved consumers + * @since 10.0.0 */ UnsafeArray resolveConsumers(Logger logger, LoggerLevel level); } diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfigLoader.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfigLoader.java index 076d1857..e9bff195 100644 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfigLoader.java +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfigLoader.java @@ -2,9 +2,26 @@ import java.util.Optional; +/** + * Loader of logger configuration. + * + * @since 10.0.0 + */ public interface LoggerConfigLoader { + /** + * Tries to load logger configuration. + * + * @return loaded configuration if available + * @since 10.0.0 + */ Optional tryToLoad(); + /** + * Returns loader order. + * + * @return the loader order + * @since 10.0.0 + */ int order(); } diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfigResolver.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfigResolver.java index 860ca777..1ddf7a5b 100644 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfigResolver.java +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/config/LoggerConfigResolver.java @@ -6,6 +6,11 @@ import javasabr.rlib.collections.array.ArrayCollectors; import javasabr.rlib.logger.impl.config.impl.DefaultLoggerConfigLoader; +/** + * Resolver of logger configuration from available loaders. + * + * @since 10.0.0 + */ public class LoggerConfigResolver { private static final Array LOADERS = Array @@ -14,6 +19,12 @@ public class LoggerConfigResolver { .sorted(Comparator.comparingInt(LoggerConfigLoader::order)) .collect(ArrayCollectors.toArray(LoggerConfigLoader.class)); + /** + * Loads logger configuration. + * + * @return the loaded logger configuration + * @since 10.0.0 + */ public static LoggerConfig load() { return LOADERS .stream() From 12794e79b34ed204d0b7df65caaae9fabe07b237 Mon Sep 17 00:00:00 2001 From: javasabr Date: Fri, 5 Jun 2026 15:04:41 +0200 Subject: [PATCH 8/8] resolve CR comments --- .../rlib/logger/api/LoggerManager.java | 2 +- .../logger/impl/DefaultLoggerFactory.java | 3 ++ .../logger/impl/DefaultLoggerService.java | 41 ++++++++++++------- .../rlib/logger/impl/DefaultLoggerTest.java | 1 - .../rlib/logger/slf4j/Slf4jLogger.java | 1 - 5 files changed, 31 insertions(+), 17 deletions(-) diff --git a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerManager.java b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerManager.java index 27529683..859e03cd 100644 --- a/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerManager.java +++ b/rlib-logger-api/src/main/java/javasabr/rlib/logger/api/LoggerManager.java @@ -38,7 +38,7 @@ public class LoggerManager { if (implementation == null) { System.err.printf( - "ERROR: No any exist implementation of [%s], will be used null logger%n", + "ERROR: No any exist implementation of [%s], will be used no ops logger%n", LoggerFactory.class); LOGGER_FACTORY = new NoOpsLoggerFactory(); } else { diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerFactory.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerFactory.java index f065d957..d532f2c3 100644 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerFactory.java +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerFactory.java @@ -4,7 +4,10 @@ import javasabr.rlib.logger.api.LoggerFactory; import javasabr.rlib.logger.api.LoggerService; import javasabr.rlib.logger.impl.config.LoggerConfigResolver; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) public class DefaultLoggerFactory implements LoggerFactory { DefaultLoggerService defaultLoggerService; diff --git a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java index 95f48068..11c513e3 100644 --- a/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java +++ b/rlib-logger-impl/src/main/java/javasabr/rlib/logger/impl/DefaultLoggerService.java @@ -63,22 +63,12 @@ public DefaultLogger getLogger(String name) { } private DefaultLogger createNew(String name) { - DefaultLogger created; - String shortName = name; - int cutUntil = name.lastIndexOf('.'); - boolean isDotLastChar = cutUntil != -1 && cutUntil == shortName.length() - 1; - if (isDotLastChar && shortName.length() > 1) { - shortName = shortName.substring(0, shortName.length() - 1); - cutUntil = shortName.lastIndexOf('.'); - } - if (cutUntil != -1) { - shortName = shortName.substring(cutUntil + 1); - } - created = new DefaultLogger(name, shortName, this); + String shortName = calculateShortName(name); + var created = new DefaultLogger(name, shortName, this); config.configureLevels(created); return created; } - + @Override public void enable(Class cs, LoggerLevel level) { getLogger(cs).overrideEnabled(level, true); @@ -109,7 +99,13 @@ public void write(Logger logger, LoggerLevel level, String message) { if (logger instanceof DefaultLogger defaultLogger) { write(defaultLogger, level, message); } else { - throw new UnsupportedOperationException("Unsupported logger type: " + logger.getClass()); + UnsafeArray consumers = config + .resolveConsumers(logger, level) + .asUnsafe(); + for (LogMessageConsumer consumer : consumers.wrapped()) { + //noinspection DataFlowIssue it's safe + consumer.consume(level, logger, message); + } } } @@ -131,4 +127,21 @@ private UnsafeArray resolveConsumers(DefaultLogger logger, L } return consumers; } + + private static String calculateShortName(String name) { + String shortName = name; + int cutUntil = name.lastIndexOf('.'); + boolean isDotLastChar = cutUntil != -1 && cutUntil == shortName.length() - 1; + if (isDotLastChar && shortName.length() > 1) { + shortName = shortName.substring(0, shortName.length() - 1); + cutUntil = shortName.lastIndexOf('.'); + } + if (cutUntil != -1) { + shortName = shortName.substring(cutUntil + 1); + } + if (shortName.isEmpty()) { + shortName = name; + } + return shortName; + } } diff --git a/rlib-logger-impl/src/test/java/javasabr/rlib/logger/impl/DefaultLoggerTest.java b/rlib-logger-impl/src/test/java/javasabr/rlib/logger/impl/DefaultLoggerTest.java index be0be026..70f5c333 100644 --- a/rlib-logger-impl/src/test/java/javasabr/rlib/logger/impl/DefaultLoggerTest.java +++ b/rlib-logger-impl/src/test/java/javasabr/rlib/logger/impl/DefaultLoggerTest.java @@ -13,7 +13,6 @@ import javasabr.rlib.logger.impl.config.impl.DefaultLoggerConfig; import javasabr.rlib.logger.impl.config.impl.DefaultLoggerConfig.LoggerConsumersKey; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class DefaultLoggerTest { diff --git a/rlib-logger-slf4j/src/main/java/javasabr/rlib/logger/slf4j/Slf4jLogger.java b/rlib-logger-slf4j/src/main/java/javasabr/rlib/logger/slf4j/Slf4jLogger.java index 96b509a7..c4c7198b 100644 --- a/rlib-logger-slf4j/src/main/java/javasabr/rlib/logger/slf4j/Slf4jLogger.java +++ b/rlib-logger-slf4j/src/main/java/javasabr/rlib/logger/slf4j/Slf4jLogger.java @@ -3,7 +3,6 @@ import javasabr.rlib.logger.api.Logger; import javasabr.rlib.logger.api.LoggerLevel; import lombok.RequiredArgsConstructor; -import org.jspecify.annotations.NonNull; @RequiredArgsConstructor public class Slf4jLogger implements Logger {