Logger refactor

This commit is contained in:
Tefek 2020-09-08 03:08:28 +02:00
parent ef714031dc
commit 60fae86237
16 changed files with 328 additions and 222 deletions

11
UPDATE_NOTES.md Normal file
View File

@ -0,0 +1,11 @@
## 20.2.0.0-alpha.1
* `[PlutoLib#cz.tefek.pluto.io.logger]` Refactored the Logger subsystem
* Renamed `Logger#logException` to `Logger#log` to match the rest
of log methods and updated references to this method accordingly
* Streamlined `StdOutSplitStream` and `StdErrSplitStream` into a more generalized
`OutputSplitStream`
* `Logger`'s output filenames now look cleaner with `log--YYYY-MM-DD--HH-MM-SS.txt`
* `[Logger#setup]` can now throw `IOException`
* `[PlutoCore]` As a result, `[PlutoApplication#run]` can now throw `Exception`
* `[PlutoCore]` `[PlutoApplication]` now properly closes the `Logger` on exit
* `[PlutoLib]` Various typo fixes

View File

@ -1,5 +1,6 @@
package cz.tefek.pluto; package cz.tefek.pluto;
import java.io.IOException;
import java.util.Locale; import java.util.Locale;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
@ -44,7 +45,7 @@ public abstract class PlutoApplication
} }
} }
public final void run(String[] args, StartupConfig config) public final void run(String[] args, StartupConfig config) throws Exception
{ {
if (config == null) if (config == null)
{ {
@ -117,6 +118,8 @@ public abstract class PlutoApplication
this.display.destroy(); this.display.destroy();
DisplayBuilder.destroyGLFW(); DisplayBuilder.destroyGLFW();
Logger.close();
} }
public Display getDisplayInstance() public Display getDisplayInstance()

View File

@ -223,7 +223,7 @@ public class MiniTime
* than {@link Integer#MAX_VALUE} will be permanently converted to * than {@link Integer#MAX_VALUE} will be permanently converted to
* "forever".</i> * "forever".</i>
* *
* @param input The source time span in milliseconds * @param future The source time span in milliseconds
* @return The resulting MiniTime string * @return The resulting MiniTime string
* *
* @throws IllegalArgumentException on a negative time duration * @throws IllegalArgumentException on a negative time duration

View File

@ -199,7 +199,7 @@ public class StaticPlutoEventManager
} }
catch (Exception e) catch (Exception e)
{ {
Logger.logException(e); Logger.log(e);
} }
} }
} }

View File

@ -9,7 +9,7 @@ package cz.tefek.pluto.io.asl.resource;
* *
* @author 493msi * @author 493msi
* *
* @param R The type of the loaded <tt>Resource</tt>. * @param <R> The type of the loaded <tt>Resource</tt>.
*/ */
public abstract class Resource<R> public abstract class Resource<R>
{ {

View File

@ -39,7 +39,7 @@ public class ResourceImage extends Resource<BufferedImage>
catch (IOException e) catch (IOException e)
{ {
Logger.log(Severity.ERROR, "Could not load BufferedImage: " + this.address.toString() + ", will load placeholder."); Logger.log(Severity.ERROR, "Could not load BufferedImage: " + this.address.toString() + ", will load placeholder.");
Logger.logException(e); Logger.log(e);
try try
{ {
@ -50,7 +50,7 @@ public class ResourceImage extends Resource<BufferedImage>
Logger.log(Severity.ERROR, "Placeholder BufferedImage not found: " + ResourceHelper.GLOBAL_ROOT + "data/assets/err/missingTex.png"); Logger.log(Severity.ERROR, "Placeholder BufferedImage not found: " + ResourceHelper.GLOBAL_ROOT + "data/assets/err/missingTex.png");
Logger.log("This is not good! :C"); Logger.log("This is not good! :C");
Logger.logException(e1); Logger.log(e1);
} }
return null; return null;

View File

@ -27,7 +27,7 @@ public class TextIn
} }
catch (URISyntaxException e) catch (URISyntaxException e)
{ {
Logger.logException(e); Logger.log(e);
} }
return null; return null;
@ -41,7 +41,7 @@ public class TextIn
} }
catch (Exception e) catch (Exception e)
{ {
Logger.logException(e); Logger.log(e);
} }
return null; return null;
@ -55,7 +55,7 @@ public class TextIn
} }
catch (Exception e) catch (Exception e)
{ {
Logger.logException(e); Logger.log(e);
} }
return null; return null;

View File

