Compare commits

..

No commits in common. "master" and "v0.1.0" have entirely different histories.

12 changed files with 161 additions and 1397 deletions

2
.gitignore vendored
View File

@ -2,5 +2,3 @@
.dub
*.o
lib
bin
test.log

View File

@ -1,121 +0,0 @@
# Changelog
## [1.0.1] - 2025-03-27
### Fixed
- Correct transfer of priority when writing to the system log.
## [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
- Added the ability to output messages to the standard error stream. Now messages above the `WARNING` level will not be output to the `stdout`. To output them, need to use `stderr`
- Write message to specific outputs via `log.now`
- Now `log.output` allows to set the output as an argument
- Now `log.level` allows to set the level as an argument
### Bug fixes
- Fixed streams redirection in Windows
## [0.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/))
## [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
## [0.3.1] - 2023-05-30
### Bug fixes
- Log of debug messages
## [0.3.0] - 2023-04-28
- Minor changes
### New
- Windows OS Logging support
## [0.2.1] - 2023-03-29
### New
- Added aliases for the short form of function calls
### Bug fixes
- Calling the main object
## [0.2.0] - 2023-03-29
- Removed functions `fileOn()` and `fileOff()`
### New
- Simultaneous writing to the standard stream, syslog and file by binary setting the output flag
## [0.1.0] - 2023-03-23
### The first stable working release
- Output to the standard stream or syslog
- Enable an entry in the file

162
README.md
View File

@ -1,152 +1,62 @@
![singlog](singlog.png)
# singlog
[![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&logo=D)](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&logo=D)](https://github.com/AlexanderZhirov/singlog)
[![dub](https://img.shields.io/dub/v/singlog.svg?sort=semver&style=for-the-badge&color=orange&logo=D)](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
![output](tests/terminal.png)
![output](tests/cmd.png)
# Singleton Logging Module
`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.
## Features
- **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 projects 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. Heres a basic example:
## Basic Usage
```d
import singlog;
import simplog;
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");
void main()
{
Log.msg.level(Log.DEBUG);
Log.msg.output(Log.SYSLOG);
Log.msg.file("./file.log");
Log.msg.warning("Hello, World!");
}
```
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.
## Examples
### 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:
Setting the error output level:
```d
log.d("Debug message");
log.i("Info message");
log.e("Error message");
Log.msg.level(Log.DEBUG);
Log.msg.level(Log.ALERT);
Log.msg.level(Log.CRIT);
Log.msg.level(Log.ERR);
Log.msg.level(Log.WARNING);
Log.msg.level(Log.NOTICE);
Log.msg.level(Log.INFO);
```
### 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:
Assigning a target output:
```d
log.output(log.output.std.file); // Console and file output
log.i("This goes to console and file");
Log.msg.output(Log.SYSLOG);
Log.msg.output(Log.STDOUT);
```
### Temporary Output Override
Use `now()` to temporarily override output targets for the next log call:
Setup and allowing writing to a file:
```d
log.now(log.output.std).n("This goes only to console");
log.i("This uses default outputs again");
Log.msg.file("./file.log");
Log.msg.fileOn();
Log.msg.fileOff();
```
### Colored Output
Enable colored output with `color(true)`:
Output of messages to the log:
```d
log.color(true);
log.w("This warning will be yellow on POSIX or Windows");
log.c("This critical message will be magenta");
Log.msg.alert("Alert message");
Log.msg.critical("Critical message");
Log.msg.error("Error message");
Log.msg.warning("Warning message");
Log.msg.notice("Notice message");
Log.msg.informations("Information message");
Log.msg.debugging("Debugging message");
```
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`).
## Dub
### File Logging
Set a log file with `file()`:
```d
log.file("app.log");
log.e("This error goes to app.log");
```
The file is opened in append mode (`"a+"`) and includes timestamps.
Add a dependency on `"singlog": "~>0.1.0"`.

View File

@ -7,22 +7,9 @@
"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": "test",
"importPaths": ["source","tests"],
"sourcePaths": ["tests"]
}
],
"dependencies": {
"datefmt": "~>1.0.4"
}

BIN
logo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@ -1,111 +0,0 @@
.\" 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.<method>(<args>); // 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

View File

