FlatImage
A configurable Linux containerization system
Loading...
Searching...
No Matches
log.hpp File Reference

A library for file logging. More...

#include <pthread.h>
#include <filesystem>
#include <iostream>
#include <fstream>
#include <print>
#include <ranges>
#include <unistd.h>
#include "../std/concept.hpp"
#include "../std/string.hpp"
Include dependency graph for log.hpp:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Classes

class  ns_log::anonymous_namespace{log.hpp}::Logger
 
struct  ns_log::Location
 Source code location information for log messages. More...
 
class  ns_log::Writer
 Base class for level-specific log writers (debug, info, warn, error, critical) More...
 
class  ns_log::debug
 Debug-level logger (lowest priority) More...
 
class  ns_log::info
 Info-level logger (informational messages) More...
 
class  ns_log::warn
 Warning-level logger (potential issues) More...
 
class  ns_log::error
 Error-level logger (recoverable errors) More...
 
class  ns_log::critical
 Critical-level logger (highest priority, always shown) More...
 

Namespaces

namespace  ns_log
 Multi-level logging system with file and stdout sinks.
 

Macros

#define logger(fmt, ...)
 Compile-time log level dispatch macro with automatic location capture.
 
#define logger_loc(loc, fmt, ...)
 Compile-time log level dispatch macro with manual location specification.
 

Enumerations

enum class  Level : int {
  CRITICAL , ERROR , WARN , INFO ,
  DEBUG
}
 

Functions

void ns_log::anonymous_namespace{log.hpp}::fork_handler_child ()
 Fork handler that resets the logger in child processes.
 
void ns_log::set_level (Level level)
 Sets the logging verbosity (CRITICAL,ERROR,INFO,DEBUG)
 
Level ns_log::get_level ()
 Get current verbosity level of the logger.
 
void ns_log::set_sink_file (fs::path const &path_file_sink)
 Sets the sink file of the logger.
 
void ns_log::set_as_fork ()
 Marks the logger as being in a forked child process.
 
template<typename... Ts>
std::string ns_log::vformat (std::string_view fmt, Ts &&... ts)
 Workaround make_format_args only taking references.
 
template<ns_string::static_string str>
constexpr decltype(auto) ns_log::impl_log (Location loc)
 Implementation function for compile-time log level dispatch.
 

Variables

thread_local Logger ns_log::anonymous_namespace{log.hpp}::logger
 Thread-local logger instance.
 

Detailed Description

A library for file logging.

Author
Ruan Formigoni

Thread-Local Logger with Fork Safety

This logger uses thread_local storage combined with pthread_atfork() to handle both multi-threading and process forking correctly.

Thread-Local Storage (thread_local)

The thread_local Logger logger ensures that each thread in a process gets its own independent Logger instance:

  • Single-threaded process: One logger instance
  • Multi-threaded process: One logger instance per thread (isolated, no races)
  • Shared memory: NOT shared between threads (unlike static/global variables)

Benefits:

  • No mutex/locking needed (each thread has private instance)
  • No race conditions on logger state (m_sink, m_level)
  • Each thread can log to different files if needed

Fork Behavior

When fork() is called, the child process inherits a copy of the parent's memory:

Without pthread_atfork (problematic):

Parent opens /tmp/parent.log // FD 5 points to file
fork()
-> Parent: FD 5 still open
-> Child: FD 5 still open (SAME file, SAME position, CORRUPTION!)

With pthread_atfork (this implementation):

Parent opens /tmp/parent.log // FD 5 points to file
fork()
-> pthread_atfork child handler runs automatically
-> fork_handler_child() calls logger.set_sink_file("/dev/null")
-> Closes FD 5 in child, opens /dev/null (new FD)
-> Parent: FD 5 still valid (reference count decremented but not closed)
-> Child: FD 5 closed, new FD for /dev/null (clean slate)
#define logger(fmt,...)
Compile-time log level dispatch macro with automatic location capture.
Definition log.hpp:682

pthread_atfork() Registration

In the Logger constructor:

static thread_local bool registered = false;
if (!registered) {
pthread_atfork(nullptr, nullptr, fork_handler_child);
registered = true;
}
  • Registered once per thread (static thread_local guard)
  • fork_handler_child runs in child process after fork()
  • Resets logger file to (/dev/null)
  • Child can then set its own log file independently

File Descriptor Semantics

File descriptors are NOT shared memory - they're kernel objects with reference counts:

  • Before fork: Parent has FD → File (refcount = 1)
  • After fork: Parent FD → File (refcount = 2), Child FD → File (refcount = 2)
  • Child closes FD: Parent FD → File (refcount = 1), Child FD closed
  • File closed when: Refcount reaches 0 (all processes close their FDs)

Therefore, closing the file in the child does NOT affect the parent's ability to log.

Non-Copyable, Non-Movable Design

The Logger class is completely non-copyable and non-movable:

Logger(Logger const&) = delete; // Deleted copy constructor
Logger& operator=(Logger const&) = delete; // Deleted copy assignment
Logger(Logger&&) = delete; // Deleted move constructor
Logger& operator=(Logger&&) = delete; // Deleted move assignment
  • Each thread_local Logger instance is tied to its thread
  • After fork(), fork_handler_child() calls set_sink_file("/dev/null")
  • This closes the old file descriptor and opens /dev/null in the child
  • No move/copy operations needed - just reopen the sink file

