diff --git a/.gitignore b/.gitignore index 3a55aef..27e3830 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ .dub *.o lib +bin +test.log diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ed29d1..f6bd7b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [v0.3.0](https://git.zhirov.kz/dlang/singlog/compare/v0.2.1...v0.3.0) (2023.04.28) + +- Minor changes + +### New + +- Windows OS Logging support + ## [v0.2.1](https://git.zhirov.kz/dlang/singlog/compare/v0.2.0...v0.2.1) (2023.03.29) ### New diff --git a/README.md b/README.md index 4ec6a2a..c81f8a5 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ -# singlog +![singlog](singlog.png) [![license](https://img.shields.io/github/license/AlexanderZhirov/singlog.svg?sort=semver&style=for-the-badge&color=green)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.html) [![main](https://img.shields.io/badge/dynamic/json.svg?label=git.zhirov.kz&style=for-the-badge&url=https://git.zhirov.kz/api/v1/repos/dlang/singlog/tags&query=$[0].name&color=violet)](https://git.zhirov.kz/dlang/singlog) [![githab](https://img.shields.io/github/v/tag/AlexanderZhirov/singlog.svg?sort=semver&style=for-the-badge&color=blue&label=github)](https://github.com/AlexanderZhirov/singlog) [![dub](https://img.shields.io/dub/v/singlog.svg?sort=semver&style=for-the-badge&color=orange)](https://code.dlang.org/packages/singlog) +[![linux](https://img.shields.io/badge/Linux-FCC624?style=for-the-badge&logo=linux&logoColor=black)](https://www.linux.org/) +[![windows](https://img.shields.io/badge/Windows-0078D6?style=for-the-badge&logo=windows&logoColor=white)](https://support.microsoft.com/en-US/windows) Singleton for simple logging @@ -12,19 +14,27 @@ Singleton for simple logging ```d import singlog; -void main() -{ - log.level(log.DEBUG); - // write to syslog and file - log.output(log.SYSLOG | log.FILE); - log.file("./file.log"); - log.warning("Hello, World!"); - log.w("The same thing"); +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) + .level(log.DEBUGGING) // logging level + .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"); } ``` ## Examples +Setting the name of the logged program (it matters for Windows OS): + +```d +log.name("My program"); +``` + Setting the error output level: ```d @@ -42,7 +52,6 @@ Assigning a target output: ```d log.output(log.SYSLOG); log.output(log.STDOUT); - ``` Setup and allowing writing to a file: @@ -64,6 +73,6 @@ log.i("Information message") => log.information("Information message"); log.d("Debugging message") => log.debugging("Debugging message"); ``` -## Dub +## DUB -Add a dependency on `"singlog": "~>0.2.1"`. +Add a dependency on `"singlog": "~>0.3.0"`. diff --git a/dub.json b/dub.json index e2dbe17..97549a7 100644 --- a/dub.json +++ b/dub.json @@ -7,9 +7,22 @@ "license": "GPL-2.0", "copyright": "© Alexander Zhirov, 2023", "description": "Singleton for simple logging", - "targetType": "library", - "targetPath": "lib", "targetName": "singlog", + "configurations": [ + { + "name": "library", + "targetType": "library", + "targetPath": "lib" + }, + { + "name": "test", + "targetType": "executable", + "targetPath": "bin", + "targetName": "app", + "importPaths": ["source","tests"], + "sourcePaths": ["tests"] + } + ], "dependencies": { "datefmt": "~>1.0.4" } diff --git a/singlog.png b/singlog.png new file mode 100644 index 0000000..966037c Binary files /dev/null and b/singlog.png differ diff --git a/source/singlog.d b/source/singlog.d index 52d316a..20aa256 100644 --- a/source/singlog.d +++ b/source/singlog.d @@ -1,9 +1,13 @@ module singlog; -import core.sys.posix.syslog; +version(Windows) + import core.sys.windows.windows; +else + import core.sys.posix.syslog; + +import std.string; import std.stdio; import std.conv; -import std.meta; import std.file; import std.datetime; import datefmt; @@ -14,6 +18,8 @@ alias log = Log.msg; Singleton for simple logging --- + // Setting the name of the logged program + log.name("My program"); // Setting the error output level log.level(log.DEBUG); log.level(log.ALERT); @@ -28,8 +34,6 @@ alias log = Log.msg; log.output(log.FILE); // Setup and allowing writing to a file log.file("./file.log"); - log.fileOn(); - log.fileOff(); // Output of messages to the log log.alert("Alert message"); log.critical("Critical message"); @@ -40,57 +44,87 @@ alias log = Log.msg; log.debugging("Debugging message"); --- +/ -class Log -{ - private static Log log; - private string path; - private bool writeToFile = true; - private static SysTime time; +class Log { +private: + static Log log; + string path; + string nameProgram = "singlog"; + bool writeToFile = true; + + this() {} - // Target output - enum { +version(Windows) { + public enum { + DEBUGGING = 0, + ALERT = 1, + CRITICAL = 1, + ERROR = 1, + WARNING = 2, + NOTICE = 3, + INFORMATION = 3, + } + WORD[] sysLevel = [ + EVENTLOG_SUCCESS, + EVENTLOG_ERROR_TYPE, + EVENTLOG_WARNING_TYPE, + EVENTLOG_INFORMATION_TYPE + ]; + + void syslog(WORD priority, LPCSTR message) { + HANDLE handleEventLog = RegisterEventSourceA(NULL, this.nameProgram.toStringz()); + + if (handleEventLog == NULL) + return; + + ReportEventA(handleEventLog, priority, 0, 0, NULL, 1, 0, &message, NULL); + DeregisterEventSource(handleEventLog); + } +} else version(Posix) { + public enum { + DEBUGGING = 0, + ALERT = 1, + CRITICAL = 2, + ERROR = 3, + WARNING = 4, + NOTICE = 5, + INFORMATION = 6 + } + int[] sysLevel = [ + LOG_DEBUG, + LOG_ALERT, + LOG_CRIT, + LOG_ERR, + LOG_WARNING, + LOG_NOTICE, + LOG_INFO + ]; +} + + public enum { SYSLOG = 1, STDOUT = 2, FILE = 4 } - // Message output level - enum { - DEBUG = 0, - CRIT = 1, - ERR = 2, - WARNING = 3, - NOTICE = 4, - INFO = 5, - ALERT = 6 - } - int msgOutput = STDOUT; - int msgLevel = INFO; + int msgLevel = INFORMATION; - private this() {} - - private void writeLog(string message, int msgLevel, int priority) - { + void writeLog(string message, int msgLevel) { if (this.msgLevel > msgLevel) return; if (this.msgOutput & 1) - syslog(priority, (message ~ "\0").ptr); + syslog(sysLevel[msgLevel], message.toStringz()); if (this.msgOutput & 2) writeln(message); if (this.msgOutput & 4) writeFile(message); } - private void writeFile(string message) - { + void writeFile(string message) { if (!this.writeToFile) return; - if (this.path.exists) - this.writeToFile = true; - else - { + if (!this.path.exists) { this.writeToFile = false; this.warning("The log file does not exist: " ~ this.path); } @@ -103,16 +137,16 @@ class Log } catch (Exception e) { this.writeToFile = false; this.error("Unable to open the log file " ~ this.path); - this.critical(e); + this.information(e); return; } try { - file.writeln(this.time.format("%Y.%m.%d %H:%M:%S: ") ~ message); + file.writeln(Clock.currTime().format("%Y.%m.%d %H:%M:%S: ") ~ message); } catch (Exception e) { this.writeToFile = false; this.error("Unable to write to the log file " ~ this.path); - this.critical(e); + this.information(e); return; } @@ -121,33 +155,31 @@ class Log } catch (Exception e) { this.writeToFile = false; this.error("Unable to close the log file " ~ this.path); - this.critical(e); + this.information(e); return; } } - @property static Log msg() - { +public: + @property static Log msg() { if (this.log is null) - { this.log = new Log; - this.time = Clock.currTime(); - } return this.log; } - void output(int msgOutput) { this.msgOutput = msgOutput; } - void level(int msgLevel) { this.msgLevel = msgLevel; } - void file(string path) { this.path = path; } + 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; } - void alert(T)(T message) { writeLog(message.to!string, ALERT, LOG_ALERT); } - void critical(T)(T message) { writeLog(message.to!string, CRIT, LOG_CRIT); } - void error(T)(T message) { writeLog(message.to!string, ERR, LOG_ERR); } - void warning(T)(T message) { writeLog(message.to!string, WARNING, LOG_WARNING); } - void notice(T)(T message) { writeLog(message.to!string, NOTICE, LOG_NOTICE); } - void information(T)(T message) { writeLog(message.to!string, INFO, LOG_INFO); } - void debugging(T)(T message) {writeLog(message.to!string, DEBUG, LOG_DEBUG); } + 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, DEBUG); } alias a = alert; alias c = critical; diff --git a/tests/test.d b/tests/test.d new file mode 100644 index 0000000..d272174 --- /dev/null +++ b/tests/test.d @@ -0,0 +1,13 @@ +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) + .level(log.DEBUGGING) // logging level + .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"); +}