@ -1,111 +0,0 @@
.\" Страница руководства для библиотеки 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 АВТОР
Александр Жиров

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@ -1,875 +1,147 @@
/++
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) {
import core.sys.posix.syslog;
}
import core.sync.mutex : Mutex;
import std.string;
import core.sys.posix.syslog;
import std.stdio;
import std.conv;
import std.meta;
import std.file;
import std.datetime;
import datefmt;
/++
Singleton logging class with thread-safe operations and flexible output.
Singleton for simple logging
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:
---
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");
}
// Setting the error output level
Log.msg.level(Log.DEBUG);
Log.msg.level(Log.ALERT);
Log.msg.level(Log.CRIT);
Log.msg.level(Log.ERR);
Log.msg.level(Log.WARNING);
Log.msg.level(Log.NOTICE);
Log.msg.level(Log.INFO);
// Assigning a target output
Log.msg.output(Log.SYSLOG);
Log.msg.output(Log.STDOUT);
// Setup and allowing writing to a file
Log.msg.file("./file.log");
Log.msg.fileOn();
Log.msg.fileOff();
// Output of messages to the log
Log.msg.alert("Alert message");
Log.msg.critical("Critical message");
Log.msg.error("Error message");
Log.msg.warning("Warning message");
Log.msg.notice("Notice message");
Log.msg.informations("Information message");
Log.msg.debugging("Debugging message");
---
+/
class Log {
private:
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
class Log
{
private static Log log;
private string path;
private bool writeToFile = false;
private bool fileExist = true;
private static SysTime time;
/++ Private constructor to enforce singleton pattern +/
this() {
_mutex = new Mutex();
// Target output
enum {SYSLOG, STDOUT}
// Message output level
enum {
DEBUG = 0,
CRIT = 1,
ERR = 2,
WARNING = 3,
NOTICE = 4,
INFO = 5,
ALERT = 6
}
int msgOutput = STDOUT;
int msgLevel = INFO;
private this() {}
private void writeLog(string message, int msgLevel, int priority)
{
if (this.msgLevel > msgLevel)
return;
if (this.msgOutput == STDOUT)
writeln(message);
else if (this.msgOutput == SYSLOG)
syslog(priority, (message ~ "\0").ptr);
writeFile(message);
}
/++ Destructor to ensure the log file is closed +/
~this() {
synchronized (_mutex) {
if (_fileOpen) {
_file.close();
_fileOpen = false;
}
private void writeFile(string message)
{
if (!this.writeToFile || !this.fileExist)
return;
if (this.path.exists)
this.fileExist = true;
else
{
this.fileExist = false;
this.warning("The log file does not exist: " ~ this.path);
this.fileExist = true;
}
File file;
try {
file = File(this.path, "a+");
} catch (Exception e) {
this.fileOff();
this.error("Unable to open the log file " ~ this.path);
this.critical(e);
return;
}
try {
file.writeln(this.time.format("%Y.%m.%d %H:%M:%S: ") ~ message);
} catch (Exception e) {
this.fileOff();
this.error("Unable to write to the log file " ~ this.path);
this.critical(e);
return;
}
try {
file.close();
} catch (Exception e) {
this.fileOff();
this.error("Unable to close the log file " ~ this.path);
this.critical(e);
return;
}
}
version(Windows) {
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, _name.toUTF16z());
if (handleEventLog == null) return;
ReportEventW(handleEventLog, priority, 0, 0, null, 1, 0, &wMessage, null);
DeregisterEventSource(handleEventLog);
}
/++
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;
if (!GetConsoleScreenBufferInfo(handle, &defaultConsole)) return;
wstring wTime = "%s ".format(time).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, _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);
break;
default:
writesyslog("Unknown output file", _sysPriorityOS[_sysPriority[ERROR]]);
@property static Log msg()
{
if (this.log is null)
{
this.log = new Log;
this.time = Clock.currTime();
}
return this.log;
}
/++
Outputs a plain log message to the console on Windows.
void output(int msgOutput) { this.msgOutput = msgOutput; }
void level(int msgLevel) { this.msgLevel = msgLevel; }
void file(string path) { this.path = path; }
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, _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]]);
}
}
void fileOn() { this.writeToFile = true; }
void fileOff() { this.writeToFile = false; }
/++
Writes a message to the console on Windows, 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", "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) {
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
];
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)
];
/++
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));
}
}
/++
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());
}
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); }
}
/// 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)
}
/// 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]:"
];
/++
Core logging function that writes a message to configured outputs.
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) {
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 (!_writeToFile) return;
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;
}
}
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);
}
}
}
public:
/++
Property to access the singleton instance of the logger.
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;
}
/++
Sets the program name for syslog identification.
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;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

View File

@ -1,60 +0,0 @@
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) {
// 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)
// Application start
log.i("ChainDemo application started"); // INFO (6) >= 3
log.e("Logging with ERROR level activated"); // ERROR (3) >= 3
// 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
}