Remade logging system, cleaned up BaseApp.

v5stable
Ondřej Hruška 11 years ago
parent e940c53dd6
commit ebbcf50f74
  1. 102
      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. 281
      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.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();
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,15 +349,42 @@ 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...");
try {
if (getEventBus() != null) {
getEventBus().send(new DestroyEvent());
getEventBus().destroy();
}
} catch (final Exception e) {
// ignore it
}
Log.i("Terminating...");
System.exit(0);

@ -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()
{
//

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

@ -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 {
//
}

@ -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)
{
//

@ -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<Scr
/**
* Called when the screen becomes active
*/
@NoImpl
@DefaultImpl
protected void onScreenEnter()
{
//
@ -85,7 +85,7 @@ public abstract class ScreenLayer extends AppSubModule implements Comparable<Scr
/**
* Called when the screen is no longer active
*/
@NoImpl
@DefaultImpl
protected void onScreenLeave()
{
//
@ -97,7 +97,7 @@ public abstract class ScreenLayer extends AppSubModule implements Comparable<Scr
*
* @param size screen size
*/
@NoImpl
@DefaultImpl
protected void onSizeChanged(Coord size)
{
//

@ -6,7 +6,7 @@ import java.util.HashMap;
import mightypork.gamecore.control.AppAccess;
import mightypork.gamecore.control.AppModule;
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.utils.logging.Log;
@ -66,7 +66,7 @@ public class ScreenRegistry extends AppModule implements ScreenRequestEvent.List
@Override
@NoImpl
@DefaultImpl
protected void deinit()
{
//

@ -2,10 +2,7 @@ package mightypork.rogue;
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.GameLoop;
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_font.ScreenTestFont;
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 {
/** 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
protected void initScreens(ScreenRegistry screens)
{
@ -71,9 +41,6 @@ public class App extends BaseApp {
@Override
protected void initBus(EventBus bus)
{
super.initBus(bus);
// custom channels
bus.addChannel(ActionRequest.class, ActionRequest.Listener.class);
//bus.detailedLogging = true;
@ -115,26 +82,17 @@ public class App extends BaseApp {
}
@Override
protected void preInit()
{
// to get dot instead of comma in floats
Locale.setDefault(Locale.ENGLISH);
}
@Override
protected LogInstance createLog()
{
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
// protected LogWriter createLog()
// {
// Locale.setDefault(Locale.ENGLISH);
//
// final LogWriter log = Log.create("runtime", Paths.LOG_FILE, 10);
// log.setLevel(Level.WARNING);
// log.enable(Config.LOGGING_ENABLED);
//
// return log;
// }
@Override
protected void initDisplay(DisplaySystem display)
@ -144,13 +102,6 @@ public class App extends BaseApp {
}
@Override
protected void initSoundSystem(SoundSystem audio)
{
audio.setMasterVolume(1);
}
@Override
protected GameLoop createLoop()
{
@ -171,22 +122,4 @@ public class App extends BaseApp {
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";
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 LOCK = new File(WORKDIR, ".lock");
public static final String DIR_EFFECTS = "res/sounds/effects/";

@ -71,8 +71,6 @@ public class LayerFlyingCat extends ScreenLayer implements Updateable, MouseButt
size.update(delta);
xPos.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.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<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 FINE message in main logger
* 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
*
* @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<String, LogInstance> 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.<br>
* 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);
final String nl = System.getProperty("line.separator");
if (message.equals("\n")) {
return nl;
}
if (message.charAt(0) == '\n') {
message = nl + message.substring(1);
}
public void removeMonitor(int id)
{
if (!ready()) throw new IllegalStateException("Main logger not initialized.");
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!] ";
}
message = time + prefix + message.replaceAll("\n", nl + time_blank + prefix) + nl;
main.removeMonitor(id);
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;
}
}

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

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

@ -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)
{
if (s == null) return null;
if (s.lastIndexOf(c) == -1) return s;
return s.substring(0, s.lastIndexOf(c));
}

Loading…
Cancel
Save