Introduction: Why Logging is More Than Just Printing Text?
In modern C++ development, logging has moved beyond simple std::cout. It is a critically important tool for debugging, monitoring, and auditing applications. However, an incorrect implementation can lead to performance degradation, data loss, or disk space clutter. In this article, we will break down best practices for logging in C++ with concrete examples that can be immediately applied in real projects.
1. Choosing a Logging Library: spdlog and Alternatives
The C++ standard library does not provide built-in tools for flexible logging. It is recommended to use proven third-party solutions. Spdlog is one of the most popular libraries due to its high performance and ease of integration.
Installing spdlog (vcpkg or header-only)
// Installation via vcpkgvcpkg install spdlog
// Or use the header-only version#include "spdlog/spdlog.h"#include "spdlog/sinks/basic_file_sink.h"#include "spdlog/sinks/rotating_file_sink.h"Basic Logger Configuration Example
#include <memory>#include "spdlog/spdlog.h"#include "spdlog/sinks/stdout_color_sinks.h"
int main() { // Create a console logger with colored output auto console = spdlog::stdout_color_mt("console"); // Set the logging level spdlog::set_level(spdlog::level::debug); // Example messages console->info("Application started"); console->warn("Potential issue detected"); console->error("Critical error: {}", errno); return 0;}Alternatives: Boost.Log (for enterprise projects), Google Glog (if integration with Google Test is needed). For embedded systems, use EasyLogger.
2. Logging Levels and Filtering
Proper use of logging levels is key to log readability. In C++, the following hierarchy is accepted (from most to least critical):
- TRACE — detailed execution tracing (debugging only)
- DEBUG — debugging information
- INFO — informational messages about application operation
- WARN — warnings about potential issues
- ERROR — errors that do not disrupt application operation
- CRITICAL — fatal errors requiring immediate intervention
Configuring Filtering in spdlog
// Enable only WARN and abovespdlog::set_level(spdlog::level::warn);
// For debugging, enable all levels#ifdef _DEBUG spdlog::set_level(spdlog::level::trace);#endif
// Example usage with macros#define LOG_TRACE(...) spdlog::trace(__VA_ARGS__)#define LOG_ERROR(...) spdlog::error(__VA_ARGS__)
LOG_TRACE("Entering function processData()");LOG_ERROR("Failed to open file: {}", filename);Tip: Never use TRACE in production builds — it can reduce performance by 10-100 times due to string formatting.
3. Logging Performance: Synchronous vs Asynchronous Mode
In high-load systems, synchronous logging can become a bottleneck. Each logger call blocks the thread until the write operation completes. The solution is asynchronous logging.
Configuring an Asynchronous Logger in spdlog
#include "spdlog/async.h"#include "spdlog/sinks/rotating_file_sink.h"
int main() { // Create an asynchronous logger with a queue size of 8192 spdlog::init_thread_pool(8192, 1); // 1 thread for processing auto async_file = spdlog::rotating_logger_mt<spdlog::async_factory>( "async_logger", "logs/app.log", 102