Remade logging system, cleaned up BaseApp.

v5stable
Ondřej Hruška 10 years ago
parent e940c53dd6
commit ebbcf50f74
  1. 110
      src/mightypork/gamecore/control/BaseApp.java
  2. 6
      src/mightypork/gamecore/control/GameLoop.java
  3. 22
      src/mightypork/gamecore/control/SlickLogRedirector.java
  4. 6
      src/mightypork/gamecore/control/interf/DefaultImpl.java
  5. 8
      src/mightypork/gamecore/gui/screens/Screen.java
  6. 8
      src/mightypork/gamecore/gui/screens/ScreenLayer.java
  7. 4
      src/mightypork/gamecore/gui/screens/ScreenRegistry.java
  8. 89
      src/mightypork/rogue/App.java
  9. 16
      src/mightypork/rogue/CrashHandler.java
  10. 16
      src/mightypork/rogue/Main.java
  11. 8
      src/mightypork/rogue/Paths.java
  12. 2
      src/mightypork/rogue/screens/test_cat_sound/LayerFlyingCat.java
  13. 125
      src/mightypork/utils/logging/ArchivingLog.java
  14. 39
      src/mightypork/utils/logging/BaseLogMonitor.java
  15. 287
      src/mightypork/utils/logging/Log.java
  16. 424
      src/mightypork/utils/logging/LogInstance.java
  17. 18
      src/mightypork/utils/logging/LogMonitor.java
  18. 23
      src/mightypork/utils/logging/LogToSysoutMonitor.java
  19. 70
      src/mightypork/utils/logging/LogWriter.java
  20. 251
      src/mightypork/utils/logging/SimpleLog.java
  21. 1
      src/mightypork/utils/string/StringUtils.java

