diff --git a/LoggerProxy.class b/LoggerProxy.class new file mode 100644 index 0000000..5593a9e Binary files /dev/null and b/LoggerProxy.class differ diff --git a/logger/build.gradle.kts b/logger/build.gradle.kts new file mode 100644 index 0000000..dfaacd9 --- /dev/null +++ b/logger/build.gradle.kts @@ -0,0 +1,8 @@ +dependencies { + api(project(":")) + + compileOnly("org.apache.logging.log4j:log4j-api:2.23.1") + // https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl + testImplementation("org.apache.logging.log4j:log4j-slf4j-impl:2.23.1") + +} \ No newline at end of file diff --git a/logger/src/main/java/org/inksnow/cputil/logger/AuroraLogger.java b/logger/src/main/java/org/inksnow/cputil/logger/AuroraLogger.java new file mode 100644 index 0000000..0ec86b0 --- /dev/null +++ b/logger/src/main/java/org/inksnow/cputil/logger/AuroraLogger.java @@ -0,0 +1,331 @@ +package org.inksnow.cputil.logger; + +import org.slf4j.Logger; +import org.slf4j.Marker; + +public class AuroraLogger implements Logger { + public static final String UNMAPPED_SLF4J_PACKAGE_NAME = new String(new char[]{ + 'o', 'r', 'g', '.', 's', 'l', 'f', '4', 'j' + }); + public static final String SLF4J_PACKAGE_NAME = Logger.class.getName().substring(0, + Logger.class.getName().lastIndexOf('.') + ); + /* package-private */ final String name; + private final AuroraLoggerFactory factory; + /* package-private */ Logger delegate; + /* package-private */ int version = -1; + + public AuroraLogger(AuroraLoggerFactory factory, String name) { + this.factory = factory; + this.name = name; + } + + private Logger delegate() { + return factory.updateDelegate(this); + } + + @Override + public String getName() { + return delegate().getName(); + } + + @Override + public boolean isTraceEnabled() { + return delegate().isTraceEnabled(); + } + + @Override + public void trace(String msg) { + delegate().trace(msg); + } + + @Override + public void trace(String format, Object arg) { + delegate().trace(format, arg); + } + + @Override + public void trace(String format, Object arg1, Object arg2) { + delegate().trace(format, arg1, arg2); + } + + @Override + public void trace(String format, Object... arguments) { + delegate().trace(format, arguments); + } + + @Override + public void trace(String msg, Throwable t) { + delegate().trace(msg, t); + } + + @Override + public boolean isTraceEnabled(Marker marker) { + return delegate().isTraceEnabled(marker); + } + + @Override + public void trace(Marker marker, String msg) { + delegate().trace(marker, msg); + } + + @Override + public void trace(Marker marker, String format, Object arg) { + delegate().trace(marker, format, arg); + } + + @Override + public void trace(Marker marker, String format, Object arg1, Object arg2) { + delegate().trace(marker, format, arg1, arg2); + } + + @Override + public void trace(Marker marker, String format, Object... argArray) { + delegate().trace(marker, format, argArray); + } + + @Override + public void trace(Marker marker, String msg, Throwable t) { + delegate().trace(marker, msg, t); + } + + @Override + public boolean isDebugEnabled() { + return delegate().isDebugEnabled(); + } + + @Override + public void debug(String msg) { + delegate().debug(msg); + } + + @Override + public void debug(String format, Object arg) { + delegate().debug(format, arg); + } + + @Override + public void debug(String format, Object arg1, Object arg2) { + delegate().debug(format, arg1, arg2); + } + + @Override + public void debug(String format, Object... arguments) { + delegate().debug(format, arguments); + } + + @Override + public void debug(String msg, Throwable t) { + delegate().debug(msg, t); + } + + @Override + public boolean isDebugEnabled(Marker marker) { + return delegate().isDebugEnabled(marker); + } + + @Override + public void debug(Marker marker, String msg) { + delegate().debug(marker, msg); + } + + @Override + public void debug(Marker marker, String format, Object arg) { + delegate().debug(marker, format, arg); + } + + @Override + public void debug(Marker marker, String format, Object arg1, Object arg2) { + delegate().debug(marker, format, arg1, arg2); + } + + @Override + public void debug(Marker marker, String format, Object... arguments) { + delegate().debug(marker, format, arguments); + } + + @Override + public void debug(Marker marker, String msg, Throwable t) { + delegate().debug(marker, msg, t); + } + + @Override + public boolean isInfoEnabled() { + return delegate().isInfoEnabled(); + } + + @Override + public void info(String msg) { + delegate().info(msg); + } + + @Override + public void info(String format, Object arg) { + delegate().info(format, arg); + } + + @Override + public void info(String format, Object arg1, Object arg2) { + delegate().info(format, arg1, arg2); + } + + @Override + public void info(String format, Object... arguments) { + delegate().info(format, arguments); + } + + @Override + public void info(String msg, Throwable t) { + delegate().info(msg, t); + } + + @Override + public boolean isInfoEnabled(Marker marker) { + return delegate().isInfoEnabled(marker); + } + + @Override + public void info(Marker marker, String msg) { + delegate().info(marker, msg); + } + + @Override + public void info(Marker marker, String format, Object arg) { + delegate().info(marker, format, arg); + } + + @Override + public void info(Marker marker, String format, Object arg1, Object arg2) { + delegate().info(marker, format, arg1, arg2); + } + + @Override + public void info(Marker marker, String format, Object... arguments) { + delegate().info(marker, format, arguments); + } + + @Override + public void info(Marker marker, String msg, Throwable t) { + delegate().info(marker, msg, t); + } + + @Override + public boolean isWarnEnabled() { + return delegate().isWarnEnabled(); + } + + @Override + public void warn(String msg) { + delegate().warn(msg); + } + + @Override + public void warn(String format, Object arg) { + delegate().warn(format, arg); + } + + @Override + public void warn(String format, Object... arguments) { + delegate().warn(format, arguments); + } + + @Override + public void warn(String format, Object arg1, Object arg2) { + delegate().warn(format, arg1, arg2); + } + + @Override + public void warn(String msg, Throwable t) { + delegate().warn(msg, t); + } + + @Override + public boolean isWarnEnabled(Marker marker) { + return delegate().isWarnEnabled(marker); + } + + @Override + public void warn(Marker marker, String msg) { + delegate().warn(marker, msg); + } + + @Override + public void warn(Marker marker, String format, Object arg) { + delegate().warn(marker, format, arg); + } + + @Override + public void warn(Marker marker, String format, Object arg1, Object arg2) { + delegate().warn(marker, format, arg1, arg2); + } + + @Override + public void warn(Marker marker, String format, Object... arguments) { + delegate().warn(marker, format, arguments); + } + + @Override + public void warn(Marker marker, String msg, Throwable t) { + delegate().warn(marker, msg, t); + } + + @Override + public boolean isErrorEnabled() { + return delegate().isErrorEnabled(); + } + + @Override + public void error(String msg) { + delegate().error(msg); + } + + @Override + public void error(String format, Object arg) { + delegate().error(format, arg); + } + + @Override + public void error(String format, Object arg1, Object arg2) { + delegate().error(format, arg1, arg2); + } + + @Override + public void error(String format, Object... arguments) { + delegate().error(format, arguments); + } + + @Override + public void error(String msg, Throwable t) { + delegate().error(msg, t); + } + + @Override + public boolean isErrorEnabled(Marker marker) { + return delegate().isErrorEnabled(marker); + } + + @Override + public void error(Marker marker, String msg) { + delegate().error(marker, msg); + } + + @Override + public void error(Marker marker, String format, Object arg) { + delegate().error(marker, format, arg); + } + + @Override + public void error(Marker marker, String format, Object arg1, Object arg2) { + delegate().error(marker, format, arg1, arg2); + } + + @Override + public void error(Marker marker, String format, Object... arguments) { + delegate().error(marker, format, arguments); + } + + @Override + public void error(Marker marker, String msg, Throwable t) { + delegate().error(marker, msg, t); + } +} diff --git a/logger/src/main/java/org/inksnow/cputil/logger/AuroraLoggerFactory.java b/logger/src/main/java/org/inksnow/cputil/logger/AuroraLoggerFactory.java new file mode 100644 index 0000000..7b6d87c --- /dev/null +++ b/logger/src/main/java/org/inksnow/cputil/logger/AuroraLoggerFactory.java @@ -0,0 +1,69 @@ +package org.inksnow.cputil.logger; + +import org.inksnow.cputil.logger.impl.jul.JulLoggerFactory; +import org.inksnow.cputil.logger.impl.log4j2.Log4j2LoggerFactory; +import org.inksnow.cputil.logger.impl.parent.AuroraParentLogger; +import org.slf4j.ILoggerFactory; +import org.slf4j.Logger; + +public class AuroraLoggerFactory implements ILoggerFactory { + private static AuroraLoggerFactory INSTANCE; + + private ILoggerFactory provider; + private int currentVersion = 0; + + public AuroraLoggerFactory() { + if (INSTANCE != null) { + throw new IllegalStateException("AuroraLoggerFactory is a singleton"); + } + INSTANCE = this; + this.provider = selectDefaultProvider(); + } + + public static AuroraLoggerFactory instance() { + return INSTANCE; + } + + private ILoggerFactory selectDefaultProvider() { + try { + Class.forName(AuroraLogger.UNMAPPED_SLF4J_PACKAGE_NAME + ".Logger", false, Logger.class.getClassLoader()); + + if (!Logger.class.getName().equals(AuroraLogger.UNMAPPED_SLF4J_PACKAGE_NAME + ".Logger")) { + return new AuroraParentLogger(false); + } + } catch (ClassNotFoundException e) { + // + } + + try { + Class.forName("org.apache.logging.log4j.Logger", false, Logger.class.getClassLoader()); + return new Log4j2LoggerFactory(); + } catch (ClassNotFoundException e) { + // + } + + return new JulLoggerFactory(); + } + + public void setProvider(ILoggerFactory provider) { + if (this.provider != provider) { + this.provider = provider; + currentVersion++; + } + } + + @Override + public Logger getLogger(String name) { + return new AuroraLogger(this, name); + } + + public Logger updateDelegate(AuroraLogger auroraLogger) { + if (auroraLogger.version != currentVersion) { + Logger newLogger = provider.getLogger(auroraLogger.name); + auroraLogger.version = currentVersion; + auroraLogger.delegate = newLogger; + return newLogger; + } + return auroraLogger.delegate; + } +} diff --git a/logger/src/main/java/org/inksnow/cputil/logger/impl/jul/JulLoggerAdapter.java b/logger/src/main/java/org/inksnow/cputil/logger/impl/jul/JulLoggerAdapter.java new file mode 100644 index 0000000..b183c4e --- /dev/null +++ b/logger/src/main/java/org/inksnow/cputil/logger/impl/jul/JulLoggerAdapter.java @@ -0,0 +1,571 @@ +package org.inksnow.cputil.logger.impl.jul; + +import org.slf4j.Logger; +import org.slf4j.event.EventConstants; +import org.slf4j.event.LoggingEvent; +import org.slf4j.helpers.FormattingTuple; +import org.slf4j.helpers.MarkerIgnoringBase; +import org.slf4j.helpers.MessageFormatter; +import org.slf4j.spi.LocationAwareLogger; + +import java.util.logging.Level; +import java.util.logging.LogRecord; + +public final class JulLoggerAdapter extends MarkerIgnoringBase implements Logger { + static String SELF = JulLoggerAdapter.class.getName(); + static String SUPER = MarkerIgnoringBase.class.getName(); + transient final java.util.logging.Logger logger; + + // WARN: JDK14LoggerAdapter constructor should have only package access so + // that only JDK14LoggerFactory be able to create one. + JulLoggerAdapter(java.util.logging.Logger logger) { + this.logger = logger; + this.name = logger.getName(); + } + + public boolean isTraceEnabled() { + return logger.isLoggable(Level.FINEST); + } + + public void trace(String msg) { + if (logger.isLoggable(Level.FINEST)) { + log(SELF, Level.FINEST, msg, null); + } + } + + public void trace(String format, Object arg) { + if (logger.isLoggable(Level.FINEST)) { + FormattingTuple ft = MessageFormatter.format(format, arg); + log(SELF, Level.FINEST, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log a message at level FINEST according to the specified format and + * arguments. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the FINEST level. + *

+ * + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + public void trace(String format, Object arg1, Object arg2) { + if (logger.isLoggable(Level.FINEST)) { + FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); + log(SELF, Level.FINEST, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log a message at level FINEST according to the specified format and + * arguments. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the FINEST level. + *

+ * + * @param format the format string + * @param argArray an array of arguments + */ + public void trace(String format, Object... argArray) { + if (logger.isLoggable(Level.FINEST)) { + FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray); + log(SELF, Level.FINEST, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log an exception (throwable) at level FINEST with an accompanying message. + * + * @param msg the message accompanying the exception + * @param t the exception (throwable) to log + */ + public void trace(String msg, Throwable t) { + if (logger.isLoggable(Level.FINEST)) { + log(SELF, Level.FINEST, msg, t); + } + } + + /** + * Is this logger instance enabled for the FINE level? + * + * @return True if this Logger is enabled for level FINE, false otherwise. + */ + public boolean isDebugEnabled() { + return logger.isLoggable(Level.FINE); + } + + /** + * Log a message object at level FINE. + * + * @param msg - the message object to be logged + */ + public void debug(String msg) { + if (logger.isLoggable(Level.FINE)) { + log(SELF, Level.FINE, msg, null); + } + } + + /** + * Log a message at level FINE according to the specified format and argument. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for level FINE. + *

+ * + * @param format the format string + * @param arg the argument + */ + public void debug(String format, Object arg) { + if (logger.isLoggable(Level.FINE)) { + FormattingTuple ft = MessageFormatter.format(format, arg); + log(SELF, Level.FINE, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log a message at level FINE according to the specified format and + * arguments. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the FINE level. + *

+ * + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + public void debug(String format, Object arg1, Object arg2) { + if (logger.isLoggable(Level.FINE)) { + FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); + log(SELF, Level.FINE, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log a message at level FINE according to the specified format and + * arguments. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the FINE level. + *

+ * + * @param format the format string + * @param argArray an array of arguments + */ + public void debug(String format, Object... argArray) { + if (logger.isLoggable(Level.FINE)) { + FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray); + log(SELF, Level.FINE, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log an exception (throwable) at level FINE with an accompanying message. + * + * @param msg the message accompanying the exception + * @param t the exception (throwable) to log + */ + public void debug(String msg, Throwable t) { + if (logger.isLoggable(Level.FINE)) { + log(SELF, Level.FINE, msg, t); + } + } + + /** + * Is this logger instance enabled for the INFO level? + * + * @return True if this Logger is enabled for the INFO level, false otherwise. + */ + public boolean isInfoEnabled() { + return logger.isLoggable(Level.INFO); + } + + /** + * Log a message object at the INFO level. + * + * @param msg - the message object to be logged + */ + public void info(String msg) { + if (logger.isLoggable(Level.INFO)) { + log(SELF, Level.INFO, msg, null); + } + } + + /** + * Log a message at level INFO according to the specified format and argument. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the INFO level. + *

+ * + * @param format the format string + * @param arg the argument + */ + public void info(String format, Object arg) { + if (logger.isLoggable(Level.INFO)) { + FormattingTuple ft = MessageFormatter.format(format, arg); + log(SELF, Level.INFO, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log a message at the INFO level according to the specified format and + * arguments. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the INFO level. + *

+ * + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + public void info(String format, Object arg1, Object arg2) { + if (logger.isLoggable(Level.INFO)) { + FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); + log(SELF, Level.INFO, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log a message at level INFO according to the specified format and + * arguments. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the INFO level. + *

+ * + * @param format the format string + * @param argArray an array of arguments + */ + public void info(String format, Object... argArray) { + if (logger.isLoggable(Level.INFO)) { + FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray); + log(SELF, Level.INFO, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log an exception (throwable) at the INFO level with an accompanying + * message. + * + * @param msg the message accompanying the exception + * @param t the exception (throwable) to log + */ + public void info(String msg, Throwable t) { + if (logger.isLoggable(Level.INFO)) { + log(SELF, Level.INFO, msg, t); + } + } + + /** + * Is this logger instance enabled for the WARNING level? + * + * @return True if this Logger is enabled for the WARNING level, false + * otherwise. + */ + public boolean isWarnEnabled() { + return logger.isLoggable(Level.WARNING); + } + + /** + * Log a message object at the WARNING level. + * + * @param msg - the message object to be logged + */ + public void warn(String msg) { + if (logger.isLoggable(Level.WARNING)) { + log(SELF, Level.WARNING, msg, null); + } + } + + /** + * Log a message at the WARNING level according to the specified format and + * argument. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the WARNING level. + *

+ * + * @param format the format string + * @param arg the argument + */ + public void warn(String format, Object arg) { + if (logger.isLoggable(Level.WARNING)) { + FormattingTuple ft = MessageFormatter.format(format, arg); + log(SELF, Level.WARNING, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log a message at the WARNING level according to the specified format and + * arguments. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the WARNING level. + *

+ * + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + public void warn(String format, Object arg1, Object arg2) { + if (logger.isLoggable(Level.WARNING)) { + FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); + log(SELF, Level.WARNING, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log a message at level WARNING according to the specified format and + * arguments. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the WARNING level. + *

+ * + * @param format the format string + * @param argArray an array of arguments + */ + public void warn(String format, Object... argArray) { + if (logger.isLoggable(Level.WARNING)) { + FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray); + log(SELF, Level.WARNING, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log an exception (throwable) at the WARNING level with an accompanying + * message. + * + * @param msg the message accompanying the exception + * @param t the exception (throwable) to log + */ + public void warn(String msg, Throwable t) { + if (logger.isLoggable(Level.WARNING)) { + log(SELF, Level.WARNING, msg, t); + } + } + + /** + * Is this logger instance enabled for level SEVERE? + * + * @return True if this Logger is enabled for level SEVERE, false otherwise. + */ + public boolean isErrorEnabled() { + return logger.isLoggable(Level.SEVERE); + } + + /** + * Log a message object at the SEVERE level. + * + * @param msg - the message object to be logged + */ + public void error(String msg) { + if (logger.isLoggable(Level.SEVERE)) { + log(SELF, Level.SEVERE, msg, null); + } + } + + /** + * Log a message at the SEVERE level according to the specified format and + * argument. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the SEVERE level. + *

+ * + * @param format the format string + * @param arg the argument + */ + public void error(String format, Object arg) { + if (logger.isLoggable(Level.SEVERE)) { + FormattingTuple ft = MessageFormatter.format(format, arg); + log(SELF, Level.SEVERE, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log a message at the SEVERE level according to the specified format and + * arguments. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the SEVERE level. + *

+ * + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + public void error(String format, Object arg1, Object arg2) { + if (logger.isLoggable(Level.SEVERE)) { + FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); + log(SELF, Level.SEVERE, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log a message at level SEVERE according to the specified format and + * arguments. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the SEVERE level. + *

+ * + * @param format the format string + * @param arguments an array of arguments + */ + public void error(String format, Object... arguments) { + if (logger.isLoggable(Level.SEVERE)) { + FormattingTuple ft = MessageFormatter.arrayFormat(format, arguments); + log(SELF, Level.SEVERE, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log an exception (throwable) at the SEVERE level with an accompanying + * message. + * + * @param msg the message accompanying the exception + * @param t the exception (throwable) to log + */ + public void error(String msg, Throwable t) { + if (logger.isLoggable(Level.SEVERE)) { + log(SELF, Level.SEVERE, msg, t); + } + } + + /** + * Log the message at the specified level with the specified throwable if any. + * This method creates a LogRecord and fills in caller date before calling + * this instance's JDK14 logger. + *

+ * See bug report #13 for more details. + * + * @param level + * @param msg + * @param t + */ + private void log(String callerFQCN, Level level, String msg, Throwable t) { + // millis and thread are filled by the constructor + LogRecord record = new LogRecord(level, msg); + record.setLoggerName(getName()); + record.setThrown(t); + // Note: parameters in record are not set because SLF4J only + // supports a single formatting style + fillCallerData(callerFQCN, record); + logger.log(record); + } + + /** + * Fill in caller data if possible. + * + * @param record The record to update + */ + final private void fillCallerData(String callerFQCN, LogRecord record) { + StackTraceElement[] steArray = new Throwable().getStackTrace(); + + int selfIndex = -1; + for (int i = 0; i < steArray.length; i++) { + final String className = steArray[i].getClassName(); + if (className.equals(callerFQCN) || className.equals(SUPER)) { + selfIndex = i; + break; + } + } + + int found = -1; + for (int i = selfIndex + 1; i < steArray.length; i++) { + final String className = steArray[i].getClassName(); + if (!(className.equals(callerFQCN) || className.equals(SUPER))) { + found = i; + break; + } + } + + if (found != -1) { + StackTraceElement ste = steArray[found]; + // setting the class name has the side effect of setting + // the needToInferCaller variable to false. + record.setSourceClassName(ste.getClassName()); + record.setSourceMethodName(ste.getMethodName()); + } + } + + private Level slf4jLevelIntToJULLevel(int slf4jLevelInt) { + Level julLevel; + switch (slf4jLevelInt) { + case LocationAwareLogger.TRACE_INT: + julLevel = Level.FINEST; + break; + case LocationAwareLogger.DEBUG_INT: + julLevel = Level.FINE; + break; + case LocationAwareLogger.INFO_INT: + julLevel = Level.INFO; + break; + case LocationAwareLogger.WARN_INT: + julLevel = Level.WARNING; + break; + case LocationAwareLogger.ERROR_INT: + julLevel = Level.SEVERE; + break; + default: + throw new IllegalStateException("Level number " + slf4jLevelInt + " is not recognized."); + } + return julLevel; + } + + /** + * @since 1.7.15 + */ + public void log(LoggingEvent event) { + Level julLevel = slf4jLevelIntToJULLevel(event.getLevel().toInt()); + if (logger.isLoggable(julLevel)) { + LogRecord record = eventToRecord(event, julLevel); + logger.log(record); + } + } + + private LogRecord eventToRecord(LoggingEvent event, Level julLevel) { + String format = event.getMessage(); + Object[] arguments = event.getArgumentArray(); + FormattingTuple ft = MessageFormatter.arrayFormat(format, arguments); + if (ft.getThrowable() != null && event.getThrowable() != null) { + throw new IllegalArgumentException("both last element in argument array and last argument are of type Throwable"); + } + + Throwable t = event.getThrowable(); + if (ft.getThrowable() != null) { + t = ft.getThrowable(); + throw new IllegalStateException("fix above code"); + } + + LogRecord record = new LogRecord(julLevel, ft.getMessage()); + record.setLoggerName(event.getLoggerName()); + record.setMillis(event.getTimeStamp()); + record.setSourceClassName(EventConstants.NA_SUBST); + record.setSourceMethodName(EventConstants.NA_SUBST); + + record.setThrown(t); + return record; + } +} \ No newline at end of file diff --git a/logger/src/main/java/org/inksnow/cputil/logger/impl/jul/JulLoggerFactory.java b/logger/src/main/java/org/inksnow/cputil/logger/impl/jul/JulLoggerFactory.java new file mode 100644 index 0000000..d76b8e0 --- /dev/null +++ b/logger/src/main/java/org/inksnow/cputil/logger/impl/jul/JulLoggerFactory.java @@ -0,0 +1,19 @@ +package org.inksnow.cputil.logger.impl.jul; + +import org.slf4j.ILoggerFactory; +import org.slf4j.Logger; + +public class JulLoggerFactory implements ILoggerFactory { + public JulLoggerFactory() { + java.util.logging.Logger.getLogger(""); + } + + public Logger getLogger(String name) { + // the root logger is called "" in JUL + if (name.equalsIgnoreCase(Logger.ROOT_LOGGER_NAME)) { + name = ""; + } + + return new JulLoggerAdapter(java.util.logging.Logger.getLogger(name)); + } +} \ No newline at end of file diff --git a/logger/src/main/java/org/inksnow/cputil/logger/impl/log4j2/Log4j2Logger.java b/logger/src/main/java/org/inksnow/cputil/logger/impl/log4j2/Log4j2Logger.java new file mode 100644 index 0000000..829920c --- /dev/null +++ b/logger/src/main/java/org/inksnow/cputil/logger/impl/log4j2/Log4j2Logger.java @@ -0,0 +1,173 @@ +package org.inksnow.cputil.logger.impl.log4j2; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.spi.ExtendedLogger; +import org.slf4j.Logger; +import org.slf4j.helpers.MarkerIgnoringBase; + +public class Log4j2Logger extends MarkerIgnoringBase implements Logger { + + public static final String FQCN = Log4j2Logger.class.getName(); + private final String name; + private transient ExtendedLogger logger; + + public Log4j2Logger(final ExtendedLogger logger, final String name) { + this.logger = logger; + this.name = name; + } + + @Override + public void trace(final String format) { + logger.logIfEnabled(FQCN, Level.TRACE, null, format); + } + + @Override + public void trace(final String format, final Object o) { + logger.logIfEnabled(FQCN, Level.TRACE, null, format, o); + } + + @Override + public void trace(final String format, final Object arg1, final Object arg2) { + logger.logIfEnabled(FQCN, Level.TRACE, null, format, arg1, arg2); + } + + @Override + public void trace(final String format, final Object... args) { + logger.logIfEnabled(FQCN, Level.TRACE, null, format, args); + } + + @Override + public void trace(final String format, final Throwable t) { + logger.logIfEnabled(FQCN, Level.TRACE, null, format, t); + } + + @Override + public boolean isTraceEnabled() { + return logger.isEnabled(Level.TRACE, null, null); + } + + @Override + public void debug(final String format) { + logger.logIfEnabled(FQCN, Level.DEBUG, null, format); + } + + @Override + public void debug(final String format, final Object o) { + logger.logIfEnabled(FQCN, Level.DEBUG, null, format, o); + } + + @Override + public void debug(final String format, final Object arg1, final Object arg2) { + logger.logIfEnabled(FQCN, Level.DEBUG, null, format, arg1, arg2); + } + + @Override + public void debug(final String format, final Object... args) { + logger.logIfEnabled(FQCN, Level.DEBUG, null, format, args); + } + + @Override + public void debug(final String format, final Throwable t) { + logger.logIfEnabled(FQCN, Level.DEBUG, null, format, t); + } + + @Override + public boolean isDebugEnabled() { + return logger.isEnabled(Level.DEBUG, null, null); + } + + @Override + public void info(final String format) { + logger.logIfEnabled(FQCN, Level.INFO, null, format); + } + + @Override + public void info(final String format, final Object o) { + logger.logIfEnabled(FQCN, Level.INFO, null, format, o); + } + + @Override + public void info(final String format, final Object arg1, final Object arg2) { + logger.logIfEnabled(FQCN, Level.INFO, null, format, arg1, arg2); + } + + @Override + public void info(final String format, final Object... args) { + logger.logIfEnabled(FQCN, Level.INFO, null, format, args); + } + + @Override + public void info(final String format, final Throwable t) { + logger.logIfEnabled(FQCN, Level.INFO, null, format, t); + } + + @Override + public boolean isInfoEnabled() { + return logger.isEnabled(Level.INFO, null, null); + } + + @Override + public void warn(final String format) { + logger.logIfEnabled(FQCN, Level.WARN, null, format); + } + + @Override + public void warn(final String format, final Object o) { + logger.logIfEnabled(FQCN, Level.WARN, null, format, o); + } + + @Override + public void warn(final String format, final Object arg1, final Object arg2) { + logger.logIfEnabled(FQCN, Level.WARN, null, format, arg1, arg2); + } + + @Override + public void warn(final String format, final Object... args) { + logger.logIfEnabled(FQCN, Level.WARN, null, format, args); + } + + @Override + public void warn(final String format, final Throwable t) { + logger.logIfEnabled(FQCN, Level.WARN, null, format, t); + } + + @Override + public boolean isWarnEnabled() { + return logger.isEnabled(Level.WARN, null, null); + } + + @Override + public void error(final String format) { + logger.logIfEnabled(FQCN, Level.ERROR, null, format); + } + + @Override + public void error(final String format, final Object o) { + logger.logIfEnabled(FQCN, Level.ERROR, null, format, o); + } + + @Override + public void error(final String format, final Object arg1, final Object arg2) { + logger.logIfEnabled(FQCN, Level.ERROR, null, format, arg1, arg2); + } + + @Override + public void error(final String format, final Object... args) { + logger.logIfEnabled(FQCN, Level.ERROR, null, format, args); + } + + @Override + public void error(final String format, final Throwable t) { + logger.logIfEnabled(FQCN, Level.ERROR, null, format, t); + } + + @Override + public boolean isErrorEnabled() { + return logger.isEnabled(Level.ERROR, null, null); + } + + @Override + public String getName() { + return name; + } +} diff --git a/logger/src/main/java/org/inksnow/cputil/logger/impl/log4j2/Log4j2LoggerFactory.java b/logger/src/main/java/org/inksnow/cputil/logger/impl/log4j2/Log4j2LoggerFactory.java new file mode 100644 index 0000000..8323e92 --- /dev/null +++ b/logger/src/main/java/org/inksnow/cputil/logger/impl/log4j2/Log4j2LoggerFactory.java @@ -0,0 +1,37 @@ +package org.inksnow.cputil.logger.impl.log4j2; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.spi.AbstractLoggerAdapter; +import org.apache.logging.log4j.spi.LoggerContext; +import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.StackLocatorUtil; +import org.inksnow.cputil.logger.AuroraLogger; +import org.slf4j.ILoggerFactory; +import org.slf4j.Logger; + +import java.util.function.Predicate; + +/** + * Log4j implementation of SLF4J ILoggerFactory interface. + */ +public class Log4j2LoggerFactory extends AbstractLoggerAdapter implements ILoggerFactory { + + private static final StatusLogger LOGGER = StatusLogger.getLogger(); + private static final Predicate> CALLER_PREDICATE = clazz -> + !AbstractLoggerAdapter.class.equals(clazz) && !clazz.getName().startsWith(AuroraLogger.SLF4J_PACKAGE_NAME); + + @Override + protected Logger newLogger(final String name, final LoggerContext context) { + final String key = Logger.ROOT_LOGGER_NAME.equals(name) ? LogManager.ROOT_LOGGER_NAME : name; + return new Log4j2Logger(context.getLogger(key), name); + } + + @Override + protected LoggerContext getContext() { + final Class anchor = LogManager.getFactory().isClassLoaderDependent() + ? StackLocatorUtil.getCallerClass(Log4j2LoggerFactory.class, CALLER_PREDICATE) + : null; + LOGGER.trace("Log4jLoggerFactory.getContext() found anchor {}", anchor); + return anchor == null ? LogManager.getContext(false) : getContext(anchor); + } +} diff --git a/logger/src/main/java/org/inksnow/cputil/logger/impl/parent/AuroraParentLogger.java b/logger/src/main/java/org/inksnow/cputil/logger/impl/parent/AuroraParentLogger.java new file mode 100644 index 0000000..92827d0 --- /dev/null +++ b/logger/src/main/java/org/inksnow/cputil/logger/impl/parent/AuroraParentLogger.java @@ -0,0 +1,179 @@ +package org.inksnow.cputil.logger.impl.parent; + +import org.inksnow.cputil.UnsafeUtil; +import org.inksnow.cputil.asm.SafeClassWriter; +import org.inksnow.cputil.classloader.AuroraClassLoader; +import org.inksnow.cputil.logger.AuroraLogger; +import org.objectweb.asm.*; +import org.slf4j.ILoggerFactory; +import org.slf4j.Logger; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.ProtectionDomain; +import java.util.function.Function; + +public class AuroraParentLogger implements ILoggerFactory { + public static final String PARENT_PACKAGE_NAME = new String(new char[]{ + 'o', 'r', 'g', '.', 's', 'l', 'f', '4', 'j' + }); + public static final String PARENT_INTERNAL_NAME = PARENT_PACKAGE_NAME.replace('.', '/'); + + public static final String SLF4J_PACKAGE_NAME = AuroraLogger.SLF4J_PACKAGE_NAME; + public static final String SLF4J_INTERNAL_NAME = SLF4J_PACKAGE_NAME.replace('.', '/'); + + private static final String LOGGER_PROXY_CLASS_NAME = SLF4J_PACKAGE_NAME + ".generated.LoggerProxy"; + private static final String LOGGER_PROXY_INTERNAL_NAME = LOGGER_PROXY_CLASS_NAME.replace('.', '/'); + + private static final String LOGGER_PROXY_FACTORY_CLASS_NAME = SLF4J_PACKAGE_NAME + ".generated.LoggerProxyFactory"; + private static final String LOGGER_PROXY_FACTORY_INTERNAL_NAME = LOGGER_PROXY_FACTORY_CLASS_NAME.replace('.', '/'); + + private final boolean enableMarker; + private final AuroraClassLoader classLoader; + private final Function loggerFunction; + + public AuroraParentLogger(boolean enableMarker) { + this.enableMarker = enableMarker; + this.classLoader = AuroraClassLoader.builder() + .parent(Logger.class.getClassLoader()) + .build(); + + byte[] loggerProxyClassBytes = generateLoggerProxy(classLoader); + try { + Files.write(Paths.get("LoggerProxy.class"), loggerProxyClassBytes); + } catch (IOException e) { + throw new RuntimeException(e); + } + Class loggerClass = classLoader.defineClass0(LOGGER_PROXY_CLASS_NAME, loggerProxyClassBytes, 0, loggerProxyClassBytes.length, (ProtectionDomain) null); + + byte[] loggerProxyFactoryClassBytes = generateLoggerFactory(classLoader); + Class loggerFactoryClass = classLoader.defineClass0(LOGGER_PROXY_FACTORY_CLASS_NAME, loggerProxyFactoryClassBytes, 0, loggerProxyFactoryClassBytes.length, (ProtectionDomain) null); + try { + this.loggerFunction = (Function) loggerFactoryClass.getConstructor().newInstance(); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw UnsafeUtil.unsafeThrow(e); + } + } + + @Override + public Logger getLogger(String name) { + return loggerFunction.apply(name); + } + + public boolean isAvailable() { + if (PARENT_PACKAGE_NAME.equals(SLF4J_PACKAGE_NAME)) { + return false; + } + try { + Class.forName(PARENT_PACKAGE_NAME + ".Logger", false, Logger.class.getClassLoader()); + } catch (ClassNotFoundException e) { + return false; + } + return true; + } + + private byte[] generateLoggerProxy(AuroraClassLoader classLoader) { + String parentLoggerName = PARENT_INTERNAL_NAME + "/Logger"; + + ClassWriter cw = new SafeClassWriter(classLoader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, LOGGER_PROXY_INTERNAL_NAME, null, "java/lang/Object", + new String[]{Logger.class.getName().replace('.', '/')}); + + { + FieldVisitor fv = cw.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, "delegate", "L" + parentLoggerName + ";", null, null); + fv.visitEnd(); + } + + { + MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "", "(L" + parentLoggerName + ";)V", null, null); + mv.visitCode(); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitFieldInsn(Opcodes.PUTFIELD, LOGGER_PROXY_INTERNAL_NAME, "delegate", "L" + parentLoggerName + ";"); + mv.visitInsn(Opcodes.RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + for (Method method : Logger.class.getDeclaredMethods()) { + if ((method.getModifiers() & Opcodes.ACC_STATIC) != 0) { + continue; + } + Type methodType = Type.getType(method); + Type[] argumentTypes = methodType.getArgumentTypes(); + Type returnType = methodType.getReturnType(); + + MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, method.getName(), methodType.getDescriptor(), null, null); + mv.visitCode(); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitFieldInsn(Opcodes.GETFIELD, LOGGER_PROXY_INTERNAL_NAME, "delegate", "L" + parentLoggerName + ";"); + + StringBuilder methodDescriptor = new StringBuilder("("); + int variableIndex = 1; + for (Type argumentType : argumentTypes) { + + if (argumentType.getSort() == Type.OBJECT && argumentType.getInternalName().equals(SLF4J_INTERNAL_NAME + "/Marker")) { + if (enableMarker) { + mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, SLF4J_INTERNAL_NAME + "/Marker", "getName", "()Ljava/lang/String;", true); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, PARENT_INTERNAL_NAME + "/MarkerFactory", "getMarker", "(Ljava/lang/String;)L" + PARENT_INTERNAL_NAME + "/Marker;", false); + methodDescriptor.append("L").append(PARENT_INTERNAL_NAME).append("/Marker;"); + } else { + variableIndex++; + } + } else { + mv.visitVarInsn(argumentType.getOpcode(Opcodes.ILOAD), variableIndex); + variableIndex += argumentType.getSize(); + methodDescriptor.append(argumentType.getDescriptor()); + } + } + methodDescriptor.append(")").append(returnType.getDescriptor()); + + mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, parentLoggerName, method.getName(), methodDescriptor.toString(), true); + mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN)); + + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + cw.visitEnd(); + return cw.toByteArray(); + } + + private byte[] generateLoggerFactory(AuroraClassLoader classLoader) { + ClassWriter cw = new SafeClassWriter(classLoader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, LOGGER_PROXY_FACTORY_INTERNAL_NAME, null, "java/lang/Object", + new String[]{"java/util/function/Function"}); + + { + MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(Opcodes.RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + { + MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "apply", "(Ljava/lang/Object;)Ljava/lang/Object;", null, null); + mv.visitCode(); + mv.visitTypeInsn(Opcodes.NEW, LOGGER_PROXY_INTERNAL_NAME); + mv.visitInsn(Opcodes.DUP); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/String"); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, PARENT_INTERNAL_NAME + "/LoggerFactory", "getLogger", "(Ljava/lang/String;)L" + PARENT_INTERNAL_NAME + "/Logger;", false); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, LOGGER_PROXY_INTERNAL_NAME, "", "(L" + PARENT_INTERNAL_NAME + "/Logger;)V", false); + mv.visitInsn(Opcodes.ARETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + cw.visitEnd(); + return cw.toByteArray(); + } +} diff --git a/logger/src/main/java/org/slf4j/impl/StaticLoggerBinder.java b/logger/src/main/java/org/slf4j/impl/StaticLoggerBinder.java new file mode 100644 index 0000000..b8974f8 --- /dev/null +++ b/logger/src/main/java/org/slf4j/impl/StaticLoggerBinder.java @@ -0,0 +1,33 @@ +package org.slf4j.impl; + +import org.inksnow.cputil.logger.AuroraLoggerFactory; +import org.slf4j.ILoggerFactory; +import org.slf4j.helpers.NOPLoggerFactory; +import org.slf4j.spi.LoggerFactoryBinder; + +public class StaticLoggerBinder implements LoggerFactoryBinder { + private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder(); + + public static final StaticLoggerBinder getSingleton() { + return SINGLETON; + } + + // to avoid constant folding by the compiler, this field must *not* be final + public static String REQUESTED_API_VERSION = "1.6.99"; // !final + + private static final String loggerFactoryClassStr = NOPLoggerFactory.class.getName(); + + private final ILoggerFactory loggerFactory; + + private StaticLoggerBinder() { + loggerFactory = new AuroraLoggerFactory(); + } + + public ILoggerFactory getLoggerFactory() { + return loggerFactory; + } + + public String getLoggerFactoryClassStr() { + return loggerFactoryClassStr; + } +} \ No newline at end of file diff --git a/logger/src/main/java/org/slf4j/impl/StaticMDCBinder.java b/logger/src/main/java/org/slf4j/impl/StaticMDCBinder.java new file mode 100644 index 0000000..563875c --- /dev/null +++ b/logger/src/main/java/org/slf4j/impl/StaticMDCBinder.java @@ -0,0 +1,23 @@ +package org.slf4j.impl; + +import org.slf4j.helpers.NOPMDCAdapter; +import org.slf4j.spi.MDCAdapter; + +public class StaticMDCBinder { + public static final StaticMDCBinder SINGLETON = new StaticMDCBinder(); + + private StaticMDCBinder() { + } + + public static final StaticMDCBinder getSingleton() { + return SINGLETON; + } + + public MDCAdapter getMDCA() { + return new NOPMDCAdapter(); + } + + public String getMDCAdapterClassStr() { + return NOPMDCAdapter.class.getName(); + } +} \ No newline at end of file diff --git a/logger/src/main/java/org/slf4j/impl/StaticMarkerBinder.java b/logger/src/main/java/org/slf4j/impl/StaticMarkerBinder.java new file mode 100644 index 0000000..5c971a9 --- /dev/null +++ b/logger/src/main/java/org/slf4j/impl/StaticMarkerBinder.java @@ -0,0 +1,28 @@ +package org.slf4j.impl; + +import org.slf4j.IMarkerFactory; +import org.slf4j.MarkerFactory; +import org.slf4j.helpers.BasicMarkerFactory; +import org.slf4j.spi.MarkerFactoryBinder; + +public class StaticMarkerBinder implements MarkerFactoryBinder { + public static final StaticMarkerBinder SINGLETON = new StaticMarkerBinder(); + + final IMarkerFactory markerFactory = new BasicMarkerFactory(); + + private StaticMarkerBinder() { + } + + public static StaticMarkerBinder getSingleton() { + return SINGLETON; + } + + public IMarkerFactory getMarkerFactory() { + return markerFactory; + } + + public String getMarkerFactoryClassStr() { + return BasicMarkerFactory.class.getName(); + } + +} \ No newline at end of file diff --git a/logger/src/test/java/org/inksnow/cputil/logger/RunTest.java b/logger/src/test/java/org/inksnow/cputil/logger/RunTest.java new file mode 100644 index 0000000..f8e4deb --- /dev/null +++ b/logger/src/test/java/org/inksnow/cputil/logger/RunTest.java @@ -0,0 +1,14 @@ +package org.inksnow.cputil.logger; + +import org.inksnow.cputil.logger.impl.parent.AuroraParentLogger; +import org.slf4j.Logger; + +import java.io.IOException; + +public class RunTest { + public static void main(String[] args) throws IOException { + Logger logger = new AuroraParentLogger(false).getLogger("Hello"); + + logger.info("Hello, world!"); + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index bf57bb3..260dced 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,2 +1,3 @@ rootProject.name = "aurora-cputil" include("database") +include("logger")