@ -1,96 +1,224 @@
package cz.tefek.pluto.io.logger; package cz.tefek.pluto.io.logger;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PrintStream; import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
import cz.tefek.pluto.io.asl.resource.ResourceHelper; import cz.tefek.pluto.io.asl.resource.ResourceHelper;
import cz.tefek.pluto.io.asl.textio.TextOut;
/** /**
* Logger. 'nuff said. * <p>
* A simple static logger writing to both standard output and a log file.
* </p>
*
* <p>
* The log file is currently hardcoded to {@link ResourceHelper#GLOBAL_ROOT}/<code>log--YYYY-MM-DD--HH-MM-SS.txt</code>
* </p>
* *
* @author 493msi * @author 493msi
* *
* @since pre-alpha
*/ */
public class Logger public class Logger
{ {
static OutputStream stdout; private static PrintStream stdout = null;
static OutputStream stderr; private static PrintStream stderr = null;
static FileOutputStream file_log; private static OutputStream fileLog = null;
static PrintStream stdoutStream; /**
static PrintStream stderrStream; * Initializes up the logger and replaces the standard output and standard error output with the logger methods.
*
public static void setup() * <p>
* <em>
* This method will close the logger first (if it is already open).
* </em>
* </p>
*
* @since pre-alpha
* */
public static void setup() throws IOException
{ {
close();
stdout = new PrintStream(System.out); stdout = new PrintStream(System.out);
stderr = new PrintStream(System.err); stderr = new PrintStream(System.err);
setupFileStream(); // TODO: Unhardcode the log directory
stdoutStream = new PrintStream(new StdOutSplitStream()); var logsDir = Path.of(ResourceHelper.GLOBAL_ROOT, "logs");
stderrStream = new PrintStream(new StdErrSplitStream());
System.setOut(stdoutStream); if (!Files.isDirectory(logsDir))
System.setErr(stderrStream);
}
private static void setupFileStream()
{
try
{ {
if (!new File(ResourceHelper.GLOBAL_ROOT + "logs").exists() || !new File(ResourceHelper.GLOBAL_ROOT + "logs").isDirectory()) if (Files.exists(logsDir))
{ {
new File(ResourceHelper.GLOBAL_ROOT + "logs").mkdirs(); System.err.printf("[#] Failed to initialize the logger, the path `%s` is obstructed by a non-directory!%n", logsDir.toAbsolutePath().toString());
return;
} }
file_log = TextOut.createFOStream(ResourceHelper.GLOBAL_ROOT + "logs/log" + System.currentTimeMillis() + ".txt"); Files.createDirectories(logsDir);
}
catch (IOException e)
{
e.printStackTrace();
} }
var dateTime = LocalDateTime.now();
var logName = String.format("log--%04d-%02d-%02d--%02d-%02d-%02d.txt",
dateTime.getYear(), dateTime.getMonthValue(), dateTime.getDayOfMonth(),
dateTime.getHour(), dateTime.getMinute(), dateTime.getSecond());
fileLog = Files.newOutputStream(logsDir.resolve(logName));
System.setOut(new PrintStream(new OutputSplitStream(stdout, false, fileLog, false)));
System.setErr(new PrintStream(new OutputSplitStream(stderr, false, fileLog, false)));
} }
public static void close() /**
* Closes the logger and its respective log file, if there is one.
*
* <p>
* <em>
* This method is a noop for an already closed logger.
* </em>
* </p>
*
* @since pre-alpha
* */
public static void close() throws IOException
{ {
System.out.close(); if (fileLog != null)
fileLog.close();
fileLog = null;
if (stdout != null)
System.setOut(stdout);
stdout = null;
if (stderr != null)
System.setErr(stderr);
stderr = null;
} }
/**
* Logs an object converted to a {@link String} to the stdout stream.
*
* <em>
* Appends a newline.
* </em>
*
* @param string The object convertible to a {@link String}
*
* @since pre-alpha
* */
public static synchronized void log(Object string) public static synchronized void log(Object string)
{ {
log(Severity.NONE, string); System.out.println(string);
} }
public static synchronized void logf(String string, Object... o) /**
* Logs a printf-style message to the stdout stream.
*
* <em>
* No newline is appended.
* </em>
*
* <em>
* Appends a newline.
* </em>
*
* @param formatSpec The format specifier {@link String}
* @param o A list of input objects
*
* @since pre-alpha
* */
public static synchronized void logf(String formatSpec, Object... o)
{ {
System.out.printf(string, o); System.out.printf(formatSpec, o);
} }
/**
* Logs an object converted to a {@link String} to the stdout stream <em>without appending a newline</em>.
*
* @param string The object convertible to a {@link String}
*
* @since pre-alpha
* */
public static synchronized void logNoLine(Object string) public static synchronized void logNoLine(Object string)
{ {
System.out.print(string); System.out.print(string);
} }
public static synchronized void logException(Exception e) /**
* Logs the stack trace of a {@link Throwable} to the stderr stream.
*
* @param e The throwable to log
*
* @since 20.2.0.0-alpha.1
* */
public static synchronized void log(Throwable e)
{ {
e.printStackTrace(); e.printStackTrace();
} }
/**
* Logs an empty message with the specified severity. That's it.
*
* <em>
* A newline is automatically appended.
* </em>
*
* @param s The severity of the logged message
*
* @since 20.2.0.0-alpha.1
* */
public static synchronized void log(ISeverity s)
{
(s.isStdErr() ? System.err : System.out).println(s.getDisplayName());
}
/**
* Logs an object converted to a {@link String} with the specified severity.
*
* <em>
* A newline is automatically appended.
* </em>
*
* @param s The severity of the logged message
* @param string The object convertible to a {@link String}
*
* @since pre-alpha
* */
public static synchronized void log(ISeverity s, Object string) public static synchronized void log(ISeverity s, Object string)
{ {
(s.isStdErr() ? System.err : System.out).println(s.getDisplayName() + string); (s.isStdErr() ? System.err : System.out).println(s.getDisplayName() + string);
} }
public static synchronized void logf(ISeverity s, String string, Object... o) /**
* Logs a printf-style message with the specified severity.
*
* <em>
* No newline is appended.
* </em>
*
* @param s The severity of the logged message
* @param formatSpec The format specifier {@link String}
* @param o A list of input objects
*
* @since pre-alpha
* */
public static synchronized void logf(ISeverity s, String formatSpec, Object... o)
{ {
(s.isStdErr() ? System.err : System.out).printf(s.getDisplayName() + string, o); (s.isStdErr() ? System.err : System.out).printf(s.getDisplayName() + formatSpec, o);
} }
/**
* Logs an object converted to a {@link String} with the specified severity <em>without appending a newline</em>.
*
* @param s The severity of the logged message
* @param string The object convertable to a {@link String}
*
* @since pre-alpha
* */
public static synchronized void logNoLine(ISeverity s, Object string) public static synchronized void logNoLine(ISeverity s, Object string)
{ {
(s.isStdErr() ? System.err : System.out).print(s.getDisplayName() + string); (s.isStdErr() ? System.err : System.out).print(s.getDisplayName() + string);

View File

@ -0,0 +1,68 @@
package cz.tefek.pluto.io.logger;
import java.io.IOException;
import java.io.OutputStream;
/**
* Splits one {@link OutputStream} into two to allow writing to both log file
* and console output.
*
* @author 493msi
*
*/
class OutputSplitStream extends OutputStream
{
private final OutputStream outputStreamA;
private final boolean shouldAClose;
private final OutputStream outputStreamB;
private final boolean shouldBClose;
public OutputSplitStream(OutputStream outputStreamA, boolean shouldAClose,
OutputStream outputStreamB, boolean shouldBClose)
{
this.outputStreamA = outputStreamA;
this.shouldAClose = shouldAClose;
this.outputStreamB = outputStreamB;
this.shouldBClose = shouldBClose;
}
@Override
public void write(int b) throws IOException
{
this.outputStreamA.write(b);
this.outputStreamB.write(b);
}
@Override
public void write(byte[] b) throws IOException
{
this.outputStreamA.write(b);
this.outputStreamB.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException
{
this.outputStreamA.write(b, off, len);
this.outputStreamB.write(b, off, len);
}
@Override
public void flush() throws IOException
{
this.outputStreamA.flush();
this.outputStreamB.flush();
}
@Override
public void close() throws IOException
{
if (this.shouldAClose)
outputStreamA.close();
if (this.shouldBClose)
outputStreamB.close();
}
}

View File

@ -4,6 +4,8 @@ package cz.tefek.pluto.io.logger;
* A more visual way to denote what's actually happening. * A more visual way to denote what's actually happening.
* *
* @author 493msi * @author 493msi
*
* @since pre-alpha
*/ */
public enum SmartSeverity implements ISeverity public enum SmartSeverity implements ISeverity
{ {
@ -34,8 +36,8 @@ public enum SmartSeverity implements ISeverity
EVENT_WARNING("[!] [E] ", true), EVENT_WARNING("[!] [E] ", true),
EVENT_ERROR("[X] [E] ", true); EVENT_ERROR("[X] [E] ", true);
private String displayName; private final String displayName;
private boolean usesStdErr; private final boolean usesStdErr;
SmartSeverity(String name, boolean usesStdErr) SmartSeverity(String name, boolean usesStdErr)
{ {

View File

@ -1,53 +0,0 @@
package cz.tefek.pluto.io.logger;
import java.io.IOException;
import java.io.OutputStream;
/**
* Splits one {@link OutputStream} into two to allow writing to both log file
* and console output.
*
* @author 493msi
*
*/
class StdErrSplitStream extends OutputStream
{
public StdErrSplitStream()
{
}
@Override
public void write(int b) throws IOException
{
Logger.file_log.write(b);
Logger.stderr.write(b);
}
@Override
public void write(byte[] b) throws IOException
{
Logger.file_log.write(b);
Logger.stderr.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException
{
Logger.file_log.write(b, off, len);
Logger.stderr.write(b, off, len);
}
@Override
public void flush() throws IOException
{
Logger.file_log.flush();
Logger.stderr.flush();
}
@Override
public void close() throws IOException
{
Logger.file_log.close();
}
}

View File

@ -1,53 +0,0 @@
package cz.tefek.pluto.io.logger;
import java.io.IOException;
import java.io.OutputStream;
/**
* Splits one {@link OutputStream} into two to allow writing to both log file
* and console output.
*
* @author 493msi
*
*/
class StdOutSplitStream extends OutputStream
{
public StdOutSplitStream()
{
}
@Override
public void write(int b) throws IOException
{
Logger.file_log.write(b);
Logger.stdout.write(b);
}
@Override
public void write(byte[] b) throws IOException
{
Logger.file_log.write(b);
Logger.stdout.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException
{
Logger.file_log.write(b, off, len);
Logger.stdout.write(b, off, len);
}
@Override
public void flush() throws IOException
{
Logger.file_log.flush();
Logger.stdout.flush();
}
@Override
public void close() throws IOException
{
Logger.file_log.close();
}
}

View File

@ -56,7 +56,7 @@ public class ModClassLoader
} }
catch (Exception e) catch (Exception e)
{ {
Logger.logException(e); Logger.log(e);
} }
} }
} }

View File

@ -59,7 +59,7 @@ public class ModInstaller
catch (IOException e) catch (IOException e)
{ {
Logger.log(SmartSeverity.MODULE_ERROR, "Unpacking of " + file + " failed!"); Logger.log(SmartSeverity.MODULE_ERROR, "Unpacking of " + file + " failed!");
Logger.logException(e); Logger.log(e);
} }
new File(ResourceHelper.GLOBAL_ROOT + "packages/" + file).delete(); new File(ResourceHelper.GLOBAL_ROOT + "packages/" + file).delete();

View File

@ -211,7 +211,7 @@ public class ModLoaderCore
Logger.log(SmartSeverity.MODULE_ERROR, "Problem encountered while pre-loading mods."); Logger.log(SmartSeverity.MODULE_ERROR, "Problem encountered while pre-loading mods.");
Logger.log(SmartSeverity.MODULE_ERROR, "Mod loading stopped."); Logger.log(SmartSeverity.MODULE_ERROR, "Mod loading stopped.");
Logger.logException(e); Logger.log(e);
cancelLoading(); cancelLoading();
} }
@ -232,7 +232,7 @@ public class ModLoaderCore
Logger.log(SmartSeverity.MODULE_ERROR, "Problem encountered while loading mods."); Logger.log(SmartSeverity.MODULE_ERROR, "Problem encountered while loading mods.");
Logger.log(SmartSeverity.MODULE_ERROR, "Mod loading stopped."); Logger.log(SmartSeverity.MODULE_ERROR, "Mod loading stopped.");
Logger.logException(e); Logger.log(e);
cancelLoading(); cancelLoading();
} }
@ -253,7 +253,7 @@ public class ModLoaderCore
Logger.log(SmartSeverity.MODULE_ERROR, "Problem encountered while post-loading mods."); Logger.log(SmartSeverity.MODULE_ERROR, "Problem encountered while post-loading mods.");
Logger.log(SmartSeverity.MODULE_ERROR, "Mod loading stopped."); Logger.log(SmartSeverity.MODULE_ERROR, "Mod loading stopped.");
Logger.logException(e); Logger.log(e);
cancelLoading(); cancelLoading();
} }

View File

@ -46,7 +46,7 @@ public class TPL
catch (Exception e) catch (Exception e)
{ {
Logger.log(SmartSeverity.ERROR, "[TPL] Image could not be loaded: " + file); Logger.log(SmartSeverity.ERROR, "[TPL] Image could not be loaded: " + file);
Logger.logException(e); Logger.log(e);
return loadImage(null); return loadImage(null);
} }
@ -139,7 +139,7 @@ public class TPL
catch (Exception e) catch (Exception e)
{ {
Logger.log(SmartSeverity.ERROR, "[TPL] Image could not be loaded: " + file); Logger.log(SmartSeverity.ERROR, "[TPL] Image could not be loaded: " + file);
Logger.logException(e); Logger.log(e);
remake = true; remake = true;
} }