@ -2,14 +2,16 @@ package mightypork.gamecore.control;
import java.io.File; import java.io.File;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.logging.Level;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import mightypork.gamecore.audio.SoundSystem; import mightypork.gamecore.audio.SoundSystem;
import mightypork.gamecore.control.bus.EventBus; import mightypork.gamecore.control.bus.EventBus;
import mightypork.gamecore.control.bus.events.*; import mightypork.gamecore.control.bus.events.*;
import mightypork.gamecore.control.interf.DefaultImpl;
import mightypork.gamecore.control.interf.Destroyable; import mightypork.gamecore.control.interf.Destroyable;
import mightypork.gamecore.control.interf.NoImpl;
import mightypork.gamecore.control.timing.Updateable; import mightypork.gamecore.control.timing.Updateable;
import mightypork.gamecore.gui.screens.ScreenRegistry; import mightypork.gamecore.gui.screens.ScreenRegistry;
import mightypork.gamecore.input.InputSystem; import mightypork.gamecore.input.InputSystem;
@ -17,7 +19,7 @@ import mightypork.gamecore.loading.AsyncResourceLoader;
import mightypork.gamecore.render.DisplaySystem; import mightypork.gamecore.render.DisplaySystem;
import mightypork.utils.files.InstanceLock; import mightypork.utils.files.InstanceLock;
import mightypork.utils.logging.Log; 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 * @author MightyPork
*/ */
public abstract class BaseApp implements AppAccess { public abstract class BaseApp implements AppAccess, UncaughtExceptionHandler {
// modules // modules
private InputSystem inputSystem; private InputSystem inputSystem;
@ -40,8 +42,9 @@ public abstract class BaseApp implements AppAccess {
/** /**
* Start the application * Start the application
*/ */
public void start() public final void start()
{ {
Thread.setDefaultUncaughtExceptionHandler(this);
initialize(); initialize();
@ -57,7 +60,6 @@ public abstract class BaseApp implements AppAccess {
*/ */
protected void initialize() protected void initialize()
{ {
/* /*
* Lock working directory * Lock working directory
*/ */
@ -69,8 +71,11 @@ public abstract class BaseApp implements AppAccess {
/* /*
* Setup logging * Setup logging
*/ */
final LogInstance log = createLog(); final LogWriter log = createLog();
org.newdawn.slick.util.Log.setLogSystem(new SlickLogRedirector(log)); if (log != null) {
Log.setMainLogger(log);
org.newdawn.slick.util.Log.setLogSystem(new SlickLogRedirector(log));
}
// only here it makes sense to log. // only here it makes sense to log.
Log.i("=== Commencing initialization sequence ==="); Log.i("=== Commencing initialization sequence ===");
@ -82,6 +87,7 @@ public abstract class BaseApp implements AppAccess {
eventBus = new EventBus(); eventBus = new EventBus();
Log.f3("Registering channels..."); Log.f3("Registering channels...");
initDefaultBusChannels(eventBus);
initBus(eventBus); initBus(eventBus);
/* /*
@ -139,16 +145,16 @@ public abstract class BaseApp implements AppAccess {
* Called at the beginning of the initialization sequence, right after lock * Called at the beginning of the initialization sequence, right after lock
* was obtained. * was obtained.
*/ */
@NoImpl @DefaultImpl
protected void preInit() protected void preInit()
{ {
} }
/** /**
* * Called at the end of init sequence, before main loop starts.
*/ */
@NoImpl @DefaultImpl
protected void postInit() protected void postInit()
{ {
} }
@ -159,7 +165,13 @@ public abstract class BaseApp implements AppAccess {
* *
* @return new log instance * @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 * @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 * @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 * @param input
*/ */
protected abstract void initInputSystem(InputSystem input); @DefaultImpl
protected void initInputSystem(InputSystem input)
{
}
/** /**
* Initialize resource banks; {@link AsyncResourceLoader} is already * Initialize resource banks; {@link AsyncResourceLoader} is already
* started. * started.
*/ */
protected abstract void initResources(); @DefaultImpl
protected void initResources()
{
}
/** /**
@ -198,7 +224,10 @@ public abstract class BaseApp implements AppAccess {
* *
* @param screens * @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 * @param bus
*/ */
@DefaultImpl
protected void initBus(EventBus bus) protected void initBus(EventBus bus)
{
}
private void initDefaultBusChannels(EventBus bus)
{ {
// framework events // framework events
bus.addChannel(DestroyEvent.class, Destroyable.class); bus.addChannel(DestroyEvent.class, Destroyable.class);
@ -259,7 +294,7 @@ public abstract class BaseApp implements AppAccess {
*/ */
protected void onLockError() 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 //@formatter:off
JOptionPane.showMessageDialog( JOptionPane.showMessageDialog(
@ -279,7 +314,11 @@ public abstract class BaseApp implements AppAccess {
* *
* @return lock file, or null to disable lock. * @return lock file, or null to disable lock.
*/ */
protected abstract File getLockFile(); @DefaultImpl
protected File getLockFile()
{
return new File(".lock");
}
@Override @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 @Override
public void shutdown() public final void shutdown()
{ {
beforeShutdown();
Log.i("Shutting down subsystems..."); Log.i("Shutting down subsystems...");
if (getEventBus() != null) { try {
getEventBus().send(new DestroyEvent()); if (getEventBus() != null) {
getEventBus().destroy(); getEventBus().send(new DestroyEvent());
getEventBus().destroy();
}
} catch (final Exception e) {
// ignore it
} }
Log.i("Terminating..."); Log.i("Terminating...");

@ -6,7 +6,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import mightypork.gamecore.control.bus.events.MainLoopTaskRequest; import mightypork.gamecore.control.bus.events.MainLoopTaskRequest;
import mightypork.gamecore.control.bus.events.UpdateEvent; 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.control.timing.TimerDelta;
import mightypork.gamecore.gui.components.Renderable; import mightypork.gamecore.gui.components.Renderable;
import mightypork.gamecore.gui.screens.ScreenRegistry; import mightypork.gamecore.gui.screens.ScreenRegistry;
@ -78,7 +78,7 @@ public abstract class GameLoop extends AppModule implements MainLoopTaskRequest.
/** /**
* Called before render * Called before render
*/ */
@NoImpl @DefaultImpl
protected void beforeRender() protected void beforeRender()
{ {
// //
@ -88,7 +88,7 @@ public abstract class GameLoop extends AppModule implements MainLoopTaskRequest.
/** /**
* Called after render * Called after render
*/ */
@NoImpl @DefaultImpl
protected void afterRender() protected void afterRender()
{ {
// //

@ -1,7 +1,9 @@
package mightypork.gamecore.control; 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; import org.newdawn.slick.util.LogSystem;
@ -13,13 +15,13 @@ import org.newdawn.slick.util.LogSystem;
*/ */
public class SlickLogRedirector implements LogSystem { public class SlickLogRedirector implements LogSystem {
LogInstance l; LogWriter l;
/** /**
* @param log log to redirect into * @param log log to redirect into
*/ */
public SlickLogRedirector(LogInstance log) { public SlickLogRedirector(LogWriter log) {
this.l = log; this.l = log;
} }
@ -27,49 +29,49 @@ public class SlickLogRedirector implements LogSystem {
@Override @Override
public void error(String msg, Throwable e) public void error(String msg, Throwable e)
{ {
l.e(msg, e); l.log(Level.SEVERE, msg, e);
} }
@Override @Override
public void error(Throwable e) public void error(Throwable e)
{ {
l.e(e); l.log(Level.SEVERE, null, e);
} }
@Override @Override
public void error(String msg) public void error(String msg)
{ {
l.e(msg); l.log(Level.SEVERE, msg);
} }
@Override @Override
public void warn(String msg) public void warn(String msg)
{ {
l.w(msg); l.log(Level.WARNING, msg);
} }
@Override @Override
public void warn(String msg, Throwable e) public void warn(String msg, Throwable e)
{ {
l.e(msg, e); l.log(Level.WARNING, msg, e);
} }
@Override @Override
public void info(String msg) public void info(String msg)
{ {
l.i(msg); l.log(Level.INFO, msg);
} }
@Override @Override
public void debug(String msg) public void debug(String msg)
{ {
l.f3(msg); l.log(Level.FINEST, msg);
} }
} }

@ -9,14 +9,14 @@ import java.lang.annotation.Target;
/** /**
* Indicates that the marked method is not implemented and can be safely * Marked method can be safely overriden; it's left blank (or with default
* overriden. * implementation) as a convenience.
* *
* @author MightyPork * @author MightyPork
*/ */
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(value = { ElementType.METHOD }) @Target(value = { ElementType.METHOD })
public @interface NoImpl { public @interface DefaultImpl {
// //
} }

@ -4,7 +4,7 @@ package mightypork.gamecore.gui.screens;
import mightypork.gamecore.control.AppAccess; import mightypork.gamecore.control.AppAccess;
import mightypork.gamecore.control.AppSubModule; import mightypork.gamecore.control.AppSubModule;
import mightypork.gamecore.control.bus.events.ScreenChangeEvent; 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.components.Renderable;
import mightypork.gamecore.gui.constraints.RectConstraint; import mightypork.gamecore.gui.constraints.RectConstraint;
import mightypork.gamecore.input.KeyBinder; import mightypork.gamecore.input.KeyBinder;
@ -130,7 +130,7 @@ public abstract class Screen extends AppSubModule implements Renderable, KeyBind
/** /**
* Called when the screen becomes active * Called when the screen becomes active
*/ */
@NoImpl @DefaultImpl
protected void onScreenEnter() protected void onScreenEnter()
{ {
// //
@ -140,7 +140,7 @@ public abstract class Screen extends AppSubModule implements Renderable, KeyBind
/** /**
* Called when the screen is no longer active * Called when the screen is no longer active
*/ */
@NoImpl @DefaultImpl
protected void onScreenLeave() protected void onScreenLeave()
{ {
// //
@ -152,7 +152,7 @@ public abstract class Screen extends AppSubModule implements Renderable, KeyBind
* *
* @param size screen size * @param size screen size
*/ */
@NoImpl @DefaultImpl
protected void onSizeChanged(Coord size) protected void onSizeChanged(Coord size)
{ {
// //

@ -2,7 +2,7 @@ package mightypork.gamecore.gui.screens;
import mightypork.gamecore.control.AppSubModule; 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.components.Renderable;
import mightypork.gamecore.gui.constraints.RectConstraint; import mightypork.gamecore.gui.constraints.RectConstraint;
import mightypork.gamecore.input.KeyBinder; import mightypork.gamecore.input.KeyBinder;
@ -75,7 +75,7 @@ public abstract class ScreenLayer extends AppSubModule implements Comparable<Scr
/** /**
* Called when the screen becomes active * Called when the screen becomes active
*/ */
@NoImpl @DefaultImpl
protected void onScreenEnter() protected void onScreenEnter()
{ {
// //
@ -85,7 +85,7 @@ public abstract class ScreenLayer extends AppSubModule implements Comparable<Scr
/** /**
* Called when the screen is no longer active * Called when the screen is no longer active
*/ */
@NoImpl @DefaultImpl
protected void onScreenLeave() protected void onScreenLeave()
{ {
// //
@ -97,7 +97,7 @@ public abstract class ScreenLayer extends AppSubModule implements Comparable<Scr
* *
* @param size screen size * @param size screen size
*/ */
@NoImpl @DefaultImpl
protected void onSizeChanged(Coord size) protected void onSizeChanged(Coord size)
{ {
// //

@ -6,7 +6,7 @@ import java.util.HashMap;
import mightypork.gamecore.control.AppAccess; import mightypork.gamecore.control.AppAccess;
import mightypork.gamecore.control.AppModule; import mightypork.gamecore.control.AppModule;
import mightypork.gamecore.control.bus.events.ScreenRequestEvent; import mightypork.gamecore.control.bus.events.ScreenRequestEvent;
import mightypork.gamecore.control.interf.NoImpl; import mightypork.gamecore.control.interf.DefaultImpl;
import mightypork.gamecore.gui.components.Renderable; import mightypork.gamecore.gui.components.Renderable;
import mightypork.utils.logging.Log; import mightypork.utils.logging.Log;
@ -66,7 +66,7 @@ public class ScreenRegistry extends AppModule implements ScreenRequestEvent.List
@Override @Override
@NoImpl @DefaultImpl
protected void deinit() protected void deinit()
{ {
// //

@ -2,10 +2,7 @@ package mightypork.rogue;
import java.io.File; import java.io.File;
import java.util.Locale;
import java.util.logging.Level;
import mightypork.gamecore.audio.SoundSystem;
import mightypork.gamecore.control.BaseApp; import mightypork.gamecore.control.BaseApp;
import mightypork.gamecore.control.GameLoop; import mightypork.gamecore.control.GameLoop;
import mightypork.gamecore.control.bus.EventBus; import mightypork.gamecore.control.bus.EventBus;
@ -20,8 +17,6 @@ import mightypork.rogue.screens.test_bouncyboxes.ScreenTestBouncy;
import mightypork.rogue.screens.test_cat_sound.ScreenTestCat; import mightypork.rogue.screens.test_cat_sound.ScreenTestCat;
import mightypork.rogue.screens.test_font.ScreenTestFont; import mightypork.rogue.screens.test_font.ScreenTestFont;
import mightypork.rogue.screens.test_render.ScreenTestRender; import mightypork.rogue.screens.test_render.ScreenTestRender;
import mightypork.utils.logging.Log;
import mightypork.utils.logging.LogInstance;
/** /**
@ -31,31 +26,6 @@ import mightypork.utils.logging.LogInstance;
*/ */
public class App extends BaseApp { public class App extends BaseApp {
/** instance pointer */
private static BaseApp inst;
/**
* @param args
*/
public static void main(String[] args)
{
Config.init();
Config.save();
Thread.setDefaultUncaughtExceptionHandler(new CrashHandler());
inst = new App();
try {
inst.start();
} catch (final Throwable t) {
onCrash(t);
}
}
@Override @Override
protected void initScreens(ScreenRegistry screens) protected void initScreens(ScreenRegistry screens)
{ {
@ -71,9 +41,6 @@ public class App extends BaseApp {
@Override @Override
protected void initBus(EventBus bus) protected void initBus(EventBus bus)
{ {
super.initBus(bus);
// custom channels
bus.addChannel(ActionRequest.class, ActionRequest.Listener.class); bus.addChannel(ActionRequest.class, ActionRequest.Listener.class);
//bus.detailedLogging = true; //bus.detailedLogging = true;
@ -115,26 +82,17 @@ public class App extends BaseApp {
} }
@Override // @Override
protected void preInit() // protected LogWriter createLog()
{ // {
// to get dot instead of comma in floats // Locale.setDefault(Locale.ENGLISH);
Locale.setDefault(Locale.ENGLISH); //
} // final LogWriter log = Log.create("runtime", Paths.LOG_FILE, 10);
// log.setLevel(Level.WARNING);
// log.enable(Config.LOGGING_ENABLED);
@Override //
protected LogInstance createLog() // return log;
{ // }
final LogInstance log = Log.create("runtime", Paths.LOGS, 10);
log.setFileLevel(Level.WARNING);
log.setSysoutLevel(Level.ALL);
log.enable(Config.LOGGING_ENABLED);
log.enableSysout(Config.LOG_TO_STDOUT);
return log;
}
@Override @Override
protected void initDisplay(DisplaySystem display) protected void initDisplay(DisplaySystem display)
@ -144,13 +102,6 @@ public class App extends BaseApp {
} }
@Override
protected void initSoundSystem(SoundSystem audio)
{
audio.setMasterVolume(1);
}
@Override @Override
protected GameLoop createLoop() protected GameLoop createLoop()
{ {
@ -171,22 +122,4 @@ public class App extends BaseApp {
return Paths.LOCK; return Paths.LOCK;
} }
/**
* Handle a crash
*
* @param error
*/
public static void onCrash(Throwable error)
{
if (Log.ready()) {
Log.e("The game has crashed!", error);
} else {
System.err.println("The game has crashed!");
error.printStackTrace();
}
if (inst != null) inst.shutdown();
}
} }

@ -1,16 +0,0 @@
package mightypork.rogue;
import java.lang.Thread.UncaughtExceptionHandler;
public class CrashHandler implements UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e)
{
e.printStackTrace();
App.onCrash(e);
}
}

@ -0,0 +1,16 @@
package mightypork.rogue;
public class Main {
/**
* @param args
*/
public static void main(String[] args)
{
Config.init();
Config.save();
(new App()).start();
}
}

@ -11,9 +11,13 @@ public class Paths {
private static final String APPDIR_NAME = "rogue"; private static final String APPDIR_NAME = "rogue";
public static final File WORKDIR = OsUtils.getWorkDir(APPDIR_NAME); public static final File WORKDIR = OsUtils.getWorkDir(APPDIR_NAME);
public static final File LOGS = OsUtils.getWorkDir(APPDIR_NAME, "logs");
public static final File SCREENSHOTS = OsUtils.getWorkDir(APPDIR_NAME, "screenshots"); public static final File LOG_FILE = new File(WORKDIR, "runtime.log");
public static final File SCREENSHOTS = new File(WORKDIR, "screenshots");
public static final File CONFIG = new File(WORKDIR, "config.ini"); public static final File CONFIG = new File(WORKDIR, "config.ini");
public static final File LOCK = new File(WORKDIR, ".lock"); public static final File LOCK = new File(WORKDIR, ".lock");
public static final String DIR_EFFECTS = "res/sounds/effects/"; public static final String DIR_EFFECTS = "res/sounds/effects/";

@ -71,8 +71,6 @@ public class LayerFlyingCat extends ScreenLayer implements Updateable, MouseButt
size.update(delta); size.update(delta);
xPos.update(delta); xPos.update(delta);
yPos.update(delta); yPos.update(delta);
System.out.println(cat.getRect());
} }

@ -0,0 +1,125 @@
package mightypork.utils.logging;
import java.io.File;
import java.io.FileFilter;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import mightypork.utils.files.FileUtils;
/**
* Logger that cleans directory & archives old logs
*
* @author MightyPork
* @copy (c) 2014
*/
public class ArchivingLog extends SimpleLog {
/** Number of old logs to keep */
private final int logs_to_keep;
/**
* Log
*
* @param name log name
* @param file log file (in log directory)
* @param oldLogCount number of old log files to keep: -1 all, 0 none.
*/
public ArchivingLog(String name, File file, int oldLogCount) {
super(name, file);
this.logs_to_keep = oldLogCount;
}
/**
* Log, not keeping 5 last log files (default);
*
* @param name log name
* @param file log file (in log directory)
*/
public ArchivingLog(String name, File file) {
super(name, file);
this.logs_to_keep = 5;
}
@Override
public void init()
{
cleanLoggingDirectory();
super.init();
}
private void cleanLoggingDirectory()
{
if (logs_to_keep == 0) return; // overwrite
final File log_file = getFile();
final File log_dir = log_file.getParentFile();
final String fname = FileUtils.getBasename(log_file.toString());
// move old file
for (final File f : FileUtils.listDirectory(log_dir)) {
if (!f.isFile()) continue;
if (f.equals(getFile())) {
final Date d = new Date(f.lastModified());
final String fbase = fname + '_' + (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<File> 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<File>() {
@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());
}
}

@ -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;
}
}

@ -2,195 +2,306 @@ package mightypork.utils.logging;
import java.io.File; import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap; import java.util.HashMap;
import java.util.logging.Level;
import mightypork.utils.string.StringUtils;
/**
* A log.
*
* @author MightyPork
*/
public class Log { public class Log {
/** enable static logging */ private static LogWriter main = null;
private static boolean staticLogging = true; private static boolean enabled = true;
private static final LogToSysoutMonitor sysoMonitor = new LogToSysoutMonitor();
private static final long start_ms = System.currentTimeMillis();
private static HashMap<String, SimpleLog> 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 * @param msg message
*/ */
public static void f1(String msg) 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 * @param msg message
*/ */
public static void f2(String msg) 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 * @param msg message
*/ */
public static void f3(String msg) 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 * @param msg message
*/ */
public static void i(String msg) 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 * @param msg message
*/ */
public static void w(String msg) public static void w(String msg)
{ {
if (staticLogging && ready()) { log(Level.WARNING, msg);
main.w(msg);
} else {
System.err.println(msg);
}
} }
/** /**
* Log ERROR message in main logger * Log ERROR message
* *
* @param msg message * @param msg message
*/ */
public static void e(String msg) public static void e(String msg)
{ {
if (staticLogging && ready()) { log(Level.SEVERE, msg);
main.e(msg);
} else {
System.err.println(msg);
}
} }
/** /**
* Log EXCEPTION and ERROR message in main logger * Log THROWING message
* *
* @param msg message * @param msg message
* @param thrown thrown exception * @param thrown thrown exception
*/ */
public static void e(String msg, Throwable thrown) public static void e(String msg, Throwable thrown)
{ {
if (staticLogging && ready()) { log(Level.SEVERE, msg, thrown);
main.e(msg, thrown);
} else {
System.err.println(msg);
thrown.printStackTrace();
}
} }
/** /**
* Log EXCEPTION in main logger * Log exception thrown
* *
* @param thrown thrown exception * @param thrown thrown exception
*/ */
public static void e(Throwable thrown) public static void e(Throwable thrown)
{ {
if (staticLogging && ready()) { log(Level.SEVERE, null, thrown);
main.e(thrown);
} else {
thrown.printStackTrace();
}
} }
public static void enable(boolean flag) public static void enable(boolean flag)
{ {
if (staticLogging && ready()) main.enable(flag); enabled = flag;
} }
/** public static void setSysoutLevel(Level level)
* Enable / disable static log delegate methods
*
* @param flag enable
*/
public static void enableStaticLogging(boolean flag)
{ {
staticLogging = flag; sysoMonitor.setLevel(level);
} }
private static HashMap<String, LogInstance> logs = new HashMap<>();
private static LogInstance main = null;
/** public static void setLevel(Level level)
* 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)
{ {
if (logs.containsKey(logName)) return logs.get(logName); assertInited();
final LogInstance log = new LogInstance(logName, logsDir, oldLogsCount);
if (main == null) main = log;
logs.put(logName, log);
return log; main.setLevel(level);
} }
/** /**
* Create a logger. If this is the first logger made, then it'll be made * Get stack trace from throwable
* available via the static methods.<br>
* Old logs will be kept.
* *
* @param logName log name (used for filename, must be application-unique) * @param t
* @param logsDir directory to store logs in * @return trace
* @return the created Log instance
*/ */
public static synchronized LogInstance create(String logName, File logsDir) public static String getStackTrace(Throwable t)
{ {
if (logs.containsKey(logName)) return logs.get(logName); final StringWriter sw = new StringWriter();
final LogInstance log = new LogInstance(logName, logsDir, -1); final PrintWriter pw = new PrintWriter(sw, true);
if (main == null) main = log; t.printStackTrace(pw);
logs.put(logName, log); pw.flush();
sw.flush();
return log; 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); final String nl = System.getProperty("line.separator");
}
if (message.equals("\n")) {
return nl;
public void removeMonitor(int id) }
{
if (!ready()) throw new IllegalStateException("Main logger not initialized."); 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; return (enclosing == null ? "" : str(enclosing) + sep) + name;
} }
public static boolean ready()
{
return main != 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<Integer, LogMonitor> 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<File> 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<File>() {
@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();
}
}

@ -4,18 +4,14 @@ package mightypork.utils.logging;
import java.util.logging.Level; import java.util.logging.Level;
/**
* Log monitor, receives all logged messages
*
* @author MightyPork
*/
public interface LogMonitor { public interface LogMonitor {
/**
* Message logged;
*
* @param level message level
* @param message message text, already formatted.
*/
void onMessageLogged(Level level, String message); void onMessageLogged(Level level, String message);
void setLevel(Level level);
void enable(boolean flag);
} }

@ -4,24 +4,11 @@ package mightypork.utils.logging;
import java.util.logging.Level; import java.util.logging.Level;
public class LogToSysoutMonitor implements LogMonitor { public class LogToSysoutMonitor extends BaseLogMonitor {
private boolean enabled = true;
private Level accepted = Level.ALL;
public void setLevel(Level level)
{
this.accepted = level;
}
@Override @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) { if (level == Level.SEVERE || level == Level.WARNING) {
System.err.print(message); System.err.print(message);
} else { } else {
@ -29,10 +16,4 @@ public class LogToSysoutMonitor implements LogMonitor {
} }
} }
public void enable(boolean enable)
{
this.enabled = enable;
}
} }

@ -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);
}

@ -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<LogMonitor> 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);
}
}

@ -54,6 +54,7 @@ public class StringUtils {
public static String toLastChar(String s, char c) public static String toLastChar(String s, char c)
{ {
if (s == null) return null; if (s == null) return null;
if (s.lastIndexOf(c) == -1) return s;
return s.substring(0, s.lastIndexOf(c)); return s.substring(0, s.lastIndexOf(c));
} }

Loading…
Cancel
Save