summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDirk-Jan C. Binnema <djcb@djcbsoftware.nl>2023-04-08 10:26:15 +0300
committerDirk-Jan C. Binnema <djcb@djcbsoftware.nl>2023-04-08 10:34:16 +0300
commite29dc2d9d83c015227e937da41617c15e47d5f4d (patch)
tree5a450e26b27adce94aa6b65769aade08d1675b02
parentf6cf7720e090a6eb4f07d71e1ee049da932a870e (diff)
mu-logger: Add lock for file-logger and stress test
Add a lock to the file logger and add a unit-test to stress test it. (It does fail without the lock, and passed with it). Should help for: #2469.
-rw-r--r--lib/utils/meson.build6
-rw-r--r--lib/utils/mu-logger.cc63
-rw-r--r--lib/utils/mu-logger.hh8
3 files changed, 71 insertions, 6 deletions
diff --git a/lib/utils/meson.build b/lib/utils/meson.build
index da8bca2..63e8dd2 100644
--- a/lib/utils/meson.build
+++ b/lib/utils/meson.build
@@ -65,4 +65,10 @@ test('test-utils-file',
cpp_args: ['-DBUILD_TESTS'],
dependencies: [glib_dep, config_h_dep, lib_mu_utils_dep]))
+test('test-logger',
+ executable('test-logger', 'mu-logger.cc',
+ install: false,
+ cpp_args: ['-DBUILD_TESTS'],
+ dependencies: [glib_dep, lib_mu_utils_dep]))
+
subdir('tests')
diff --git a/lib/utils/mu-logger.cc b/lib/utils/mu-logger.cc
index 4a661b5..882f3ba 100644
--- a/lib/utils/mu-logger.cc
+++ b/lib/utils/mu-logger.cc
@@ -28,6 +28,9 @@
#include <fstream>
#include <cstring>
+#include <thread>
+#include <mutex>
+
#include "mu-logger.hh"
using namespace Mu;
@@ -36,6 +39,7 @@ static bool MuLogInitialized = false;
static Mu::Logger::Options MuLogOptions;
static std::ofstream MuStream;
static auto MaxLogFileSize = 1000 * 1024;
+static std::mutex logger_mtx;
static std::string MuLogPath;
@@ -84,6 +88,8 @@ maybe_rotate_logfile()
static GLogWriterOutput
log_file(GLogLevelFlags level, const GLogField* fields, gsize n_fields, gpointer user_data)
{
+ std::lock_guard lock{logger_mtx};
+
if (!maybe_open_logfile())
return G_LOG_WRITER_UNHANDLED;
@@ -124,7 +130,6 @@ Mu::Logger::make(const std::string& path, Mu::Logger::Options opts)
return Ok(Logger(path, opts));
}
-
Mu::Logger::Logger(const std::string& path, Mu::Logger::Options opts)
{
if (g_getenv("MU_LOG_STDOUTERR"))
@@ -147,7 +152,8 @@ Mu::Logger::Logger(const std::string& path, Mu::Logger::Options opts)
}
// log to the journal, or, if not available to a file.
- if (log_journal(level, fields, n_fields, user_data) != G_LOG_WRITER_HANDLED)
+ if (any_of(MuLogOptions & Options::File) ||
+ log_journal(level, fields, n_fields, user_data) != G_LOG_WRITER_HANDLED)
return log_file(level, fields, n_fields, user_data);
else
return G_LOG_WRITER_HANDLED;
@@ -172,3 +178,56 @@ Logger::~Logger()
MuLogInitialized = false;
}
+
+
+#ifdef BUILD_TESTS
+#include <vector>
+#include <atomic>
+
+#include "mu-test-utils.hh"
+
+static void
+test_logger_threads(void)
+{
+ const auto testpath{test_random_tmpdir() + "/test.log"};
+ g_message("log-file: %s", testpath.c_str());
+
+ auto logger = Logger::make(testpath.c_str(), Logger::Options::File | Logger::Options::Debug);
+ assert_valid_result(logger);
+
+ const auto thread_num = 16;
+ std::atomic<bool> running = true;
+
+ std::vector<std::thread> threads;
+
+ /* log to the logger file from many threass */
+ for (auto n = 0; n != thread_num; ++n)
+ threads.emplace_back(
+ std::thread([n,&running]{
+ while (running) {
+ g_debug("log message from thread <%d>", n);
+ std::this_thread::yield();
+ }
+ }));
+
+ using namespace std::chrono_literals;
+ std::this_thread::sleep_for(1s);
+ running = false;
+
+ for (auto n = 0; n != 16; ++n)
+ if (threads[n].joinable())
+ threads[n].join();
+}
+
+
+int
+main(int argc, char* argv[])
+{
+ mu_test_init(&argc, &argv);
+
+ g_test_add_func("/utils/logger", test_logger_threads);
+
+ return g_test_run();
+}
+
+#endif /*BUILD_TESTS*/
diff --git a/lib/utils/mu-logger.hh b/lib/utils/mu-logger.hh
index 54290db..6024e28 100644
--- a/lib/utils/mu-logger.hh
+++ b/lib/utils/mu-logger.hh
@@ -1,5 +1,5 @@
/*
-** Copyright (C) 2020-2022 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
+** Copyright (C) 2020-2023 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
**
** This program is free software; you can redistribute it and/or modify it
** under the terms of the GNU General Public License as published by the
@@ -39,12 +39,12 @@ struct Logger {
enum struct Options {
None = 0, /**< Nothing specific */
StdOutErr = 1 << 1, /**< Log to stdout/stderr */
- Debug = 1 << 2, /**< Include debug-level logs */
+ File = 1 << 2, /**< Force logging to file, even if journal available */
+ Debug = 1 << 3, /**< Include debug-level logs */
};
-
/**
- * Initialize the logging system.
+ * Initialize the logging sub-system.
*
* Note that the path is only used if structured logging fails --
* practically, it goes to the file if there's no systemd/journald.