package xapi.fu; import xapi.fu.Log.LogLevel; import static xapi.fu.Log.printLevel; /** * @author James X. Nelson (james@wetheinter.net) * Created on 07/11/15. */ public interface Log extends Debuggable { enum LogLevel { ALL, DEBUG, TRACE, INFO, WARN, ERROR ; public boolean isLoggable(LogLevel level) { return level.ordinal() >= ordinal(); } static LogLevel DEFAULT = valueOf(System.getProperty("xapi.log.level", "INFO")); } interface DefaultLog extends Log { @Override default void print(LogLevel level, String debug) { DefaultLoggers.DEFAULT.log(level, debug); } } default Log log(LogLevel level, Object ... values) { if (isLoggable(level)) { print(level, debug(values)); } return this; } default Log log(Class forClass, Object ... values) { LogLevel level = levelForClass(forClass); if (isLoggable(level)) { print(level, debug(values)); } return this; } default LogLevel levelForClass(Class forClass) { gen_logLevelForClass : { // This level will always be loggable, // but note the named block here; // use of this label allows use to mark anywhere we want to generate code // that translates a class parameter into a log level. // This functionality is being developed in a separate repository, // but this code is left here so this class can be used in testing that javac source transform plugin return getLogLevel(); } } void print(LogLevel level, String debug); default String maybePrintLevel(LogLevel level) { return printLevel(level); } default boolean isLoggable(LogLevel level) { LogLevel myLevel = getLogLevel(); return myLevel.ordinal() <= level.ordinal(); } default LogLevel getLogLevel() { return LogLevel.DEFAULT; } static String printLevel(LogLevel level) { return "["+level+"] "; } static Log normalize(Log log) { return log == null ? defaultLogger() : log; } static Log allLogs(Object ... maybeLogs) { Log stacked = null; for (Object maybeLog : maybeLogs) { if (maybeLog instanceof Log) { final Log log = (Log) maybeLog; if (stacked == null) { stacked = log; } else { final Log parents = stacked; stacked = (level, debug) -> { parents.log(level, debug); log.log(level, debug); }; } } } return stacked == null ? defaultLogger() : stacked; } static Log firstLog(Object ... log) { if (log == null) { // someone might send us a null array reference... return defaultLogger(); } for (Object maybe : log) { if (maybe instanceof Log) { return (Log) maybe; } } return defaultLogger(); } static Log defaultLogger() { return DefaultLoggers.DEFAULT; } static Log sysOut() { return DefaultLoggers.SYS_OUT; } static Log sysErr() { return DefaultLoggers.SYS_ERR; } static Log voidLogger() { return DefaultLoggers.VOID; } } interface DefaultLoggers { // Some default implementations for when you are lazy. // We stash them in this ugly package-private class, // to encourage the use of static methods in the Log class, // Log.defaultLogger() or Log.sysOut/Err. // This will allow magic-method injection to swap these out, if desired Log VOID = (level, debug) -> {}; Log SYS_OUT = (level, debug) -> System.out.println(printLevel(level) + debug); Log SYS_ERR = (level, debug) -> System.err.println(printLevel(level) + debug); Log DEFAULT = (level, debug) -> (level == LogLevel.ERROR ? SYS_OUT : SYS_ERR) .print(level, debug); }