diff --git a/CHANGELOG.md b/CHANGELOG.md index 51dc283..b770f80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,53 @@ # Changelog -## [v0.5.0](https://git.zhirov.kz/dlang/singlog/compare/v0.4.0...v0.5.0) (2023.07.21) +## [1.0.0] - 2025-03-23 + +### Added +- **Thread-safety**: Added a `Mutex` to ensure thread-safe logging operations across all methods (`_mutex` in `Log` class). +- **Destructor**: Implemented `~this()` to properly close the log file when the `Log` instance is destroyed. +- **File handle management**: Introduced `_file` (File handle) and `_fileOpen` (flag) for better file management, reducing repeated file opening/closing. +- **Enhanced documentation**: Added detailed DDoc comments for the module, `Log` class, and all public/private methods, including examples. +- **Immutable arrays**: Made `_sysPriority`, `_sysPriorityOS`, `_color` (Windows), `_colorCodes` (both platforms), and `_type` arrays immutable for better safety and performance. +- **Singleton initialization**: Improved singleton pattern with double-checked locking in `@property static Log msg()` for thread-safe initialization. +- **Fluent interface naming**: Renamed output-related enums (`SYSLOG`, `STD`, `FILE`) and methods (`std`, `syslog`, `file`) for consistency and clarity (e.g., `STDOUT` → `STD`). +- **Error handling**: Enhanced error reporting in `writefile` by logging exception messages instead of the full exception object. + +### Changed +- **Output handling**: + - Removed separate `writestdout` and `writestderr` methods; consolidated into a single `writestd` method that dynamically selects `stdout` or `stderr` based on log level (`ERROR` and above go to `stderr`, others to `stdout`). + - Adjusted output enum values: `SYSLOG = 1`, `STD = 2`, `FILE = 8` (removed `STDERR = 4` as it's now handled by `STD`). +- **Windows-specific**: + - Renamed `_color` to `_colorCodes` for consistency with POSIX. + - Updated `writesyslog` to use `toUTF16z()` for `_name` and added null checks. +- **POSIX-specific**: + - Renamed `_color` to `_colorCodes` and simplified console output logic in `writestd`. + - Changed `writesyslog` to pass priority directly instead of mapping it. +- **Log level filtering**: Moved priority check (`_priority > priority`) into `writelog` under the mutex for consistency. +- **File logging**: + - Simplified `writefile` by maintaining an open `File` handle (`_file`) instead of opening/closing on each write. + - Removed redundant file existence check (`this._path.exists`) as `File` opening handles it implicitly. +- **Configuration methods**: Made all setters (`program`, `file`, `level`, `color`, `output`) thread-safe with `synchronized (_mutex)`. +- **Naming consistency**: + - Renamed `Output.output()` to internal use; public access is via `Output` struct methods. + +### Removed +- **Deprecated method**: Removed the deprecated `Log output(int outs)` method; users must now use the fluent `Output` struct. +- **Redundant output flags**: Removed `STDERR` from output enum as it's now handled dynamically by `STD`. +- **Unnecessary struct fields**: Removed `_output` and `_newoutput` from `Output` struct; replaced with a single `value` field. +- **Redundant methods**: Removed separate `writestdout` and `writestderr` in favor of `writestd`. + +### Fixed +- **Windows console output**: Added error checking in `colorTextOutput` and `defaultTextOutput` with `GetConsoleScreenBufferInfo`. +- **File closing**: Ensured proper file closure in `file` method when changing the log file path. + +### Breaking Changes +- **Output enum changes**: + - `STDOUT` renamed to `STD`, `STDERR` removed; code relying on `STDERR = 4` will need adjustment. + - Users must update output configuration to use `STD` instead of separate `STDOUT`/`STDERR`. +- **Method removal**: Code using the deprecated `Log output(int outs)` must switch to `Log.output(Output)`. +- **Console output behavior**: Messages with priority `ERROR` and above now go to `stderr` by default when `STD` is enabled, which may change existing output redirection logic. + +## [0.5.0] - 2023-07-21 ### New @@ -13,7 +60,7 @@ - Fixed streams redirection in Windows -## [v0.4.0](https://git.zhirov.kz/dlang/singlog/compare/v0.3.2...v0.4.0) (2023.06.07) +## [0.4.0] - 2023-06-07 - Part of the code has been changed/rewritten @@ -25,18 +72,18 @@ - 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) +## [0.3.2] - 2023-06-01 - Printing information about the type of the logged message to the standard output stream and file - Printing the date and time to the standard output stream -## [v0.3.1](https://git.zhirov.kz/dlang/singlog/compare/v0.3.0...v0.3.1) (2023.05.30) +## [0.3.1] - 2023-05-30 ### Bug fixes - Log of debug messages -## [v0.3.0](https://git.zhirov.kz/dlang/singlog/compare/v0.2.1...v0.3.0) (2023.04.28) +## [0.3.0] - 2023-04-28 - Minor changes @@ -44,7 +91,7 @@ - Windows OS Logging support -## [v0.2.1](https://git.zhirov.kz/dlang/singlog/compare/v0.2.0...v0.2.1) (2023.03.29) +## [0.2.1] - 2023-03-29 ### New @@ -54,7 +101,7 @@ - Calling the main object -## [v0.2.0](https://git.zhirov.kz/dlang/singlog/compare/v0.1.0...v0.2.0) (2023.03.29) +## [0.2.0] - 2023-03-29 - Removed functions `fileOn()` and `fileOff()` @@ -62,7 +109,7 @@ - Simultaneous writing to the standard stream, syslog and file by binary setting the output flag -## [v0.1.0](https://git.zhirov.kz/dlang/singlog/commits/df602a8d0083249068b480e4a92cf7932f2c582b) (2023.03.23) +## [0.1.0] - 2023-03-23 ### The first stable working release diff --git a/README.md b/README.md index facbede..1bb602d 100644 --- a/README.md +++ b/README.md @@ -7,95 +7,146 @@ [![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 +# `singlog` - Singleton Logging Module -## Basic Usage - -```d -import singlog; - -void main(string[] argv) { - log.output(log.output.syslog.stderr.stdout.file) // write to syslog, standard error/output streams and file - .program(argv[0]) // program name as an identifier (for Windows OS) - .level(log.level.debugging) // logging level - .color(true) // color text output - .file("./test.log"); // the path to the log file - - 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"); - - log.now(log.output.stdout).n("This error message will only be written to the standard output stream"); - log.now(log.output.syslog.file).c("This error message will only be written to the syslog and file"); -} -``` +`singlog` is a singleton logging module written in the D programming language, designed to provide thread-safe, cross-platform logging with flexible output targets. It supports multiple log levels, configurable output destinations (syslog, console, file), and optional colored console output, making it a versatile tool for application logging. ![output](tests/terminal.png) ![output](tests/cmd.png) -## Examples +## Features -Setting the name of the logged program (it matters for Windows OS): +- **Thread-Safety**: Utilizes a `Mutex` to ensure safe logging in multi-threaded applications. +- **Cross-Platform**: Works on both Windows (Event Log, console) and POSIX (syslog, console) systems. +- **Flexible Output Targets**: Supports logging to: + - System logs (syslog on POSIX, Event Log on Windows). + - Standard output (`stdout` for NOTICE and above, `stderr` for ERROR and below). + - Files (with configurable file paths). +- **Log Levels**: Seven configurable levels: + - `DEBUGGING` (highest priority) + - `ALERT` + - `CRITICAL` + - `ERROR` + - `WARNING` + - `NOTICE` + - `INFORMATION` (lowest priority) +- **Fluent Interface**: Provides a chaining API for easy configuration of output targets, log levels, and settings. +- **Colored Output**: Optional ANSI color support for console messages (on POSIX) or Windows console colors. +- **Singleton Design**: Ensures a single logger instance throughout the application, accessible via `Log.msg` or the `log` alias. +- **Aliases**: Short aliases (e.g., `d` for `debugging`, `e` for `error`) for concise logging. + +## Installation + +To use `singlog`, include it in your D project: + +1. **Via Source**: Copy the `singlog.d` file into your project’s source directory. +2. **Via DUB** (if packaged): + + Add it to your `dub.json`: + ```json + "dependencies": { + "singlog": "~>1.0.0" + } + ``` + +## Usage + +### Basic Configuration and Logging + +The `singlog` module provides a singleton logger instance accessible via `Log.msg` or the global `log` alias. Here’s a basic example: ```d -log.program("My program"); +import singlog; + +void main() { + // Configure the logger + log.program("MyApp") // Set program name for syslog/Event Log + .color(true) // Enable colored console output + .level(log.level.debugging) // Set minimum log level to DEBUGGING + .output(log.output.std.file.syslog) // Output to console, file, and syslog + .file("./myapp.log"); // Set log file path + + // Log messages + log.debugging("Starting application in debug mode"); + log.information("Initialization complete"); + log.error("Failed to load resource"); +} ``` -Setting the status of color text output (`false` by default): +This configures the logger to: +- Identify as "MyApp" in system logs. +- Use colored output on the console. +- Log all messages (from `DEBUGGING` up). +- Write to the console, a file (`myapp.log`), and the system log. + +### Log Levels and Aliases + +The logger supports seven log levels with corresponding methods and aliases: + +| Level | Method | Alias | Description | +|---------------|-------------------|-------|------------------------------| +| `DEBUGGING` | `debugging()` | `d()` | Debugging information | +| `ALERT` | `alert()` | `a()` | High-priority alerts | +| `CRITICAL` | `critical()` | `c()` | Critical errors | +| `ERROR` | `error()` | `e()` | General errors | +| `WARNING` | `warning()` | `w()` | Warnings | +| `NOTICE` | `notice()` | `n()` | Notices | +| `INFORMATION` | `information()` | `i()` | Informational messages | + +Example using aliases: + +```d +log.d("Debug message"); +log.i("Info message"); +log.e("Error message"); +``` + +### Output Targets + +Output targets can be configured using the `output()` method and its fluent interface: + +- `syslog()`: Logs to the system log (Event Log on Windows, syslog on POSIX). +- `std()`: Logs to the console (`stdout` or `stderr` based on log level). +- `file()`: Logs to a file (requires `file()` to set the path). + +Example: + +```d +log.output(log.output.std.file); // Console and file output +log.i("This goes to console and file"); +``` + +### Temporary Output Override + +Use `now()` to temporarily override output targets for the next log call: + +```d +log.now(log.output.std).n("This goes only to console"); +log.i("This uses default outputs again"); +``` + +### Colored Output + +Enable colored output with `color(true)`: ```d log.color(true); +log.w("This warning will be yellow on POSIX or Windows"); +log.c("This critical message will be magenta"); ``` -Setting the error output level: +Colors differ by platform: +- **POSIX**: Uses ANSI escape codes (e.g., green for `DEBUGGING`, red for `ERROR`). +- **Windows**: Uses console color attributes (e.g., yellow for `WARNING`, white for `INFORMATION`). + +### File Logging + +Set a log file with `file()`: ```d -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); +log.file("app.log"); +log.e("This error goes to app.log"); ``` -Assigning a target output: - -```d -log.output(log.output.syslog.stderr.stdout); -``` - -Setup and allowing writing to a file: - -```d -log.output(log.output.file); -log.file("./file.log"); -``` - -Write messages to the log: - -```d -log.a("Alert message") => log.alert("Alert message"); -log.c("Critical message") => log.critical("Critical message"); -log.e("Error message") => log.error("Error message"); -log.w("Warning message") => log.warning("Warning message"); -log.n("Notice message") => log.notice("Notice message"); -log.i("Information message") => log.information("Information message"); -log.d("Debugging message") => log.debugging("Debugging message"); -``` - -Write message to specific outputs: - -```d -log.now(log.output.stdout).n("This error message will only be written to the standard output stream"); -log.now(log.output.syslog.file).c("This error message will only be written to the syslog and file"); -``` - -## DUB - -Add a dependency on `"singlog": "~>0.5.0"`. +The file is opened in append mode (`"a+"`) and includes timestamps. diff --git a/man/singlog.3 b/man/singlog.3 new file mode 100644 index 0000000..a2e7574 --- /dev/null +++ b/man/singlog.3 @@ -0,0 +1,111 @@ +.\" Man page for the singlog library +.TH SINGLOG 3 "March 23, 2025" "singlog" "Programmer's Manual" +.SH NAME +singlog \- Singleton logging library with thread-safety and flexible output targets + +.SH SYNOPSIS +.nf +import singlog; + +Log logger = Log.msg; // Access singleton instance +log.(); // Use global alias +.fi + +.SH DESCRIPTION +The \fBsinglog\fR library provides a thread-safe, singleton-based logging utility designed for cross-platform use on Windows and POSIX systems. It supports multiple output targets (syslog, stdout/stderr, file), configurable log levels, and optional colored console output. The library uses a fluent interface for easy configuration and provides short aliases for common log levels. + +.SH FEATURES +.TP +.B Thread-safety +Uses a \fBMutex\fR to ensure safe logging in multi-threaded applications. +.TP +.B Cross-platform +Supports Windows (Event Log, console) and POSIX (syslog, console). +.TP +.B Flexible output +Logs to syslog, stdout/stderr (based on log level), or files. +.TP +.B Log levels +Seven levels: \fBDEBUGGING\fR, \fBALERT\fR, \fBCRITICAL\fR, \fBERROR\fR, \fBWARNING\fR, \fBNOTICE\fR, \fBINFORMATION\fR. +.TP +.B Fluent interface +Chainable configuration for output targets, levels, and more. +.TP +.B Colored output +Optional color support for console messages (STD output only). + +.SH METHODS +.TP +.B Log.msg +Returns the singleton instance of the \fBLog\fR class. +.RS +Example: \fBauto logger = Log.msg; logger.i("Logger retrieved");\fR +.RE +.TP +.B program(string name) +Sets the program name for syslog identification. Returns \fBLog\fR for chaining. +.RS +Example: \fBlog.program("MyApp").i("Name set");\fR +.RE +.TP +.B file(string path) +Sets the log file path. Returns \fBLog\fR for chaining. +.RS +Example: \fBlog.file("app.log").i("File configured");\fR +.RE +.TP +.B level(int priority) +Sets the minimum log level. Returns \fBLog\fR for chaining. +.RS +Example: \fBlog.level(log.level.warning).w("This shows");\fR +.RE +.TP +.B color(bool condition) +Enables/disables colored console output. Returns \fBLog\fR for chaining. +.RS +Example: \fBlog.color(true).i("Colored output");\fR +.RE +.TP +.B output(Output outs) +Sets default output targets. Returns \fBLog\fR for chaining. +.RS +Example: \fBlog.output(log.output.std.file).i("To console and file");\fR +.RE +.TP +.B now(Output outs) +Temporarily overrides output targets for the next log call. Returns \fBNow\fR for chaining. +.RS +Example: \fBlog.now(log.output.std).n("Temp console output");\fR +.RE +.TP +.B Logging methods +Log messages at different levels: \fBalert\fR, \fBcritical\fR, \fBerror\fR, \fBwarning\fR, \fBnotice\fR, \fBinformation\fR, \fBdebugging\fR (with aliases \fBa\fR, \fBc\fR, \fBe\fR, \fBw\fR, \fBn\fR, \fBi\fR, \fBd\fR). +.RS +Example: \fBlog.e("Error occurred"); log.i(42);\fR +.RE + +.SH EXAMPLES +Configure and use the logger: +.nf +import singlog; + +void main() { + log.program("MyApp") + .color(true) + .level(log.level.debugging) + .output(log.output.std.file.syslog) + .file("myapp.log"); + + log.d("Starting in debug mode"); + log.i("App running"); + log.e("Error occurred"); + log.now(log.output.std).n("Temp console message"); +} +.fi + +.SH SEE ALSO +.BR dmd (1), +.BR syslog (3) + +.SH AUTHOR +Alexander Zhirov diff --git a/man/singlog.ru.3 b/man/singlog.ru.3 new file mode 100644 index 0000000..583c54f --- /dev/null +++ b/man/singlog.ru.3 @@ -0,0 +1,111 @@ +.\" Страница руководства для библиотеки singlog +.TH SINGLOG 3 "23 марта 2025" "singlog" "Руководство программиста" +.SH ИМЯ +singlog \- Библиотека логирования с singleton-паттерном, потокобезопасностью и гибкими целями вывода + +.SH СИНТАКСИС +.nf +import singlog; + +Log logger = Log.msg; // Получение singleton-экземпляра +log.<метод>(<аргументы>); // Использование глобального алиаса +.fi + +.SH ОПИСАНИЕ +Библиотека \fBsinglog\fR предоставляет потокобезопасную утилиту логирования на основе singleton-паттерна, предназначенную для кроссплатформенного использования в системах Windows и POSIX. Поддерживает несколько целей вывода (syslog, stdout/stderr, файл), настраиваемые уровни логирования и опциональный цветной вывод в консоль. Библиотека использует плавный интерфейс для удобной настройки и предоставляет короткие алиасы для распространенных уровней логирования. + +.SH ОСОБЕННОСТИ +.TP +.B Потокобезопасность +Использует \fBMutex\fR для обеспечения безопасного логирования в многопоточных приложениях. +.TP +.B Кроссплатформенность +Поддерживает Windows (журнал событий, консоль) и POSIX (syslog, консоль). +.TP +.B Гибкий вывод +Вывод логов в syslog, stdout/stderr (в зависимости от уровня) или файлы. +.TP +.B Уровни логирования +Семь уровней: \fBDEBUGGING\fR, \fBALERT\fR, \fBCRITICAL\fR, \fBERROR\fR, \fBWARNING\fR, \fBNOTICE\fR, \fBINFORMATION\fR. +.TP +.B Плавный интерфейс +Цепочная настройка целей вывода, уровней и других параметров. +.TP +.B Цветной вывод +Опциональная поддержка цветного вывода в консоль (только для STD). + +.SH МЕТОДЫ +.TP +.B Log.msg +Возвращает singleton-экземпляр класса \fBLog\fR. +.RS +Пример: \fBauto logger = Log.msg; logger.i("Логгер получен");\fR +.RE +.TP +.B program(string name) +Устанавливает имя программы для идентификации в syslog. Возвращает \fBLog\fR для цепочки. +.RS +Пример: \fBlog.program("МоеПриложение").i("Имя установлено");\fR +.RE +.TP +.B file(string path) +Устанавливает путь к лог-файлу. Возвращает \fBLog\fR для цепочки. +.RS +Пример: \fBlog.file("app.log").i("Файл настроен");\fR +.RE +.TP +.B level(int priority) +Устанавливает минимальный уровень логирования. Возвращает \fBLog\fR для цепочки. +.RS +Пример: \fBlog.level(log.level.warning).w("Это отобразится");\fR +.RE +.TP +.B color(bool condition) +Включает/выключает цветной вывод в консоль. Возвращает \fBLog\fR для цепочки. +.RS +Пример: \fBlog.color(true).i("Цветной вывод");\fR +.RE +.TP +.B output(Output outs) +Устанавливает цели вывода по умолчанию. Возвращает \fBLog\fR для цепочки. +.RS +Пример: \fBlog.output(log.output.std.file).i("В консоль и файл");\fR +.RE +.TP +.B now(Output outs) +Временно переопределяет цели вывода для следующего вызова. Возвращает \fBNow\fR для цепочки. +.RS +Пример: \fBlog.now(log.output.std).n("Временный вывод в консоль");\fR +.RE +.TP +.B Методы логирования +Запись сообщений на разных уровнях: \fBalert\fR, \fBcritical\fR, \fBerror\fR, \fBwarning\fR, \fBnotice\fR, \fBinformation\fR, \fBdebugging\fR (с алиасами \fBa\fR, \fBc\fR, \fBe\fR, \fBw\fR, \fBn\fR, \fBi\fR, \fBd\fR). +.RS +Пример: \fBlog.e("Произошла ошибка"); log.i(42);\fR +.RE + +.SH ПРИМЕРЫ +Настройка и использование логгера: +.nf +import singlog; + +void main() { + log.program("МоеПриложение") + .color(true) + .level(log.level.debugging) + .output(log.output.std.file.syslog) + .file("myapp.log"); + + log.d("Запуск в режиме отладки"); + log.i("Приложение работает"); + log.e("Произошла ошибка"); + log.now(log.output.std).n("Временное сообщение в консоль"); +} +.fi + +.SH СМ. ТАКЖЕ +.BR dmd (1), +.BR syslog (3) + +.SH АВТОР +Александр Жиров diff --git a/source/singlog.d b/source/singlog.d index 5c725d3..048d28e 100644 --- a/source/singlog.d +++ b/source/singlog.d @@ -1,11 +1,23 @@ +/++ + Singleton logging module with thread-safety and flexible output targets. + + This module provides a simple, thread-safe logging utility with support for multiple output targets + (syslog, standard output/error, file), configurable log levels, and optional colored console output. + It is designed to be cross-platform, working on both Windows and POSIX systems. + + The logger is implemented as a singleton, ensuring a single instance throughout the application. + It supports fluent configuration for ease of use and provides short aliases for common log levels. ++/ module singlog; version(Windows) { import core.sys.windows.windows; import std.utf : toUTF8, toUTF16z; -} else version(Posix) +} else version(Posix) { import core.sys.posix.syslog; +} +import core.sync.mutex : Mutex; import std.string; import std.stdio; import std.conv; @@ -13,353 +25,851 @@ import std.file; import std.datetime; import datefmt; -alias log = Log.msg; - /++ - Singleton for simple logging + Singleton logging class with thread-safe operations and flexible output. + The `Log` class is the core of the `singlog` module, providing a robust logging system with the following features: + - **Thread-safety**: Uses a `Mutex` to ensure safe logging in multi-threaded applications. + - **Cross-platform**: Supports Windows (Event Log, console) and POSIX (syslog, console) systems. + - **Flexible output**: Allows logging to syslog, stdout/stderr (based on log level), and files. + - **Log levels**: Supports seven levels (`DEBUGGING`, `ALERT`, `CRITICAL`, `ERROR`, `WARNING`, `NOTICE`, `INFORMATION`). + - **Fluent interface**: Enables easy configuration chaining for output targets, levels, and more. + - **Colored output**: Optional color support for console messages (STD output only). + + The class is a singleton, accessible via the static `msg` property or the global `log` alias. + + Example: --- - // Setting the name of the logged program - log.program("My program"); - // Setting the status of color text output - log.color(true); - // Setting the error output level - 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); - // Assigning a target output - log.output(log.output.syslog.stderr.stdout.file); - // Setup and allowing writing to a file - log.file("./file.log"); - // Output of messages to the log - log.debugging("Debugging message"); - log.alert("Alert message"); - log.critical("Critical message"); - log.error("Error message"); - log.warning("Warning message"); - log.notice("Notice message"); - log.informations("Information message"); - // Write message to specific outputs - log.now(log.output.stdout.file).informations("Information message"); + import singlog; + + void main() { + // Configure the logger + Log logger = Log.msg; // Get singleton instance + logger.program("TestApp") + .color(true) + .level(Log.DEBUGGING) + .output(Log.Output().std.file.syslog) + .file("test.log"); + + // Log messages with full method names + logger.debugging("Starting in debug mode"); + logger.information("App is running"); + logger.error("An error occurred"); + + // Log messages with aliases + logger.d("Debug alias"); + logger.i("Info alias"); + logger.e("Error alias"); + + // Temporary output override + logger.now(Log.Output().std).notice("Temporary console message"); + } --- +/ class Log { private: - static Log _log; - string _path; - wstring _name = "singlog"; - bool _writeToFile = true; - bool _ccolor = false; - - this() {} + static Log _log; /// Singleton instance of the logger + Mutex _mutex; /// Mutex for thread-safety + string _path; /// Path to the log file + File _file; /// File handle for logging + bool _fileOpen = false; /// Indicates if the log file is open + wstring _name = "singlog"; /// Program name for syslog identification + bool _writeToFile = true; /// Flag to enable/disable file logging + bool _color = false; /// Flag to enable/disable colored console output + int _output = STD; /// Default output flags (STD by default) + int _priority = INFORMATION; /// Minimum log level for output + int _nowOutput = 0; /// Temporary output override for the next log call + + /++ Private constructor to enforce singleton pattern +/ + this() { + _mutex = new Mutex(); + } + + /++ Destructor to ensure the log file is closed +/ + ~this() { + synchronized (_mutex) { + if (_fileOpen) { + _file.close(); + _fileOpen = false; + } + } + } version(Windows) { - int[] _sysPriority = [0, 1, 1, 1, 2, 3, 3]; - - WORD[] _sysPriorityOS = [ - EVENTLOG_SUCCESS, - EVENTLOG_ERROR_TYPE, - EVENTLOG_WARNING_TYPE, - EVENTLOG_INFORMATION_TYPE + immutable int[] _sysPriority = [0, 1, 1, 1, 2, 3, 3]; /// Mapping of log levels to syslog priorities + immutable WORD[] _sysPriorityOS = [ /// Windows Event Log types + EVENTLOG_SUCCESS, // DEBUGGING + EVENTLOG_ERROR_TYPE, // ALERT, CRITICAL, ERROR + EVENTLOG_WARNING_TYPE, // WARNING + EVENTLOG_INFORMATION_TYPE // NOTICE, INFORMATION ]; + immutable WORD[] _colorCodes = [ /// Console color codes for each log level + FOREGROUND_GREEN, // DEBUGGING (green) + FOREGROUND_BLUE, // ALERT (blue) + FOREGROUND_RED | FOREGROUND_BLUE, // CRITICAL (magenta) + FOREGROUND_RED, // ERROR (red) + FOREGROUND_RED | FOREGROUND_GREEN, // WARNING (yellow) + FOREGROUND_BLUE | FOREGROUND_GREEN, // NOTICE (cyan) + FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN // INFORMATION (white) + ]; + + /++ + Writes a message to the Windows Event Log. + + Params: + message = The message to log. + priority = The Windows Event Log type (e.g., EVENTLOG_ERROR_TYPE). + +/ void writesyslog(string message, WORD priority) { auto wMessage = message.toUTF16z(); - HANDLE handleEventLog = RegisterEventSourceW(NULL, this._name.ptr); - - if (handleEventLog == NULL) - return; - - ReportEventW(handleEventLog, priority, 0, 0, NULL, 1, 0, &wMessage, NULL); + HANDLE handleEventLog = RegisterEventSourceW(null, _name.toUTF16z()); + if (handleEventLog == null) return; + 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 - ]; + /++ + Outputs a colored log message to the console on Windows. + Params: + handle = The console handle (STD_OUTPUT_HANDLE or STD_ERROR_HANDLE). + time = The timestamp of the message. + message = The message to log. + priority = The log level (used to select the color). + +/ void colorTextOutput(HANDLE handle, string time, string message, int priority) { CONSOLE_SCREEN_BUFFER_INFO defaultConsole; - GetConsoleScreenBufferInfo(handle, &defaultConsole); + if (!GetConsoleScreenBufferInfo(handle, &defaultConsole)) return; wstring wTime = "%s ".format(time).to!wstring; - wstring wType = this._type[priority].to!wstring; + wstring wType = _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); + WriteConsoleW(handle, wTime.ptr, cast(DWORD)wTime.length, null, null); + SetConsoleTextAttribute(handle, _colorCodes[priority] | FOREGROUND_INTENSITY); + WriteConsoleW(handle, wType.ptr, cast(DWORD)wType.length, null, null); + SetConsoleTextAttribute(handle, _colorCodes[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); + WriteFile(handle, utf8Message.ptr, cast(DWORD)utf8Message.length, null, null); break; default: writesyslog("Unknown output file", _sysPriorityOS[_sysPriority[ERROR]]); } } + /++ + Outputs a plain log message to the console on Windows. + + Params: + handle = The console handle (STD_OUTPUT_HANDLE or STD_ERROR_HANDLE). + time = The timestamp of the message. + message = The message to log. + priority = The log level (used to format the message). + +/ void defaultTextOutput(HANDLE handle, string time, string message, int priority) { - wstring wMessage = "%s %s %s\n".format(time, this._type[priority], message).to!wstring; + wstring wMessage = "%s %s %s\n".format(time, _type[priority], message).to!wstring; switch (GetFileType(handle)) { case FILE_TYPE_CHAR: - WriteConsoleW(handle, wMessage.ptr, cast(DWORD)wMessage.length, NULL, NULL); + 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); + WriteFile(handle, utf8Message.ptr, cast(DWORD)utf8Message.length, null, null); break; default: writesyslog("Unknown output file", _sysPriorityOS[_sysPriority[ERROR]]); } } - void writestdout(string time, string message, int priority) { - HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); - this._ccolor ? - colorTextOutput(handle, time, message, priority) : - defaultTextOutput(handle, time, message, priority); - } + /++ + Writes a message to the console on Windows, choosing stdout or stderr based on log level. - 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); - } + Params: + time = The timestamp of the message. + message = The message to log. + priority = The log level (ERROR and above go to stderr, others to stdout). + Example: + --- + log.writestd("2025.03.23 12:00:00", "Test error", ERROR); // Outputs to stderr + log.writestd("2025.03.23 12:00:01", "Test info", INFORMATION); // Outputs to stdout + --- + +/ + void writestd(string time, string message, int priority) { + HANDLE handle = (priority <= ERROR) ? + GetStdHandle(STD_ERROR_HANDLE) : + GetStdHandle(STD_OUTPUT_HANDLE); + _color ? colorTextOutput(handle, time, message, priority) : defaultTextOutput(handle, time, message, 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 + immutable int[] _sysPriority = [0, 1, 2, 3, 4, 5, 6]; /// Mapping of log levels to syslog priorities + immutable int[] _sysPriorityOS = [ /// POSIX syslog priorities + LOG_DEBUG, // DEBUGGING + LOG_ALERT, // ALERT + LOG_CRIT, // CRITICAL + LOG_ERR, // ERROR + LOG_WARNING, // WARNING + LOG_NOTICE, // NOTICE + LOG_INFO // INFORMATION ]; - 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 + immutable string[] _colorCodes = [ /// ANSI color codes for console output + "\x1b[1;32m%s\x1b[0;32m %s\x1b[0;0m", // DEBUGGING (green) + "\x1b[1;34m%s\x1b[0;34m %s\x1b[0;0m", // ALERT (blue) + "\x1b[1;35m%s\x1b[0;35m %s\x1b[0;0m", // CRITICAL (magenta) + "\x1b[1;31m%s\x1b[0;31m %s\x1b[0;0m", // ERROR (red) + "\x1b[1;33m%s\x1b[0;33m %s\x1b[0;0m", // WARNING (yellow) + "\x1b[1;36m%s\x1b[0;36m %s\x1b[0;0m", // NOTICE (cyan) + "\x1b[1;97m%s\x1b[0;97m %s\x1b[0;0m" // INFORMATION (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) - ); + /++ + Writes a message to the console on POSIX, choosing stdout or stderr based on log level. + + Params: + time = The timestamp of the message. + message = The message to log. + priority = The log level (ERROR and above go to stderr, others to stdout). + + Example: + --- + log.writestd("2025.03.23 12:00:00", "Critical failure", CRITICAL); // Outputs to stderr + log.writestd("2025.03.23 12:00:01", "System ready", NOTICE); // Outputs to stdout + --- + +/ + void writestd(string time, string message, int priority) { + if (priority <= ERROR) { + stderr.writefln("%s %s", time, (_color ? _colorCodes[priority] : "%s %s").format(_type[priority], message)); + } else { + writefln("%s %s", time, (_color ? _colorCodes[priority] : "%s %s").format(_type[priority], message)); + } } - 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) - ); - } + /++ + Writes a message to the POSIX syslog. + Params: + message = The message to log. + priority = The syslog priority level (e.g., LOG_ERR). + + Example: + --- + log.writesyslog("System crash", ERROR); // Logs to syslog with LOG_ERR + --- + +/ void writesyslog(string message, int priority) { - syslog(priority, message.toStringz()); + syslog(_sysPriorityOS[priority], message.toStringz()); } } - public enum { - DEBUGGING = 0, - ALERT = 1, - CRITICAL = 2, - ERROR = 3, - WARNING = 4, - NOTICE = 5, - INFORMATION = 6 + /// Log level constants + public enum : int { + DEBUGGING = 0, /// Debugging messages (lowest priority) + ALERT = 1, /// Alert messages (high priority) + CRITICAL = 2, /// Critical errors + ERROR = 3, /// General errors + WARNING = 4, /// Warnings + NOTICE = 5, /// Notices + INFORMATION = 6 /// Informational messages (highest priority) } - string[] _type = [ - "[DEBUG]:", - "[ALERT]:", - "[CRITICAL]:", - "[ERROR]:", - "[WARNING]:", - "[NOTICE]:", - "[INFO]:" + /// Output target flags + public enum : int { + SYSLOG = 1, /// System log (Event Log on Windows, syslog on POSIX) + STD = 2, /// Standard output (stdout for >= WARNING, stderr for <= ERROR) + FILE = 8 /// File output + } + + immutable string[] _type = [ /// Log level prefixes for formatting + "[DEBUG]:", "[ALERT]:", "[CRITICAL]:", "[ERROR]:", "[WARNING]:", "[NOTICE]:", "[INFO]:" ]; - public enum { - SYSLOG = 1, - STDOUT = 2, - STDERR = 4, - FILE = 8 - } + /++ + Core logging function that writes a message to configured outputs. - int _nowoutput = 0; - int _output = STDOUT; - int _priority = INFORMATION; + Params: + message = The message to log. + priority = The log level of the message. + Example: + --- + log.writelog("Application started", INFORMATION); // Logs to configured outputs + log.writelog("Fatal error", CRITICAL); // Logs to stderr and other outputs + --- + +/ void writelog(string message, int priority) { - string time; - int output = this._nowoutput ? this._nowoutput : this._output; - this._nowoutput = 0; - if (this._priority > priority) - return; - if (output & 1) - writesyslog(message, _sysPriorityOS[_sysPriority[priority]]); - if (output & 14) - time = Clock.currTime().format("%Y.%m.%d %H:%M:%S"); - if (output & 2 && priority >= WARNING) - writestdout(time, message, priority); - if (output & 4 && priority <= ERROR) - writestderr(time, message, priority); - if (output & 8) - writefile(time, message, priority); + synchronized (_mutex) { + if (_priority > priority) return; + int output = _nowOutput ? _nowOutput : _output; + _nowOutput = 0; + + string time; + if (output & (STD | FILE)) { + time = Clock.currTime().format("%Y.%m.%d %H:%M:%S"); + } + + if (output & SYSLOG) writesyslog(message, _sysPriorityOS[_sysPriority[priority]]); + if (output & STD) writestd(time, message, priority); + if (output & FILE) writefile(time, message, priority); + } } + /++ + Writes a message to the configured log file. + + Params: + time = The timestamp of the message. + message = The message to log. + priority = The log level of the message. + + Example: + --- + log.writefile("2025.03.23 12:00:00", "File operation failed", ERROR); // Writes to log file + --- + +/ void writefile(string time, string message, int priority) { - if (!this._writeToFile) - return; + if (!_writeToFile) return; - if (!this._path.exists) { - this._writeToFile = false; - this.warning("The log file does not exist: " ~ this._path); - } + synchronized (_mutex) { + if (!_fileOpen) { + try { + _file = File(_path, "a+"); + _fileOpen = true; + } catch (Exception e) { + _writeToFile = false; + now(this.output.std).error("Unable to open the log file " ~ _path); + information(e.msg); + return; + } + } - File file; - - try { - file = File(this._path, "a+"); - this._writeToFile = true; - } catch (Exception e) { - this._writeToFile = false; - this.now(output.stderr).error("Unable to open the log file " ~ this._path); - this.information(e); - return; - } - - try { - file.writefln("%s %s %s", time, this._type[priority], message); - } catch (Exception e) { - this._writeToFile = false; - this.now(output.stderr).error("Unable to write to the log file " ~ this._path); - this.information(e); - return; - } - - try { - file.close(); - } catch (Exception e) { - this._writeToFile = false; - this.now(output.stderr).error("Unable to close the log file " ~ this._path); - this.information(e); - return; + try { + _file.writefln("%s %s %s", time, _type[priority], message); + } catch (Exception e) { + _writeToFile = false; + now(this.output.std).error("Unable to write to the log file " ~ _path); + information(e.msg); + } } } - 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; - } public: - @property static Log msg() { - if (this._log is null) - this._log = new Log; + /++ + Property to access the singleton instance of the logger. - return this._log; + Returns: + The single instance of the `Log` class. + + Example: + --- + auto logger = Log.msg; // Access the singleton logger + logger.information("Logger retrieved"); + --- + +/ + @property static Log msg() { + if (_log is null) { + synchronized { + if (_log is null) _log = new Log(); + } + } + return _log; } - Output output() { return Output(); } - Level level() { return Level(); } - Now now(Output outs) { return Now(outs); } + /++ + Sets the program name for syslog identification. - Log output(Output outs) { this._output = outs.output(); return this._log; } - deprecated("Use passing the argument as a `log.output.` object") - 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; } + Params: + name = The name of the program. + Returns: + This `Log` instance for chaining. + + Example: + --- + log.program("MyProgram"); // Sets syslog identifier to "MyProgram" + log.i("Program name set"); + --- + +/ + Log program(string name) { + synchronized (_mutex) { _name = name.to!wstring; } + return this; + } + + /++ + Sets the file path for logging. + + Params: + path = The path to the log file. + + Returns: + This `Log` instance for chaining. + + Example: + --- + log.file("myapp.log"); // Sets log file to "myapp.log" + log.i("Log file configured"); + --- + +/ + Log file(string path) { + synchronized (_mutex) { + if (_fileOpen) { _file.close(); _fileOpen = false; } + _path = path; + _writeToFile = true; + } + return this; + } + + /++ + Sets the minimum log level. + + Params: + priority = The minimum log level (e.g., DEBUGGING, ERROR). + + Returns: + This `Log` instance for chaining. + + Example: + --- + log.level(log.level.warning); // Only WARNING and above will be logged + log.d("This won't show"); // Ignored due to level + log.w("This will show"); + --- + +/ + Log level(int priority) { + synchronized (_mutex) { _priority = priority; } + return this; + } + + /++ + Enables or disables colored console output. + + Params: + condition = `true` to enable colors, `false` to disable. + + Returns: + This `Log` instance for chaining. + + Example: + --- + log.color(true); // Enables colored output + log.i("This will be colored"); + log.color(false); // Disables colored output + log.i("This will be plain"); + --- + +/ + Log color(bool condition) { + synchronized (_mutex) { _color = condition; } + return this; + } + + /++ + Starts configuring output targets using a fluent interface. + + Returns: + An `Output` struct for chaining output target methods. + + Example: + --- + auto outs = log.output.std.file; // Configures std and file output + log.output(outs); + log.i("Logged to console and file"); + --- + +/ + Output output() { + return Output(); + } + + /++ + Sets the default output targets. + + Params: + outs = An `Output` struct with configured targets. + + Returns: + This `Log` instance for chaining. + + Example: + --- + log.output(log.output.syslog.std); // Sets output to syslog and console + log.i("Logged to syslog and console"); + --- + +/ + Log output(Output outs) { + synchronized (_mutex) { _output = outs.value; } + return this; + } + + /++ + Temporarily overrides output targets for the next log call. + + Params: + outs = An `Output` struct with temporary targets. + + Returns: + A `Now` struct to chain the log call. + + Example: + --- + log.now(log.output.std).n("Temporary console output"); // Only to console + log.i("Back to default outputs"); + --- + +/ + Now now(Output outs) { + synchronized (_mutex) { _nowOutput = outs.value; } + return Now(this); + } + + /++ + Logs an alert message (priority ALERT). + + Params: + message = The message to log (converted to string). + + Example: + --- + log.alert("System alert!"); // Logs with ALERT level + log.a(42); // Logs "42" with ALERT level + --- + +/ void alert(T)(T message) { writelog(message.to!string, ALERT); } + + /++ + Logs a critical error message (priority CRITICAL). + + Params: + message = The message to log (converted to string). + + Example: + --- + log.critical("Critical failure"); // Logs with CRITICAL level + log.c("Out of memory"); // Alias usage + --- + +/ void critical(T)(T message) { writelog(message.to!string, CRITICAL); } + + /++ + Logs an error message (priority ERROR). + + Params: + message = The message to log (converted to string). + + Example: + --- + log.error("File not found"); // Logs with ERROR level to stderr + log.e("Error code: 404"); // Alias usage + --- + +/ void error(T)(T message) { writelog(message.to!string, ERROR); } + + /++ + Logs a warning message (priority WARNING). + + Params: + message = The message to log (converted to string). + + Example: + --- + log.warning("Low disk space"); // Logs with WARNING level + log.w("Check disk"); // Alias usage + --- + +/ void warning(T)(T message) { writelog(message.to!string, WARNING); } + + /++ + Logs a notice message (priority NOTICE). + + Params: + message = The message to log (converted to string). + + Example: + --- + log.notice("User logged in"); // Logs with NOTICE level + log.n("Session started"); // Alias usage + --- + +/ void notice(T)(T message) { writelog(message.to!string, NOTICE); } + + /++ + Logs an informational message (priority INFORMATION). + + Params: + message = The message to log (converted to string). + + Example: + --- + log.information("App started"); // Logs with INFORMATION level + log.i("Version 1.0"); // Alias usage + --- + +/ void information(T)(T message) { writelog(message.to!string, INFORMATION); } + + /++ + Logs a debugging message (priority DEBUGGING). + + Params: + message = The message to log (converted to string). + + Example: + --- + log.debugging("Variable x = 5"); // Logs with DEBUGGING level + log.d("Entering loop"); // Alias usage + --- + +/ void debugging(T)(T message) { writelog(message.to!string, DEBUGGING); } + /++ Alias for `alert` +/ alias a = alert; + /++ Alias for `critical` +/ alias c = critical; + /++ Alias for `error` +/ alias e = error; + /++ Alias for `warning` +/ alias w = warning; + /++ Alias for `notice` +/ alias n = notice; + /++ Alias for `information` +/ alias i = information; + /++ Alias for `debugging` +/ alias d = debugging; + + /++ + Struct for fluent configuration of output targets. + + Provides methods to chain output targets, accumulating them into a bitmask. + +/ + struct Output { + private int value = 0; + + /++ + Adds syslog to the output targets. + + Example: + --- + log.output(log.output.syslog); // Enables syslog output + log.i("Logged to syslog"); + --- + +/ + Output syslog() { value |= SYSLOG; return this; } + + /++ + Adds standard output (stdout/stderr) to the output targets. + + Example: + --- + log.output(log.output.std); // Enables console output + log.w("Logged to console"); + --- + +/ + Output std() { value |= STD; return this; } + + /++ + Adds file output to the output targets. + + Example: + --- + log.output(log.output.file); // Enables file output + log.i("Logged to file"); + --- + +/ + Output file() { value |= FILE; return this; } + } + + /++ + Struct for fluent configuration of log levels. + + Provides methods to specify log levels. + +/ + struct Level { + /++ Returns the DEBUGGING level +/ + int debugging() { return DEBUGGING; } + /++ Returns the ALERT level +/ + int alert() { return ALERT; } + /++ Returns the CRITICAL level +/ + int critical() { return CRITICAL; } + /++ Returns the ERROR level +/ + int error() { return ERROR; } + /++ Returns the WARNING level +/ + int warning() { return WARNING; } + /++ Returns the NOTICE level +/ + int notice() { return NOTICE; } + /++ Returns the INFORMATION level +/ + int information() { return INFORMATION; } + + /++ Alias for `debugging` +/ + alias d = debugging; + /++ Alias for `alert` +/ + alias a = alert; + /++ Alias for `critical` +/ + alias c = critical; + /++ Alias for `error` +/ + alias e = error; + /++ Alias for `warning` +/ + alias w = warning; + /++ Alias for `notice` +/ + alias n = notice; + /++ Alias for `information` +/ + alias i = information; + + /++ + Example: + --- + log.level(log.level.d); // Sets level to DEBUGGING + log.d("Debug message"); // Visible + --- + +/ + } + + /++ + Helper method to start level configuration. + + Returns: + A `Level` struct for chaining level methods. + + Example: + --- + log.level(log.level().warning); // Sets level to WARNING + log.i("This won't show"); // Ignored due to level + log.w("This will show"); + --- + +/ + Level level() { return Level(); } + + /++ + Struct for temporary output override. + + Provides methods to log messages with temporary output settings. + +/ + struct Now { + private Log _log; + + this(Log log) { _log = log; } + + /++ + Logs an alert message. + + Example: + --- + log.now(log.output.std).alert("Temp alert"); // Only to console + --- + +/ + void alert(T)(T message) { _log.alert(message); } + + /++ + Logs a critical error message. + + Example: + --- + log.now(log.output.file).c("Temp critical"); // Only to file + --- + +/ + void critical(T)(T message) { _log.critical(message); } + + /++ + Logs an error message. + + Example: + --- + log.now(log.output.std).e("Temp error"); // Only to stderr + --- + +/ + void error(T)(T message) { _log.error(message); } + + /++ + Logs a warning message. + + Example: + --- + log.now(log.output.syslog).w("Temp warning"); // Only to syslog + --- + +/ + void warning(T)(T message) { _log.warning(message); } + + /++ + Logs a notice message. + + Example: + --- + log.now(log.output.std).n("Temp notice"); // Only to stdout + --- + +/ + void notice(T)(T message) { _log.notice(message); } + + /++ + Logs an informational message. + + Example: + --- + log.now(log.output.file).i("Temp info"); // Only to file + --- + +/ + void information(T)(T message) { _log.information(message); } + + /++ + Logs a debugging message. + + Example: + --- + log.now(log.output.std).d("Temp debug"); // Only to stdout + --- + +/ + void debugging(T)(T message) { _log.debugging(message); } + + /++ Alias for `alert` +/ + alias a = alert; + /++ Alias for `critical` +/ + alias c = critical; + /++ Alias for `error` +/ + alias e = error; + /++ Alias for `warning` +/ + alias w = warning; + /++ Alias for `notice` +/ + alias n = notice; + /++ Alias for `information` +/ + alias i = information; + /++ Alias for `debugging` +/ + alias d = debugging; + } } + +/++ + Global alias for easy access to the logger instance. + + The `log` alias provides a convenient shortcut to the singleton instance of the `Log` class, + allowing direct access to all logging functionality without explicitly calling `Log.msg`. + It supports the same methods, configuration options, and features as the `Log` class. + + Example: + --- + import singlog; + + void main() { + // Configure the logger using the alias + log.program("MyApp") + .color(true) + .level(log.level.debugging) // Using log.level directly + .output(log.output.std.file.syslog) + .file("myapp.log"); + + // Log messages with full method names + log.debugging("App starting in debug mode"); + log.information("Initialization complete"); + log.error("Failed to load resource"); + + // Log messages with aliases + log.d("Debug message via alias"); + log.i("Info message via alias"); + log.e("Error message via alias"); + + // Temporary output override + log.now(log.output.std).n("Temporary console-only message"); + } + --- ++/ +alias log = Log.msg; diff --git a/tests/cmd.png b/tests/cmd.png index 5399d47..8dde736 100644 Binary files a/tests/cmd.png and b/tests/cmd.png differ diff --git a/tests/terminal.png b/tests/terminal.png index abf1815..2e54224 100644 Binary files a/tests/terminal.png and b/tests/terminal.png differ diff --git a/tests/test.d b/tests/test.d index 68d9e83..b178abf 100644 --- a/tests/test.d +++ b/tests/test.d @@ -1,20 +1,60 @@ import singlog; +import std.format : format; +import std.exception : enforce; +/++ + Logging Levels Table: + Level | Value | Description + --------------|----------|--------- + DEBUGGING | 0 | Debugging information (highest) + ALERT | 1 | Urgent alerts + CRITICAL | 2 | Critical errors + ERROR | 3 | Errors + WARNING | 4 | Warnings + NOTICE | 5 | Notices + INFORMATION | 6 | Informational messages (lowest) ++/ void main(string[] argv) { - log.output(log.output.syslog.stderr.stdout.file) // write to syslog, standard error/output streams and file - .program(argv[0]) // program name as an identifier (for Windows OS) - .level(log.level.debugging) // logging level - .color(true) // color text output - .file("./test.log"); // the path to the log file + // Logger configuration + log.color(true) // Enable colored output + .level(log.level.error) // Threshold ERROR (3): shows ERROR and less critical (≥3) + .output(log.output.std.file.syslog) // Set all three output targets + .file("./test.log") // Set log file + .program(argv[0]); // Set program name (Windows only) - 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"); + // Application start + log.i("ChainDemo application started"); // INFO (6) >= 3 + log.e("Logging with ERROR level activated"); // ERROR (3) >= 3 - log.now(log.output.stdout).n("This error message will only be written to the standard output stream"); - log.now(log.output.syslog.file).c("This error message will only be written to the syslog and file"); + // Level demonstration + log.e("Error during operation"); // ERROR (3) >= 3 + log.w("Warning"); // WARNING (4) >= 3 + log.n("Important notice"); // NOTICE (5) >= 3 + log.d("Debugging not shown"); // DEBUGGING (0) < 3 + log.i("General information"); // INFO (6) >= 3 + log.a("Alert not shown"); // ALERT (1) < 3 + + // Example with data types + int errorCode = 500; + log.e("Server error %d".format(errorCode)); // ERROR (3) >= 3 + + // Temporary output redirection + log.now(log.output.std).e("Error only to console"); // ERROR (3) >= 3 + + // Exception handling + try { + enforce(false, "Test exception"); + } catch (Exception e) { + log.e("Exception: %s".format(e.msg)); // ERROR (3) >= 3 + } + + // Configuration change + log.color(true) + .level(log.level.alert) // Threshold CRITICAL (2): shows CRITICAL and less critical (≥2) + .output(log.output.std.file); + log.e("This message will be shown (ERROR >= CRITICAL)"); // ERROR (3) >= 2 + log.a("Configuration changed, ALERT messages"); // ALERT (1) >= 2 + + // Finale + log.c("Demonstration completed"); // CRITICAL (2) >= 2 }