Skip to content

Logging

Oleksandr Geronime edited this page Jun 27, 2026 · 2 revisions

Logging

The SERP logging framework (serp::logger) provides structured, leveled, multi-strategy logging with thread tracking and a hierarchical context model. It is designed for multi-threaded service architectures where each log message should carry enough context to trace it back to the originating service, thread, and method call.


CMake Targets

Target Includes
Serp::logger Core API (levels, tags, strategies interface)
Serp::logger_console Core + console output strategy
Serp::logger_file Core + file output strategy (with rotation)

Link the appropriate target in your CMakeLists.txt:

target_link_libraries(my_service PRIVATE Serp::logger_console)

Architecture

Application code
       │
       ▼
  logInfo()->info(...)       ◄── fluent API (LogWriter)
       │
       ▼
  LogManager (singleton)     ◄── thread registry, strategy dispatch
       │
       ├──► LogStrategyConsole   (stdout with color)
       ├──► LogStrategyFile      (rotating file)
       └──► MyCustomStrategy     (your plugin)

Key components:

Component Role
LogManager Global singleton. Owns strategy list, thread name registry, and global log level.
LogWriter Fluent message builder returned by the log*() macros.
LogTag Named tag for method/communication annotations.
Loggable Mixin for objects that participate in the logging hierarchy.
Log strategies Pluggable output handlers implementing a common interface.

Log Levels

Levels are ordered from most verbose to most silent:

Level Macro Use case
verbose logVerbose() Detailed trace data; high frequency.
debug logDebug() Developer diagnostics.
info logInfo() Notable events, state changes.
warn logWarn() Unexpected but recoverable situations.
error logError() Failures that require attention.
silent Suppresses all output (used as a filter threshold).

Set the global minimum level:

serp::logger::setLevel(eLogLevel::debug);

Basic API

Include the header:

#include <serp/logger/Logger.hpp>

Write log messages using the fluent macro interface:

logVerbose()->verbose("Detailed trace: value=" + std::to_string(val));
logDebug()->debug("Processing request id=" + std::to_string(id));
logInfo()->info("Service started successfully");
logWarn()->warning("Retry attempt " + std::to_string(n) + " of 3");
logError()->error("Connection failed: " + errMsg);

Each log*() macro returns a LogWriter that captures the call site (file, line, thread) automatically. The ->level(message) call commits the message to all registered strategies.


Adding Strategies

Strategies must be registered before any log output is produced — typically at the top of main() or in the process entry point.

#include <serp/logger/strategies/LogStrategyConsole.hpp>
#include <serp/logger/strategies/LogStrategyFile.hpp>

// Console output (color-coded by level)
serp::logger::addStrategy<LogStrategyConsole>();

// File output with rotation
serp::logger::addStrategy<LogStrategyFile>("/var/log/myapp/myapp.log");

// Custom strategy
serp::logger::addStrategy(std::make_shared<MyCustomStrategy>());

Multiple strategies may be active simultaneously. Every log message is dispatched to all registered strategies.


File Strategy and Rotation

LogStrategyFile supports automatic log rotation by size and file count:

serp::logger::addStrategy<LogStrategyFile>(
    "/var/log/myapp/myapp.log",
    5 * 1024 * 1024,   // rotate after 5 MB
    4                  // keep 4 rotated files
);

When the active file reaches the size limit, it is renamed (e.g., myapp.log.1) and a new file is opened. Old rotation files beyond the count limit are deleted.


Method Traffic Logging

Log the entry and exit of service method calls with a structured tag:

logMethod("ClimateService.setTemperature", zone, temp);

This emits a structured entry that can be filtered and visualized separately from general log messages. The logMethod macro is typically called at the top of a method implementation to record inbound traffic.


ITC Logging (Inter-Thread Communication)

Log cross-thread communication explicitly, capturing caller thread, callee thread, service name, and arguments:

itcLog(eLogLevel::info,
       "UIThread",           // caller thread name
       "ClimateThread",      // callee thread name
       "ClimateService::setTemperature",
       zone, temp);

ITC log entries are decorated differently from general messages to make cross-thread call flows easy to trace.


Loggable Mixin

serp::Loggable is a mixin that allows an object to participate in the logging hierarchy. When mixed in, the object can link itself to a parent context, and log messages it emits carry that context chain.

#include <serp/logger/Loggable.hpp>

class MyService : public serp::Service, public serp::Loggable {
public:
    MyService(serp::EventLoop& loop)
        : serp::Service(loop, "MyService") {}

    void initialize(std::shared_ptr<ParentContext> parent) {
        Loggable::link(parent);   // attach to parent for context chaining
    }

    void doSomething() {
        logInfo()->info("Called from MyService");
        // Message is tagged with MyService's context in the hierarchy
    }
};

Why use it? In a tree of services and components, Loggable::link() lets the log infrastructure know where a given object lives in the ownership graph. This context appears in log output and makes it possible to filter messages by subsystem.


Thread Name Registration

Thread names appear in log output to identify which thread produced each message. SERP registers its own EventLoop threads automatically. For custom threads:

serp::logger::registerThread("MyWorkerThread");

Call this once at thread startup.


Custom Strategy

Implement the strategy interface to route log output anywhere — a network socket, a database, a test recorder, etc.:

class MyCustomStrategy : public serp::logger::ILogStrategy {
public:
    void log(eLogLevel level,
             const std::string& thread,
             const std::string& file,
             int line,
             const std::string& message) override
    {
        // Route message to your output
        myOutput.write(level, thread, message);
    }
};

serp::logger::addStrategy(std::make_shared<MyCustomStrategy>());

Example: Full Setup

#include <serp/logger/Logger.hpp>
#include <serp/logger/strategies/LogStrategyConsole.hpp>
#include <serp/logger/strategies/LogStrategyFile.hpp>

int main(int argc, char** argv) {
    // Set up logging before anything else
    serp::logger::setLevel(eLogLevel::debug);
    serp::logger::addStrategy<LogStrategyConsole>();
    serp::logger::addStrategy<LogStrategyFile>(
        "/var/log/myapp/myapp.log",
        10 * 1024 * 1024,  // 10 MB rotation
        3                  // keep 3 files
    );

    logInfo()->info("Process starting");

    return serp::App::instance().run(argc, argv);
}

See Also

Clone this wiki locally