From ebbcf50f74332bb84ed98b7f47ec72d2063c3c8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Fri, 11 Apr 2014 01:23:16 +0200 Subject: [PATCH] Remade logging system, cleaned up BaseApp. --- src/mightypork/gamecore/control/BaseApp.java | 110 ++++- src/mightypork/gamecore/control/GameLoop.java | 6 +- .../gamecore/control/SlickLogRedirector.java | 22 +- .../interf/{NoImpl.java => DefaultImpl.java} | 6 +- .../gamecore/gui/screens/Screen.java | 8 +- .../gamecore/gui/screens/ScreenLayer.java | 8 +- .../gamecore/gui/screens/ScreenRegistry.java | 4 +- src/mightypork/rogue/App.java | 89 +--- src/mightypork/rogue/CrashHandler.java | 16 - src/mightypork/rogue/Main.java | 16 + src/mightypork/rogue/Paths.java | 8 +- .../test_cat_sound/LayerFlyingCat.java | 2 - .../utils/logging/ArchivingLog.java | 125 ++++++ .../utils/logging/BaseLogMonitor.java | 39 ++ src/mightypork/utils/logging/Log.java | 287 ++++++++---- src/mightypork/utils/logging/LogInstance.java | 424 ------------------ src/mightypork/utils/logging/LogMonitor.java | 18 +- .../utils/logging/LogToSysoutMonitor.java | 23 +- src/mightypork/utils/logging/LogWriter.java | 70 +++ src/mightypork/utils/logging/SimpleLog.java | 251 +++++++++++ src/mightypork/utils/string/StringUtils.java | 1 + 21 files changed, 840 insertions(+), 693 deletions(-) rename src/mightypork/gamecore/control/interf/{NoImpl.java => DefaultImpl.java} (72%) delete mode 100644 src/mightypork/rogue/CrashHandler.java create mode 100644 src/mightypork/rogue/Main.java create mode 100644 src/mightypork/utils/logging/ArchivingLog.java create mode 100644 src/mightypork/utils/logging/BaseLogMonitor.java delete mode 100644 src/mightypork/utils/logging/LogInstance.java create mode 100644 src/mightypork/utils/logging/LogWriter.java create mode 100644 src/mightypork/utils/logging/SimpleLog.java diff --git a/src/mightypork/gamecore/control/BaseApp.java b/src/mightypork/gamecore/control/BaseApp.java index f810537..fed7fae 100644 --- a/src/mightypork/gamecore/control/BaseApp.java +++ b/src/mightypork/gamecore/control/BaseApp.java @@ -2,14 +2,16 @@ package mightypork.gamecore.control; import java.io.File; +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.logging.Level; import javax.swing.JOptionPane; import mightypork.gamecore.audio.SoundSystem; import mightypork.gamecore.control.bus.EventBus; import mightypork.gamecore.control.bus.events.*; +import mightypork.gamecore.control.interf.DefaultImpl; import mightypork.gamecore.control.interf.Destroyable; -import mightypork.gamecore.control.interf.NoImpl; import mightypork.gamecore.control.timing.Updateable; import mightypork.gamecore.gui.screens.ScreenRegistry; import mightypork.gamecore.input.InputSystem; @@ -17,7 +19,7 @@ import mightypork.gamecore.loading.AsyncResourceLoader; import mightypork.gamecore.render.DisplaySystem; import mightypork.utils.files.InstanceLock; import mightypork.utils.logging.Log; -import mightypork.utils.logging.LogInstance; +import mightypork.utils.logging.LogWriter; /** @@ -26,7 +28,7 @@ import mightypork.utils.logging.LogInstance; * * @author MightyPork */ -public abstract class BaseApp implements AppAccess { +public abstract class BaseApp implements AppAccess, UncaughtExceptionHandler { // modules private InputSystem inputSystem; @@ -40,8 +42,9 @@ public abstract class BaseApp implements AppAccess { /** * Start the application */ - public void start() + public final void start() { + Thread.setDefaultUncaughtExceptionHandler(this); initialize(); @@ -57,7 +60,6 @@ public abstract class BaseApp implements AppAccess { */ protected void initialize() { - /* * Lock working directory */ @@ -69,8 +71,11 @@ public abstract class BaseApp implements AppAccess { /* * Setup logging */ - final LogInstance log = createLog(); - org.newdawn.slick.util.Log.setLogSystem(new SlickLogRedirector(log)); + final LogWriter log = createLog(); + if (log != null) { + Log.setMainLogger(log); + org.newdawn.slick.util.Log.setLogSystem(new SlickLogRedirector(log)); + } // only here it makes sense to log. Log.i("=== Commencing initialization sequence ==="); @@ -82,6 +87,7 @@ public abstract class BaseApp implements AppAccess { eventBus = new EventBus(); Log.f3("Registering channels..."); + initDefaultBusChannels(eventBus); initBus(eventBus); /* @@ -139,16 +145,16 @@ public abstract class BaseApp implements AppAccess { * Called at the beginning of the initialization sequence, right after lock * was obtained. */ - @NoImpl + @DefaultImpl protected void preInit() { } /** - * + * Called at the end of init sequence, before main loop starts. */ - @NoImpl + @DefaultImpl protected void postInit() { } @@ -159,7 +165,13 @@ public abstract class BaseApp implements AppAccess { * * @return new log instance */ - protected abstract LogInstance createLog(); + @DefaultImpl + protected LogWriter createLog() + { + final LogWriter log = Log.create("runtime", new File("runtime.log")); + log.setLevel(Level.ALL); + return log; + } /** @@ -167,7 +179,12 @@ public abstract class BaseApp implements AppAccess { * * @param display */ - protected abstract void initDisplay(DisplaySystem display); + @DefaultImpl + protected void initDisplay(DisplaySystem display) + { + display.createMainWindow(800, 600, true, false, "BaseApp using LWJGL display."); + display.setTargetFps(60); + } /** @@ -175,7 +192,10 @@ public abstract class BaseApp implements AppAccess { * * @param audio */ - protected abstract void initSoundSystem(SoundSystem audio); + @DefaultImpl + protected void initSoundSystem(SoundSystem audio) + { + } /** @@ -183,14 +203,20 @@ public abstract class BaseApp implements AppAccess { * * @param input */ - protected abstract void initInputSystem(InputSystem input); + @DefaultImpl + protected void initInputSystem(InputSystem input) + { + } /** * Initialize resource banks; {@link AsyncResourceLoader} is already * started. */ - protected abstract void initResources(); + @DefaultImpl + protected void initResources() + { + } /** @@ -198,7 +224,10 @@ public abstract class BaseApp implements AppAccess { * * @param screens */ - protected abstract void initScreens(ScreenRegistry screens); + @DefaultImpl + protected void initScreens(ScreenRegistry screens) + { + } /** @@ -215,7 +244,13 @@ public abstract class BaseApp implements AppAccess { * * @param bus */ + @DefaultImpl protected void initBus(EventBus bus) + { + } + + + private void initDefaultBusChannels(EventBus bus) { // framework events bus.addChannel(DestroyEvent.class, Destroyable.class); @@ -259,7 +294,7 @@ public abstract class BaseApp implements AppAccess { */ protected void onLockError() { - System.err.println("Could not obtain lock file.\nOnly one instance can run at a time."); + Log.e("Could not obtain lock file.\nOnly one instance can run at a time."); //@formatter:off JOptionPane.showMessageDialog( @@ -279,7 +314,11 @@ public abstract class BaseApp implements AppAccess { * * @return lock file, or null to disable lock. */ - protected abstract File getLockFile(); + @DefaultImpl + protected File getLockFile() + { + return new File(".lock"); + } @Override @@ -310,14 +349,41 @@ public abstract class BaseApp implements AppAccess { } + @DefaultImpl + protected void beforeShutdown() + { + } + + + @Override + public final void uncaughtException(Thread t, Throwable e) + { + onCrash(e); + } + + + @DefaultImpl + protected void onCrash(Throwable e) + { + Log.e("The game has crashed.", e); + shutdown(); + } + + @Override - public void shutdown() + public final void shutdown() { + beforeShutdown(); + Log.i("Shutting down subsystems..."); - if (getEventBus() != null) { - getEventBus().send(new DestroyEvent()); - getEventBus().destroy(); + try { + if (getEventBus() != null) { + getEventBus().send(new DestroyEvent()); + getEventBus().destroy(); + } + } catch (final Exception e) { + // ignore it } Log.i("Terminating..."); diff --git a/src/mightypork/gamecore/control/GameLoop.java b/src/mightypork/gamecore/control/GameLoop.java index 18bfa53..a688547 100644 --- a/src/mightypork/gamecore/control/GameLoop.java +++ b/src/mightypork/gamecore/control/GameLoop.java @@ -6,7 +6,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; import mightypork.gamecore.control.bus.events.MainLoopTaskRequest; import mightypork.gamecore.control.bus.events.UpdateEvent; -import mightypork.gamecore.control.interf.NoImpl; +import mightypork.gamecore.control.interf.DefaultImpl; import mightypork.gamecore.control.timing.TimerDelta; import mightypork.gamecore.gui.components.Renderable; import mightypork.gamecore.gui.screens.ScreenRegistry; @@ -78,7 +78,7 @@ public abstract class GameLoop extends AppModule implements MainLoopTaskRequest. /** * Called before render */ - @NoImpl + @DefaultImpl protected void beforeRender() { // @@ -88,7 +88,7 @@ public abstract class GameLoop extends AppModule implements MainLoopTaskRequest. /** * Called after render */ - @NoImpl + @DefaultImpl protected void afterRender() { // diff --git a/src/mightypork/gamecore/control/SlickLogRedirector.java b/src/mightypork/gamecore/control/SlickLogRedirector.java index 077e3cc..d861c12 100644 --- a/src/mightypork/gamecore/control/SlickLogRedirector.java +++ b/src/mightypork/gamecore/control/SlickLogRedirector.java @@ -1,7 +1,9 @@ package mightypork.gamecore.control; -import mightypork.utils.logging.LogInstance; +import java.util.logging.Level; + +import mightypork.utils.logging.LogWriter; import org.newdawn.slick.util.LogSystem; @@ -13,13 +15,13 @@ import org.newdawn.slick.util.LogSystem; */ public class SlickLogRedirector implements LogSystem { - LogInstance l; + LogWriter l; /** * @param log log to redirect into */ - public SlickLogRedirector(LogInstance log) { + public SlickLogRedirector(LogWriter log) { this.l = log; } @@ -27,49 +29,49 @@ public class SlickLogRedirector implements LogSystem { @Override public void error(String msg, Throwable e) { - l.e(msg, e); + l.log(Level.SEVERE, msg, e); } @Override public void error(Throwable e) { - l.e(e); + l.log(Level.SEVERE, null, e); } @Override public void error(String msg) { - l.e(msg); + l.log(Level.SEVERE, msg); } @Override public void warn(String msg) { - l.w(msg); + l.log(Level.WARNING, msg); } @Override public void warn(String msg, Throwable e) { - l.e(msg, e); + l.log(Level.WARNING, msg, e); } @Override public void info(String msg) { - l.i(msg); + l.log(Level.INFO, msg); } @Override public void debug(String msg) { - l.f3(msg); + l.log(Level.FINEST, msg); } } diff --git a/src/mightypork/gamecore/control/interf/NoImpl.java b/src/mightypork/gamecore/control/interf/DefaultImpl.java similarity index 72% rename from src/mightypork/gamecore/control/interf/NoImpl.java rename to src/mightypork/gamecore/control/interf/DefaultImpl.java index 6efd2f7..c6d33de 100644 --- a/src/mightypork/gamecore/control/interf/NoImpl.java +++ b/src/mightypork/gamecore/control/interf/DefaultImpl.java @@ -9,14 +9,14 @@ import java.lang.annotation.Target; /** - * Indicates that the marked method is not implemented and can be safely - * overriden. + * Marked method can be safely overriden; it's left blank (or with default + * implementation) as a convenience. * * @author MightyPork */ @Documented @Retention(RetentionPolicy.SOURCE) @Target(value = { ElementType.METHOD }) -public @interface NoImpl { +public @interface DefaultImpl { // } diff --git a/src/mightypork/gamecore/gui/screens/Screen.java b/src/mightypork/gamecore/gui/screens/Screen.java index a672c2c..87274ee 100644 --- a/src/mightypork/gamecore/gui/screens/Screen.java +++ b/src/mightypork/gamecore/gui/screens/Screen.java @@ -4,7 +4,7 @@ package mightypork.gamecore.gui.screens; import mightypork.gamecore.control.AppAccess; import mightypork.gamecore.control.AppSubModule; import mightypork.gamecore.control.bus.events.ScreenChangeEvent; -import mightypork.gamecore.control.interf.NoImpl; +import mightypork.gamecore.control.interf.DefaultImpl; import mightypork.gamecore.gui.components.Renderable; import mightypork.gamecore.gui.constraints.RectConstraint; import mightypork.gamecore.input.KeyBinder; @@ -130,7 +130,7 @@ public abstract class Screen extends AppSubModule implements Renderable, KeyBind /** * Called when the screen becomes active */ - @NoImpl + @DefaultImpl protected void onScreenEnter() { // @@ -140,7 +140,7 @@ public abstract class Screen extends AppSubModule implements Renderable, KeyBind /** * Called when the screen is no longer active */ - @NoImpl + @DefaultImpl protected void onScreenLeave() { // @@ -152,7 +152,7 @@ public abstract class Screen extends AppSubModule implements Renderable, KeyBind * * @param size screen size */ - @NoImpl + @DefaultImpl protected void onSizeChanged(Coord size) { // diff --git a/src/mightypork/gamecore/gui/screens/ScreenLayer.java b/src/mightypork/gamecore/gui/screens/ScreenLayer.java index 5c355f8..5a47c5e 100644 --- a/src/mightypork/gamecore/gui/screens/ScreenLayer.java +++ b/src/mightypork/gamecore/gui/screens/ScreenLayer.java @@ -2,7 +2,7 @@ package mightypork.gamecore.gui.screens; import mightypork.gamecore.control.AppSubModule; -import mightypork.gamecore.control.interf.NoImpl; +import mightypork.gamecore.control.interf.DefaultImpl; import mightypork.gamecore.gui.components.Renderable; import mightypork.gamecore.gui.constraints.RectConstraint; import mightypork.gamecore.input.KeyBinder; @@ -75,7 +75,7 @@ public abstract class ScreenLayer extends AppSubModule implements Comparable oldLogs = FileUtils.listDirectory(log_dir, new FileFilter() { + + @Override + public boolean accept(File f) + { + if (f.isDirectory()) return false; + if (!f.getName().endsWith(getSuffix())) return false; + if (!f.getName().startsWith(fname)) return false; + + return true; + } + + }); + + Collections.sort(oldLogs, new Comparator() { + + @Override + public int compare(File o1, File o2) + { + return o1.getName().compareTo(o2.getName()); + } + }); + + // playing with fireee + for (int i = 0; i < oldLogs.size() - logs_to_keep; i++) { + oldLogs.get(i).delete(); + } + } + + + /** + * @return log filename suffix + */ + private String getSuffix() + { + return FileUtils.getExtension(getFile()); + } +} diff --git a/src/mightypork/utils/logging/BaseLogMonitor.java b/src/mightypork/utils/logging/BaseLogMonitor.java new file mode 100644 index 0000000..728320b --- /dev/null +++ b/src/mightypork/utils/logging/BaseLogMonitor.java @@ -0,0 +1,39 @@ +package mightypork.utils.logging; + + +import java.util.logging.Level; + + +public abstract class BaseLogMonitor implements LogMonitor { + + private boolean enabled = true; + private Level accepted = Level.ALL; + + + @Override + public void onMessageLogged(Level level, String message) + { + if (!enabled) return; + if (accepted.intValue() > level.intValue()) return; + + logMessage(level, message); + } + + + protected abstract void logMessage(Level level, String message); + + + @Override + public void setLevel(Level level) + { + this.accepted = level; + } + + + @Override + public void enable(boolean flag) + { + this.enabled = flag; + } + +} diff --git a/src/mightypork/utils/logging/Log.java b/src/mightypork/utils/logging/Log.java index 948de66..0286e58 100644 --- a/src/mightypork/utils/logging/Log.java +++ b/src/mightypork/utils/logging/Log.java @@ -2,195 +2,306 @@ package mightypork.utils.logging; import java.io.File; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.HashMap; +import java.util.logging.Level; +import mightypork.utils.string.StringUtils; + +/** + * A log. + * + * @author MightyPork + */ public class Log { - /** enable static logging */ - private static boolean staticLogging = true; + private static LogWriter main = null; + private static boolean enabled = true; + private static final LogToSysoutMonitor sysoMonitor = new LogToSysoutMonitor(); + private static final long start_ms = System.currentTimeMillis(); + + private static HashMap logs = new HashMap<>(); + + + /** + * Create a logger. If another with the name already exists, it'll be + * retrieved instead of creating a new one. + * + * @param logName log name (used for filename, should be application-unique) + * @param logFile log file; old logs will be kept here too. + * @param oldLogsCount number of old logs to keep, -1 infinite, 0 none. + * @return the created Log instance + */ + public static synchronized LogWriter create(String logName, File logFile, int oldLogsCount) + { + if (logs.containsKey(logName)) return logs.get(logName); + + final ArchivingLog log = new ArchivingLog(logName, logFile, oldLogsCount); + log.init(); + + logs.put(logName, log); + + return log; + } + + + /** + * Create a logger. If another with the name already exists, it'll be + * retrieved instead of creating a new one. + * + * @param logName log name (used for filename, must be application-unique) + * @param logFile log file; old logs will be kept here too. + * @return the created Log instance + */ + public static synchronized LogWriter create(String logName, File logFile) + { + if (logs.containsKey(logName)) return logs.get(logName); + + final SimpleLog log = new SimpleLog(logName, logFile); + log.init(); + + logs.put(logName, log); + + return log; + } + + + public static void setMainLogger(LogWriter log) + { + main = log; + } + + + public static void addMonitor(LogMonitor mon) + { + assertInited(); + + main.addMonitor(mon); + } + + + public static void removeMonitor(LogMonitor mon) + { + assertInited(); + + main.removeMonitor(mon); + } + + + private static void assertInited() + { + if (main == null) throw new IllegalStateException("Main logger not initialized."); + } + + + /** + * Log a message + * + * @param level message level + * @param msg message text + */ + public static void log(Level level, String msg) + { + if (enabled) { + final long ms = System.currentTimeMillis() - start_ms; + sysoMonitor.onMessageLogged(level, formatMessage(level, msg, null, ms)); + + if (main != null) { + main.log(level, msg); + } + } + } + + + /** + * Log a message + * + * @param level message level + * @param msg message text + * @param t thrown exception + */ + public static void log(Level level, String msg, Throwable t) + { + if (enabled) { + final long ms = System.currentTimeMillis() - start_ms; + sysoMonitor.onMessageLogged(level, formatMessage(level, msg, t, ms)); + + if (main != null) { + main.log(level, msg, t); + } + } + } /** - * Log FINE message in main logger + * Log FINE message * * @param msg message */ public static void f1(String msg) { - if (staticLogging && ready()) main.f1(msg); + log(Level.FINE, msg); } /** - * Log FINER message in main logger + * Log FINER message * * @param msg message */ public static void f2(String msg) { - if (staticLogging && ready()) main.f2(msg); + log(Level.FINER, msg); } /** - * Log FINEST message in main logger + * Log FINEST message * * @param msg message */ public static void f3(String msg) { - if (staticLogging && ready()) main.f3(msg); + log(Level.FINEST, msg); } /** - * Log INFO message in main logger + * Log INFO message * * @param msg message */ public static void i(String msg) { - if (staticLogging && ready()) main.i(msg); + log(Level.INFO, msg); } /** - * Log WARNING message in main logger + * Log WARNING message (less severe than ERROR) * * @param msg message */ public static void w(String msg) { - if (staticLogging && ready()) { - main.w(msg); - } else { - System.err.println(msg); - } + log(Level.WARNING, msg); } /** - * Log ERROR message in main logger + * Log ERROR message * * @param msg message */ public static void e(String msg) { - if (staticLogging && ready()) { - main.e(msg); - } else { - System.err.println(msg); - } + log(Level.SEVERE, msg); } /** - * Log EXCEPTION and ERROR message in main logger + * Log THROWING message * * @param msg message * @param thrown thrown exception */ public static void e(String msg, Throwable thrown) { - if (staticLogging && ready()) { - main.e(msg, thrown); - } else { - System.err.println(msg); - thrown.printStackTrace(); - } + log(Level.SEVERE, msg, thrown); } /** - * Log EXCEPTION in main logger + * Log exception thrown * * @param thrown thrown exception */ public static void e(Throwable thrown) { - if (staticLogging && ready()) { - main.e(thrown); - } else { - thrown.printStackTrace(); - } + log(Level.SEVERE, null, thrown); } public static void enable(boolean flag) { - if (staticLogging && ready()) main.enable(flag); + enabled = flag; } - /** - * Enable / disable static log delegate methods - * - * @param flag enable - */ - public static void enableStaticLogging(boolean flag) + public static void setSysoutLevel(Level level) { - staticLogging = flag; + sysoMonitor.setLevel(level); } - private static HashMap logs = new HashMap<>(); - private static LogInstance main = null; - - /** - * Create a logger. If this is the first logger made, then it'll be made - * available via the static methods. - * - * @param logName log name (used for filename, must be application-unique) - * @param logsDir directory to store logs in - * @param oldLogsCount number of old logs to keep, -1 for infinite, 0 for - * none. - * @return the created Log instance - */ - public static synchronized LogInstance create(String logName, File logsDir, int oldLogsCount) + public static void setLevel(Level level) { - if (logs.containsKey(logName)) return logs.get(logName); - final LogInstance log = new LogInstance(logName, logsDir, oldLogsCount); - if (main == null) main = log; - logs.put(logName, log); + assertInited(); - return log; + main.setLevel(level); } /** - * Create a logger. If this is the first logger made, then it'll be made - * available via the static methods.
- * Old logs will be kept. + * Get stack trace from throwable * - * @param logName log name (used for filename, must be application-unique) - * @param logsDir directory to store logs in - * @return the created Log instance + * @param t + * @return trace */ - public static synchronized LogInstance create(String logName, File logsDir) + public static String getStackTrace(Throwable t) { - if (logs.containsKey(logName)) return logs.get(logName); - final LogInstance log = new LogInstance(logName, logsDir, -1); - if (main == null) main = log; - logs.put(logName, log); - - return log; + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw, true); + t.printStackTrace(pw); + pw.flush(); + sw.flush(); + return sw.toString(); } - public int addMonitor(LogMonitor mon) + static String formatMessage(Level level, String message, Throwable throwable, long start_ms) { - if (!ready()) throw new IllegalStateException("Main logger not initialized."); - return main.addMonitor(mon); - } - - - public void removeMonitor(int id) - { - if (!ready()) throw new IllegalStateException("Main logger not initialized."); + final String nl = System.getProperty("line.separator"); + + if (message.equals("\n")) { + return nl; + } + + if (message.charAt(0) == '\n') { + message = nl + message.substring(1); + } + + final long time_ms = (System.currentTimeMillis() - start_ms); + final double time_s = time_ms / 1000D; + final String time = String.format("%6.2f ", time_s); + final String time_blank = StringUtils.repeat(" ", time.length()); + + String prefix = "[ ? ]"; + + if (level == Level.FINE) { + prefix = "[ # ] "; + } else if (level == Level.FINER) { + prefix = "[ - ] "; + } else if (level == Level.FINEST) { + prefix = "[ ] "; + } else if (level == Level.INFO) { + prefix = "[ i ] "; + } else if (level == Level.SEVERE) { + prefix = "[!E!] "; + } else if (level == Level.WARNING) { + prefix = "[!W!] "; + } - main.removeMonitor(id); + message = time + prefix + message.replaceAll("\n", nl + time_blank + prefix) + nl; + + if (throwable != null) { + message += getStackTrace(throwable); + } + + return message; } @@ -237,10 +348,4 @@ public class Log { return (enclosing == null ? "" : str(enclosing) + sep) + name; } - - - public static boolean ready() - { - return main != null; - } } diff --git a/src/mightypork/utils/logging/LogInstance.java b/src/mightypork/utils/logging/LogInstance.java deleted file mode 100644 index 1af6696..0000000 --- a/src/mightypork/utils/logging/LogInstance.java +++ /dev/null @@ -1,424 +0,0 @@ -package mightypork.utils.logging; - - -import java.io.File; -import java.io.FileFilter; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.text.SimpleDateFormat; -import java.util.Collections; -import java.util.Comparator; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.logging.FileHandler; -import java.util.logging.Formatter; -import java.util.logging.Level; -import java.util.logging.LogRecord; -import java.util.logging.Logger; - -import mightypork.utils.files.FileUtils; -import mightypork.utils.string.StringUtils; - - -/** - * Static logger class. - * - * @author MightyPork - * @copy (c) 2014 - */ -public class LogInstance { - - /** log file */ - private final File file; - - /** Log name */ - private final String name; - - /** Number of old logs to keep */ - private final int logs_to_keep; - - /** Logs dir */ - private final File log_dir; - - /** Logger instance. */ - private Logger logger; - - /** Logging enabled */ - private boolean enabled = true; - - private boolean sysout = true; - - private int monitorId = 0; - private final HashMap monitors = new HashMap<>(); - - private LogToSysoutMonitor sysoutMonitor; - - private final long started_ms; - - - /** - * Log - * - * @param name log name - * @param dir log directory - * @param oldLogCount number of old log files to keep: -1 all, 0 none. - */ - public LogInstance(String name, File dir, int oldLogCount) { - this.name = name; - this.file = new File(dir, name + getSuffix()); - this.log_dir = dir; - this.logs_to_keep = oldLogCount; - this.started_ms = System.currentTimeMillis(); - - init(); - } - - - /** - * Prepare logs for logging - */ - private void init() - { - logger = Logger.getLogger(name); - - cleanLoggingDirectory(); - - FileHandler handler = null; - - try { - handler = new FileHandler(file.getPath()); - } catch (final Exception e) { - throw new RuntimeException("Failed to init log", e); - } - - handler.setFormatter(new LogFormatter()); - logger.addHandler(handler); - - enabled = true; - - sysoutMonitor = new LogToSysoutMonitor(); - - addMonitor(sysoutMonitor); - - logger.setUseParentHandlers(false); - logger.setLevel(Level.ALL); - final String stamp = (new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(new Date()); - i("Logger \"" + name + "\" initialized.\n" + stamp); - } - - - private void cleanLoggingDirectory() - { - if (logs_to_keep == 0) return; // overwrite - - // move old file - for (final File f : FileUtils.listDirectory(file.getParentFile())) { - if (!f.isFile()) continue; - if (f.equals(file)) { - - final Date d = new Date(f.lastModified()); - final String fbase = name + '_' + (new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss")).format(d); - final String suff = getSuffix(); - String cntStr = ""; - File f2; - - for (int cnt = 0; (f2 = new File(log_dir, fbase + cntStr + suff)).exists(); cntStr = "_" + (++cnt)) {} - - f.renameTo(f2); - } - } - - if (logs_to_keep == -1) return; // keep all - - final List oldLogs = FileUtils.listDirectory(log_dir, new FileFilter() { - - @Override - public boolean accept(File f) - { - if (f.isDirectory()) return false; - if (!f.getName().endsWith(getSuffix())) return false; - if (!f.getName().startsWith(name)) return false; - - return true; - } - - }); - - Collections.sort(oldLogs, new Comparator() { - - @Override - public int compare(File o1, File o2) - { - return o1.getName().compareTo(o2.getName()); - } - }); - - for (int i = 0; i < oldLogs.size() - logs_to_keep; i++) { - oldLogs.get(i).delete(); - } - } - - - /** - * Add log monitor - * - * @param mon monitor - * @return assigned ID - */ - public synchronized int addMonitor(LogMonitor mon) - { - final int id = monitorId; - monitorId++; - monitors.put(id, mon); - return id; - } - - - /** - * Remove a monitor by ID - * - * @param id monitor ID - */ - public synchronized void removeMonitor(int id) - { - monitors.remove(id); - } - - - public void setSysoutLevel(Level level) - { - sysoutMonitor.setLevel(level); - } - - - public void setFileLevel(Level level) - { - logger.setLevel(level); - } - - - /** - * Enable logging. - * - * @param flag do enable logging - */ - public void enable(boolean flag) - { - enabled = flag; - } - - - /** - * Enable printing logs to sysout - * - * @param flag do enable logging - */ - public void enableSysout(boolean flag) - { - sysout = flag; - sysoutMonitor.enable(sysout); - } - - - public void log(Level level, String msg) - { - if (enabled) { - logger.log(level, msg); - - final String fmt = formatMessage(level, msg, null); - - for (final LogMonitor mon : monitors.values()) { - mon.onMessageLogged(level, fmt); - } - } - } - - - public void log(Level level, String msg, Throwable t) - { - if (enabled) { - logger.log(level, msg, t); - - final String fmt = formatMessage(level, msg, t); - - for (final LogMonitor mon : monitors.values()) { - mon.onMessageLogged(level, fmt); - } - } - } - - - /** - * Log FINE message - * - * @param msg message - */ - public void f1(String msg) - { - log(Level.FINE, msg); - } - - - /** - * Log FINER message - * - * @param msg message - */ - public void f2(String msg) - { - log(Level.FINER, msg); - } - - - /** - * Log FINEST message - * - * @param msg message - */ - public void f3(String msg) - { - log(Level.FINEST, msg); - } - - - /** - * Log INFO message - * - * @param msg message - */ - public void i(String msg) - { - log(Level.INFO, msg); - } - - - /** - * Log WARNING message (less severe than ERROR) - * - * @param msg message - */ - public void w(String msg) - { - log(Level.WARNING, msg); - } - - - /** - * Log ERROR message - * - * @param msg message - */ - public void e(String msg) - { - log(Level.SEVERE, msg); - } - - - /** - * Log THROWING message - * - * @param msg message - * @param thrown thrown exception - */ - public void e(String msg, Throwable thrown) - { - log(Level.SEVERE, msg, thrown); - } - - - /** - * Log exception thrown - * - * @param thrown thrown exception - */ - public void e(Throwable thrown) - { - log(Level.SEVERE, null, thrown); - } - - /** - * PowerCraft Log file formatter. - * - * @author MightyPork - * @copy (c) 2012 - */ - private class LogFormatter extends Formatter { - - @Override - public String format(LogRecord record) - { - return LogInstance.this.formatMessage(record.getLevel(), record.getMessage(), record.getThrown()); - } - } - - - /** - * @return log filename suffix (incl. dot) - */ - protected String getSuffix() - { - return ".log"; - } - - - private String formatMessage(Level level, String message, Throwable throwable) - { - - final String nl = System.getProperty("line.separator"); - - if (message.equals("\n")) { - return nl; - } - - if (message.charAt(0) == '\n') { - message = nl + message.substring(1); - } - - final long time_ms = (System.currentTimeMillis() - started_ms); - final double time_s = time_ms / 1000D; - final String time = String.format("%6.2f ", time_s); - final String time_blank = StringUtils.repeat(" ", time.length()); - - String prefix = "[ ? ]"; - - if (level == Level.FINE) { - prefix = "[ # ] "; - } else if (level == Level.FINER) { - prefix = "[ - ] "; - } else if (level == Level.FINEST) { - prefix = "[ ] "; - } else if (level == Level.INFO) { - prefix = "[ i ] "; - } else if (level == Level.SEVERE) { - prefix = "[!E!] "; - } else if (level == Level.WARNING) { - prefix = "[!W!] "; - } - - message = time + prefix + message.replaceAll("\n", nl + time_blank + prefix) + nl; - - if (throwable != null) { - message += getStackTrace(throwable); - } - - return message; - } - - - /** - * Get stack trace from throwable - * - * @param t - * @return trace - */ - private static String getStackTrace(Throwable t) - { - final StringWriter sw = new StringWriter(); - final PrintWriter pw = new PrintWriter(sw, true); - t.printStackTrace(pw); - pw.flush(); - sw.flush(); - return sw.toString(); - } -} diff --git a/src/mightypork/utils/logging/LogMonitor.java b/src/mightypork/utils/logging/LogMonitor.java index 9232c07..7b76627 100644 --- a/src/mightypork/utils/logging/LogMonitor.java +++ b/src/mightypork/utils/logging/LogMonitor.java @@ -4,18 +4,14 @@ package mightypork.utils.logging; import java.util.logging.Level; -/** - * Log monitor, receives all logged messages - * - * @author MightyPork - */ public interface LogMonitor { - /** - * Message logged; - * - * @param level message level - * @param message message text, already formatted. - */ void onMessageLogged(Level level, String message); + + + void setLevel(Level level); + + + void enable(boolean flag); + } diff --git a/src/mightypork/utils/logging/LogToSysoutMonitor.java b/src/mightypork/utils/logging/LogToSysoutMonitor.java index 188e420..1daee12 100644 --- a/src/mightypork/utils/logging/LogToSysoutMonitor.java +++ b/src/mightypork/utils/logging/LogToSysoutMonitor.java @@ -4,24 +4,11 @@ package mightypork.utils.logging; import java.util.logging.Level; -public class LogToSysoutMonitor implements LogMonitor { - - private boolean enabled = true; - private Level accepted = Level.ALL; - - - public void setLevel(Level level) - { - this.accepted = level; - } - +public class LogToSysoutMonitor extends BaseLogMonitor { @Override - public void onMessageLogged(Level level, String message) + protected void logMessage(Level level, String message) { - if (!enabled) return; - if (accepted.intValue() > level.intValue()) return; - if (level == Level.SEVERE || level == Level.WARNING) { System.err.print(message); } else { @@ -29,10 +16,4 @@ public class LogToSysoutMonitor implements LogMonitor { } } - - public void enable(boolean enable) - { - this.enabled = enable; - } - } diff --git a/src/mightypork/utils/logging/LogWriter.java b/src/mightypork/utils/logging/LogWriter.java new file mode 100644 index 0000000..d225291 --- /dev/null +++ b/src/mightypork/utils/logging/LogWriter.java @@ -0,0 +1,70 @@ +package mightypork.utils.logging; + + +import java.util.logging.Level; + + +/** + * Log interface + * + * @author MightyPork + */ +public interface LogWriter { + + /** + * Prepare logs for logging + */ + void init(); + + + /** + * Add log monitor + * + * @param mon monitor + */ + void addMonitor(LogMonitor mon); + + + /** + * Remove a monitor + * + * @param removed monitor to remove + */ + void removeMonitor(LogMonitor removed); + + + /** + * Set logging level + * + * @param level + */ + void setLevel(Level level); + + + /** + * Enable logging. + * + * @param flag do enable logging + */ + void enable(boolean flag); + + + /** + * Log a message + * + * @param level message level + * @param msg message text + */ + void log(Level level, String msg); + + + /** + * Log a message + * + * @param level message level + * @param msg message text + * @param t thrown exception + */ + void log(Level level, String msg, Throwable t); + +} diff --git a/src/mightypork/utils/logging/SimpleLog.java b/src/mightypork/utils/logging/SimpleLog.java new file mode 100644 index 0000000..eb74007 --- /dev/null +++ b/src/mightypork/utils/logging/SimpleLog.java @@ -0,0 +1,251 @@ +package mightypork.utils.logging; + + +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashSet; +import java.util.logging.FileHandler; +import java.util.logging.Formatter; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + + +/** + * Basic logger + * + * @author MightyPork + */ +public class SimpleLog implements LogWriter { + + /** + * Log file formatter. + */ + class LogFormatter extends Formatter { + + @Override + public String format(LogRecord record) + { + return Log.formatMessage(record.getLevel(), record.getMessage(), record.getThrown(), started_ms); + } + } + + /** Log file */ + private final File file; + + /** Log name */ + private final String name; + + /** Logger instance. */ + private Logger logger; + + private boolean enabled = true; + private final HashSet monitors = new HashSet<>(); + private final long started_ms; + + + public SimpleLog(String name, File file) { + this.name = name; + this.file = file; + this.started_ms = System.currentTimeMillis(); + } + + + @Override + public void init() + { + logger = Logger.getLogger(getName()); + + FileHandler handler = null; + try { + handler = new FileHandler(getFile().getPath()); + } catch (final Exception e) { + throw new RuntimeException("Failed to init log.", e); + } + + handler.setFormatter(new LogFormatter()); + logger.addHandler(handler); + logger.setUseParentHandlers(false); + logger.setLevel(Level.ALL); + + printHeader(); + } + + + protected void printHeader() + { + final String stamp = (new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(new Date()); + i("Logger \"" + getName() + "\" initialized.\n" + stamp); + } + + + /** + * Add log monitor + * + * @param mon monitor + */ + @Override + public synchronized void addMonitor(LogMonitor mon) + { + monitors.add(mon); + } + + + /** + * Remove a monitor + * + * @param removed monitor to remove + */ + @Override + public synchronized void removeMonitor(LogMonitor removed) + { + monitors.remove(removed); + } + + + @Override + public void setLevel(Level level) + { + logger.setLevel(level); + } + + + @Override + public void enable(boolean flag) + { + enabled = flag; + } + + + public File getFile() + { + return file; + } + + + public String getName() + { + return name; + } + + + @Override + public void log(Level level, String msg) + { + if (enabled) { + logger.log(level, msg); + + final String fmt = Log.formatMessage(level, msg, null, started_ms); + + for (final LogMonitor mon : monitors) { + mon.onMessageLogged(level, fmt); + } + } + } + + + @Override + public void log(Level level, String msg, Throwable t) + { + if (enabled) { + logger.log(level, msg, t); + + final String fmt = Log.formatMessage(level, msg, t, started_ms); + + for (final LogMonitor mon : monitors) { + mon.onMessageLogged(level, fmt); + } + } + } + + + /** + * Log FINE message + * + * @param msg message + */ + public void f1(String msg) + { + log(Level.FINE, msg); + } + + + /** + * Log FINER message + * + * @param msg message + */ + public void f2(String msg) + { + log(Level.FINER, msg); + } + + + /** + * Log FINEST message + * + * @param msg message + */ + public void f3(String msg) + { + log(Level.FINEST, msg); + } + + + /** + * Log INFO message + * + * @param msg message + */ + public void i(String msg) + { + log(Level.INFO, msg); + } + + + /** + * Log WARNING message (less severe than ERROR) + * + * @param msg message + */ + public void w(String msg) + { + log(Level.WARNING, msg); + } + + + /** + * Log ERROR message + * + * @param msg message + */ + public void e(String msg) + { + log(Level.SEVERE, msg); + } + + + /** + * Log THROWING message + * + * @param msg message + * @param thrown thrown exception + */ + public void e(String msg, Throwable thrown) + { + log(Level.SEVERE, msg, thrown); + } + + + /** + * Log exception thrown + * + * @param thrown thrown exception + */ + public void e(Throwable thrown) + { + log(Level.SEVERE, null, thrown); + } + +} diff --git a/src/mightypork/utils/string/StringUtils.java b/src/mightypork/utils/string/StringUtils.java index 42b2b92..985cd57 100644 --- a/src/mightypork/utils/string/StringUtils.java +++ b/src/mightypork/utils/string/StringUtils.java @@ -54,6 +54,7 @@ public class StringUtils { public static String toLastChar(String s, char c) { if (s == null) return null; + if (s.lastIndexOf(c) == -1) return s; return s.substring(0, s.lastIndexOf(c)); }