2#ifndef LOGIT_CPP_HEADER_LOGIT_CPP_LOGIT_LOGGERS_MDBXLOGGER_HPP_INCLUDED
3#define LOGIT_CPP_HEADER_LOGIT_CPP_LOGIT_LOGGERS_MDBXLOGGER_HPP_INCLUDED
8#include <mdbx_containers/KeyValueTable.hpp>
16#include <unordered_map>
35 LogReadError
error() const noexcept {
51 std::string
path =
"logs.mdbx";
64 std::function<void(
const std::string&)>
on_error;
97 }
catch (
const std::exception& e) {
114 void log(
const LogRecord& record,
const std::string& message)
override {
125 std::lock_guard<std::mutex> lock(
m_mutex);
132 std::vector<MdbxLogItem> batch;
133 batch.push_back(std::move(item));
138 std::unique_lock<std::mutex> lock(
m_mutex);
164 m_queue.push_back(std::move(item));
175 std::unique_lock<std::mutex> lock(
m_mutex);
176 m_cv.wait(lock, [
this]() {
183 bool expected =
false;
184 if (!
m_shutdown.compare_exchange_strong(expected,
true, std::memory_order_acq_rel)) {
189 std::lock_guard<std::mutex> lock(
m_mutex);
215 std::size_t limit = 0)
const override {
217 return result.value ? *result.value : std::vector<LogRecordSnapshot>();
224 std::size_t limit = 0)
const override {
225 LogReadResult<std::vector<LogRecordSnapshot>> result;
226 result.value.emplace();
227 if (to_ms <= from_ms) {
235 (std::numeric_limits<uint32_t>::max)());
237 std::lock_guard<std::mutex> db_lock(
m_db_mutex);
238 m_records->for_each_range(from_key, to_key,
239 [&result, limit](
const std::string&,
const Record& record) ->
bool {
241 return limit == 0 || result.value->size() < limit;
244 result.value.reset();
245 result.error = e.
error();
246 result.message = e.what();
247 }
catch (
const mdbxc::MdbxException& e) {
248 result.value.reset();
249 result.error = LogReadError::StorageError;
250 result.message = e.what();
251 }
catch (
const std::exception& e) {
252 result.value.reset();
253 result.error = LogReadError::DecodeError;
254 result.message = e.what();
256 result.value.reset();
257 result.error = LogReadError::DecodeError;
258 result.message =
"MdbxLogger: unknown read_range error";
271 int64_t period_ms = 0,
274 return result.value ? *result.value : std::vector<LogRecordSnapshot>();
280 int64_t period_ms = 0,
283 const int64_t from_ms = (period_ms > 0) ? (now_ms - period_ms) : 0;
288 if (limit > 0 && result.value->size() > limit) {
290 result.value->begin(),
291 result.value->begin() +
static_cast<std::ptrdiff_t
>(result.value->size() - limit));
294 std::reverse(result.value->begin(), result.value->end());
306 LogReadResult<PayloadView> result;
307 if (payload_id == 0) {
308 result.error = LogReadError::NotFound;
309 result.message =
"MdbxLogger: payload id is zero";
314 std::lock_guard<std::mutex> db_lock(
m_db_mutex);
315#if __cplusplus >= 201703L
318 result.value =
to_view(*value);
320 std::pair<bool, Payload> value =
m_payloads->find(payload_id);
322 result.value =
to_view(value.second);
325 result.error = e.
error();
326 result.message = e.what();
327 }
catch (
const mdbxc::MdbxException& e) {
328 result.error = LogReadError::StorageError;
329 result.message = e.what();
330 }
catch (
const std::exception& e) {
331 result.error = LogReadError::DecodeError;
332 result.message = e.what();
334 result.error = LogReadError::DecodeError;
335 result.message =
"MdbxLogger: unknown payload read error";
348 LogReadResult<std::string> result;
351 result.error = view.error;
352 result.message = view.message;
356 result.value = view.value->data;
367 result.value = std::move(output);
369 result.error = LogReadError::DecompressionError;
370 result.message =
"MdbxLogger: failed to decompress payload data";
382 LogReadResult<SessionView> result;
384 result.error = LogReadError::NotFound;
385 result.message =
"MdbxLogger: session id is zero";
390 std::lock_guard<std::mutex> db_lock(
m_db_mutex);
391#if __cplusplus >= 201703L
394 result.value =
to_view(*value);
398 result.value =
to_view(value.second);
401 result.error = e.
error();
402 result.message = e.what();
403 }
catch (
const mdbxc::MdbxException& e) {
404 result.error = LogReadError::StorageError;
405 result.message = e.what();
406 }
catch (
const std::exception& e) {
407 result.error = LogReadError::DecodeError;
408 result.message = e.what();
410 result.error = LogReadError::DecodeError;
411 result.message =
"MdbxLogger: unknown session read error";
425 return std::string();
457 m_log_level.store(
static_cast<int>(level), std::memory_order_release);
470 std::lock_guard<std::mutex> db_lock(
m_db_mutex);
471 auto txn =
m_connection->transaction(mdbxc::TransactionMode::WRITABLE);
472 if (options.include_persistent_records) {
476 if (options.include_payloads) {
479 if (options.include_sessions) {
494 std::lock_guard<std::mutex> lock(
m_mutex);
503 }
catch (
const std::exception& e) {
506 result.
message = std::string(
"MdbxLogger clear error: ") + e.what();
510 result.
message =
"MdbxLogger clear error";
516 return m_dropped.load(std::memory_order_acquire);
552 std::vector<uint8_t>
to_bytes()
const;
567 std::vector<uint8_t>
to_bytes()
const;
576 std::vector<uint8_t>
to_bytes()
const;
614 template <
typename T>
616 LogReadResult<T> result;
617 result.error = LogReadError::NotFound;
618 result.message = message;
623 std::vector<Callback> callbacks_copy;
628 callbacks_copy.push_back(kv.second);
631 for (
const auto& snapshot : snapshots) {
632 for (
const auto& cb : callbacks_copy) {
635 }
catch (
const std::exception& e) {
693 const uint32_t version = in.
read_u32();
696 LogReadError::UnsupportedVersion,
697 "MdbxLogger: unsupported session value version");
726 const uint32_t version = in.
read_u32();
729 LogReadError::UnsupportedVersion,
730 "MdbxLogger: unsupported record value version");
757 const uint32_t version = in.
read_u32();
760 LogReadError::UnsupportedVersion,
761 "MdbxLogger: unsupported payload value version");
765 const uint8_t compression = in.
read_u8();
768 LogReadError::DecodeError,
769 "MdbxLogger: unsupported payload compression");
788#if !defined(LOGIT_HAS_ZLIB)
789 throw std::runtime_error(
"MdbxLogger: gzip payload compression requested but LOGIT_WITH_GZIP is not enabled");
793#if !defined(LOGIT_HAS_ZSTD)
794 throw std::runtime_error(
"MdbxLogger: zstd payload compression requested but LOGIT_WITH_ZSTD is not enabled");
800 mdbxc::Config db_config;
802 db_config.max_dbs = 4;
803 db_config.no_subdir =
true;
804 db_config.sync_durable =
true;
814 if (!db_config.read_only && db_config.no_subdir) {
815 mdbxc::create_directories(db_config.pathname);
829 std::lock_guard<std::mutex> db_lock(
m_db_mutex);
830 auto txn =
m_connection->transaction(mdbxc::TransactionMode::WRITABLE);
858 for (
int attempt = 0; attempt < 1024; ++attempt) {
866 if (
m_sessions->insert(candidate, session, txn)) {
872 throw std::runtime_error(
"MdbxLogger: failed to allocate unique session id");
876#if __cplusplus >= 201703L
878 if (!value)
return false;
883 if (!value.first)
return false;
890 std::lock_guard<std::mutex> db_lock(
m_db_mutex);
891 auto txn =
m_connection->transaction(mdbxc::TransactionMode::WRITABLE);
906 std::vector<MdbxLogItem> batch;
910 std::unique_lock<std::mutex> lock(
m_mutex);
914 [
this]() { return m_stopping || !m_queue.empty(); });
928 batch.push_back(std::move(
m_queue.front()));
937 std::lock_guard<std::mutex> lock(
m_mutex);
949 std::vector<LogRecordSnapshot> written_snapshots;
951 std::lock_guard<std::mutex> db_lock(
m_db_mutex);
952 auto txn =
m_connection->transaction(mdbxc::TransactionMode::WRITABLE);
953 for (
size_t i = 0; i < batch.size(); ++i) {
958 }
catch (
const std::exception& e) {
996 if (
m_records->insert(key, record, txn)) {
1011 return message.substr(0, count);
1016 payload.
data = message;
1022 std::string compressed;
1032 payload.
data = std::move(compressed);
1039 for (
int attempt = 0; attempt < 1024; ++attempt) {
1045 throw std::runtime_error(
"MdbxLogger: failed to allocate unique payload id");
1049 int64_t timestamp_ms,
1051 mdbxc::Transaction& txn) {
1052 std::unordered_map<int64_t, uint32_t>::iterator it =
1059 sequence = candidate;
1060 if (candidate != (std::numeric_limits<uint32_t>::max)()) {
1067 if (candidate == (std::numeric_limits<uint32_t>::max)()) {
1068 throw std::runtime_error(
"MdbxLogger: sequence exhausted for timestamp");
1075 if (sequence == (std::numeric_limits<uint32_t>::max)()) {
1076 throw std::runtime_error(
"MdbxLogger: sequence exhausted for timestamp");
1082 static std::atomic<uint64_t> counter(0);
1084 const uint64_t n = counter.fetch_add(1, std::memory_order_relaxed) & 0xFFFFu;
1086 return id == 0 ? (n + 1) : id;
1090 m_last_log_ts.store(timestamp_ms, std::memory_order_release);
1100 if (last_mono <= 0) {
1104 return now > last_mono ? now - last_mono : 0;
1108 const uint64_t max_value =
static_cast<uint64_t
>((std::numeric_limits<int64_t>::max)());
1109 return value > max_value ? (std::numeric_limits<int64_t>::max)() :
static_cast<int64_t
>(value);
Optional interface for backends that expose stored records.
Optional interface for backends that can notify callers when a record is successfully written.
std::function< void(const LogRecordSnapshot &)> Callback
Optional buffered snapshot access returns empty results by default.
Stores formatted logs in MDBX tables with optional async batching.
std::atomic< int > m_log_level
void log(const LogRecord &record, const std::string &message) override
Queues or writes a formatted log record.
std::vector< LogRecordSnapshot > read_recent(std::size_t limit, int64_t period_ms=0, LogReadOrder order=LogReadOrder::Ascending) const override
Reads the most recent records.
MdbxLogger(const MdbxLogger &)=delete
std::deque< MdbxLogItem > m_queue
uint64_t session_id() const
Returns this logger session id.
std::mutex m_callbacks_mutex
void update_session_end()
static std::vector< uint8_t > serialize_payload(const Payload &p)
int64_t get_int_param(const LoggerParam ¶m) const override
Retrieves an integer parameter from the logger.
std::string next_record_key_locked(int64_t timestamp_ms, uint32_t &sequence, mdbxc::Transaction &txn)
uint64_t add_log_callback(Callback callback) override
Registers a callback to be invoked after each successfully written record.
void validate_compression_config() const
LogReadResult< std::vector< LogRecordSnapshot > > read_range_result(int64_t from_ms, int64_t to_ms, std::size_t limit=0) const override
Reads records and preserves storage/decode errors.
std::atomic< uint64_t > m_dropped
std::atomic< bool > m_shutdown
MdbxLogger & operator=(const MdbxLogger &)=delete
uint64_t failed_export_count() const
std::optional< SessionView > read_session(uint64_t session_id) const
Reads session metadata by id.
std::unique_ptr< PayloadTable > m_payloads
LogClearResult clear_logs(const LogClearOptions &options=LogClearOptions()) override
Clears logger-owned buffered or persisted records when supported.
MdbxLogger(const Config &config)
static Payload deserialize_payload(const void *data, size_t size)
Record write_item_locked(const MdbxLogItem &item, mdbxc::Transaction &txn)
std::atomic< uint64_t > m_next_callback_id
double get_float_param(const LoggerParam ¶m) const override
Retrieves a floating-point parameter from the logger.
std::vector< LogRecordSnapshot > read_range(int64_t from_ms, int64_t to_ms, std::size_t limit=0) const override
Reads records in [from_ms, to_ms) ordered by timestamp and sequence.
std::unique_ptr< SessionTable > m_sessions
std::condition_variable m_space_cv
std::unique_ptr< RecordTable > m_records
std::optional< PayloadView > read_payload(uint64_t payload_id) const
Reads a payload by id.
std::unordered_map< int64_t, uint32_t > m_next_sequence_by_timestamp
std::string make_payload_preview(const std::string &message) const
static std::vector< uint8_t > serialize_session(const Session &s)
uint64_t allocate_payload_id_locked(mdbxc::Transaction &txn)
LogReadResult< SessionView > read_session_result(uint64_t session_id) const
Reads session metadata by id and preserves storage/decode errors.
void report_init_error(const std::string &message) const noexcept
LogReadResult< PayloadView > read_payload_result(uint64_t payload_id) const
Reads a payload by id and preserves storage/decode errors.
std::map< uint64_t, Callback > m_callbacks
LogReadResult< std::vector< LogRecordSnapshot > > read_recent_result(std::size_t limit, int64_t period_ms=0, LogReadOrder order=LogReadOrder::Ascending) const override
Reads the most recent records and preserves storage/decode errors.
static std::vector< uint8_t > serialize_record(const Record &r)
static Record deserialize_record(const void *data, size_t size)
std::shared_ptr< mdbxc::Connection > m_connection
void ensure_storage_parent(const mdbxc::Config &db_config) const
void advance_sequence_after_collision(int64_t timestamp_ms, uint32_t sequence)
static SessionView to_view(const Session &s)
LogReadResult< std::string > read_payload_data_result(uint64_t payload_id) const
Reads and decompresses payload data while preserving failure details.
static Session deserialize_session(const void *data, size_t size)
void notify_callbacks(const std::vector< LogRecordSnapshot > &snapshots) const
void write_batch(const std::vector< MdbxLogItem > &batch)
void wait() override
Waits until accepted async records are written.
void mark_last_log(int64_t timestamp_ms)
bool remove_log_callback(uint64_t callback_id) override
Unregisters a previously added callback.
mdbxc::KeyValueTable< std::string, Record > RecordTable
uint64_t dropped_count() const
mdbxc::KeyValueTable< uint64_t, Session > SessionTable
std::string get_string_param(const LoggerParam ¶m) const override
Retrieves a string parameter from the logger.
int64_t get_time_since_last_log() const
static int64_t counter_to_int64(uint64_t value)
static LogReadResult< T > make_not_found_result(const std::string &message)
std::atomic< uint64_t > m_failed_writes
bool should_spill_payload(const std::string &message) const
mdbxc::KeyValueTable< uint64_t, Payload > PayloadTable
static LogRecordSnapshot to_log_record_snapshot(const Record &r)
void shutdown() override
Stops the worker after draining accepted records.
std::atomic< int64_t > m_last_log_mono_ts
static PayloadView to_view(const Payload &p)
bool find_session_locked(uint64_t session_id, Session &out, MDBX_txn *txn) const
void fill_payload(const std::string &message, Payload &payload)
LogLevel get_log_level() const override
Gets the minimal log level for this logger.
static uint64_t make_unique_id()
std::condition_variable m_cv
std::optional< std::string > read_payload_data(uint64_t payload_id) const
Reads and decompresses payload data by id.
std::atomic< int64_t > m_last_log_ts
int64_t get_last_log_ts() const
void set_log_level(LogLevel level) override
Sets the minimal log level for this logger.
std::string read_string()
void write_u64(uint64_t value)
const std::vector< uint8_t > & bytes() const
void write_u32(uint32_t value)
void write_i64(int64_t value)
void write_string(const std::string &value)
void write_u8(uint8_t value)
MdbxReadException(LogReadError error, const std::string &message)
LogReadError error() const noexcept
#define LOGIT_CURRENT_TIMESTAMP_MS()
Macro to get the current timestamp in milliseconds.
#define LOGIT_MONOTONIC_MS()
std::string make_mdbx_record_key(int64_t timestamp_ms, uint32_t sequence)
bool decompress_string_zstd(const std::string &input, std::string &output)
Decompress zstd-compressed bytes.
bool compress_string_gzip(const std::string &input, std::string &output, int level)
Compress a string with gzip.
bool decompress_string_gzip(const std::string &input, std::string &output)
Decompress gzip-compressed bytes.
uint64_t current_process_id()
bool compress_string_zstd(const std::string &input, std::string &output, int level)
Compress a string with zstd.
The primary namespace for the LogIt++ library.
@ LOG_LVL_TRACE
Trace level logging.
MdbxPayloadCompression
Compression algorithm used for payload rows.
@ Zstd
Store zstd-compressed payload bytes.
@ Gzip
Store gzip-compressed payload bytes.
@ None
Store payload bytes as-is.
LoggerParam
Enumeration for different logger parameters that can be retrieved.
@ 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.
LogReadOrder
Ordering for query results returned by ILogReader.
@ Descending
Newest first.
Selects which data categories a logger cleanup should remove.
Result returned by logger cleanup operations.
std::string message
Optional diagnostic or unsupported reason.
std::size_t cleared_records
Number of primary records/files cleared when available.
LogClearStatus status
Detailed cleanup status.
bool ok
True when the backend completed cleanup.
Common owning snapshot of a stored log record.
std::string message
Formatted message or payload preview.
uint32_t sequence
Per-timestamp sequence, or 0 if unused.
int64_t timestamp_ms
Log timestamp in milliseconds.
int line
Source line number.
uint64_t session_id
Owning session id, or 0 if unused.
uint64_t payload_id
Payload row id, or 0 when absent.
std::string file
Source file path.
std::string function
Source function name.
LogLevel level
Log severity level.
Stores log metadata and content.
const std::string function
Function name.
const int line
Line number in the source file.
const LogLevel log_level
Log level (severity).
const int64_t timestamp_ms
Timestamp in milliseconds.
const std::string file
Source file name.
Configuration for the MDBX logger backend.
std::size_t payload_preview_size
Bytes kept inline when a message spills.
bool store_large_payloads_separately
Store large messages in log_payloads.
std::size_t max_batch_size
Maximum records per write transaction.
uint64_t session_id
Existing session id, or 0 to auto-create.
std::size_t max_queue_size
Maximum async queue size; 0 means unlimited.
std::function< void(const std::string &)> on_error
Optional callback invoked on initialization and write errors instead of stderr.
int payload_compression_level
Compression level for gzip/zstd.
std::string path
MDBX environment path.
bool drop_on_overflow
Drop newest record when the async queue is full.
bool async
Queue records and write them from a worker thread.
std::size_t large_payload_threshold
Spill messages larger than this.
int flush_interval_ms
Worker wake interval.
std::string app_name
Optional application name stored in session metadata.
MdbxPayloadCompression payload_compression
Payload compression.
Public read-only view of a large payload.
uint64_t payload_id
Stable payload id.
std::string data
Stored payload bytes.
MdbxPayloadCompression compression
Stored compression.
static Payload from_bytes(const void *data, size_t size)
MdbxPayloadCompression compression
std::vector< uint8_t > to_bytes() const
std::vector< uint8_t > to_bytes() const
static Record from_bytes(const void *data, size_t size)
Public read-only view of session metadata.
int64_t start_time_ms
Session start timestamp.
int64_t end_time_ms
Session end timestamp, or 0 while active.
uint64_t process_id
Process id that opened the session.
uint32_t schema_version
Storage schema version.
std::string app_name
Optional application name.
static Session from_bytes(const void *data, size_t size)
std::vector< uint8_t > to_bytes() const