2023-03-23 09:20:46 +00:00
|
|
|
module singlog;
|
|
|
|
|
2023-07-18 18:01:43 +00:00
|
|
|
version(Windows) {
|
2023-04-27 18:08:12 +00:00
|
|
|
import core.sys.windows.windows;
|
2023-07-18 18:01:43 +00:00
|
|
|
import std.utf : toUTF8, toUTF16z;
|
|
|
|
} else version(Posix)
|
2023-04-27 18:08:12 +00:00
|
|
|
import core.sys.posix.syslog;
|
|
|
|
|
|
|
|
import std.string;
|
2023-03-23 09:20:46 +00:00
|
|
|
import std.stdio;
|
|
|
|
import std.conv;
|
|
|
|
import std.file;
|
|
|
|
import std.datetime;
|
|
|
|
import datefmt;
|
|
|
|
|
2023-03-29 15:13:56 +00:00
|
|
|
alias log = Log.msg;
|
2023-03-29 12:53:00 +00:00
|
|
|
|
2023-03-23 09:20:46 +00:00
|
|
|
/++
|
|
|
|
Singleton for simple logging
|
|
|
|
|
|
|
|
---
|
2023-04-27 20:53:45 +00:00
|
|
|
// Setting the name of the logged program
|
2023-06-05 21:17:56 +00:00
|
|
|
log.program("My program");
|
|
|
|
// Setting the status of color text output
|
|
|
|
log.color(true);
|
2023-03-23 09:20:46 +00:00
|
|
|
// Setting the error output level
|
2023-07-18 18:01:43 +00:00
|
|
|
log.level(log.level.debugging);
|
|
|
|
log.level(log.level.alert);
|
|
|
|
log.level(log.level.critical);
|
|
|
|
log.level(log.level.error);
|
|
|
|
log.level(log.level.warning);
|
|
|
|
log.level(log.level.notice);
|
|
|
|
log.level(log.level.information);
|
2023-03-23 09:20:46 +00:00
|
|
|
// Assigning a target output
|
2023-07-18 18:01:43 +00:00
|
|
|
log.output(log.output.syslog.stderr.stdout.file);
|
2023-03-23 09:20:46 +00:00
|
|
|
// Setup and allowing writing to a file
|
2023-03-29 12:53:00 +00:00
|
|
|
log.file("./file.log");
|
2023-03-23 09:20:46 +00:00
|
|
|
// Output of messages to the log
|
2023-05-30 16:32:09 +00:00
|
|
|
log.debugging("Debugging message");
|
2023-03-29 12:53:00 +00:00
|
|
|
log.alert("Alert message");
|
|
|
|
log.critical("Critical message");
|
|
|
|
log.error("Error message");
|
|
|
|
log.warning("Warning message");
|
|
|
|
log.notice("Notice message");
|
|
|
|
log.informations("Information message");
|
2023-07-18 18:01:43 +00:00
|
|
|
// Write message to specific outputs
|
|
|
|
log.now(log.output.stdout.file).informations("Information message");
|
2023-03-23 09:20:46 +00:00
|
|
|
---
|
|
|
|
+/
|
2023-04-27 18:08:12 +00:00
|
|
|
class Log {
|
|
|
|
private:
|
2023-06-05 21:17:56 +00:00
|
|
|
static Log _log;
|
|
|
|
string _path;
|
2023-06-07 07:02:47 +00:00
|
|
|
wstring _name = "singlog";
|
2023-06-05 21:17:56 +00:00
|
|
|
bool _writeToFile = true;
|
|
|
|
bool _ccolor = false;
|
2023-04-27 18:08:12 +00:00
|
|
|
|
|
|
|
this() {}
|
|
|
|
|
|
|
|
version(Windows) {
|
2023-06-06 23:12:20 +00:00
|
|
|
int[] _sysPriority = [0, 1, 1, 1, 2, 3, 3];
|
2023-06-05 21:17:56 +00:00
|
|
|
|
2023-06-06 23:12:20 +00:00
|
|
|
WORD[] _sysPriorityOS = [
|
2023-04-27 18:08:12 +00:00
|
|
|
EVENTLOG_SUCCESS,
|
|
|
|
EVENTLOG_ERROR_TYPE,
|
|
|
|
EVENTLOG_WARNING_TYPE,
|
|
|
|
EVENTLOG_INFORMATION_TYPE
|
|
|
|
];
|
|
|
|
|
2023-06-06 23:12:20 +00:00
|
|
|
void writesyslog(string message, WORD priority) {
|
|
|
|
auto wMessage = message.toUTF16z();
|
2023-06-07 07:02:47 +00:00
|
|
|
HANDLE handleEventLog = RegisterEventSourceW(NULL, this._name.ptr);
|
2023-04-27 18:08:12 +00:00
|
|
|
|
|
|
|
if (handleEventLog == NULL)
|
|
|
|
return;
|
|
|
|
|
2023-06-06 23:12:20 +00:00
|
|
|
ReportEventW(handleEventLog, priority, 0, 0, NULL, 1, 0, &wMessage, NULL);
|
2023-04-27 18:08:12 +00:00
|
|
|
DeregisterEventSource(handleEventLog);
|
|
|
|
}
|
2023-06-06 23:12:20 +00:00
|
|
|
|
|
|
|
WORD[] _color = [
|
2023-06-07 07:02:47 +00:00
|
|
|
FOREGROUND_GREEN, // green
|
|
|
|
FOREGROUND_BLUE, // blue
|
|
|
|
FOREGROUND_RED | FOREGROUND_BLUE, // magenta
|
|
|
|
FOREGROUND_RED, // red
|
|
|
|
FOREGROUND_RED | FOREGROUND_GREEN, // yellow
|
|
|
|
FOREGROUND_BLUE | FOREGROUND_GREEN, // cyan
|
|
|
|
FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN // white
|
2023-06-06 23:12:20 +00:00
|
|
|
];
|
|
|
|
|
2023-07-18 18:01:43 +00:00
|
|
|
void colorTextOutput(HANDLE handle, string time, string message, int priority) {
|
2023-06-06 23:12:20 +00:00
|
|
|
CONSOLE_SCREEN_BUFFER_INFO defaultConsole;
|
|
|
|
GetConsoleScreenBufferInfo(handle, &defaultConsole);
|
|
|
|
|
2023-07-18 18:01:43 +00:00
|
|
|
wstring wTime = "%s ".format(time).to!wstring;
|
|
|
|
wstring wType = this._type[priority].to!wstring;
|
|
|
|
wstring wMessage = " %s\n".format(message).to!wstring;
|
|
|
|
|
|
|
|
switch (GetFileType(handle)) {
|
|
|
|
case FILE_TYPE_CHAR:
|
|
|
|
WriteConsoleW(handle, wTime.ptr, cast(DWORD)wTime.length, NULL, NULL);
|
|
|
|
SetConsoleTextAttribute(handle, this._color[priority] | FOREGROUND_INTENSITY);
|
|
|
|
WriteConsoleW(handle, wType.ptr, cast(DWORD)wType.length, NULL, NULL);
|
|
|
|
SetConsoleTextAttribute(handle, this._color[priority]);
|
|
|
|
WriteConsoleW(handle, wMessage.ptr, cast(DWORD)wMessage.length, NULL, NULL);
|
|
|
|
SetConsoleTextAttribute(handle, defaultConsole.wAttributes);
|
|
|
|
break;
|
|
|
|
case FILE_TYPE_PIPE, FILE_TYPE_DISK:
|
|
|
|
auto utf8Message = (wTime ~ wType ~ wMessage).toUTF8;
|
|
|
|
WriteFile(handle, utf8Message.ptr, cast(DWORD)utf8Message.length, NULL, NULL);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
writesyslog("Unknown output file", _sysPriorityOS[_sysPriority[ERROR]]);
|
|
|
|
}
|
2023-06-06 23:12:20 +00:00
|
|
|
}
|
|
|
|
|
2023-07-18 18:01:43 +00:00
|
|
|
void defaultTextOutput(HANDLE handle, string time, string message, int priority) {
|
|
|
|
wstring wMessage = "%s %s %s\n".format(time, this._type[priority], message).to!wstring;
|
|
|
|
switch (GetFileType(handle)) {
|
|
|
|
case FILE_TYPE_CHAR:
|
|
|
|
WriteConsoleW(handle, wMessage.ptr, cast(DWORD)wMessage.length, NULL, NULL);
|
|
|
|
break;
|
|
|
|
case FILE_TYPE_PIPE, FILE_TYPE_DISK:
|
|
|
|
auto utf8Message = wMessage.toUTF8;
|
|
|
|
WriteFile(handle, utf8Message.ptr, cast(DWORD)utf8Message.length, NULL, NULL);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
writesyslog("Unknown output file", _sysPriorityOS[_sysPriority[ERROR]]);
|
|
|
|
}
|
2023-06-06 23:12:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void writestdout(string time, string message, int priority) {
|
2023-07-18 18:01:43 +00:00
|
|
|
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
2023-06-06 23:12:20 +00:00
|
|
|
this._ccolor ?
|
2023-07-18 18:01:43 +00:00
|
|
|
colorTextOutput(handle, time, message, priority) :
|
|
|
|
defaultTextOutput(handle, time, message, priority);
|
|
|
|
}
|
|
|
|
|
|
|
|
void writestderr(string time, string message, int priority) {
|
|
|
|
HANDLE handle = GetStdHandle(STD_ERROR_HANDLE);
|
|
|
|
this._ccolor ?
|
|
|
|
colorTextOutput(handle, time, message, priority) :
|
|
|
|
defaultTextOutput(handle, time, message, priority);
|
2023-06-06 23:12:20 +00:00
|
|
|
}
|
|
|
|
|
2023-04-27 18:08:12 +00:00
|
|
|
} else version(Posix) {
|
2023-06-06 23:12:20 +00:00
|
|
|
int[] _sysPriority = [0, 1, 2, 3, 4, 5, 6];
|
2023-06-05 21:17:56 +00:00
|
|
|
|
2023-06-06 23:12:20 +00:00
|
|
|
int[] _sysPriorityOS = [
|
2023-04-27 18:08:12 +00:00
|
|
|
LOG_DEBUG,
|
|
|
|
LOG_ALERT,
|
|
|
|
LOG_CRIT,
|
|
|
|
LOG_ERR,
|
|
|
|
LOG_WARNING,
|
|
|
|
LOG_NOTICE,
|
|
|
|
LOG_INFO
|
|
|
|
];
|
2023-06-05 21:17:56 +00:00
|
|
|
|
|
|
|
string[] _color = [
|
|
|
|
"\x1b[1;32m%s\x1b[0;32m %s\x1b[0;0m", // green
|
|
|
|
"\x1b[1;34m%s\x1b[0;34m %s\x1b[0;0m", // blue
|
|
|
|
"\x1b[1;35m%s\x1b[0;35m %s\x1b[0;0m", // magenta
|
|
|
|
"\x1b[1;31m%s\x1b[0;31m %s\x1b[0;0m", // red
|
|
|
|
"\x1b[1;33m%s\x1b[0;33m %s\x1b[0;0m", // yellow
|
|
|
|
"\x1b[1;36m%s\x1b[0;36m %s\x1b[0;0m", // cyan
|
|
|
|
"\x1b[1;97m%s\x1b[0;97m %s\x1b[0;0m", // white
|
|
|
|
];
|
|
|
|
|
2023-06-06 23:12:20 +00:00
|
|
|
void writestdout(string time, string message, int priority) {
|
|
|
|
writefln("%s %s",
|
2023-06-05 21:54:21 +00:00
|
|
|
time,
|
2023-06-06 23:12:20 +00:00
|
|
|
(this._ccolor ? this._color[priority] : "%s %s").format(this._type[priority], message)
|
2023-06-05 21:17:56 +00:00
|
|
|
);
|
|
|
|
}
|
2023-06-06 23:12:20 +00:00
|
|
|
|
2023-07-18 18:01:43 +00:00
|
|
|
void writestderr(string time, string message, int priority) {
|
|
|
|
stderr.writefln("%s %s",
|
|
|
|
time,
|
|
|
|
(this._ccolor ? this._color[priority] : "%s %s").format(this._type[priority], message)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-06-06 23:12:20 +00:00
|
|
|
void writesyslog(string message, int priority) {
|
|
|
|
syslog(priority, message.toStringz());
|
|
|
|
}
|
2023-04-27 18:08:12 +00:00
|
|
|
}
|
|
|
|
|
2023-06-05 21:17:56 +00:00
|
|
|
public enum {
|
|
|
|
DEBUGGING = 0,
|
|
|
|
ALERT = 1,
|
|
|
|
CRITICAL = 2,
|
|
|
|
ERROR = 3,
|
|
|
|
WARNING = 4,
|
|
|
|
NOTICE = 5,
|
|
|
|
INFORMATION = 6
|
|
|
|
}
|
|
|
|
|
|
|
|
string[] _type = [
|
|
|
|
"[DEBUG]:",
|
|
|
|
"[ALERT]:",
|
|
|
|
"[CRITICAL]:",
|
|
|
|
"[ERROR]:",
|
|
|
|
"[WARNING]:",
|
|
|
|
"[NOTICE]:",
|
|
|
|
"[INFO]:"
|
2023-06-05 18:33:02 +00:00
|
|
|
];
|
|
|
|
|
2023-04-27 18:08:12 +00:00
|
|
|
public enum {
|
2023-03-29 12:53:00 +00:00
|
|
|
SYSLOG = 1,
|
|
|
|
STDOUT = 2,
|
2023-07-18 18:01:43 +00:00
|
|
|
STDERR = 4,
|
|
|
|
FILE = 8
|
2023-03-29 12:53:00 +00:00
|
|
|
}
|
2023-03-23 09:20:46 +00:00
|
|
|
|
2023-07-18 18:01:43 +00:00
|
|
|
int _nowoutput = 0;
|
2023-06-05 21:17:56 +00:00
|
|
|
int _output = STDOUT;
|
2023-06-06 23:12:20 +00:00
|
|
|
int _priority = INFORMATION;
|
2023-03-23 09:20:46 +00:00
|
|
|
|
2023-06-06 23:12:20 +00:00
|
|
|
void writelog(string message, int priority) {
|
2023-06-05 21:17:56 +00:00
|
|
|
string time;
|
2023-07-18 18:01:43 +00:00
|
|
|
int output = this._nowoutput ? this._nowoutput : this._output;
|
|
|
|
this._nowoutput = 0;
|
2023-06-06 23:12:20 +00:00
|
|
|
if (this._priority > priority)
|
2023-03-23 09:20:46 +00:00
|
|
|
return;
|
2023-07-18 18:01:43 +00:00
|
|
|
if (output & 1)
|
2023-06-06 23:12:20 +00:00
|
|
|
writesyslog(message, _sysPriorityOS[_sysPriority[priority]]);
|
2023-07-18 18:01:43 +00:00
|
|
|
if (output & 14)
|
2023-06-05 21:17:56 +00:00
|
|
|
time = Clock.currTime().format("%Y.%m.%d %H:%M:%S");
|
2023-07-18 18:01:43 +00:00
|
|
|
if (output & 2 && priority >= WARNING)
|
2023-06-06 23:12:20 +00:00
|
|
|
writestdout(time, message, priority);
|
2023-07-18 18:01:43 +00:00
|
|
|
if (output & 4 && priority <= ERROR)
|
|
|
|
writestderr(time, message, priority);
|
|
|
|
if (output & 8)
|
2023-06-06 23:12:20 +00:00
|
|
|
writefile(time, message, priority);
|
2023-03-23 09:20:46 +00:00
|
|
|
}
|
|
|
|
|
2023-06-06 23:12:20 +00:00
|
|
|
void writefile(string time, string message, int priority) {
|
2023-06-05 21:17:56 +00:00
|
|
|
if (!this._writeToFile)
|
2023-03-23 09:20:46 +00:00
|
|
|
return;
|
|
|
|
|
2023-06-05 21:17:56 +00:00
|
|
|
if (!this._path.exists) {
|
|
|
|
this._writeToFile = false;
|
|
|
|
this.warning("The log file does not exist: " ~ this._path);
|
2023-03-23 09:20:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
File file;
|
|
|
|
|
|
|
|
try {
|
2023-06-05 21:17:56 +00:00
|
|
|
file = File(this._path, "a+");
|
|
|
|
this._writeToFile = true;
|
2023-03-23 09:20:46 +00:00
|
|
|
} catch (Exception e) {
|
2023-06-05 21:17:56 +00:00
|
|
|
this._writeToFile = false;
|
2023-07-18 18:01:43 +00:00
|
|
|
this.now(output.stderr).error("Unable to open the log file " ~ this._path);
|
2023-04-27 18:08:12 +00:00
|
|
|
this.information(e);
|
2023-03-23 09:20:46 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2023-06-06 23:12:20 +00:00
|
|
|
file.writefln("%s %s %s", time, this._type[priority], message);
|
2023-03-23 09:20:46 +00:00
|
|
|
} catch (Exception e) {
|
2023-06-05 21:17:56 +00:00
|
|
|
this._writeToFile = false;
|
2023-07-18 18:01:43 +00:00
|
|
|
this.now(output.stderr).error("Unable to write to the log file " ~ this._path);
|
2023-04-27 18:08:12 +00:00
|
|
|
this.information(e);
|
2023-03-23 09:20:46 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
file.close();
|
|
|
|
} catch (Exception e) {
|
2023-06-05 21:17:56 +00:00
|
|
|
this._writeToFile = false;
|
2023-07-18 18:01:43 +00:00
|
|
|
this.now(output.stderr).error("Unable to close the log file " ~ this._path);
|
2023-04-27 18:08:12 +00:00
|
|
|
this.information(e);
|
2023-03-23 09:20:46 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2023-07-18 18:01:43 +00:00
|
|
|
|
|
|
|
struct Output {
|
|
|
|
int _output = STDOUT;
|
|
|
|
int _newoutput = 0;
|
|
|
|
|
|
|
|
int output() { return this._newoutput ? this._newoutput : this._output; }
|
|
|
|
public:
|
|
|
|
Output syslog() { this._newoutput |= SYSLOG; return this; }
|
|
|
|
Output stdout() { this._newoutput |= STDOUT; return this; }
|
|
|
|
Output stderr() { this._newoutput |= STDERR; return this; }
|
|
|
|
Output file() { this._newoutput |= FILE; return this; }
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Level {
|
|
|
|
public:
|
|
|
|
int debugging() { return DEBUGGING; }
|
|
|
|
int alert() { return ALERT; }
|
|
|
|
int critical() { return CRITICAL; }
|
|
|
|
int error() { return ERROR; }
|
|
|
|
int warning() { return WARNING; }
|
|
|
|
int notice() { return NOTICE; }
|
|
|
|
int information() { return INFORMATION; }
|
|
|
|
|
|
|
|
alias d = debugging;
|
|
|
|
alias a = alert;
|
|
|
|
alias c = critical;
|
|
|
|
alias e = error;
|
|
|
|
alias w = warning;
|
|
|
|
alias n = notice;
|
|
|
|
alias i = information;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Now {
|
|
|
|
this(Output outs) {
|
|
|
|
_log._nowoutput = outs.output();
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
void alert(T)(T message) { _log.alert(message); }
|
|
|
|
void critical(T)(T message) { _log.critical(message); }
|
|
|
|
void error(T)(T message) { _log.error(message); }
|
|
|
|
void warning(T)(T message) { _log.warning(message); }
|
|
|
|
void notice(T)(T message) { _log.notice(message); }
|
|
|
|
void information(T)(T message) { _log.information(message); }
|
|
|
|
void debugging(T)(T message) { _log.debugging(message); }
|
|
|
|
|
|
|
|
alias a = alert;
|
|
|
|
alias c = critical;
|
|
|
|
alias e = error;
|
|
|
|
alias w = warning;
|
|
|
|
alias n = notice;
|
|
|
|
alias i = information;
|
|
|
|
alias d = debugging;
|
|
|
|
}
|
2023-04-27 18:08:12 +00:00
|
|
|
public:
|
|
|
|
@property static Log msg() {
|
2023-06-05 21:17:56 +00:00
|
|
|
if (this._log is null)
|
|
|
|
this._log = new Log;
|
2023-03-23 09:20:46 +00:00
|
|
|
|
2023-06-05 21:17:56 +00:00
|
|
|
return this._log;
|
2023-03-23 09:20:46 +00:00
|
|
|
}
|
|
|
|
|
2023-07-18 18:01:43 +00:00
|
|
|
Output output() { return Output(); }
|
|
|
|
Level level() { return Level(); }
|
|
|
|
Now now(Output outs) { return Now(outs); }
|
|
|
|
|
|
|
|
Log output(Output outs) { this._output = outs.output(); return this._log; }
|
|
|
|
deprecated("Use passing the argument as a `log.output.<outs>` object")
|
2023-06-05 21:17:56 +00:00
|
|
|
Log output(int outs) { this._output = outs; return this._log; }
|
2023-06-07 07:02:47 +00:00
|
|
|
Log program(string name) { this._name = name.to!wstring; return this._log; }
|
2023-06-05 21:17:56 +00:00
|
|
|
Log file(string path) { this._path = path; return this._log; }
|
2023-06-06 23:12:20 +00:00
|
|
|
Log level(int priority) { this._priority = priority; return this._log; }
|
2023-06-05 21:17:56 +00:00
|
|
|
Log color(bool condition) { this._ccolor = condition; return this._log; }
|
2023-04-27 18:08:12 +00:00
|
|
|
|
2023-06-06 23:12:20 +00:00
|
|
|
void alert(T)(T message) { writelog(message.to!string, ALERT); }
|
|
|
|
void critical(T)(T message) { writelog(message.to!string, CRITICAL); }
|
|
|
|
void error(T)(T message) { writelog(message.to!string, ERROR); }
|
|
|
|
void warning(T)(T message) { writelog(message.to!string, WARNING); }
|
|
|
|
void notice(T)(T message) { writelog(message.to!string, NOTICE); }
|
|
|
|
void information(T)(T message) { writelog(message.to!string, INFORMATION); }
|
|
|
|
void debugging(T)(T message) { writelog(message.to!string, DEBUGGING); }
|
2023-03-29 15:13:56 +00:00
|
|
|
|
|
|
|
alias a = alert;
|
|
|
|
alias c = critical;
|
|
|
|
alias e = error;
|
|
|
|
alias w = warning;
|
|
|
|
alias n = notice;
|
|
|
|
alias i = information;
|
|
|
|
alias d = debugging;
|
2023-03-23 09:20:46 +00:00
|
|
|
}
|