Logging in C++ Applications: Best Practices and Tools

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Logging in C++ Applications: Best Practices



Logging is a fundamental part of developing any serious software in C++. Without a reliable logging system, debugging complex errors, performance monitoring, and security auditing become nearly impossible. In this article, we will cover best practices for logging in C++, from choosing a library to advanced formatting and file rotation techniques.



1. Choosing a Logging Library



In C++, there are several mature logging libraries. Abandoning a custom implementation in favor of a ready-made solution saves time and reduces the number of bugs.



  • spdlog — the most popular library. It features high performance (header-only, asynchronous mode), a simple API, and support for multiple formats (JSON, plain text).
  • Boost.Log — a powerful but heavier library from the Boost collection. Ideal for enterprise projects requiring complex filtering and flexible backend configuration.
  • Google Logging (glog) — a simple library from Google. Well-suited for small projects but less flexible.


Example of initializing spdlog:



#include <spdlog/spdlog.h>#include <spdlog/sinks/basic_file_sink.h>

int main() { // Create a logger that writes to the file "logs/app.log" auto logger = spdlog::basic_logger_mt("file_logger", "logs/app.log"); // Set the logging level logger->set_level(spdlog::level::debug); // Log a message logger->info("Application started"); return 0;}


2. Logging Levels and Filtering



Using logging levels (trace, debug, info, warning, error, critical) is the de facto standard. They allow flexible configuration of the amount of output information depending on the environment (development, testing, production).



Recommendations for levels:

  • Trace/Debug — for development only. Detailed information about execution flows, variable values.
  • Info — standard events: service start/stop, successful operations.
  • Warning — unusual situations that do not lead to an error (e.g., using a deprecated API).
  • Error — errors that require attention but are not fatal.
  • Critical — fatal errors after which the application cannot continue working.


Example of filtering configuration in spdlog:



// In production, write only warning and aboveauto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();console_sink->set_level(spdlog::level::warn);

// Write everything, including debug, to a fileauto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/debug.log");file_sink->set_level(spdlog::level::debug);

auto logger = std::make_shared<spdlog::logger>("multi_sink", spdlog::sinks_init_list({console_sink, file_sink}));

spdlog::register_logger(logger);


3. Performance and Asynchronous Logging



Synchronous logging can become a performance bottleneck, especially in high-load systems. Each call to logger->info(...) blocks the thread until writing to disk is complete.



Best practices for performance:

  • Asynchronous logging — use a separate thread for writing logs. spdlog supports this out of the box.
  • Avoid expensive operations in log strings — do not call heavy functions inside the logging macro. For example, logger->info("Result: {}", computeExpensiveValue()); will call computeExpensiveValue() even if the log level is not

Blogs

Book Recommendations