LogIt++
Header-only, macro-first C++ logging library
Loading...
Searching...
No Matches
OtlpPayloadLogger.hpp
Go to the documentation of this file.
1#pragma once
2#ifndef LOGIT_CPP_HEADER_LOGIT_CPP_LOGIT_LOGGERS_OTLPPAYLOADLOGGER_HPP_INCLUDED
3#define LOGIT_CPP_HEADER_LOGIT_CPP_LOGIT_LOGGERS_OTLPPAYLOADLOGGER_HPP_INCLUDED
4
7
8#ifndef LOGIT_WITH_OTLP
9# error "OtlpPayloadLogger requires LOGIT_WITH_OTLP=1. Enable LOGIT_WITH_OTLP in CMake."
10#endif
11
12#include "ILogger.hpp"
16
17#include <atomic>
18#include <chrono>
19#include <condition_variable>
20#include <cstdint>
21#include <deque>
22#include <functional>
23#include <limits>
24#include <mutex>
25#include <string>
26#include <thread>
27#include <vector>
28
29namespace logit {
30
37 class OtlpPayloadLogger final : public ILogger {
38 public:
39 struct Config {
41 std::function<void(std::string)> on_payload;
42 bool async = true;
43 std::size_t max_payload_bytes = 1024 * 1024;
44 std::size_t max_batch_size = 256;
45 std::size_t max_queue_size = 1024;
46 bool drop_on_overflow = true;
47 unsigned export_interval_ms = 100;
48 };
49
52
55 explicit OtlpPayloadLogger(const Config& config)
56 : m_config(config) {
57 if (m_config.async) {
58 m_worker = std::thread(&OtlpPayloadLogger::worker_loop, this);
59 }
60 }
61
63 ~OtlpPayloadLogger() override {
64 stop();
65 }
66
69
73 void log(const LogRecord& record, const std::string& message) override {
74 if (!m_config.on_payload) {
75 return;
76 }
77
79
80 OtlpLogItem item;
81 item.record = make_otlp_record_snapshot(record);
82 item.message = message;
83
84 if (!m_config.async) {
85 {
86 std::lock_guard<std::mutex> lock(m_mutex);
87 if (m_stopping) {
88 ++m_dropped;
89 return;
90 }
91 }
92 std::vector<OtlpLogItem> batch;
93 batch.push_back(item);
96 if (m_config.on_payload) {
97 for (auto& chunk : chunks) {
98 try {
99 m_config.on_payload(std::move(chunk));
100 } catch (...) {
102 }
103 }
104 }
105 return;
106 }
107
108 std::unique_lock<std::mutex> lock(m_mutex);
109 if (m_stopping) {
110 ++m_dropped;
111 return;
112 }
113
114 if (m_queue.size() >= m_config.max_queue_size) {
116 ++m_dropped;
117 return;
118 }
119
120 m_space_cv.wait(lock, [this]() {
121 return m_stopping || m_queue.size() < m_config.max_queue_size;
122 });
123
124 if (m_stopping) {
125 ++m_dropped;
126 return;
127 }
128 }
129
130 m_queue.push_back(item);
131 lock.unlock();
132 m_cv.notify_one();
133 }
134
136 void wait() override {
137 if (!m_config.async) {
138 return;
139 }
140
141 std::unique_lock<std::mutex> lock(m_mutex);
142 m_cv.wait(lock, [this]() {
143 return m_queue.empty() && m_idle;
144 });
145 }
146
148 void shutdown() override {
149 stop();
150 }
151
155 std::string get_string_param(const LoggerParam& param) const override {
156 switch (param) {
157 case LoggerParam::LastLogTimestamp: return std::to_string(get_last_log_ts());
158 case LoggerParam::TimeSinceLastLog: return std::to_string(get_time_since_last_log());
159 case LoggerParam::DroppedLogCount: return std::to_string(dropped_count());
160 case LoggerParam::FailedExportCount: return std::to_string(failed_export_count());
161 default:
162 break;
163 }
164 return std::string();
165 }
166
170 int64_t get_int_param(const LoggerParam& param) const override {
171 switch (param) {
176 default:
177 break;
178 }
179 return 0;
180 }
181
185 double get_float_param(const LoggerParam& param) const override {
186 switch (param) {
188 return static_cast<double>(get_last_log_ts()) / 1000.0;
190 return static_cast<double>(get_time_since_last_log()) / 1000.0;
192 return static_cast<double>(dropped_count());
194 return static_cast<double>(failed_export_count());
195 default:
196 break;
197 }
198 return 0.0;
199 }
200
203 void set_log_level(LogLevel level) override {
204 m_log_level = static_cast<int>(level);
205 }
206
209 LogLevel get_log_level() const override {
210 return static_cast<LogLevel>(m_log_level.load());
211 }
212
215 uint64_t dropped_count() const {
216 return m_dropped.load();
217 }
218
221 uint64_t failed_export_count() const {
222 return m_failed_exports.load();
223 }
224
225 private:
227
228 std::mutex m_mutex;
229 std::condition_variable m_cv;
230 std::condition_variable m_space_cv;
231 std::deque<OtlpLogItem> m_queue;
232 bool m_stopping = false;
233 bool m_idle = true;
234
235 std::thread m_worker;
236
237 std::atomic<int> m_log_level = ATOMIC_VAR_INIT(static_cast<int>(LogLevel::LOG_LVL_TRACE));
238 std::atomic<int64_t> m_last_log_ts = ATOMIC_VAR_INIT(0);
239 std::atomic<uint64_t> m_dropped = ATOMIC_VAR_INIT(0);
240 std::atomic<uint64_t> m_failed_exports = ATOMIC_VAR_INIT(0);
241
243 void worker_loop() {
244 while (true) {
245 std::vector<OtlpLogItem> batch;
246 batch.reserve(m_config.max_batch_size);
247
248 {
249 std::unique_lock<std::mutex> lock(m_mutex);
250 m_cv.wait_for(
251 lock,
252 std::chrono::milliseconds(m_config.export_interval_ms),
253 [this]() {
254 return m_stopping || !m_queue.empty();
255 });
256
257 if (m_stopping && m_queue.empty()) {
258 m_idle = true;
259 m_cv.notify_all();
260 return;
261 }
262
263 if (m_queue.empty()) {
264 continue;
265 }
266
267 m_idle = false;
268
269 while (!m_queue.empty() && batch.size() < m_config.max_batch_size) {
270 batch.push_back(m_queue.front());
271 m_queue.pop_front();
272 }
273
274 m_space_cv.notify_all();
275 }
276
277 if (!batch.empty()) {
280 if (m_config.on_payload) {
281 for (auto& chunk : chunks) {
282 try {
283 m_config.on_payload(std::move(chunk));
284 } catch (...) {
286 }
287 }
288 }
289 }
290
291 {
292 std::lock_guard<std::mutex> lock(m_mutex);
293 m_idle = true;
294 }
295 m_cv.notify_all();
296 }
297 }
298
300 void stop() {
301 {
302 std::lock_guard<std::mutex> lock(m_mutex);
303 if (m_stopping) {
304 return;
305 }
306 m_stopping = true;
307 }
308
309 m_cv.notify_all();
310 m_space_cv.notify_all();
311
312 if (m_worker.joinable()) {
313 m_worker.join();
314 }
315 }
316
319 int64_t get_last_log_ts() const {
320 return m_last_log_ts.load();
321 }
322
325 int64_t get_time_since_last_log() const {
326 const int64_t last = get_last_log_ts();
327 if (last <= 0) {
328 return 0;
329 }
330 const int64_t now = LOGIT_CURRENT_TIMESTAMP_MS();
331 return now > last ? now - last : 0;
332 }
333
337 static int64_t counter_to_int64(uint64_t value) {
338 const uint64_t max_value = static_cast<uint64_t>((std::numeric_limits<int64_t>::max)());
339 return value > max_value ? (std::numeric_limits<int64_t>::max)() : static_cast<int64_t>(value);
340 }
341 };
342
343} // namespace logit
344
345#endif // LOGIT_CPP_HEADER_LOGIT_CPP_LOGIT_LOGGERS_OTLPPAYLOADLOGGER_HPP_INCLUDED
Defines the interface for loggers used in the logging system.
Defines serialization-related configuration shared by OTLP loggers.
Defines OTLP/HTTP JSON serialization helpers for logs.
Splits oversized OTLP log batches into multiple JSON payload chunks.
Optional buffered snapshot access returns empty results by default.
Definition ILogger.hpp:47
Exports logs as OTLP JSON payloads via a user-provided callback.
int64_t get_last_log_ts() const
Returns last log timestamp.
void set_log_level(LogLevel level) override
Sets minimal log level for this logger.
void shutdown() override
Stops the OTLP worker after draining queued items.
OtlpPayloadLogger & operator=(const OtlpPayloadLogger &)=delete
std::atomic< int64_t > m_last_log_ts
std::atomic< uint64_t > m_dropped
void log(const LogRecord &record, const std::string &message) override
Queues or exports a log message.
~OtlpPayloadLogger() override
Stops worker and drains queue.
int64_t get_time_since_last_log() const
Returns elapsed time since last log.
OtlpPayloadLogger(const Config &config)
Constructs OTLP payload logger with custom configuration.
static int64_t counter_to_int64(uint64_t value)
Converts unsigned counter value to int64_t with saturation.
void stop()
Stops worker and drains remaining queue.
std::condition_variable m_cv
double get_float_param(const LoggerParam &param) const override
Retrieves a floating-point parameter from the logger.
int64_t get_int_param(const LoggerParam &param) const override
Retrieves an integer parameter from the logger.
std::deque< OtlpLogItem > m_queue
OtlpPayloadLogger()
Constructs OTLP payload logger with default configuration.
void worker_loop()
Worker thread loop.
void wait() override
Waits until queue is empty and worker is idle.
LogLevel get_log_level() const override
Gets minimal log level for this logger.
std::atomic< uint64_t > m_failed_exports
OtlpPayloadLogger(const OtlpPayloadLogger &)=delete
uint64_t failed_export_count() const
Returns number of failed export attempts.
uint64_t dropped_count() const
Returns number of dropped records.
std::string get_string_param(const LoggerParam &param) const override
Retrieves a string parameter from the logger.
std::condition_variable m_space_cv
#define LOGIT_CURRENT_TIMESTAMP_MS()
Macro to get the current timestamp in milliseconds.
Definition config.hpp:84
The primary namespace for the LogIt++ library.
LogLevel
Logging levels.
Definition enums.hpp:15
@ LOG_LVL_TRACE
Trace level logging.
std::vector< std::string > build_otlp_logs_json_payload_chunks(const std::vector< OtlpLogItem > &batch, const OtlpJsonFormatConfig &format, std::size_t max_payload_bytes)
Builds OTLP JSON payload chunks from a batch, splitting when a chunk would exceed max_payload_bytes.
OtlpRecordSnapshot make_otlp_record_snapshot(const LogRecord &record)
Creates an owned snapshot from a LogRecord.
LoggerParam
Enumeration for different logger parameters that can be retrieved.
Definition enums.hpp:47
@ TimeSinceLastLog
The time elapsed since the last log in seconds.
@ LastLogTimestamp
The timestamp of the last log.
@ DroppedLogCount
Number of log records dropped by the backend.
@ FailedExportCount
Number of failed export attempts reported by the backend.
Stores log metadata and content.
Definition LogRecord.hpp:17
const int64_t timestamp_ms
Timestamp in milliseconds.
Definition LogRecord.hpp:19
Serialization settings for OTLP JSON payload construction.
Owns one formatted log message and its structured metadata.
OtlpRecordSnapshot record
Structured log metadata snapshot.
std::string message
Formatted message body.
std::function< void(std::string)> on_payload