autotoc_md1809
doc_id: "LOG-GUID-018" doc_title: "Logger System Best Practices Guide" doc_version: "1.0.0" doc_date: "2026-04-04" doc_status: "Released" project: "logger_system"
category: "GUID"
Language: English | 한국어
Logger System Best Practices Guide
SSOT: This document is the single source of truth for Logger System Best Practices Guide.
Table of Contents
- Design Principles
- Configuration Best Practices
- Performance Guidelines
- Error Handling
- Security Considerations
- Testing Strategies
- Production Deployment
- Common Pitfalls
Design Principles
1. Single Responsibility
Each logger instance should have a clear, single purpose:
auto app_logger = create_logger("application");
auto audit_logger = create_logger("audit");
auto perf_logger = create_logger("performance");
auto logger = create_logger(
"everything");
2. Dependency Injection
Pass loggers as dependencies rather than using global instances:
class DatabaseService {
std::shared_ptr<logger> logger_;
public:
explicit DatabaseService(std::shared_ptr<logger>
logger)
: logger_(std::move(
logger)) {}
};
class DatabaseService {
void query() {
global_logger->info("Query executed");
}
};
3. Interface Segregation
Use minimal interfaces when possible:
void process_data(log_writer_interface& writer);
void process_data(
logger& full_logger);
Configuration Best Practices
Environment-Specific Configuration
auto create_dev_logger() {
return logger_builder()
.with_console_writer()
.with_min_level(log_level::trace)
.with_colored_output(true)
.with_source_location(true)
.build();
}
auto create_prod_logger() {
return logger_builder()
.with_file_writer("/var/log/app.log")
.with_min_level(log_level::info)
.with_async_mode(true)
.with_batch_writing(true)
.with_rotation(100 * 1024 * 1024)
.build();
}
auto create_logger() {
const auto env = std::getenv("APP_ENV");
if (env && std::string(env) == "production") {
return create_prod_logger();
}
return create_dev_logger();
}
Configuration Validation
auto validate_logger_config(const logger_config& config) -> result<void> {
if (config.buffer_size < 1024) {
return error("Buffer size too small");
}
if (config.queue_size < 100) {
return error("Queue size insufficient for production");
}
if (config.rotation_size > 1024 * 1024 * 1024) {
return error(
"Rotation size too large (>1GB)");
}
}
Result< T > error(error_info info)
Dynamic Reconfiguration
class reconfigurable_logger {
std::atomic<log_level> min_level_;
public:
void set_level(log_level level) {
min_level_.store(level);
logger_->set_min_level(level);
}
void reload_config(const std::string& config_file) {
auto config = load_config(config_file);
apply_config(config);
}
};
Performance Guidelines
1. Minimize Allocations
void log_with_reservation(
logger& log) {
std::string message;
message.reserve(256);
message = format_message(data);
log.info(message);
}
log.info(std::string_view("Static message"));
std::string msg = "Message";
log.info(msg);
log.info(std::move(msg));
2. Lazy Evaluation
if (
logger->should_log(log_level::debug)) {
auto debug_info = collect_expensive_debug_info();
logger->debug(
"Debug info", {{
"data", debug_info}});
}
auto debug_info = collect_expensive_debug_info();
logger->debug(
"Debug info", {{
"data", debug_info}});
3. Batch Operations
class log_batcher {
std::vector<log_entry> batch_;
static constexpr size_t BATCH_SIZE = 100;
void add(log_entry entry) {
batch_.push_back(std::move(entry));
if (batch_.size() >= BATCH_SIZE) {
flush();
}
}
void flush() {
logger_->write_batch(batch_);
batch_.clear();
}
};
4. Async Logging Pattern
auto create_high_throughput_logger() {
return logger_builder()
.with_async_mode(true)
.with_queue_size(100'000)
.with_overflow_policy(overflow_policy::drop_oldest)
.with_batch_writing(true)
.with_batch_size(1000)
.with_flush_interval(std::chrono::milliseconds(100))
.build();
}
Error Handling
Graceful Degradation
class resilient_logger {
std::shared_ptr<logger> primary_;
std::shared_ptr<logger> fallback_;
public:
void log(log_level level, const std::string& msg) {
try {
primary_->log(level, msg);
} catch (const std::exception& e) {
try {
fallback_->log(level, msg);
} catch (...) {
std::cerr << "[EMERGENCY] " << msg << std::endl;
}
}
}
};
Error Recovery
class reconnecting_file_writer : public base_writer {
void write(const log_entry& entry) override {
if (!is_connected()) {
if (!try_reconnect()) {
buffer_entry(entry);
return;
}
}
try {
write_impl(entry);
} catch (const std::exception& e) {
handle_write_error(e);
}
}
};
Circuit Breaker Pattern
class circuit_breaker_logger {
enum class State { CLOSED, OPEN, HALF_OPEN };
std::atomic<State> state_{State::CLOSED};
std::atomic<int> failure_count_{0};
static constexpr int FAILURE_THRESHOLD = 5;
void log(const std::string& msg) {
if (state_ == State::OPEN) {
if (should_attempt_reset()) {
state_ = State::HALF_OPEN;
} else {
return;
}
}
try {
logger_->info(msg);
if (state_ == State::HALF_OPEN) {
state_ = State::CLOSED;
failure_count_ = 0;
}
} catch (...) {
if (++failure_count_ >= FAILURE_THRESHOLD) {
state_ = State::OPEN;
}
throw;
}
}
};
Security Considerations
1. Sanitize Sensitive Data
class sanitizing_logger {
std::regex credit_card_pattern{"\\b\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}\\b"};
std::regex email_pattern{"\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b"};
std::string sanitize(const std::string& message) {
std::string result = message;
result = std::regex_replace(result, credit_card_pattern, "****-****-****-****");
result = std::regex_replace(result, email_pattern, "****@****.***");
return result;
}
public:
void log(const std::string& message) {
logger_->info(sanitize(message));
}
};
2. Access Control
class access_controlled_logger {
enum class LogAccess { READ, WRITE, ADMIN };
std::unordered_map<std::string, LogAccess> permissions_;
bool can_log(const std::string& user, log_level level) {
auto it = permissions_.find(user);
if (it == permissions_.end()) return false;
if (level == log_level::critical) {
return it->second == LogAccess::ADMIN;
}
return it->second >= LogAccess::WRITE;
}
};
3. Log Injection Prevention
std::string escape_log_injection(const std::string& input) {
std::string output;
output.reserve(input.size() * 1.2);
for (char c : input) {
switch (c) {
case '\n': output += "\\n"; break;
case '\r': output += "\\r"; break;
case '\t': output += "\\t"; break;
case '"': output += "\\\""; break;
case '\\': output += "\\\\"; break;
default:
if (std::isprint(c)) {
output += c;
} else {
output += "\\x" + to_hex(c);
}
}
}
return output;
}
Testing Strategies
1. Mock Logger for Testing
class mock_logger : public logger_interface {
std::vector<std::string> captured_logs_;
public:
void log(log_level level, const std::string& message) override {
captured_logs_.push_back(message);
}
bool contains(const std::string& expected) const {
return std::find(captured_logs_.begin(),
captured_logs_.end(),
expected) != captured_logs_.end();
}
size_t log_count() const { return captured_logs_.size(); }
void clear() { captured_logs_.clear(); }
};
TEST(ServiceTest, LogsErrors) {
auto mock_log = std::make_shared<mock_logger>();
Service service(mock_log);
service.process_invalid_data();
EXPECT_TRUE(mock_log->contains("Invalid data"));
EXPECT_EQ(mock_log->log_count(), 1);
}
2. Performance Testing
void benchmark_logger_throughput() {
auto logger = create_test_logger();
const size_t iterations = 1'000'000;
auto start = std::chrono::high_resolution_clock::now();
for (size_t i = 0; i < iterations; ++i) {
logger->info(
"Benchmark message", {{
"iteration", i}});
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
auto throughput = iterations * 1000 / duration.count();
std::cout << "Throughput: " << throughput << " msg/sec\n";
EXPECT_GT(throughput, 100'000);
}
3. Stress Testing
void stress_test_concurrent_logging() {
auto logger = create_thread_safe_logger();
const size_t num_threads = 10;
const size_t logs_per_thread = 10'000;
std::vector<std::thread> threads;
std::atomic<size_t> total_logged{0};
for (size_t t = 0; t < num_threads; ++t) {
threads.emplace_back([&, t]() {
for (size_t i = 0; i < logs_per_thread; ++i) {
logger->info(
"Thread message", {
{"thread_id", t},
{"message_id", i}
});
total_logged++;
}
});
}
for (auto& thread : threads) {
thread.join();
}
EXPECT_EQ(total_logged, num_threads * logs_per_thread);
}
Production Deployment
1. Monitoring Integration
class monitored_logger {
std::shared_ptr<metrics_collector> metrics_;
void log(log_level level, const std::string& message) {
auto start = std::chrono::high_resolution_clock::now();
logger_->log(level, message);
auto duration = std::chrono::high_resolution_clock::now() - start;
metrics_->record_log_latency(duration);
metrics_->increment_log_count(level);
if (duration > std::chrono::milliseconds(100)) {
alert_->send("High logging latency detected");
}
}
};
2. Log Rotation Strategy
auto setup_production_rotation() {
return logger_builder()
.with_file_writer("/var/log/app.log")
.with_rotation_size(100 * 1024 * 1024)
.with_rotation_time(std::chrono::hours(24))
.with_max_files(30)
.with_compression(true)
.build();
}
3. Graceful Shutdown
class application {
std::shared_ptr<logger> logger_;
void shutdown() {
logger_->info("Application shutting down");
logger_->flush();
logger_->stop();
logger_->info("Shutdown complete");
}
};
Common Pitfalls
1. Memory Leaks
class bad_service {
std::shared_ptr<logger> logger_;
bad_service() {
logger_ = std::make_shared<logger>();
logger_->set_context("service", this);
}
};
class good_service {
std::shared_ptr<logger> logger_;
good_service() {
logger_ = std::make_shared<logger>();
std::weak_ptr<good_service> weak_this = shared_from_this();
logger_->set_error_handler([weak_this](const error& e) {
if (auto self = weak_this.lock()) {
self->handle_error(e);
}
});
}
};
2. Deadlocks
class bad_example {
std::mutex mutex_;
void process() {
std::lock_guard<std::mutex> lock(mutex_);
logger_->info("Processing");
do_work();
}
};
class good_example {
std::mutex mutex_;
void process() {
logger_->info("Starting process");
{
std::lock_guard<std::mutex> lock(mutex_);
do_work();
}
logger_->info("Process complete");
}
};
3. Performance Bottlenecks
void hot_path_bad() {
for (int i = 0; i < 1000000; ++i) {
logger_->info("Processing item " + std::to_string(i));
}
}
void hot_path_good() {
auto async_logger = create_async_logger();
for (int i = 0; i < 1000000; ++i) {
async_logger->info("Processing item", {{"id", i}});
}
}
Summary
Following these best practices will help you:
- Build more reliable and maintainable logging solutions
- Achieve better performance in production environments
- Avoid common pitfalls and security issues
- Create testable and debuggable applications
- Scale your logging infrastructure effectively
Remember: Good logging is invisible when everything works but invaluable when things go wrong.
Last Updated: 2025-10-20