Usage Example

// Parent process
ns_log::set_sink_file("/tmp/parent.log");
ns_log::info()("Parent starting");
pid_t pid = fork();
if (pid == 0) {
// Child: logger automatically reset to /dev/null
ns_log::set_sink_file("/tmp/child.log"); // Safe! New file
ns_log::info()("Child process"); // Logs to child.log
} else {
// Parent: still logging to parent.log
ns_log::info()("Parent forked child"); // Logs to parent.log
}
Info-level logger (informational messages)
Definition log.hpp:543
void set_sink_file(fs::path const &path_file_sink)
Sets the sink file of the logger.
Definition log.hpp:359

Thread Safety Summary

Scenario Behavior Thread-Safe?
Single thread One logger instance ✅ Yes
Multiple threads Separate logger per thread ✅ Yes (isolated)
Fork (parent) Keeps existing logger ✅ Yes
Fork (child) Fresh logger (/dev/null) ✅ Yes (auto-reset)
Fork then thread Child's threads get new loggers ✅ Yes

Definition in file log.hpp.

Macro Definition Documentation

◆ logger

#define logger ( fmt,
... )
Value:
constexpr decltype(auto) impl_log(Location loc)
Implementation function for compile-time log level dispatch.
Definition log.hpp:737
Source code location information for log messages.
Definition log.hpp:403

Compile-time log level dispatch macro with automatic location capture.

This macro provides a convenient interface for logging with compile-time level selection based on message prefix. The source file and line number are automatically captured.

Log Level Prefixes:

  • D:: - Debug (lowest priority, only shown when level = DEBUG)
  • I:: - Info (informational messages)
  • W:: - Warning (potential issues, uses stderr)
  • E:: - Error (recoverable errors, uses stderr)
  • C:: - Critical (highest priority, always shown, uses stderr)
  • Q:: - Quiet (message discarded, useful for conditional compilation)

Format String: Uses std::format syntax with positional arguments: {0}, {1}, {}

Examples:

// Basic usage with different log levels
logger("I::Application started");
logger("D::Debug value: {}", 42);
logger("W::Connection timeout after {} seconds", 30);
logger("E::Failed to open file: {}", filename);
logger("C::Critical error: out of memory");
// Multiple arguments
int x = 10, y = 20;
logger("I::Coordinates: ({}, {})", x, y);
// Complex formatting
logger("D::Processing item {0} of {1} ({2:.2f}%)", current, total, percent);
// Quiet logging (discarded)
logger("Q::This message will never appear");
// With containers (requires Iterable concept)
std::vector<int> vec = {1, 2, 3};
logger("I::Vector contents: {}", vec);

Output Format:

PREFIX::filename.cpp::LINE::Message

Example output:

I::main.cpp::42::Application started
D::utils.cpp::128::Debug value: 42
W::network.cpp::256::Connection timeout after 30 seconds
Parameters
fmtFormat string with log level prefix (e.g., "I::Message: {}")
...Variadic arguments for format string placeholders
Note
This macro requires C++23 for static_string template parameter
See also
logger_loc for custom location specification
ns_log::set_level to control console output verbosity
ns_log::set_sink_file to enable file logging
Examples
/flatimage/src/filesystems/layers.hpp.

Definition at line 682 of file log.hpp.

◆ logger_loc

#define logger_loc ( loc,
fmt,
... )
Value:
::ns_log::impl_log<fmt>(loc)(__VA_ARGS__)

Compile-time log level dispatch macro with manual location specification.

Similar to logger() but allows specifying a custom source location. Useful for logging from helper functions while preserving the original call site location.

Use Cases:

  • Wrapper functions that want to preserve caller location
  • Logging from template instantiations
  • Custom error handling macros

Examples:

// Capture location at call site, pass to helper function
void log_helper(ns_log::Location loc, std::string_view msg) {
logger_loc(loc, "I::{}", msg);
}
void user_function() {
// Location captured here, not inside log_helper
log_helper(ns_log::Location{}, "User function called");
}
// Output: I::main.cpp::15::User function called
// (line 15 is user_function, not log_helper)
// Custom location for testing/debugging
ns_log::Location custom_loc{"custom.cpp", 999};
logger_loc(custom_loc, "E::Simulated error from custom location");
// Output: E::custom.cpp::999::Simulated error from custom location
// Macro that preserves location
#define MY_LOG(msg) logger_loc(ns_log::Location{}, "I::{}", msg)
MY_LOG("This shows the caller's location");
#define logger_loc(loc, fmt,...)
Compile-time log level dispatch macro with manual location specification.
Definition log.hpp:727
Parameters
locns_log::Location instance with file/line information
fmtFormat string with log level prefix (e.g., "I::Message: {}")
...Variadic arguments for format string placeholders
Note
Location is consteval, so it must be constructed at compile time
See also
logger for automatic location capture
Examples
/flatimage/src/std/expected.hpp.

Definition at line 727 of file log.hpp.