Logger System 0.1.3
High-performance C++20 thread-safe logging system with asynchronous capabilities
Loading...
Searching...
No Matches
critical_writer.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
9#include <iostream>
10#include <sstream>
11#include <iomanip>
12#include <ctime>
13
14#ifdef _WIN32
15#include <io.h> // For _flushall()
16#include <stdio.h> // For _flushall()
17#elif defined(__unix__) || defined(__APPLE__)
18#include <unistd.h>
19#include <fcntl.h>
20#endif
21
22namespace kcenon::logger {
23
25 log_writer_ptr wrapped_writer,
27)
28 : config_(std::move(config))
29 , wrapped_writer_(std::move(wrapped_writer))
30{
31 if (!wrapped_writer_) {
32 throw std::invalid_argument("Wrapped writer cannot be null");
33 }
34
35 // Initialize write-ahead log if enabled
37 auto wal_result = utils::try_open_operation([&]() -> common::VoidResult {
38 wal_stream_ = std::make_unique<std::ofstream>(
39 config_.wal_path,
40 std::ios::app | std::ios::binary
41 );
42
43 auto check = utils::check_condition(
44 wal_stream_->is_open(),
45 logger_error_code::file_open_failed,
46 "Failed to open WAL: " + config_.wal_path
47 );
48
49 if (check.is_err()) {
50 wal_stream_.reset();
51 return check;
52 }
53
54 return common::ok();
55 });
56
57 if (wal_result.is_err()) {
58 std::cerr << "[critical_writer] WAL initialization failed: "
59 << wal_result.error().message << std::endl;
60 wal_stream_.reset();
61 }
62 }
63
64}
65
66critical_writer::~critical_writer() {
67 shutting_down_.store(true);
68
69 // Final flush - use safe operation to log any errors
70 utils::safe_destructor_result_operation("final_flush", [this]() {
71 return flush();
72 });
73
74 // Close WAL with proper error logging
75 if (wal_stream_) {
76 utils::safe_destructor_operation("wal_close", [this]() {
77 wal_stream_->flush();
78 wal_stream_->close();
79 });
80 }
81}
82
83common::VoidResult critical_writer::write(const log_entry& entry) {
84 // Convert log_level from logger_system to common::interfaces
85 auto level = static_cast<common::interfaces::log_level>(static_cast<int>(entry.level));
86
87 // Check if we're in emergency shutdown (signal received)
88 if (shutting_down_.load(std::memory_order_acquire)) {
89 // In shutdown mode, all writes become critical to ensure they're flushed
90 // before process termination
91 std::lock_guard<std::mutex> lock(critical_mutex_);
92
93 auto result = wrapped_writer_->write(entry);
94 if (result.is_ok()) {
95 wrapped_writer_->flush();
96 stats_.total_flushes.fetch_add(1, std::memory_order_relaxed);
97 }
98 return result;
99 }
100
101 // Check if this is a critical level
102 const bool is_critical = is_critical_level(level);
103
104 if (is_critical) {
105 // Acquire exclusive lock for critical writes
106 std::lock_guard<std::mutex> lock(critical_mutex_);
107
108 // Write to WAL first (if enabled)
109 if (wal_stream_ && wal_stream_->is_open()) {
110 write_to_wal(entry);
111 stats_.wal_writes.fetch_add(1, std::memory_order_relaxed);
112 }
113
114 // Write to wrapped writer
115 auto result = wrapped_writer_->write(entry);
116 if (result.is_err()) {
117 return result;
118 }
119
120 // Force flush immediately
121 auto flush_result = wrapped_writer_->flush();
122 stats_.total_flushes.fetch_add(1, std::memory_order_relaxed);
123
124 // Sync file descriptor if configured
125 if (config_.sync_on_critical) {
126 sync_file_descriptor();
127 stats_.sync_calls.fetch_add(1, std::memory_order_relaxed);
128 }
129
130 stats_.total_critical_writes.fetch_add(1, std::memory_order_relaxed);
131 return flush_result;
132 }
133
134 // Non-critical: delegate to wrapped writer normally
135 return wrapped_writer_->write(entry);
136}
137
138common::VoidResult critical_writer::flush() {
139 std::lock_guard<std::mutex> lock(critical_mutex_);
140
141 // Flush WAL
142 if (wal_stream_ && wal_stream_->is_open()) {
143 wal_stream_->flush();
144 }
145
146 // Flush wrapped writer
147 auto result = wrapped_writer_->flush();
148 stats_.total_flushes.fetch_add(1, std::memory_order_relaxed);
149
150 return result;
151}
152
153bool critical_writer::is_healthy() const {
154 // Check WAL health
155 if (config_.write_ahead_log && (!wal_stream_ || !wal_stream_->good())) {
156 return false;
157 }
158
159 return wrapped_writer_->is_healthy();
160}
161
162std::string critical_writer::get_name() const {
163 return "critical_" + wrapped_writer_->get_name();
164}
165
166void critical_writer::set_use_color(bool use_color) {
167 // Color setting is now handled by formatter, not writer interface
168 // This method is kept for backward compatibility but is deprecated
169 (void)use_color; // Suppress unused parameter warning
170}
171
172void critical_writer::set_force_flush_on_critical(bool enable) {
173 config_.force_flush_on_critical = enable;
174}
175
176bool critical_writer::is_critical_level(common::interfaces::log_level level) const {
177 // Critical and fatal always require immediate flush
178 if (level >= common::interfaces::log_level::critical) {
179 return config_.force_flush_on_critical;
180 }
181
182 // Error level if configured
183 if (level == common::interfaces::log_level::error) {
184 return config_.force_flush_on_error;
185 }
186
187 return false;
188}
189
190void critical_writer::write_to_wal(const log_entry& entry) {
191 if (!wal_stream_ || !wal_stream_->is_open()) {
192 return;
193 }
194
195 try {
196 // Convert log_level
197 auto level = static_cast<common::interfaces::log_level>(static_cast<int>(entry.level));
198
199 // Extract source location
200 std::string file = entry.location ? entry.location->file.to_string() : "";
201 int line = entry.location ? entry.location->line : 0;
202 std::string function = entry.location ? entry.location->function.to_string() : "";
203
204 // Format: [timestamp] [LEVEL] [file:line:function] message\n
205 auto time_t = std::chrono::system_clock::to_time_t(entry.timestamp);
206 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
207 entry.timestamp.time_since_epoch()
208 ).count() % 1000;
209
210 std::ostringstream oss;
211 oss << "[" << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S")
212 << "." << std::setfill('0') << std::setw(3) << ms << "] "
213 << "[" << utils::string_utils::level_to_string(level) << "] "
214 << "[" << file << ":" << line << ":" << function << "] "
215 << entry.message.to_string() << "\n";
216
217 *wal_stream_ << oss.str();
218 wal_stream_->flush();
219
220 } catch (const std::exception& e) {
221 std::cerr << "[critical_writer] WAL write exception: "
222 << e.what() << std::endl;
223 }
224}
225
226void critical_writer::sync_file_descriptor() {
227#ifdef _WIN32
228 // Windows: Use _commit() to flush file buffers to disk
229 // Note: std::ofstream doesn't expose HANDLE, so we flush the stream
230 // For a complete implementation, would need native Windows file handling
231 if (wal_stream_) {
232 wal_stream_->flush();
233 }
234 // Could use _flushall() to flush all open streams, but it's global
235 ::_flushall();
236#elif defined(__unix__) || defined(__APPLE__)
237 // POSIX: Use fsync() to synchronize file to disk
238 // Get file descriptor from wrapped writer if it's a file writer
239 // This is a simplified implementation - actual implementation would need
240 // to query the wrapped writer for its file descriptor
241
242 // For now, sync stdout/stderr as fallback
243 ::fsync(STDOUT_FILENO);
244 ::fsync(STDERR_FILENO);
245
246 // Sync WAL
247 if (wal_stream_) {
248 wal_stream_->flush();
249 // Note: std::ofstream doesn't expose fd, would need native file handling
250 }
251#endif
252}
253
254// ============================================================================
255// hybrid_writer implementation
256// ============================================================================
257
258hybrid_writer::hybrid_writer(
259 log_writer_ptr wrapped_writer,
260 critical_writer_config critical_config,
261 std::size_t async_queue_size
262)
263 : async_queue_size_(async_queue_size)
264{
265 // Wrap in critical_writer first
266 critical_writer_ = std::make_unique<critical_writer>(
267 std::move(wrapped_writer),
268 std::move(critical_config)
269 );
270}
271
273
275 // Critical writer handles both sync (for critical) and async (for normal)
276 return critical_writer_->write(entry);
277}
278
282
284 return critical_writer_->is_healthy();
285}
286
287std::string hybrid_writer::get_name() const {
288 return "hybrid_" + critical_writer_->get_name();
289}
290
291void hybrid_writer::set_use_color(bool use_color) {
292 // Color setting is now handled by formatter, not writer interface
293 // This method is kept for backward compatibility but is deprecated
294 (void)use_color; // Suppress unused parameter warning
295}
296
297} // namespace kcenon::logger
bool use_color() const
Get current color output setting.
critical_writer_config config_
Configuration.
critical_writer(log_writer_ptr wrapped_writer, critical_writer_config config={})
Constructor.
log_writer_ptr wrapped_writer_
Wrapped writer.
std::string get_name() const override
bool is_healthy() const override
Check if the writer is healthy and operational.
common::VoidResult write(const log_entry &entry) override
Write a log entry.
common::VoidResult flush() override
Flush any buffered data.
void set_use_color(bool use_color) override
Set whether to use color output (if supported)
std::string to_string() const
Convert to std::string.
Synchronous wrapper for critical log messages to prevent loss.
Error codes specific to the logger system.
Structured error context for debugging log system failures.
std::unique_ptr< log_writer_interface > log_writer_ptr
Type alias for writer unique pointer.
String utility functions for log formatting and conversion.
Configuration for critical log writer.
bool write_ahead_log
Enable write-ahead logging for maximum durability (default: false)
Represents a single log entry with all associated metadata.
Definition log_entry.h:155
std::optional< source_location > location
Optional source code location information.
Definition log_entry.h:183
log_level level
Severity level of the log message.
Definition log_entry.h:162
small_string_256 message
The actual log message.
Definition log_entry.h:169
std::chrono::system_clock::time_point timestamp
Timestamp when the log entry was created.
Definition log_entry.h:175