package org.argeo.api.cms;
import java.lang.System.Logger;
+import java.text.MessageFormat;
import java.util.Objects;
import java.util.function.Supplier;
-import org.argeo.api.cms.SystemLogger.Level;
-
/**
- * A Commons Logging / SLF4J style logging utilities wrapping a standard Java
- * platform {@link Logger}.
+ * A Commons Logging / SLF4J style logging utilities usually wrapping a standard
+ * Java platform {@link Logger}, but which can fallback to other mechanism, if a
+ * system logger is not available.
*/
public interface CmsLog {
- SystemLogger getLogger();
+ /*
+ * SYSTEM LOGGER STYLE METHODS
+ */
+ boolean isLoggable(Level level);
+
+ void log(Level level, Supplier<String> msgSupplier, Throwable thrown);
+
+ void log(Level level, String msg, Throwable thrown);
+
+ void log(Level level, String format, Object... params);
+
+ default void log(Level level, String msg) {
+ log(level, msg, (Throwable) null);
+ }
+
+ default void log(Level level, Supplier<String> msgSupplier) {
+ log(level, msgSupplier, (Throwable) null);
+ }
+
+ default void log(Level level, Object obj) {
+ Objects.requireNonNull(obj);
+ log(level, obj.toString());
+ }
+
+ /*
+ * SLF4j / COMMONS LOGGING STYLE METHODS
+ */
+ @Deprecated
+ CmsLog getLogger();
default boolean isDebugEnabled() {
return getLogger().isLoggable(Level.DEBUG);
getLogger().log(Level.ERROR, format, arguments);
}
+ /**
+ * Exact mapping of ${java.lang.System.Logger.Level}, in case it is not
+ * available.
+ */
+ public static enum Level {
+ ALL(Integer.MIN_VALUE), //
+ TRACE(400), //
+ DEBUG(500), //
+ INFO(800), //
+ WARNING(900), //
+ ERROR(1000), //
+ OFF(Integer.MAX_VALUE); //
+
+ final int severity;
+
+ private Level(int severity) {
+ this.severity = severity;
+ }
+
+ public final int getSeverity() {
+ return severity;
+ }
+ }
+
/*
* STATIC UTILITIES
*/
}
static CmsLog getLog(String name) {
- SystemLogger logger;
+ if (isSystemLoggerAvailable) {
+ return new SystemCmsLog(name);
+ } else { // typically Android
+ return new FallBackCmsLog();
+ }
+ }
+
+ static final boolean isSystemLoggerAvailable = isSystemLoggerAvailable();
+
+ static boolean isSystemLoggerAvailable() {
try {
- logger = new RealSystemLogger(name);
+ Logger logger = System.getLogger(CmsLog.class.getName());
+ logger.log(java.lang.System.Logger.Level.TRACE, () -> "System logger is available.");
+ return true;
} catch (NoSuchMethodError | NoClassDefFoundError e) {// Android
- logger = new FallBackSystemLogger();
+ return false;
}
- return new LoggerWrapper(logger);
+ }
+}
+
+/**
+ * Uses {@link System.Logger}, should be used on proper implementations of the
+ * Java platform.
+ */
+class SystemCmsLog implements CmsLog {
+ private final Logger logger;
+
+ SystemCmsLog(String name) {
+ logger = System.getLogger(name);
+ }
+
+ @Override
+ public boolean isLoggable(Level level) {
+ return logger.isLoggable(convertSystemLevel(level));
+ }
+
+ @Override
+ public void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
+ logger.log(convertSystemLevel(level), msgSupplier, thrown);
+ }
+
+ @Override
+ public void log(Level level, String msg, Throwable thrown) {
+ logger.log(convertSystemLevel(level), msg, thrown);
+ }
+
+ java.lang.System.Logger.Level convertSystemLevel(Level level) {
+ switch (level.severity) {
+ case Integer.MIN_VALUE:
+ return java.lang.System.Logger.Level.ALL;
+ case 400:
+ return java.lang.System.Logger.Level.TRACE;
+ case 500:
+ return java.lang.System.Logger.Level.DEBUG;
+ case 800:
+ return java.lang.System.Logger.Level.INFO;
+ case 900:
+ return java.lang.System.Logger.Level.WARNING;
+ case 1000:
+ return java.lang.System.Logger.Level.ERROR;
+ case Integer.MAX_VALUE:
+ return java.lang.System.Logger.Level.OFF;
+ default:
+ throw new IllegalArgumentException("Unexpected value: " + level.severity);
+ }
+ }
+
+ @Override
+ public void log(Level level, String format, Object... params) {
+ logger.log(convertSystemLevel(level), format, params);
}
+ @Override
+ public CmsLog getLogger() {
+ return this;
+ }
+};
+
+/** Dummy fallback for non-standard platforms such as Android. */
+class FallBackCmsLog implements CmsLog {
+ @Override
+ public boolean isLoggable(Level level) {
+ return level.getSeverity() >= 800;// INFO and higher
+ }
+
+ @Override
+ public void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
+ if (isLoggable(level))
+ if (thrown != null || level.getSeverity() >= 900) {
+ System.err.println(msgSupplier.get());
+ thrown.printStackTrace();
+ } else {
+ System.out.println(msgSupplier.get());
+ }
+ }
+
+ @Override
+ public void log(Level level, String msg, Throwable thrown) {
+ if (isLoggable(level))
+ if (thrown != null || level.getSeverity() >= 900) {
+ System.err.println(msg);
+ thrown.printStackTrace();
+ } else {
+ System.out.println(msg);
+ }
+ }
+
+ @Override
+ public void log(Level level, String format, Object... params) {
+ if (format == null)
+ return;
+ String msg = MessageFormat.format(format, params);
+ log(level, msg);
+ }
+
+ @Override
+ public CmsLog getLogger() {
+ return this;
+ }
}