From cdd7a399a9267425c28bad6c65b6442648044919 Mon Sep 17 00:00:00 2001 From: Jens Pots Date: Fri, 26 Apr 2024 22:40:30 +0200 Subject: [PATCH] refactor: custom logging solution --- .idea/.gitignore | 8 ++ .idea/codeStyles/Project.xml | 10 ++ .idea/codeStyles/codeStyleConfig.xml | 5 + .idea/gradle.xml | 17 +++ .idea/inspectionProfiles/Project_Default.xml | 17 +++ .idea/kotlinc.xml | 6 + .idea/misc.xml | 10 ++ .idea/uiDesigner.xml | 124 ++++++++++++++++++ .idea/vcs.xml | 6 + src/main/java/runner/Processor.java | 11 +- src/main/kotlin/compiler/Compiler.kt | 12 +- src/main/kotlin/compiler/MemoryClassLoader.kt | 9 +- src/main/kotlin/compiler/MemoryFileManager.kt | 6 +- src/main/kotlin/logging/Log.kt | 76 +++++++++++ src/main/kotlin/logging/Logger.kt | 68 ---------- src/main/kotlin/logging/PrettyFormatter.kt | 54 -------- src/main/kotlin/runner/Parser.kt | 18 ++- src/main/kotlin/runner/Pipeline.kt | 10 +- .../kotlin/pipelines/RangeReporterTest.kt | 1 + src/test/resources/pipeline.ttl | 1 + .../resources/pipelines/range_reporter.ttl | 2 +- src/test/resources/processors/range.ttl | 2 +- src/test/resources/processors/reporter.ttl | 2 +- src/test/resources/sources/Range.java | 8 +- src/test/resources/sources/Reporter.java | 6 +- 25 files changed, 318 insertions(+), 171 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/gradle.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/kotlinc.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/uiDesigner.xml create mode 100644 .idea/vcs.xml create mode 100644 src/main/kotlin/logging/Log.kt delete mode 100644 src/main/kotlin/logging/Logger.kt delete mode 100644 src/main/kotlin/logging/PrettyFormatter.kt create mode 120000 src/test/resources/pipeline.ttl diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..1bec35e --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..2a65317 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..590b56b --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..8d81632 --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..fe0b0da --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/main/java/runner/Processor.java b/src/main/java/runner/Processor.java index 3e50599..f4e2e67 100644 --- a/src/main/java/runner/Processor.java +++ b/src/main/java/runner/Processor.java @@ -1,11 +1,10 @@ package runner; +import technology.idlab.logging.Log; + import java.util.HashMap; import java.util.Map; import java.util.Optional; -import java.util.logging.Logger; - -import static technology.idlab.logging.LoggerKt.createLogger; public abstract class Processor { /** @@ -13,7 +12,7 @@ public abstract class Processor { * the template class. This logger is created with the name of the class * which extends the template. */ - protected final Logger logger = createLogger(); + protected final Log log = Log.Companion.getShared(); /** * The arguments of a processor are stored in a map and can be accessed by @@ -40,10 +39,10 @@ protected Optional getOptionalArgument(String name) { } public void setup() { - logger.info("Setting up processor"); + log.info("Setting up processor"); } public void exec() { - logger.info("Executing processor"); + log.info("Executing processor"); } } diff --git a/src/main/kotlin/compiler/Compiler.kt b/src/main/kotlin/compiler/Compiler.kt index 8eade84..0daa4ae 100644 --- a/src/main/kotlin/compiler/Compiler.kt +++ b/src/main/kotlin/compiler/Compiler.kt @@ -1,7 +1,6 @@ package technology.idlab.compiler -import technology.idlab.logging.createLogger -import technology.idlab.logging.fatal +import technology.idlab.logging.Log import java.io.File import java.io.PrintWriter import javax.tools.DiagnosticCollector @@ -9,14 +8,13 @@ import javax.tools.JavaFileObject import javax.tools.ToolProvider class Compiler { - private val logger = createLogger() private val compiler = - ToolProvider.getSystemJavaCompiler() ?: logger.fatal( + ToolProvider.getSystemJavaCompiler() ?: Log.shared.fatal( "No Java compiler found.", ) fun compile(file: File): ByteArray { - logger.info("Compiling file://${file.absolutePath}") + Log.shared.info("Compiling file://${file.absolutePath}") // Prepare compilation. val files = listOf(file) @@ -40,11 +38,11 @@ class Compiler { // Write diagnostics to logger. diagnosticCollector.diagnostics.forEach { - logger.info(it.toString()) + Log.shared.info(it.toString()) } if (!success) { - logger.fatal("ERROR: compilation failed") + Log.shared.fatal("ERROR: compilation failed") } return results.get(file.nameWithoutExtension) diff --git a/src/main/kotlin/compiler/MemoryClassLoader.kt b/src/main/kotlin/compiler/MemoryClassLoader.kt index 687dfae..d085eff 100644 --- a/src/main/kotlin/compiler/MemoryClassLoader.kt +++ b/src/main/kotlin/compiler/MemoryClassLoader.kt @@ -1,18 +1,15 @@ package technology.idlab.compiler -import technology.idlab.logging.createLogger -import technology.idlab.logging.fatal +import technology.idlab.logging.Log class MemoryClassLoader : ClassLoader() { - private val logger = createLogger() - fun fromBytes(bytes: ByteArray, name: String): Class<*> { - logger.info("Loading class $name") + Log.shared.info("Loading class $name") return try { defineClass(name, bytes, 0, bytes.size) } catch (e: ClassFormatError) { - logger.fatal("Failed to load class $name") + Log.shared.fatal("Failed to load class $name") } } } diff --git a/src/main/kotlin/compiler/MemoryFileManager.kt b/src/main/kotlin/compiler/MemoryFileManager.kt index 27860e2..24f5906 100644 --- a/src/main/kotlin/compiler/MemoryFileManager.kt +++ b/src/main/kotlin/compiler/MemoryFileManager.kt @@ -1,7 +1,6 @@ package technology.idlab.compiler -import technology.idlab.logging.createLogger -import technology.idlab.logging.fatal +import technology.idlab.logging.Log import java.io.ByteArrayOutputStream import java.io.OutputStream import java.net.URI @@ -19,7 +18,6 @@ class MemoryFileManager( fileManager: JavaFileManager, ) : ForwardingJavaFileManager(fileManager) { private val results: MutableMap = HashMap() - private val logger = createLogger() override fun getJavaFileForOutput( location: JavaFileManager.Location?, @@ -46,6 +44,6 @@ class MemoryFileManager( } fun get(className: String): ByteArray { - return results[className] ?: logger.fatal("Class $className not found") + return results[className] ?: Log.shared.fatal("Class $className not found") } } diff --git a/src/main/kotlin/logging/Log.kt b/src/main/kotlin/logging/Log.kt new file mode 100644 index 0000000..9326f70 --- /dev/null +++ b/src/main/kotlin/logging/Log.kt @@ -0,0 +1,76 @@ +package technology.idlab.logging + +import java.time.format.DateTimeFormatter +import java.util.Date +import java.util.TimeZone +import kotlin.Exception +import kotlin.system.exitProcess + +class Log { + init { + val header = listOf( + "TIME".padEnd(12, ' '), + "THREAD".padEnd(6, ' '), + "LEVEL".padEnd(7, ' '), + "LOCATION".padEnd(30, ' '), + "MESSAGE", + ).joinToString(" ") + println(header) + + val separator = listOf( + "----".padEnd(12, ' '), + "------".padEnd(6, ' '), + "-----".padEnd(7, ' '), + "--------".padEnd(30, ' '), + "-------", + ).joinToString(" ") + println(separator) + } + + private fun print(message: String, level: String) { + val instant = Date().toInstant() + val tz = instant.atZone(TimeZone.getDefault().toZoneId()) + val iso = DateTimeFormatter.ISO_LOCAL_TIME + val time = tz.format(iso) + + val caller = Throwable().stackTrace[2] + val name = "${caller.className.substringAfterLast(".")}::${caller.methodName}" + + val line = listOf( + time.padEnd(12, '0'), + "[${Thread.currentThread().id}]".padEnd(6, ' '), + level.padEnd(7, ' '), + name.padEnd(30, ' '), + message, + ).joinToString(" ") + + println(line) + } + + fun info(message: String) { + print(message, "INFO") + } + + fun severe(message: String) { + print(message, "SEVERE") + } + + fun fatal(message: String): Nothing { + print(message, "FATAL") + exitProcess(1) + } + + fun fatal(exception: Exception): Nothing { + print(exception.message.toString(), "FATAL") + exitProcess(1) + } + + fun fatal(message: String, exception: Exception) { + print("$message - ${exception.message}") + exitProcess(1) + } + + companion object { + val shared = Log() + } +} diff --git a/src/main/kotlin/logging/Logger.kt b/src/main/kotlin/logging/Logger.kt deleted file mode 100644 index f81f5dd..0000000 --- a/src/main/kotlin/logging/Logger.kt +++ /dev/null @@ -1,68 +0,0 @@ -package technology.idlab.logging - -import PrettyFormatter -import java.lang.Exception -import java.util.logging.ConsoleHandler -import java.util.logging.Level -import java.util.logging.Logger -import kotlin.system.exitProcess - -/** - * Create a new logger which uses the calling class name as the logger name. - * Messages are logged to the console using the PrettyFormatter. - */ -fun createLogger(): Logger { - val caller = Throwable().stackTrace[1] - val logger = Logger.getLogger(caller.className) - - // Output to the console. - logger.addHandler( - object : ConsoleHandler() { - init { - this.formatter = PrettyFormatter() - this.setOutputStream(System.out) - } - }, - ) - - logger.level = Level.ALL - return logger -} - -/** - * Log a message and exit the program with a status code of -1. This function - * is intended to use in a try-catch block. Since it returns Nothing, it can be - * the only expression in the catch block of an assignment. - * - * Example: - * ``` - * const x = try { - * 10 / 0 - * } catch (e: Exception) { - * logger.fatal("An error occurred: ${e.message}") - * } - * ``` - */ -fun Logger.fatal( - message: String, - exception: Exception? = null, -): Nothing { - // Construct message - val completeMessage = if (message != "" && exception != null) { - "$message\n${exception.message}" - } else if (message != "") { - message - } else if (exception != null) { - exception.message - } else { - "An unknown error has occurred." - } - - // Log the message. - val caller = Throwable().stackTrace[1] - logp(Level.SEVERE, caller.className, caller.methodName, completeMessage) - - - // Exit the program. - exitProcess(-1) -} diff --git a/src/main/kotlin/logging/PrettyFormatter.kt b/src/main/kotlin/logging/PrettyFormatter.kt deleted file mode 100644 index 5cb9032..0000000 --- a/src/main/kotlin/logging/PrettyFormatter.kt +++ /dev/null @@ -1,54 +0,0 @@ -import java.util.Date -import java.util.TimeZone -import java.util.logging.Formatter - -/** - * Pretty humanized formatting for use with the JVM logging framework. - * - * Included fields are: - * - Time in ISO 8601 format. - * - Thread ID. - * - Log level. - * - Class and method. - * - Message - * - * Example: `2021-09-30T12:00:00.000Z [1] INFO Main::main Hello, world!` - */ -class PrettyFormatter : Formatter() { - override fun format(record: java.util.logging.LogRecord): String { - // Parse date and time. - val instant = Date(record.millis).toInstant() - val instantTimezone = instant.atZone(TimeZone.getDefault().toZoneId()) - val isoString = - instantTimezone.format( - java.time.format.DateTimeFormatter.ISO_LOCAL_TIME, - ) - val time = isoString.padEnd(12, '0') - - // Parse thread. - val thread = "[${record.longThreadID}]".padEnd(6, ' ') - // Parse level. - val level = record.level.toString().padEnd(7, ' ') - - // Parse class and method. - var className = record.sourceClassName.split(".").last() - className = - className.contains("$").let { - className.split("$").first() - } - - val location = - "$className::${record.sourceMethodName}" - .padEnd(30, ' ') - - // Move the message to the most right column. - val message = if (record.message.contains("\n")) { - "\n> " + record.message.replace("\n", "\n> ") - } else { - record.message - } - - // Output the result as a single string. - return "$time $thread $level $location $message\n" - } -} diff --git a/src/main/kotlin/runner/Parser.kt b/src/main/kotlin/runner/Parser.kt index d35e0fc..9386571 100644 --- a/src/main/kotlin/runner/Parser.kt +++ b/src/main/kotlin/runner/Parser.kt @@ -9,13 +9,12 @@ import org.apache.jena.rdf.model.ModelFactory import org.apache.jena.rdf.model.Resource import org.apache.jena.shacl.ShaclValidator import org.apache.jena.vocabulary.OWL -import technology.idlab.logging.createLogger -import technology.idlab.logging.fatal import runner.Processor import java.io.ByteArrayOutputStream import java.io.File import technology.idlab.compiler.Compiler import technology.idlab.compiler.MemoryClassLoader +import technology.idlab.logging.Log /** * Parse a solution to a runner.Processor instance. @@ -40,7 +39,6 @@ private fun QuerySolution.toProcessor(): Class { } class Parser(file: File) { - private val logger = createLogger() private val model = ModelFactory.createDefaultModel() init { @@ -60,11 +58,11 @@ class Parser(file: File) { } // Attempt importing the dataset. - logger.info("Importing $uri") + Log.shared.info("Importing $uri") try { model.read(uri) } catch (e: Exception) { - logger.fatal("ERROR", e) + Log.shared.fatal(e) } imported.add(uri) @@ -77,12 +75,12 @@ class Parser(file: File) { if (!report.conforms()) { val out = ByteArrayOutputStream() report.model.write(out, "TURTLE") - logger.fatal("Validation failed\n$out") + Log.shared.fatal("Validation failed\n$out") } } private fun getProcessors(): List> { - logger.info("Parsing processors") + Log.shared.info("Parsing processors") val processors = mutableListOf>() val query = this.javaClass.getResource("/queries/processors.sparql") ?.readText() @@ -94,13 +92,13 @@ class Parser(file: File) { .execSelect() if (!iter.hasNext()) { - logger.fatal("No processors found in the configuration") + Log.shared.fatal("No processors found in the configuration") } while (iter.hasNext()) { val solution = iter.nextSolution() val processor = solution.toProcessor() - logger.info("Class ${processor.name} initialised successfully") + Log.shared.info("Class ${processor.name} initialised successfully") processors.add(processor) } @@ -109,7 +107,7 @@ class Parser(file: File) { fun getStages(): List { val processors = getProcessors() - logger.info("Parsing stages") + Log.shared.info("Parsing stages") // Initialize the channel. val channel = PublishSubject.create() diff --git a/src/main/kotlin/runner/Pipeline.kt b/src/main/kotlin/runner/Pipeline.kt index 96d7cdf..b807de9 100644 --- a/src/main/kotlin/runner/Pipeline.kt +++ b/src/main/kotlin/runner/Pipeline.kt @@ -2,13 +2,11 @@ package technology.idlab.runner import kotlinx.coroutines.async import kotlinx.coroutines.runBlocking -import technology.idlab.logging.createLogger import runner.Processor +import technology.idlab.logging.Log import java.io.File class Pipeline(config: File) { - private val logger = createLogger() - /** Processors described in the config. */ private val processors: List = Parser(config).getStages() @@ -18,7 +16,7 @@ class Pipeline(config: File) { */ fun executeSync() { // Run setup phase. - logger.info("Running setup phase") + Log.shared.info("Running setup phase") runBlocking { processors.map { async { it.setup() } @@ -28,7 +26,7 @@ class Pipeline(config: File) { } // Run execution phase. - logger.info("Running execution phase") + Log.shared.info("Running execution phase") runBlocking { processors.map { async { it.exec() } @@ -37,6 +35,6 @@ class Pipeline(config: File) { } } - logger.info("Pipeline executed successfully") + Log.shared.info("Pipeline executed successfully") } } diff --git a/src/test/kotlin/pipelines/RangeReporterTest.kt b/src/test/kotlin/pipelines/RangeReporterTest.kt index 0c883ec..adf7de6 100644 --- a/src/test/kotlin/pipelines/RangeReporterTest.kt +++ b/src/test/kotlin/pipelines/RangeReporterTest.kt @@ -10,5 +10,6 @@ class RangeReporterTest { val config = this.javaClass.getResource("/pipelines/range_reporter.ttl") val file = File(config!!.path) val pipeline = Pipeline(file) + pipeline.executeSync() } } diff --git a/src/test/resources/pipeline.ttl b/src/test/resources/pipeline.ttl new file mode 120000 index 0000000..6de960c --- /dev/null +++ b/src/test/resources/pipeline.ttl @@ -0,0 +1 @@ +../../../src/main/resources/pipeline.ttl \ No newline at end of file diff --git a/src/test/resources/pipelines/range_reporter.ttl b/src/test/resources/pipelines/range_reporter.ttl index fb1d665..ddbcfae 100644 --- a/src/test/resources/pipelines/range_reporter.ttl +++ b/src/test/resources/pipelines/range_reporter.ttl @@ -9,7 +9,7 @@ # Include processor definitions. <> owl:imports - <../../../main/resources/pipeline.ttl>, + <../pipeline.ttl>, <../processors/range.ttl>, <../processors/reporter.ttl>. diff --git a/src/test/resources/processors/range.ttl b/src/test/resources/processors/range.ttl index 758f26b..58a9a78 100644 --- a/src/test/resources/processors/range.ttl +++ b/src/test/resources/processors/range.ttl @@ -7,7 +7,7 @@ @prefix sh: . @prefix rdf: . -<> owl:imports <../../../main/resources/pipeline.ttl>. +<> owl:imports <../pipeline.ttl>. jvm:Range a jvm:runner.Processor; jvm:file <../sources/Range.java>; diff --git a/src/test/resources/processors/reporter.ttl b/src/test/resources/processors/reporter.ttl index a04c4d1..30cdd18 100644 --- a/src/test/resources/processors/reporter.ttl +++ b/src/test/resources/processors/reporter.ttl @@ -7,7 +7,7 @@ @prefix sh: . @prefix rdf: . -<> owl:imports <../../../main/resources/pipeline.ttl>. +<> owl:imports <../pipeline.ttl>. jvm:Reporter a jvm:runner.Processor; jvm:file <../sources/Reporter.java>; diff --git a/src/test/resources/sources/Range.java b/src/test/resources/sources/Range.java index 1d512f8..b0e9a6e 100644 --- a/src/test/resources/sources/Range.java +++ b/src/test/resources/sources/Range.java @@ -25,18 +25,18 @@ public Range(Map args) { } public void setup() { - logger.info("Binding to outgoing channel."); + log.info("Binding to outgoing channel."); } public void exec() { - logger.info("Initializing emitting loop."); + log.info("Initializing emitting loop."); for (int i = start; i < end; i += step) { - logger.info("Emitting " + i); + log.info("Emitting " + i); outgoing.onNext(Integer.toString(i)); } - logger.info("Closing outgoing channel."); + log.info("Closing outgoing channel."); outgoing.onComplete(); } } diff --git a/src/test/resources/sources/Reporter.java b/src/test/resources/sources/Reporter.java index e28601b..60d6373 100644 --- a/src/test/resources/sources/Reporter.java +++ b/src/test/resources/sources/Reporter.java @@ -17,9 +17,9 @@ public Reporter(Map args) { public void setup() { // Local variables Disposable disposable = incoming.subscribe( - item -> logger.info("Received item: " + item), - error -> logger.severe("Error: " + error), - () -> logger.info("Channel closed.") + item -> log.info("Received item: " + item), + error -> log.severe("Error: " + error), + () -> log.info("Channel closed.") ); } }