Logger System 0.1.3
High-performance C++20 thread-safe logging system with asynchronous capabilities
Loading...
Searching...
No Matches
buffered_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
13
14#include <stdexcept>
15
16namespace kcenon::logger {
17
18buffered_writer::buffered_writer(std::unique_ptr<log_writer_interface> wrapped,
19 size_t max_entries,
20 std::chrono::milliseconds flush_interval)
21 : decorator_writer_base(std::move(wrapped), "buffered")
22 , max_entries_(max_entries)
23 , flush_interval_(flush_interval)
24 , last_flush_time_(std::chrono::steady_clock::now()) {
25 if (max_entries_ == 0) {
26 throw std::invalid_argument("buffered_writer: max_entries must be greater than 0");
27 }
28 buffer_.reserve(max_entries_);
29}
30
32 // Flush remaining entries - ignore errors as we can't report them
33 std::lock_guard<std::mutex> lock(mutex_);
34 if (!buffer_.empty()) {
36 }
37}
38
40 std::lock_guard<std::mutex> lock(mutex_);
41
42 // Add entry to buffer
43 buffer_.push_back(copy_entry(entry));
44 stats_.total_entries_written.fetch_add(1, std::memory_order_relaxed);
45
46 // Check if we need to flush
47 bool flush_needed = false;
48 bool flush_by_size = buffer_.size() >= max_entries_;
49 bool flush_by_time = should_flush_by_time();
50
51 if (flush_by_size) {
52 stats_.flush_on_full.fetch_add(1, std::memory_order_relaxed);
53 flush_needed = true;
54 } else if (flush_by_time) {
55 stats_.flush_on_interval.fetch_add(1, std::memory_order_relaxed);
56 flush_needed = true;
57 }
58
59 if (flush_needed) {
60 return flush_buffer_unsafe();
61 }
62
63 return common::ok();
64}
65
67 std::lock_guard<std::mutex> lock(mutex_);
68
69 if (!buffer_.empty()) {
70 stats_.manual_flushes.fetch_add(1, std::memory_order_relaxed);
71 }
72
73 return flush_buffer_unsafe();
74}
75
77 // Write all buffered entries to wrapped writer
78 for (const auto& entry : buffer_) {
79 auto result = wrapped().write(entry);
80 if (result.is_err()) {
81 // Clear buffer even on error to avoid infinite retry
82 buffer_.clear();
83 last_flush_time_ = std::chrono::steady_clock::now();
84 return result;
85 }
86 }
87
88 buffer_.clear();
89 last_flush_time_ = std::chrono::steady_clock::now();
90
91 if (!buffer_.empty() || stats_.total_flushes > 0) {
92 stats_.total_flushes.fetch_add(1, std::memory_order_relaxed);
93 }
94
95 // Delegate to wrapped writer's flush
96 return wrapped().flush();
97}
98
100 if (flush_interval_.count() == 0) {
101 return false; // Time-based flush disabled
102 }
103
104 auto now = std::chrono::steady_clock::now();
105 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_flush_time_);
106 return elapsed >= flush_interval_;
107}
108
110 if (entry.location) {
111 return log_entry(entry.level,
112 entry.message.to_string(),
113 entry.location->file.to_string(),
114 entry.location->line,
115 entry.location->function.to_string(),
116 entry.timestamp);
117 }
118 return log_entry(entry.level, entry.message.to_string(), entry.timestamp);
119}
120
122 std::lock_guard<std::mutex> lock(mutex_);
123 return buffer_.size();
124}
125
126size_t buffered_writer::get_max_entries() const noexcept {
127 return max_entries_;
128}
129
130std::chrono::milliseconds buffered_writer::get_flush_interval() const noexcept {
131 return flush_interval_;
132}
133
135 return stats_;
136}
137
139 stats_.total_entries_written.store(0, std::memory_order_relaxed);
140 stats_.total_flushes.store(0, std::memory_order_relaxed);
141 stats_.flush_on_full.store(0, std::memory_order_relaxed);
142 stats_.flush_on_interval.store(0, std::memory_order_relaxed);
143 stats_.manual_flushes.store(0, std::memory_order_relaxed);
144}
145
146std::unique_ptr<buffered_writer> make_buffered_writer(
147 std::unique_ptr<log_writer_interface> writer,
148 size_t max_entries,
149 std::chrono::milliseconds flush_interval) {
150 return std::make_unique<buffered_writer>(std::move(writer), max_entries, flush_interval);
151}
152
153} // namespace kcenon::logger
Decorator that provides buffering for wrapped log writers.
std::vector< log_entry > buffer_
std::chrono::milliseconds get_flush_interval() const noexcept
Get the configured flush interval.
size_t get_buffer_count() const
Get the current number of buffered entries.
common::VoidResult flush_buffer_unsafe()
Flush buffer without acquiring mutex (caller must hold lock)
size_t get_max_entries() const noexcept
Get the maximum buffer size.
~buffered_writer() override
Destructor - flushes any remaining buffered entries.
common::VoidResult flush() override
Flush all buffered entries to the wrapped writer.
common::VoidResult write(const log_entry &entry) override
Write a log entry to the buffer.
const stats & get_stats() const noexcept
Get buffered writer statistics.
void reset_stats()
Reset statistics counters.
bool should_flush_by_time() const
Check if time-based flush is needed.
buffered_writer(std::unique_ptr< log_writer_interface > wrapped, size_t max_entries=DEFAULT_BUFFER_SIZE, std::chrono::milliseconds flush_interval=DEFAULT_FLUSH_INTERVAL)
Construct a buffered writer.
std::chrono::steady_clock::time_point last_flush_time_
static log_entry copy_entry(const log_entry &entry)
Copy a log entry for buffer storage.
std::chrono::milliseconds flush_interval_
Abstract base class for decorator pattern log writers.
log_writer_interface & wrapped() noexcept
Access the wrapped writer (non-const)
virtual common::VoidResult flush()=0
Flush any buffered data.
virtual common::VoidResult write(const log_entry &entry)=0
Write a log entry.
std::string to_string() const
Convert to std::string.
VoidResult ok()
std::unique_ptr< buffered_writer > make_buffered_writer(std::unique_ptr< log_writer_interface > writer, size_t max_entries=buffered_writer::DEFAULT_BUFFER_SIZE, std::chrono::milliseconds flush_interval=buffered_writer::DEFAULT_FLUSH_INTERVAL)
Factory function to create a buffered writer.
Statistics for the buffered writer.
std::atomic< uint64_t > total_entries_written
std::atomic< uint64_t > flush_on_interval
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