forked from StarOpenSource/Engine
Add crash handler and add various optimizations
This commit is contained in:
parent
2cfc8cad9d
commit
2b8cf723f6
10 changed files with 536 additions and 72 deletions
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||||
|
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||||
|
* Licensed under the GNU Affero General Public License v3
|
||||||
|
* with an exception allowing classpath linking.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.staropensource.engine.logging
|
||||||
|
|
||||||
|
import de.staropensource.engine.logging.implementable.Adapter
|
||||||
|
import de.staropensource.engine.logging.implementable.CrashCategory
|
||||||
|
import de.staropensource.engine.logging.implementable.Formatter
|
||||||
|
import de.staropensource.engine.logging.implementation.KotlinShutdownHandler
|
||||||
|
import de.staropensource.engine.logging.type.Call
|
||||||
|
import de.staropensource.engine.logging.type.ChannelSettings
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles crashes.
|
||||||
|
*
|
||||||
|
* @since v1-alpha10
|
||||||
|
*/
|
||||||
|
class CrashHandler private constructor() {
|
||||||
|
/**
|
||||||
|
* Companion object of [CrashHandler].
|
||||||
|
*
|
||||||
|
* @since v1-alpha10
|
||||||
|
*/
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Handles crashes.
|
||||||
|
*
|
||||||
|
* @param call [Call] metadata
|
||||||
|
* @param throwable the [Throwable] which caused the crash
|
||||||
|
* @param fatal terminates the engine and application if `true`
|
||||||
|
* @since v1-alpha10
|
||||||
|
*/
|
||||||
|
internal fun handle(call: Call, throwable: Throwable? = null, fatal: Boolean = true) {
|
||||||
|
val format: StringBuilder = StringBuilder()
|
||||||
|
var formatFinalized: String? = null
|
||||||
|
val channelconf: ChannelSettings? = LoggerConfiguration.channelSettings[call.channel]
|
||||||
|
|
||||||
|
if (ChannelSettings.getSetting(channelconf, "permitFormatting") as Boolean)
|
||||||
|
format.append("<red>")
|
||||||
|
|
||||||
|
format
|
||||||
|
.append("--------------------\nOh no! ")
|
||||||
|
.append(ChannelSettings.getSetting(channelconf, "applicationName"))
|
||||||
|
.append(" crashed!\nIf you're a user of this application, then please report this crash to the developer.")
|
||||||
|
|
||||||
|
for (category: CrashCategory in LoggerConfiguration.crashCategories)
|
||||||
|
if (category.check())
|
||||||
|
format
|
||||||
|
.append("\n\n${category.getName()}")
|
||||||
|
.append(compileCategory(category.execute(call, channelconf, throwable, fatal)))
|
||||||
|
|
||||||
|
format
|
||||||
|
.append("\n\n... ")
|
||||||
|
.append(ChannelSettings.getSetting(channelconf, "applicationName"))
|
||||||
|
.append(" unfortunately crashed.\nIf you're a user of this application, then please ")
|
||||||
|
.append("report this crash to the developer.\n--------------------")
|
||||||
|
|
||||||
|
// Format format
|
||||||
|
formatFinalized = if (ChannelSettings.getSetting(channelconf, "permitFormatting") as Boolean)
|
||||||
|
(ChannelSettings.getSetting(channelconf, "formatter") as Formatter).formatString(format.toString())
|
||||||
|
else
|
||||||
|
format.toString()
|
||||||
|
|
||||||
|
// Pass format to adapter
|
||||||
|
(ChannelSettings.getSetting(channelconf, "adapter") as Adapter).handle(call, formatFinalized)
|
||||||
|
|
||||||
|
if (fatal)
|
||||||
|
(LoggerConfiguration.shutdownHandler ?: KotlinShutdownHandler.instance).exit(exitcode = 69)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compiles a [CrashCategory].
|
||||||
|
*
|
||||||
|
* @param category [CrashCategory] to compile
|
||||||
|
* @return compiled output
|
||||||
|
* @since v1-alpha10
|
||||||
|
*/
|
||||||
|
private fun compileCategory(map: LinkedHashMap<*, *>, indent: Int = 1): String {
|
||||||
|
val builder: StringBuilder = StringBuilder()
|
||||||
|
var entryString: String? = null
|
||||||
|
|
||||||
|
// Iterate over all entries
|
||||||
|
for (entry in map.keys) {
|
||||||
|
// Check if key is a string
|
||||||
|
if (entry !is String)
|
||||||
|
continue
|
||||||
|
|
||||||
|
builder.append("\n${" ".repeat(indent)}-> ${entry}")
|
||||||
|
|
||||||
|
if (map[entry] is LinkedHashMap<*, *>) // Value is a map
|
||||||
|
builder.append(
|
||||||
|
compileCategory(
|
||||||
|
map[entry] as LinkedHashMap<*, *>,
|
||||||
|
indent = indent + 3 // increase the 2nd addend to change the indent size during recursion
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else {
|
||||||
|
entryString = map[entry].toString()
|
||||||
|
|
||||||
|
// Put on separate line if contains newline
|
||||||
|
if (entryString.contains("\n"))
|
||||||
|
builder
|
||||||
|
.append("\n${entryString}"
|
||||||
|
.replace(
|
||||||
|
"\n",
|
||||||
|
"\n ${" ".repeat(indent)}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else
|
||||||
|
builder.append(": ${entryString}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -61,6 +61,7 @@ class Logger {
|
||||||
* @since v1-alpha10
|
* @since v1-alpha10
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@Suppress("unused")
|
||||||
val instance = Logger()
|
val instance = Logger()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,35 +91,42 @@ class Logger {
|
||||||
/**
|
/**
|
||||||
* Logs a message.
|
* Logs a message.
|
||||||
*
|
*
|
||||||
|
* Using this method is highly discouraged as it is
|
||||||
|
* considered internal and should only be accessed
|
||||||
|
* if necessary. It provides direct access to the
|
||||||
|
* internal logging facility and can cause breakage
|
||||||
|
* if used improperly.
|
||||||
|
*
|
||||||
* @param level level to use
|
* @param level level to use
|
||||||
* @param message message to log
|
* @param message message to log
|
||||||
* @param stackTraceDistance determines which [StackTraceElement] will
|
* @param levelData data specific to a [Level]
|
||||||
* be used as the call's origin. Just fiddle
|
* @param stackTraceDistance determines which [StackTraceElement] will be used as the call's origin. Just fiddle with this number until it's correct.
|
||||||
* with this number until it's correct.
|
|
||||||
* @since v1-alpha10
|
* @since v1-alpha10
|
||||||
*/
|
*/
|
||||||
@Suppress("JoinDeclarationAndAssignment")
|
fun log(level: Level, message: String, levelData: Map<String, Object?> = emptyMap(), stackTraceDistance: Int = 0) {
|
||||||
fun log(level: Level, message: String, stackTraceDistance: Int = 0) {
|
|
||||||
val origin: StackTraceElement
|
val origin: StackTraceElement
|
||||||
var call: Call
|
|
||||||
|
|
||||||
// Set 'origin'
|
// Set 'origin'
|
||||||
try {
|
try {
|
||||||
origin = Throwable().stackTrace[1 + stackTraceDistance]
|
origin = Throwable().stackTrace[1 + stackTraceDistance]
|
||||||
} catch (exception: IndexOutOfBoundsException) {
|
} catch (_: IndexOutOfBoundsException) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set 'call'
|
// Create 'Call' instance
|
||||||
call = Call(origin, level, message, channel)
|
var call: Call = Call(origin, level, message, channel)
|
||||||
|
|
||||||
// Run processing
|
// Run processing
|
||||||
|
if (level == Level.CRASH)
|
||||||
|
CrashHandler.handle(call, levelData["throwable"] as Throwable?, levelData.getOrDefault("fatal", true) as Boolean)
|
||||||
|
else {
|
||||||
if (Processor.check(call))
|
if (Processor.check(call))
|
||||||
return
|
return
|
||||||
|
|
||||||
if (LoggerConfiguration.threadingHandler?.queue(call) == null)
|
if (LoggerConfiguration.threadingHandler?.queue(call) == null)
|
||||||
Processor.process(call)
|
Processor.process(call)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs a diagnostic message.
|
* Logs a diagnostic message.
|
||||||
|
@ -127,7 +135,7 @@ class Logger {
|
||||||
* @since v1-alpha10
|
* @since v1-alpha10
|
||||||
*/
|
*/
|
||||||
fun diag(message: String) {
|
fun diag(message: String) {
|
||||||
log(Level.DIAGNOSTIC, message, 1)
|
log(Level.DIAGNOSTIC, message, stackTraceDistance = 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -137,7 +145,7 @@ class Logger {
|
||||||
* @since v1-alpha10
|
* @since v1-alpha10
|
||||||
*/
|
*/
|
||||||
fun verb(message: String) {
|
fun verb(message: String) {
|
||||||
log(Level.VERBOSE, message, 1)
|
log(Level.VERBOSE, message, stackTraceDistance = 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -147,7 +155,7 @@ class Logger {
|
||||||
* @since v1-alpha10
|
* @since v1-alpha10
|
||||||
*/
|
*/
|
||||||
fun sarn(message: String) {
|
fun sarn(message: String) {
|
||||||
log(Level.SILENT_WARNING, message, 1)
|
log(Level.SILENT_WARNING, message, stackTraceDistance = 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -157,7 +165,7 @@ class Logger {
|
||||||
* @since v1-alpha10
|
* @since v1-alpha10
|
||||||
*/
|
*/
|
||||||
fun info(message: String) {
|
fun info(message: String) {
|
||||||
log(Level.INFORMATIONAL, message, 1)
|
log(Level.INFORMATIONAL, message, stackTraceDistance = 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -167,7 +175,7 @@ class Logger {
|
||||||
* @since v1-alpha10
|
* @since v1-alpha10
|
||||||
*/
|
*/
|
||||||
fun warn(message: String) {
|
fun warn(message: String) {
|
||||||
log(Level.WARNING, message, 1)
|
log(Level.WARNING, message, stackTraceDistance = 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -177,17 +185,22 @@ class Logger {
|
||||||
* @since v1-alpha10
|
* @since v1-alpha10
|
||||||
*/
|
*/
|
||||||
fun error(message: String) {
|
fun error(message: String) {
|
||||||
log(Level.ERROR, message, 1)
|
log(Level.ERROR, message, stackTraceDistance = 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs a fatal error.
|
* Logs a fatal error.
|
||||||
*
|
*
|
||||||
* @param message message
|
* @param error the error which caused the crash
|
||||||
|
* @param throwable the [Throwable] which caused the crash
|
||||||
|
* @param fatal terminates the engine and application if `true`
|
||||||
* @since v1-alpha10
|
* @since v1-alpha10
|
||||||
*/
|
*/
|
||||||
fun crash(message: String) {
|
fun crash(error: String, throwable: Throwable? = null, fatal: Boolean = true) {
|
||||||
log(Level.CRASH, message, 1)
|
log(Level.CRASH, error, levelData = mapOf(
|
||||||
|
Pair("throwable", throwable as Object?),
|
||||||
|
Pair("fatal", fatal as Object?)
|
||||||
|
), stackTraceDistance = 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,10 +20,13 @@
|
||||||
|
|
||||||
package de.staropensource.engine.logging
|
package de.staropensource.engine.logging
|
||||||
|
|
||||||
|
import de.staropensource.engine.logging.implementable.CrashCategory
|
||||||
import de.staropensource.engine.logging.implementation.SOSLSv2FormatBuilder
|
import de.staropensource.engine.logging.implementation.SOSLSv2FormatBuilder
|
||||||
import de.staropensource.engine.logging.type.ChannelSettings
|
import de.staropensource.engine.logging.type.ChannelSettings
|
||||||
import de.staropensource.engine.logging.type.Feature
|
import de.staropensource.engine.logging.type.Feature
|
||||||
import de.staropensource.engine.logging.implementable.FormatBuilder
|
import de.staropensource.engine.logging.implementable.FormatBuilder
|
||||||
|
import de.staropensource.engine.logging.implementable.ShutdownHandler
|
||||||
|
import de.staropensource.engine.logging.implementation.crashcategory.InfoCrashCategory
|
||||||
import de.staropensource.engine.logging.type.Level
|
import de.staropensource.engine.logging.type.Level
|
||||||
import de.staropensource.engine.logging.type.OperationMode
|
import de.staropensource.engine.logging.type.OperationMode
|
||||||
import kotlinx.datetime.TimeZone
|
import kotlinx.datetime.TimeZone
|
||||||
|
@ -79,32 +82,6 @@ class LoggerConfiguration private constructor() {
|
||||||
Feature.LINE_NUMBER,
|
Feature.LINE_NUMBER,
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
|
||||||
* Controls the [ThreadingHandler] to use.
|
|
||||||
*
|
|
||||||
* This determines how multithreading
|
|
||||||
* shall be performed. Set to `null` for
|
|
||||||
* a single-threaded logger.
|
|
||||||
*
|
|
||||||
* @see ThreadingHandler
|
|
||||||
* @since v1-alpha10
|
|
||||||
*/
|
|
||||||
@JvmStatic
|
|
||||||
val threadingHandler: ThreadingHandler? = null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Controls the [FormatBuilder] to use.
|
|
||||||
*
|
|
||||||
* This determines how formats are built
|
|
||||||
* and how the final log output looks like.
|
|
||||||
* Set to `null` to default to [SOSLSv2FormatBuilder].
|
|
||||||
*
|
|
||||||
* @see FormatBuilder
|
|
||||||
* @since v1-alpha10
|
|
||||||
*/
|
|
||||||
@JvmStatic
|
|
||||||
val formatBuilder: KClass<FormatBuilder>? = null
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controls how fast the logging thread
|
* Controls how fast the logging thread
|
||||||
* shall wait until processing the log
|
* shall wait until processing the log
|
||||||
|
@ -125,7 +102,57 @@ class LoggerConfiguration private constructor() {
|
||||||
*
|
*
|
||||||
* @since v1-alpha10
|
* @since v1-alpha10
|
||||||
*/
|
*/
|
||||||
val channelSettings: MutableMap<String, ChannelSettings> = mutableMapOf()
|
@JvmStatic
|
||||||
|
var channelSettings: MutableMap<String, ChannelSettings> = mutableMapOf()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains all registered [CrashCategory]s.
|
||||||
|
*
|
||||||
|
* @since v1-alpha10
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
var crashCategories: LinkedHashSet<CrashCategory> = linkedSetOf(
|
||||||
|
InfoCrashCategory.instance
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controls the [ThreadingHandler] to use.
|
||||||
|
*
|
||||||
|
* This determines how multithreading
|
||||||
|
* shall be performed. Set to `null` for
|
||||||
|
* a single-threaded logger.
|
||||||
|
*
|
||||||
|
* @see ThreadingHandler
|
||||||
|
* @since v1-alpha10
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
var threadingHandler: ThreadingHandler? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controls the [FormatBuilder] to use.
|
||||||
|
*
|
||||||
|
* This determines how formats are built
|
||||||
|
* and how the final log output looks like.
|
||||||
|
* Set to `null` to default to [SOSLSv2FormatBuilder].
|
||||||
|
*
|
||||||
|
* @see FormatBuilder
|
||||||
|
* @since v1-alpha10
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
var formatBuilder: KClass<FormatBuilder>? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controls the [ShutdownHandler] to use.
|
||||||
|
*
|
||||||
|
* This determines how the
|
||||||
|
* application is shut down
|
||||||
|
* after crashing fatally.
|
||||||
|
*
|
||||||
|
* @see ShutdownHandler
|
||||||
|
* @since v1-alpha10
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
var shutdownHandler: ShutdownHandler? = null
|
||||||
|
|
||||||
|
|
||||||
// -----> Feature settings
|
// -----> Feature settings
|
||||||
|
|
|
@ -20,13 +20,13 @@
|
||||||
|
|
||||||
package de.staropensource.engine.logging
|
package de.staropensource.engine.logging
|
||||||
|
|
||||||
import de.staropensource.engine.logging.implementation.NoOperationFormatter
|
import de.staropensource.engine.logging.implementable.Adapter
|
||||||
import de.staropensource.engine.logging.implementation.PrintlnAdapter
|
import de.staropensource.engine.logging.implementable.FormatBuilder
|
||||||
|
import de.staropensource.engine.logging.implementable.Formatter
|
||||||
import de.staropensource.engine.logging.implementation.SOSLSv2FormatBuilder
|
import de.staropensource.engine.logging.implementation.SOSLSv2FormatBuilder
|
||||||
import de.staropensource.engine.logging.type.Call
|
import de.staropensource.engine.logging.type.Call
|
||||||
import de.staropensource.engine.logging.type.ChannelSettings
|
import de.staropensource.engine.logging.type.ChannelSettings
|
||||||
import de.staropensource.engine.logging.type.Feature
|
import de.staropensource.engine.logging.type.Feature
|
||||||
import de.staropensource.engine.logging.implementable.FormatBuilder
|
|
||||||
import de.staropensource.engine.logging.type.OperationMode
|
import de.staropensource.engine.logging.type.OperationMode
|
||||||
import kotlin.reflect.full.primaryConstructor
|
import kotlin.reflect.full.primaryConstructor
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ class Processor private constructor() {
|
||||||
*
|
*
|
||||||
* Invoked by [Logger.log].
|
* Invoked by [Logger.log].
|
||||||
*
|
*
|
||||||
|
* @param call [Call] metadata
|
||||||
* @return terminate processing?
|
* @return terminate processing?
|
||||||
* @since v1-alpha10
|
* @since v1-alpha10
|
||||||
*/
|
*/
|
||||||
|
@ -81,6 +82,7 @@ class Processor private constructor() {
|
||||||
* Invoked by the configured
|
* Invoked by the configured
|
||||||
* [ThreadingHandler].
|
* [ThreadingHandler].
|
||||||
*
|
*
|
||||||
|
* @param call [Call] metadata
|
||||||
* @see LoggerConfiguration.threadingHandler
|
* @see LoggerConfiguration.threadingHandler
|
||||||
* @see ChannelSettings.formatter
|
* @see ChannelSettings.formatter
|
||||||
* @see ChannelSettings.adapter
|
* @see ChannelSettings.adapter
|
||||||
|
@ -91,7 +93,7 @@ class Processor private constructor() {
|
||||||
fun process(call: Call) {
|
fun process(call: Call) {
|
||||||
val format: FormatBuilder
|
val format: FormatBuilder
|
||||||
var formatFinalized: String = ""
|
var formatFinalized: String = ""
|
||||||
val channelconf: ChannelSettings = LoggerConfiguration.channelSettings[call.channel] ?: ChannelSettings.global
|
val channelconf: ChannelSettings? = LoggerConfiguration.channelSettings[call.channel]
|
||||||
var message: String = call.message
|
var message: String = call.message
|
||||||
|
|
||||||
// Set 'format'
|
// Set 'format'
|
||||||
|
@ -103,7 +105,7 @@ class Processor private constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop if channel does not permit execution
|
// Stop if channel does not permit execution
|
||||||
if ((channelconf.enable ?: ChannelSettings.global.enable) == false)
|
if (!(ChannelSettings.getSetting(channelconf, "enable") as Boolean))
|
||||||
return
|
return
|
||||||
|
|
||||||
// Build format
|
// Build format
|
||||||
|
@ -113,11 +115,11 @@ class Processor private constructor() {
|
||||||
|
|
||||||
// Update message
|
// Update message
|
||||||
if (
|
if (
|
||||||
(channelconf.sanitizeMessage ?: ChannelSettings.global.sanitizeMessage) != false
|
ChannelSettings.getSetting(channelconf, "sanitizeMessage") as Boolean
|
||||||
&& (channelconf.permitFormatting ?: ChannelSettings.global.permitFormatting) != false
|
&& ChannelSettings.getSetting(channelconf, "permitFormatting") as Boolean
|
||||||
) message = message.replace("<", "\\<")
|
) message = message.replace("<", "\\<")
|
||||||
if (
|
if (
|
||||||
((channelconf.permitFormatting ?: ChannelSettings.global.permitFormatting) != false)
|
ChannelSettings.getSetting(channelconf, "permitFormatting") as Boolean
|
||||||
&& LoggerConfiguration.features.contains(Feature.FORMATTING)
|
&& LoggerConfiguration.features.contains(Feature.FORMATTING)
|
||||||
) format.addFeature(Feature.FORMATTING)
|
) format.addFeature(Feature.FORMATTING)
|
||||||
|
|
||||||
|
@ -125,13 +127,13 @@ class Processor private constructor() {
|
||||||
format.message = message
|
format.message = message
|
||||||
|
|
||||||
// Format format
|
// Format format
|
||||||
formatFinalized = if ((channelconf.permitFormatting ?: ChannelSettings.global.permitFormatting) != false)
|
formatFinalized = if (ChannelSettings.getSetting(channelconf, "permitFormatting") as Boolean)
|
||||||
(channelconf.formatter ?: ChannelSettings.global.formatter ?: NoOperationFormatter.instance).formatString(format.toString())
|
(ChannelSettings.getSetting(channelconf, "formatter") as Formatter).formatString(format.toString())
|
||||||
else
|
else
|
||||||
format.toString()
|
format.toString()
|
||||||
|
|
||||||
// Pass format to adapter
|
// Pass format to adapter
|
||||||
(channelconf.adapter ?: ChannelSettings.global.adapter ?: PrintlnAdapter.instance).handle(call, formatFinalized)
|
(ChannelSettings.getSetting(channelconf, "adapter") as Adapter).handle(call, formatFinalized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||||
|
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||||
|
* Licensed under the GNU Affero General Public License v3
|
||||||
|
* with an exception allowing classpath linking.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.staropensource.engine.logging.implementable
|
||||||
|
|
||||||
|
import de.staropensource.engine.logging.CrashHandler
|
||||||
|
import de.staropensource.engine.logging.type.Call
|
||||||
|
import de.staropensource.engine.logging.type.ChannelSettings
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by the [CrashHandler] to
|
||||||
|
* print useful information.
|
||||||
|
*
|
||||||
|
* @since v1-alpha10
|
||||||
|
*/
|
||||||
|
interface CrashCategory {
|
||||||
|
/**
|
||||||
|
* Checks if this category
|
||||||
|
* shall be executed.
|
||||||
|
*
|
||||||
|
* @return execute?
|
||||||
|
* @since v1-alpha10
|
||||||
|
*/
|
||||||
|
fun check(): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of this crash category.
|
||||||
|
*
|
||||||
|
* @return category name
|
||||||
|
* @since v1-alpha10
|
||||||
|
*/
|
||||||
|
fun getName(): String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes this crash category.
|
||||||
|
*
|
||||||
|
* The value of this map can either
|
||||||
|
* be a [String] or recurse downwards
|
||||||
|
* with `Map<String, Any>`. Any other
|
||||||
|
* values will be ignored.
|
||||||
|
*
|
||||||
|
* @param call [Call] metadata
|
||||||
|
* @param channelconf [ChannelSettings] instance or `null`. Use this to access [ChannelSettings.getSetting]
|
||||||
|
* @param throwable the [Throwable] which caused the crash
|
||||||
|
* @param fatal terminates the engine and application if `true`
|
||||||
|
* @return crash category items
|
||||||
|
* @since v1-alpha10
|
||||||
|
*/
|
||||||
|
fun execute(
|
||||||
|
call: Call,
|
||||||
|
channelconf: ChannelSettings?,
|
||||||
|
throwable: Throwable?,
|
||||||
|
fatal: Boolean,
|
||||||
|
): LinkedHashMap<String, Any>
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||||
|
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||||
|
* Licensed under the GNU Affero General Public License v3
|
||||||
|
* with an exception allowing classpath linking.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.staropensource.engine.logging.implementable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles shutdowns.
|
||||||
|
*
|
||||||
|
* @since v1-alpha10
|
||||||
|
*/
|
||||||
|
interface ShutdownHandler {
|
||||||
|
/**
|
||||||
|
* Shuts everything down.
|
||||||
|
*
|
||||||
|
* @param exitcode the code to exit with
|
||||||
|
* @since v1-alpha10
|
||||||
|
*/
|
||||||
|
fun exit(exitcode: Byte = 0)
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||||
|
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||||
|
* Licensed under the GNU Affero General Public License v3
|
||||||
|
* with an exception allowing classpath linking.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.staropensource.engine.logging.implementation
|
||||||
|
|
||||||
|
import de.staropensource.engine.logging.implementable.ShutdownHandler
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [ShutdownHandler] implementation using
|
||||||
|
* Kotlin's [kotlin.system.exitProcess] method.
|
||||||
|
*
|
||||||
|
* @since v1-alpha10
|
||||||
|
*/
|
||||||
|
class KotlinShutdownHandler private constructor() : ShutdownHandler {
|
||||||
|
/**
|
||||||
|
* Companion object of [KotlinShutdownHandler].
|
||||||
|
*
|
||||||
|
* @since v1-alpha10
|
||||||
|
*/
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Global instance of [KotlinShutdownHandler].
|
||||||
|
*
|
||||||
|
* @since v1-alpha10
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
val instance: KotlinShutdownHandler = KotlinShutdownHandler()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun exit(exitcode: Byte) {
|
||||||
|
exitProcess(exitcode.toInt())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||||
|
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||||
|
* Licensed under the GNU Affero General Public License v3
|
||||||
|
* with an exception allowing classpath linking.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.staropensource.engine.logging.implementation.crashcategory
|
||||||
|
|
||||||
|
import de.staropensource.engine.logging.implementable.CrashCategory
|
||||||
|
import de.staropensource.engine.logging.type.Call
|
||||||
|
import de.staropensource.engine.logging.type.ChannelSettings
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [CrashCategory] implementation
|
||||||
|
* providing insight into a crash.
|
||||||
|
*
|
||||||
|
* @since v1-alpha10
|
||||||
|
*/
|
||||||
|
class InfoCrashCategory private constructor() : CrashCategory {
|
||||||
|
/**
|
||||||
|
* Companion object of [InfoCrashCategory].
|
||||||
|
*
|
||||||
|
* @since v1-alpha10
|
||||||
|
*/
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Global instance of [InfoCrashCategory].
|
||||||
|
*
|
||||||
|
* @since v1-alpha10
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
val instance: InfoCrashCategory = InfoCrashCategory()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun check(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getName(): String {
|
||||||
|
return "Crash"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun execute(
|
||||||
|
call: Call,
|
||||||
|
channelconf: ChannelSettings?,
|
||||||
|
throwable: Throwable?,
|
||||||
|
fatal: Boolean,
|
||||||
|
): LinkedHashMap<String, Any> {
|
||||||
|
return linkedMapOf(
|
||||||
|
Pair("Origin", linkedMapOf<String, Any>(
|
||||||
|
Pair("Class", call.origin.className),
|
||||||
|
Pair("Method", call.origin.methodName),
|
||||||
|
Pair("Line", call.origin.lineNumber),
|
||||||
|
Pair("Native", if (call.origin.isNativeMethod) "yes" else "false")
|
||||||
|
)),
|
||||||
|
Pair("Channel", call.channel),
|
||||||
|
Pair("Fatal", if (fatal) "yes" else "no"),
|
||||||
|
Pair("Message", call.message),
|
||||||
|
Pair("Stacktrace", throwable?.toString() ?: "Not available."), // TODO report correct stacktrace
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||||
|
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||||
|
* Licensed under the GNU Affero General Public License v3
|
||||||
|
* with an exception allowing classpath linking.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementations of the [CrashCategory] interface.
|
||||||
|
*
|
||||||
|
* @since v1-alpha10
|
||||||
|
*/
|
||||||
|
package de.staropensource.engine.logging.implementation.crashcategory
|
||||||
|
|
||||||
|
import de.staropensource.engine.logging.implementable.CrashCategory
|
|
@ -12,16 +12,18 @@ import de.staropensource.engine.logging.implementation.PrintlnAdapter
|
||||||
* @param enable enables or disables all log messages utilising this channel. Defaults to the global value or `true` if set to `null`
|
* @param enable enables or disables all log messages utilising this channel. Defaults to the global value or `true` if set to `null`
|
||||||
* @param sanitizeMessage if message sanitization (escaping `<`) should be performed. Defaults to the global value or `true` if set to `null`
|
* @param sanitizeMessage if message sanitization (escaping `<`) should be performed. Defaults to the global value or `true` if set to `null`
|
||||||
* @param permitFormatting if formatting shall be permitted for the entire format. For disallowing formatting for the message only, see [sanitizeMessage]. Defaults to the global value or `true` if set to `null`
|
* @param permitFormatting if formatting shall be permitted for the entire format. For disallowing formatting for the message only, see [sanitizeMessage]. Defaults to the global value or `true` if set to `null`
|
||||||
|
* @param applicationName name of the application. Used in crash reports. Defaults to the global value or `"This application"` if `null`
|
||||||
* @param formatter determines how messages are formatted and stylized. Defaults to the global value or [NoOperationFormatter] if set to `null`
|
* @param formatter determines how messages are formatted and stylized. Defaults to the global value or [NoOperationFormatter] if set to `null`
|
||||||
* @param adapter used for printing the finalized log format somewhere. Defaults to the global value [PrintlnAdapter] if set to `null`
|
* @param adapter used for printing the finalized log format somewhere. Defaults to the global value [PrintlnAdapter] if set to `null`
|
||||||
* @since v1-alpha10
|
* @since v1-alpha10
|
||||||
*/
|
*/
|
||||||
data class ChannelSettings(
|
data class ChannelSettings(
|
||||||
val enable: Boolean? = null,
|
private val enable: Boolean? = null,
|
||||||
val sanitizeMessage: Boolean? = null,
|
private val sanitizeMessage: Boolean? = null,
|
||||||
val permitFormatting: Boolean? = null,
|
private val permitFormatting: Boolean? = null,
|
||||||
val formatter: Formatter? = null,
|
private val applicationName: String? = null,
|
||||||
val adapter: Adapter? = null,
|
private val formatter: Formatter? = null,
|
||||||
|
private val adapter: Adapter? = null,
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* Companion object of [ChannelSettings].
|
* Companion object of [ChannelSettings].
|
||||||
|
@ -47,11 +49,34 @@ data class ChannelSettings(
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val global: ChannelSettings = ChannelSettings(
|
val global: ChannelSettings = ChannelSettings(
|
||||||
true,
|
enable = null,
|
||||||
true,
|
sanitizeMessage = null,
|
||||||
true,
|
permitFormatting = null,
|
||||||
null,
|
applicationName = null,
|
||||||
null
|
formatter = null,
|
||||||
|
adapter = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a setting's value.
|
||||||
|
*
|
||||||
|
* @param settings [ChannelSettings] instance to access. Set to `null` to only access the global settings
|
||||||
|
* @param setting setting to get
|
||||||
|
* @return setting value or `null`
|
||||||
|
* @since v1-alpha10
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
@Suppress("CyclomaticComplexMethod")
|
||||||
|
fun getSetting(settings: ChannelSettings?, setting: String): Any? {
|
||||||
|
return when (setting) {
|
||||||
|
"enable" -> (settings?.enable ?: global.enable) != false
|
||||||
|
"sanitizeMessage" -> (settings?.sanitizeMessage ?: global.sanitizeMessage) != false
|
||||||
|
"permitFormatting" -> (settings?.permitFormatting ?: global.permitFormatting) != false
|
||||||
|
"applicationName" -> settings?.applicationName ?: global.applicationName ?: "This application"
|
||||||
|
"formatter" -> settings?.formatter ?: global.formatter ?: NoOperationFormatter.instance
|
||||||
|
"adapter" -> settings?.adapter ?: global.adapter ?: PrintlnAdapter.instance
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue