C++(20) async logger
Find a file
2026-03-18 11:26:21 +03:00
.clang-format chore(readme): update 2026-03-18 11:10:00 +03:00
.clangd chore(readme): update 2026-03-18 11:10:00 +03:00
asynclog.hh fix(asynclog): indent fix 2026-02-15 09:26:01 +03:00
readme.md chore(readme): small fix 2026-03-18 11:26:21 +03:00

Asynclog - header only asynchronous logging library for C++(20)

Example

#define ASYNCLOG_ENABLED
#include "asynclog.hh"

int main() {
  // The default logger ships with a console_sink already attached.
  LOG_INFO("started, pid={}", ::getpid());

  // Attach a file sink.
  LOG_ADD_FILE("logs/app.log");

  // Or a rotating file: 5 MB per file, keep 3 backups.
  LOG_ADD_ROTATING("logs/app.log", 5 * 1024 * 1024, 3);

  LOG_WARN("disk usage {}%", 87);
  LOG_ERROR("timeout after {}ms", 3000);

  LOG_FLUSH();   // block until all pending records are written
}

Levels

Enum Macro Use case
level::tracez LOG_TRACE fine-grained control flow
level::debug LOG_DEBUG diagnostic detail
level::info LOG_INFO normal operational events
level::warn LOG_WARN unexpected but recoverable
level::error LOG_ERROR failures
level::fatal LOG_FATAL unrecoverable errors
level::off - disables all output

Levels are ordered; setting level::warn suppresses trace, debug, and info. Filtering happens both on the logger (global gate) and per-sink.

Sinks

console_sink

Writes to std::cerr. Automatically detects whether stderr is a TTY and enables ANSI colors accordingly.

auto cs = std::make_shared<asynclog::console_sink>();
cs->set_colors(false);           // force colors off
cs->set_level(asynclog::level::warn);  // only warn+ to console
logger.add_sink(cs);

file_sink

Appends to a single file. Parent directories are created automatically.

auto fs = std::make_shared<asynclog::file_sink>("/var/log/app.log");
// or truncate on open:
auto fs2 = std::make_shared<asynclog::file_sink>("debug.log", /*truncate=*/true);

rotating_file_sink

Rotates files when the current file exceeds max_bytes. Old files are renamed with a numeric suffix (.1, .2, …). When the number of backups exceeds max_files, the oldest is deleted.

auto rs = std::make_shared<asynclog::rotating_file_sink>(
  "logs/app.log",
  10 * 1024 * 1024,   // 10 MB per file
  5                   // keep 5 backups
);

Custom sinks

Derive from asynclog::sink and implement write() and flush():

class syslog_sink : public asynclog::sink {
public:
  void write(const asynclog::record& rec) override {
    if (!should_log(rec.lvl)) return;
    auto text = format(rec, /*colors=*/false);
    ::syslog(to_syslog_priority(rec.lvl), "%s", text.c_str());
  }

  void flush() override { /* no-op for syslog */ }
};

Overflow policies

When the internal queue is full:

Policy Behaviour
overflow_policy::block caller blocks until space is available (default)
overflow_policy::drop_newest incoming record is discarded; dropped_count() incremented
overflow_policy::drop_oldest oldest queued record is evicted to make room
auto& log = asynclog::default_logger();
log.set_queue_capacity(4096);
log.set_overflow_policy(asynclog::overflow_policy::drop_newest);

Formatters

A formatter is any callable matching std::string(const record&, boolcolors). Two are provided:

  • default_format — timestamp, padded level, source location, payload.
  • compact_format — time-only, three-letter level tag, payload.

Set globally:

asynclog::default_logger().set_formatter(asynclog::compact_format);

Or per-sink:

file_sink->set_formatter([](const asynclog::record& r, bool) {
  return std::format("[{}] {}\n",
                     asynclog::to_short_string(r.lvl), r.payload);
});

Compile-time filtering

Define ASYNCLOG_ACTIVE_LEVEL before including the header to strip lower-severity macros at compile time:

#define ASYNCLOG_ACTIVE_LEVEL ::asynclog::level::info
#define ASYNCLOG_ENABLED
#include "asynclog.hh"

// LOG_TRACE and LOG_DEBUG are now zero-cost no-ops.

Macro

Macro Description
LOG_TRACE(fmt, ...) trace message
LOG_DEBUG(fmt, ...) debug message
LOG_INFO(fmt, ...) info message
LOG_WARN(fmt, ...) warning
LOG_ERROR(fmt, ...) error
LOG_FATAL(fmt, ...) fatal error
LOG_SET_LEVEL(lvl) set runtime minimum level
LOG_SET_CONSOLE(bool) remove / keep the default console sink
LOG_SET_COLORS(bool) toggle ANSI colors on console sink
LOG_ADD_FILE(path) attach a file_sink
LOG_ADD_ROTATING(path, bytes, n) attach a rotating_file_sink
LOG_FLUSH() block until queue is drained and sinks flushed
LOG_SHUTDOWN() flush + join worker thread

Architecture

Logger

Central object that owns the background worker thread and a set of sinks. A global default instance is provided via default_logger(). Additional named loggers may be created for subsystem isolation.

sink (interface)

Receives pre-formatted records. The library ships three concrete sinks; users may implement the interface for custom destinations.

  • console_sink — writes to stderr with optional ANSI colors
  • file_sink — appends to a single file
  • rotating_file_sink — rotates files by size, keeps N backups

record

Immutable value type carrying the formatted payload, metadata (level, timestamp, source location), and a monotonic sequence id.

formatter

Callable with signature std::string(const record&, bool colors). The default formatter produces lines such as

2025-06-01 14:30:05.123] [INFO ] main.cc:42 — hello world

A custom formatter can be set per-sink or globally on the logger.