diff --git a/CHANGELOG.md b/CHANGELOG.md index 90e1f38..01a0229 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [v0.4.0](https://git.zhirov.kz/dlang/singlog/compare/v0.3.2...v0.4.0) (2023.06.07) + +- Part of the code has been changed/rewritten + +### New + +- Color output of messages to the terminal and console + +### Bug fixes + +- In Windows, unicode messages are output without distortion to the system log and console (thanks [Adam D. Ruppe](https://arsdnet.net/)) + ## [v0.3.2](https://git.zhirov.kz/dlang/singlog/compare/v0.3.1...v0.3.2) (2023.06.01) - Printing information about the type of the logged message to the standard output stream and file diff --git a/README.md b/README.md index 039b392..0a36762 100644 --- a/README.md +++ b/README.md @@ -16,25 +16,37 @@ import singlog; void main(string[] argv) { log.output(log.SYSLOG | log.STDOUT | log.FILE) // write to syslog, standard output stream and file - .name(argv[0]) // program name as an identifier (for Windows OS) + .program(argv[0]) // program name as an identifier (for Windows OS) .level(log.DEBUGGING) // logging level + .color(true) // color text output .file("./test.log"); // the path to the log file - log.e("This is an error message"); - log.error("And this is also an error message"); - log.w("This is a warning message"); log.i("This is an information message"); + log.n("This is a notice message"); + log.w("This is a warning message"); + log.e("This is an error message"); + log.c("This is a critical message"); + log.a("This is an alert message"); + log.d("This is a debug message"); } ``` -![output](tests/output.png) +![output](tests/terminal.png) + +![output](tests/cmd.png) ## Examples Setting the name of the logged program (it matters for Windows OS): ```d -log.name("My program"); +log.program("My program"); +``` + +Setting the status of color text output (`false` by default): + +```d +log.color(true); ``` Setting the error output level: @@ -77,4 +89,4 @@ log.d("Debugging message") => log.debugging("Debugging message"); ## DUB -Add a dependency on `"singlog": "~>0.3.2"`. +Add a dependency on `"singlog": "~>0.4.0"`. diff --git a/dub.json b/dub.json index 97549a7..1ab2258 100644 --- a/dub.json +++ b/dub.json @@ -18,7 +18,7 @@ "name": "test", "targetType": "executable", "targetPath": "bin", - "targetName": "app", + "targetName": "test", "importPaths": ["source","tests"], "sourcePaths": ["tests"] } diff --git a/source/singlog.d b/source/singlog.d index 0202885..5caa979 100644 --- a/source/singlog.d +++ b/source/singlog.d @@ -2,7 +2,7 @@ module singlog; version(Windows) import core.sys.windows.windows; -else +else version(Posix) import core.sys.posix.syslog; import std.string; @@ -19,7 +19,9 @@ alias log = Log.msg; --- // Setting the name of the logged program - log.name("My program"); + log.program("My program"); + // Setting the status of color text output + log.color(true); // Setting the error output level log.level(log.DEBUGGING); log.level(log.ALERT); @@ -46,40 +48,107 @@ alias log = Log.msg; +/ class Log { private: - static Log log; - string path; - string nameProgram = "singlog"; - bool writeToFile = true; + static Log _log; + string _path; + wstring _name = "singlog"; + bool _writeToFile = true; + bool _ccolor = false; this() {} version(Windows) { - public enum { - DEBUGGING = 0, - ALERT = 1, - CRITICAL = 1, - ERROR = 1, - WARNING = 2, - NOTICE = 3, - INFORMATION = 3, - } - WORD[] sysLevel = [ + int[] _sysPriority = [0, 1, 1, 1, 2, 3, 3]; + + WORD[] _sysPriorityOS = [ EVENTLOG_SUCCESS, EVENTLOG_ERROR_TYPE, EVENTLOG_WARNING_TYPE, EVENTLOG_INFORMATION_TYPE ]; - void syslog(WORD priority, LPCSTR message) { - HANDLE handleEventLog = RegisterEventSourceA(NULL, this.nameProgram.toStringz()); + void writesyslog(string message, WORD priority) { + import std.utf: toUTF16z; + auto wMessage = message.toUTF16z(); + HANDLE handleEventLog = RegisterEventSourceW(NULL, this._name.ptr); if (handleEventLog == NULL) return; - ReportEventA(handleEventLog, priority, 0, 0, NULL, 1, 0, &message, NULL); + ReportEventW(handleEventLog, priority, 0, 0, NULL, 1, 0, &wMessage, NULL); DeregisterEventSource(handleEventLog); } + + WORD[] _color = [ + 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 + ]; + + void colorTextOutput(string time, wstring message, int priority) { + HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO defaultConsole; + GetConsoleScreenBufferInfo(handle, &defaultConsole); + + writef("%s ", time); + SetConsoleTextAttribute(handle, this._color[priority] | FOREGROUND_INTENSITY); + write(this._type[priority]); + SetConsoleTextAttribute(handle, this._color[priority]); + WriteConsoleW(handle, message.ptr, cast(DWORD)message.length, NULL, NULL); + SetConsoleTextAttribute(handle, defaultConsole.wAttributes); + } + + void defaultTextOutput(string time, wstring message, int priority) { + HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); + writef("%s %s", time, this._type[priority]); + WriteConsoleW(handle, message.ptr, cast(DWORD)message.length, NULL, NULL); + } + + void writestdout(string time, string message, int priority) { + wstring wMessage = " %s\n".format(message).to!wstring; + this._ccolor ? + colorTextOutput(time, wMessage, priority) : + defaultTextOutput(time, wMessage, priority); + } + } else version(Posix) { + int[] _sysPriority = [0, 1, 2, 3, 4, 5, 6]; + + int[] _sysPriorityOS = [ + LOG_DEBUG, + LOG_ALERT, + LOG_CRIT, + LOG_ERR, + LOG_WARNING, + LOG_NOTICE, + LOG_INFO + ]; + + 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 + ]; + + void writestdout(string time, string message, int priority) { + writefln("%s %s", + time, + (this._ccolor ? this._color[priority] : "%s %s").format(this._type[priority], message) + ); + } + + void writesyslog(string message, int priority) { + syslog(priority, message.toStringz()); + } +} + public enum { DEBUGGING = 0, ALERT = 1, @@ -89,16 +158,16 @@ version(Windows) { NOTICE = 5, INFORMATION = 6 } - int[] sysLevel = [ - LOG_DEBUG, - LOG_ALERT, - LOG_CRIT, - LOG_ERR, - LOG_WARNING, - LOG_NOTICE, - LOG_INFO + + string[] _type = [ + "[DEBUG]:", + "[ALERT]:", + "[CRITICAL]:", + "[ERROR]:", + "[WARNING]:", + "[NOTICE]:", + "[INFO]:" ]; -} public enum { SYSLOG = 1, @@ -106,48 +175,49 @@ version(Windows) { FILE = 4 } - int msgOutput = STDOUT; - int msgLevel = INFORMATION; + int _output = STDOUT; + int _priority = INFORMATION; - void writeLog(string message, string type, int msgLevel) { - if (this.msgLevel > msgLevel) + void writelog(string message, int priority) { + string time; + if (this._priority > priority) return; - if (this.msgOutput & 1) - syslog(sysLevel[msgLevel], message.toStringz()); - if (this.msgOutput & 6) - message = format("%s [%s]: %s", Clock.currTime().format("%Y.%m.%d %H:%M:%S"), type, message); - if (this.msgOutput & 2) - writeln(message); - if (this.msgOutput & 4) - writeFile(message); + if (this._output & 1) + writesyslog(message, _sysPriorityOS[_sysPriority[priority]]); + if (this._output & 6) + time = Clock.currTime().format("%Y.%m.%d %H:%M:%S"); + if (this._output & 2) + writestdout(time, message, priority); + if (this._output & 4) + writefile(time, message, priority); } - void writeFile(string message) { - if (!this.writeToFile) + void writefile(string time, string message, int priority) { + if (!this._writeToFile) return; - if (!this.path.exists) { - this.writeToFile = false; - this.warning("The log file does not exist: " ~ this.path); + if (!this._path.exists) { + this._writeToFile = false; + this.warning("The log file does not exist: " ~ this._path); } File file; try { - file = File(this.path, "a+"); - this.writeToFile = true; + file = File(this._path, "a+"); + this._writeToFile = true; } catch (Exception e) { - this.writeToFile = false; - this.error("Unable to open the log file " ~ this.path); + this._writeToFile = false; + this.error("Unable to open the log file " ~ this._path); this.information(e); return; } try { - file.writeln(message); + file.writefln("%s %s %s", time, this._type[priority], message); } catch (Exception e) { - this.writeToFile = false; - this.error("Unable to write to the log file " ~ this.path); + this._writeToFile = false; + this.error("Unable to write to the log file " ~ this._path); this.information(e); return; } @@ -155,8 +225,8 @@ version(Windows) { try { file.close(); } catch (Exception e) { - this.writeToFile = false; - this.error("Unable to close the log file " ~ this.path); + this._writeToFile = false; + this.error("Unable to close the log file " ~ this._path); this.information(e); return; } @@ -164,24 +234,25 @@ version(Windows) { public: @property static Log msg() { - if (this.log is null) - this.log = new Log; + if (this._log is null) + this._log = new Log; - return this.log; + return this._log; } - Log output(int msgOutput) { this.msgOutput = msgOutput; return this.log; } - Log name(string nameProgram) { this.nameProgram = nameProgram; return this.log; } - Log file(string path) { this.path = path; return this.log; } - Log level(int msgLevel) { this.msgLevel = msgLevel; return this.log; } + Log output(int outs) { this._output = outs; return this._log; } + Log program(string name) { this._name = name.to!wstring; return this._log; } + Log file(string path) { this._path = path; return this._log; } + Log level(int priority) { this._priority = priority; return this._log; } + Log color(bool condition) { this._ccolor = condition; return this._log; } - void alert(T)(T message) { writeLog(message.to!string, "ALERT", ALERT); } - void critical(T)(T message) { writeLog(message.to!string, "CRITICAL", CRITICAL); } - void error(T)(T message) { writeLog(message.to!string, "ERROR", ERROR); } - void warning(T)(T message) { writeLog(message.to!string, "WARNING", WARNING); } - void notice(T)(T message) { writeLog(message.to!string, "NOTICE", NOTICE); } - void information(T)(T message) { writeLog(message.to!string, "INFORMATION", INFORMATION); } - void debugging(T)(T message) { writeLog(message.to!string, "DEBUGGING", DEBUGGING); } + 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); } alias a = alert; alias c = critical; diff --git a/tests/cmd.png b/tests/cmd.png new file mode 100644 index 0000000..d15e7e6 Binary files /dev/null and b/tests/cmd.png differ diff --git a/tests/output.png b/tests/output.png deleted file mode 100644 index 7d024e3..0000000 Binary files a/tests/output.png and /dev/null differ diff --git a/tests/terminal.png b/tests/terminal.png new file mode 100644 index 0000000..cc0777b Binary files /dev/null and b/tests/terminal.png differ diff --git a/tests/test.d b/tests/test.d index d272174..d3c46aa 100644 --- a/tests/test.d +++ b/tests/test.d @@ -2,12 +2,16 @@ import singlog; void main(string[] argv) { log.output(log.SYSLOG | log.STDOUT | log.FILE) // write to syslog, standard output stream and file - .name(argv[0]) // program name as an identifier (for Windows OS) + .program(argv[0]) // program name as an identifier (for Windows OS) .level(log.DEBUGGING) // logging level + .color(true) // color text output .file("./test.log"); // the path to the log file - log.e("This is an error message"); - log.error("And this is also an error message"); - log.w("This is a warning message"); log.i("This is an information message"); + log.n("This is a notice message"); + log.w("This is a warning message"); + log.e("This is an error message"); + log.c("This is a critical message"); + log.a("This is an alert message"); + log.d("This is a debug message"); }