Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 31 additions & 7 deletions include/logit_cpp/logit/loggers/MdbxLogger.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ namespace logit {
bool store_large_payloads_separately = true;///< Store large messages in `log_payloads`.
MdbxPayloadCompression payload_compression = MdbxPayloadCompression::None; ///< Payload compression.
int payload_compression_level = 6; ///< Compression level for gzip/zstd.
std::function<void(const std::string&)> on_error; ///< Optional callback invoked on write errors instead of stderr.
std::function<void(const std::string&)> on_error; ///< Optional callback invoked on initialization and write errors instead of stderr.
};

/// \struct SessionView
Expand All @@ -70,12 +70,20 @@ namespace logit {

explicit MdbxLogger(const Config& config)
: m_config(config) {
normalize_config();
validate_compression_config();
open_storage();
m_session_id = open_session();
if (m_config.async) {
m_worker = std::thread(&MdbxLogger::worker_loop, this);
try {
normalize_config();
validate_compression_config();
open_storage();
m_session_id = open_session();
if (m_config.async) {
m_worker = std::thread(&MdbxLogger::worker_loop, this);
}
} catch (const std::exception& e) {
report_init_error(std::string("MdbxLogger initialization error: ") + e.what());
throw;
} catch (...) {
report_init_error("MdbxLogger initialization error");
throw;
}
}

Expand Down Expand Up @@ -623,12 +631,28 @@ namespace logit {
db_config.no_subdir = true;
db_config.sync_durable = true;

ensure_storage_parent(db_config);
m_connection = mdbxc::Connection::create(db_config);
m_sessions.reset(new SessionTable(m_connection, "log_sessions"));
m_records.reset(new RecordTable(m_connection, "log_records_by_time"));
m_payloads.reset(new PayloadTable(m_connection, "log_payloads"));
}

void ensure_storage_parent(const mdbxc::Config& db_config) const {
if (!db_config.read_only && db_config.no_subdir) {
mdbxc::create_directories(db_config.pathname);
}
}

void report_init_error(const std::string& message) const noexcept {
if (m_config.on_error) {
try {
m_config.on_error(message);
} catch (...) {
}
}
}

uint64_t open_session() {
std::lock_guard<std::mutex> db_lock(m_db_mutex);
auto txn = m_connection->transaction(mdbxc::TransactionMode::WRITABLE);
Expand Down
91 changes: 91 additions & 0 deletions tests/mdbx_logger_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
#include <sstream>
#include <string>
#include <vector>
#if __cplusplus >= 201703L
#include <filesystem>
#endif

namespace {

Expand All @@ -23,11 +26,44 @@ std::string make_db_path(const std::string& suffix) {
return os.str();
}

std::string make_nested_db_path(const std::string& suffix) {
std::ostringstream os;
os << logit::get_exec_dir()
<< "/mdbx_logger_test_"
<< suffix
<< "_"
<< LOGIT_CURRENT_TIMESTAMP_MS()
<< "/nested/logs.mdbx";
return os.str();
}

std::string make_nested_db_root(const std::string& path) {
#if __cplusplus >= 201703L
# if __cplusplus >= 202002L
const auto root = std::filesystem::u8path(path).parent_path().parent_path().u8string();
return std::string(root.begin(), root.end());
# else
return std::filesystem::u8path(path).parent_path().parent_path().u8string();
# endif
#else
const std::string marker = "/nested/logs.mdbx";
const std::string::size_type pos = path.rfind(marker);
return pos == std::string::npos ? std::string() : path.substr(0, pos);
#endif
}

void cleanup_db(const std::string& path) {
std::remove(path.c_str());
std::remove((path + "-lck").c_str());
}

void cleanup_path_tree(const std::string& path) {
cleanup_db(path);
#if __cplusplus >= 201703L
std::filesystem::remove_all(std::filesystem::u8path(path).parent_path().parent_path());
#endif
}

logit::LogRecord make_record(logit::LogLevel level, int64_t timestamp_ms, int line) {
return logit::LogRecord(
level,
Expand Down Expand Up @@ -223,6 +259,59 @@ void test_on_error_callback() {
cleanup_db(path);
}

void test_nested_parent_directory_created() {
const std::string path = make_nested_db_path("nested");
cleanup_path_tree(path);

{
logit::MdbxLogger::Config config;
config.path = path;
config.async = false;

logit::MdbxLogger logger(config);
logger.log(make_record(logit::LogLevel::LOG_LVL_INFO, 6100, 51), "nested-ok");

auto records = logger.read_range(6100, 6101);
assert(records.size() == 1);
assert(records[0].message == "nested-ok");
logger.shutdown();
}

cleanup_path_tree(path);
}

void test_init_error_callback_and_rethrow() {
const std::string path = make_nested_db_path("init_error");
cleanup_path_tree(path);

std::vector<std::string> errors;
logit::MdbxLogger::Config config;
config.path = path;
config.async = false;
config.on_error = [&errors](const std::string& msg) {
errors.push_back(msg);
};

const std::string blocker = make_nested_db_root(path);
FILE* blocker_file = std::fopen(blocker.c_str(), "wb");
assert(blocker_file != nullptr);
std::fclose(blocker_file);

bool thrown = false;
try {
logit::MdbxLogger logger(config);
} catch (const std::exception&) {
thrown = true;
}

assert(thrown);
assert(!errors.empty());
assert(errors[0].find("MdbxLogger initialization error") != std::string::npos);

std::remove(blocker.c_str());
cleanup_path_tree(path);
}

void test_read_range_empty_and_limits() {
const std::string path = make_db_path("range");
cleanup_db(path);
Expand Down Expand Up @@ -412,6 +501,8 @@ int main() {
#endif
test_counters_zero_for_sync_writes();
test_on_error_callback();
test_nested_parent_directory_created();
test_init_error_callback_and_rethrow();
test_read_range_empty_and_limits();
test_read_recent();
test_callback_sync();
Expand Down
Loading