From e72b507c43dbab6804da08ca88a9922b7a647de0 Mon Sep 17 00:00:00 2001 From: dd86k Date: Fri, 23 Aug 2024 12:12:05 -0400 Subject: [PATCH] Moved logging module as git package Since more of my projects will depend on that --- dub.sdl | 1 + source/adapters/base.d | 2 +- source/adapters/dap.d | 2 +- source/logging.d | 293 ----------------------------------------- source/server.d | 2 +- 5 files changed, 4 insertions(+), 296 deletions(-) delete mode 100644 source/logging.d diff --git a/dub.sdl b/dub.sdl index 1433d82..55b4558 100644 --- a/dub.sdl +++ b/dub.sdl @@ -5,6 +5,7 @@ copyright "Copyright © 2024, dd86k " license "BSD-3-Clause-Clear" dependency "alicedbg" repository="git+https://github.com/dd86k/alicedbg.git" version="852c20d01cd6f90abc37a66927014eafd2ca3423" +dependency "ddlogger" repository="git+https://github.com/dd86k/ddlogger.git" version="a739a8d1091ed935e9de276194378715b07730ee" # NOTE: By default, docs are built with dependencies, which is silly! buildType "docs" { diff --git a/source/adapters/base.d b/source/adapters/base.d index 691d3aa..2f7b65c 100644 --- a/source/adapters/base.d +++ b/source/adapters/base.d @@ -6,9 +6,9 @@ module adapters.base; import transports.base; -import logging; import core.thread : Thread; import std.datetime : Duration, dur; +import ddlogger; enum RequestType { diff --git a/source/adapters/dap.d b/source/adapters/dap.d index 9043f5b..cdc2ae2 100644 --- a/source/adapters/dap.d +++ b/source/adapters/dap.d @@ -11,8 +11,8 @@ import std.conv : text; import std.utf : validate; import adapters.base; import transports.base : ITransport; -import logging; import utils.json; +import ddlogger; // References: // - https://microsoft.github.io/debug-adapter-protocol/ diff --git a/source/logging.d b/source/logging.d deleted file mode 100644 index 06fc58b..0000000 --- a/source/logging.d +++ /dev/null @@ -1,293 +0,0 @@ -/// Logging facility. -/// -/// Inspired by Apache log4net, without the hierarchy. -/// -/// Authors: dd86k -/// Copyright: dd86k -/// License: BSD-3-Clause-Clear -module logging; - -import std.stdio; -import std.datetime; -import std.datetime.stopwatch; -import std.container : Array; -import std.format; -import std.conv; -import core.sync.mutex; - -// NOTE: Made this since std.logger does a weird non-linear thing with its log level. - -// TODO: Message passing -// To avoid slowing down the caller thread, formatting on a different thread -// would be beneficial. The issue would be the thread mailbox and how to handle -// a full mailbox. -// Make feature opt-in. - -// TODO: Appender ideas -// MemoryAppender -// ColoredConsoleAppender -// SyslogAppender - -// TODO: Flag/function to enable "debug info"? Like module/line. - -/// Log level used on a per-message basis. -/// -/// The higher the level, the more verbose the logger will be. As in, -/// "give me more information". -enum LogLevel -{ - /// Silence. Appender is disabled. - none, - - /// When the execution of the entire program cannot continue. - critical, - /// When a specific action resulted in an error. - error, - /// When a specific action can continue, but its setting was not optimal. - warning, - /// Informational message. - info, - /// Debugging messages. - debugging, - /// Information dumps and traces. - trace, - - /// Include every log message possible. - all, -} - -/// Log message given to all appenders. -struct LogMessage -{ - /// Log level. - LogLevel level; - /// System time. - SysTime time; - /// Time since startup. - long usecs; - /// Formatted text. - const(char)[] text; - /// Module. - const(char)[] mod; - /// Line. - int line; -} - -/// Get the name of a level. -/// -/// This excludes "none". -/// Params: level = LogLevel value. -/// Returns: Name, like "CRITICAL". -string logLevelName(LogLevel level) -{ - static immutable string[5] leveltable = [ - "CRITICAL", - "ERROR", - "WARNING", - "INFO", - "TRACE", - ]; - size_t idx = level-1; - return idx < leveltable.length ? leveltable[idx] : "???"; -} - -/// Main interface for implementing and appender. -abstract class Appender -{ - void setLogLevel(LogLevel level) - { - loglevel = level; - } - LogLevel getLogLevel() - { - return loglevel; - } - void log(ref LogMessage message); - -private: - LogLevel loglevel; -} - -/// Implements a logger that prints logs to the process's stderr stream. -class ConsoleAppender : Appender -{ - this() - { - } - - override - void log(ref LogMessage message) - { - enum second_us = 1_000_000; - long secs = message.usecs / second_us; - long frac = message.usecs % second_us; - // NOTE: 999,999 seconds is 277,8 Hours, so 6 digits is okay - // NOTE: stderr is not buffered by default (vs. stdout/stdin) - with (message) - stderr.writefln("[%6d.%06d] %-8s [%s:%d] %s", - secs, frac, logLevelName(level), - mod, line, - text); - } -} - -/// Implements a logger that prints logs to a file. -class FileAppender : Appender -{ - File file; - - this(string path) - { - file = File(path, "a"); - } - - override - void log(ref LogMessage message) - { - // 2024-02-06T10:26:23.0468545 - with (message) - file.writefln("%-27s %-8s [%s:%d] %s", - time.toISOExtString(), - logLevelName(level), - mod, line, text); - file.flush(); - } -} - -private __gshared -{ - Array!Appender appenders; - StopWatch watch; - Mutex mutx; -} - -shared static this() -{ - watch.start(); - appenders = Array!Appender(); - mutx = new Mutex(); -} - -/// Set log level to all appenders. -/// Params: level = New log level. -void logSetLevel(LogLevel level) -{ - foreach (appender; appenders) - appender.setLogLevel(level); -} - -void logAddAppender(Appender appender) -{ - appenders.insertBack(appender); -} - -// Function template will make the target binary bigger but it is the -// only sane way to deal with format() and variadic parameters for it... -private -void logt(A...)(LogLevel level, string mod, int line, const(char)[] fmt, A args) -{ - if (appenders.length == 0) return; - -Ltest: - if (mutx.tryLock_nothrow() == false) - goto Ltest; - - LogMessage msg = void; - bool prepped; - foreach (appender; appenders) - { - // Do not bother if the appender's level is too low against requested level - if (appender.getLogLevel() < level) - continue; - - // At least one appender has the required level, init message - if (prepped == false) - { - Duration since = watch.peek(); - SysTime time = Clock.currTime(); // NOTE: takes ~500 µs on Windows - msg = LogMessage(level, - time, - since.total!"usecs"(), - format(fmt, args), - mod, - line); - prepped = true; - } - - // Send message to appender - appender.log(msg); - } - - mutx.unlock_nothrow(); -} - -void logCritical(A...)(string fmt, A args, string MODULE = __MODULE__, int LINE = __LINE__) -{ - logt(LogLevel.critical, MODULE, LINE, fmt, args); -} -void logError(A...)(string fmt, A args, string MODULE = __MODULE__, int LINE = __LINE__) -{ - logt(LogLevel.error, MODULE, LINE, fmt, args); -} -void logWarn(A...)(string fmt, A args, string MODULE = __MODULE__, int LINE = __LINE__) -{ - logt(LogLevel.warning, MODULE, LINE, fmt, args); -} -void logInfo(A...)(string fmt, A args, string MODULE = __MODULE__, int LINE = __LINE__) -{ - logt(LogLevel.info, MODULE, LINE, fmt, args); -} -void logDebugging(A...)(string fmt, A args, string MODULE = __MODULE__, int LINE = __LINE__) -{ - logt(LogLevel.debugging, MODULE, LINE, fmt, args); -} -void logTrace(A...)(string fmt, A args, string MODULE = __MODULE__, int LINE = __LINE__) -{ - logt(LogLevel.trace, MODULE, LINE, fmt, args); -} - -unittest -{ - // Define custom appender - class UnittestAppender : Appender - { - int count; - - LogMessage lastmsg; - - override - void log(ref LogMessage message) - { - ++count; - lastmsg = message; - } - } - - // Create new appender - scope app = new UnittestAppender(); - app.setLogLevel(LogLevel.warning); - assert(app.getLogLevel() == LogLevel.warning); - - // Add it to global list - logAddAppender(app); - assert(appenders.length == 1); - - // Set level to all (including ours) - logSetLevel(LogLevel.all); - assert(app.getLogLevel() == LogLevel.all); - - // Trace message - logTrace("Here's a number: %d", 42); - assert(app.lastmsg.mod == __MODULE__); - assert(app.lastmsg.line); - assert(app.lastmsg.text == "Here's a number: 42"); - assert(app.lastmsg.level == LogLevel.trace); - assert(app.lastmsg.usecs); - assert(app.lastmsg.time.day); - - // Set new level - logSetLevel(LogLevel.warning); - assert(app.getLogLevel() == LogLevel.warning); - - // TODO: Thread test -} \ No newline at end of file diff --git a/source/server.d b/source/server.d index f5eaac9..01121bc 100644 --- a/source/server.d +++ b/source/server.d @@ -9,8 +9,8 @@ import std.concurrency; import std.conv; import std.string; import core.thread; -import logging; import adapters, debuggers; +import ddlogger; // NOTE: Structure //