2023-10-18 20:31:50 +02:00
// What is this module called?
module log ;
// What does this module require to function?
2016-08-04 23:35:58 +02:00
import std.stdio ;
2018-03-14 05:43:40 +01:00
import std.file ;
import std.datetime ;
2023-12-08 23:19:57 +01:00
import std.concurrency ;
import std.typecons ;
import core.sync.mutex ;
import core.thread ;
import std.format ;
2023-10-18 20:31:50 +02:00
import std.string ;
2018-12-05 20:19:00 +01:00
version ( Notifications ) {
import dnotify ;
}
2018-04-07 09:06:57 +02:00
2023-12-08 23:19:57 +01:00
// Shared module object
shared LogBuffer logBuffer ;
class LogBuffer {
private :
string [ 3 ] [ ] buffer ;
Mutex bufferLock ;
string logFilePath ;
bool writeToFile ;
bool verboseLogging ;
bool debugLogging ;
Thread flushThread ;
bool isRunning ;
bool sendGUINotification ;
public :
this ( bool verboseLogging , bool debugLogging ) {
// Initialise the mutex
bufferLock = new Mutex ( ) ;
// Initialise other items
this . logFilePath = logFilePath ;
this . writeToFile = writeToFile ;
this . verboseLogging = verboseLogging ;
this . debugLogging = debugLogging ;
this . isRunning = true ;
this . sendGUINotification = true ;
this . flushThread = new Thread ( & flushBuffer ) ;
flushThread . isDaemon ( true ) ;
flushThread . start ( ) ;
}
~ this ( ) {
isRunning = false ;
flushThread . join ( ) ;
flush ( ) ;
}
shared void logThisMessage ( string message , string [ ] levels = [ "info" ] ) {
// Generate the timestamp for this log entry
auto timeStamp = leftJustify ( Clock . currTime ( ) . toString ( ) , 28 , '0' ) ;
2023-10-18 20:31:50 +02:00
2023-12-08 23:19:57 +01:00
synchronized ( bufferLock ) {
foreach ( level ; levels ) {
// Normal application output
if ( ! debugLogging ) {
2023-12-21 08:13:56 +01:00
if ( ( level = = "info" ) | | ( ( verboseLogging ) & & ( level = = "verbose" ) ) | | ( level = = "logFileOnly" ) | | ( level = = "consoleOnly" ) | | ( level = = "consoleOnlyNoNewLine" ) ) {
2023-12-08 23:19:57 +01:00
// Add this message to the buffer, with this format
buffer ~ = [ timeStamp , level , format ( "%s" , message ) ] ;
}
} else {
// Debug Logging (--verbose --verbose | -v -v | -vv) output
// Add this message, regardless of 'level' to the buffer, with this format
buffer ~ = [ timeStamp , level , format ( "DEBUG: %s" , message ) ] ;
// If there are multiple 'levels' configured, ignore this and break as we are doing debug logging
break ;
}
// Submit the message to the dbus / notification daemon for display within the GUI being used
// Will not send GUI notifications when running in debug mode
if ( ( ! debugLogging ) & & ( level = = "notify" ) ) {
version ( Notifications ) {
if ( sendGUINotification ) {
notify ( message ) ;
}
}
}
}
}
}
shared void notify ( string message ) {
// Use dnotify's functionality for GUI notifications, if GUI notifications is enabled
version ( Notifications ) {
auto n = new Notification ( "Log Notification" , message , "IGNORED" ) ;
n . show ( ) ;
}
}
private void flushBuffer ( ) {
while ( isRunning ) {
Thread . sleep ( dur ! ( "msecs" ) ( 200 ) ) ;
flush ( ) ;
}
}
2023-12-22 21:45:48 +01:00
private void flush ( ) {
2023-12-08 23:19:57 +01:00
string [ 3 ] [ ] messages ;
synchronized ( bufferLock ) {
messages = buffer ;
buffer . length = 0 ;
}
foreach ( msg ; messages ) {
// timestamp, logLevel, message
// Always write the log line to the console, if level != logFileOnly
if ( msg [ 1 ] ! = "logFileOnly" ) {
// Console output .. what sort of output
if ( msg [ 1 ] = = "consoleOnlyNoNewLine" ) {
2023-12-21 08:13:56 +01:00
// This is used write out a message to the console only, without a new line
// This is used in non-verbose mode to indicate something is happening when downloading JSON data from OneDrive or when we need user input from --resync
2023-12-08 23:19:57 +01:00
write ( msg [ 2 ] ) ;
} else {
// write this to the console with a new line
writeln ( msg [ 2 ] ) ;
}
}
// Was this just console only output?
2023-12-21 08:13:56 +01:00
if ( ( msg [ 1 ] ! = "consoleOnlyNoNewLine" ) & & ( msg [ 1 ] ! = "consoleOnly" ) ) {
// Write to the logfile only if configured to do so - console only items should not be written out
2023-12-08 23:19:57 +01:00
if ( writeToFile ) {
string logFileLine = format ( "[%s] %s" , msg [ 0 ] , msg [ 2 ] ) ;
std . file . append ( logFilePath , logFileLine ~ "\n" ) ;
}
}
}
}
2018-05-16 11:19:43 +02:00
}
2023-12-08 23:19:57 +01:00
// Function to initialize the logging system
void initialiseLogging ( bool verboseLogging = false , bool debugLogging = false ) {
logBuffer = cast ( shared ) new LogBuffer ( verboseLogging , debugLogging ) ;
2018-12-05 20:19:00 +01:00
}
2023-12-08 23:19:57 +01:00
// Function to add a log entry with multiple levels
void addLogEntry ( string message = "" , string [ ] levels = [ "info" ] ) {
logBuffer . logThisMessage ( message , levels ) ;
2016-08-04 23:35:58 +02:00
}
2023-12-08 23:19:57 +01:00
// Function to set logFilePath and enable logging to a file
void enableLogFileOutput ( string configuredLogFilePath ) {
logBuffer . logFilePath = configuredLogFilePath ;
logBuffer . writeToFile = true ;
2018-12-05 20:19:00 +01:00
}
2023-12-08 23:19:57 +01:00
void disableGUINotifications ( bool userConfigDisableNotifications ) {
logBuffer . sendGUINotification = userConfigDisableNotifications ;
2023-12-17 01:10:29 +01:00
}