Skip to content

Commit

Permalink
Add overload to create_or_get_logger for creating a logger with inher…
Browse files Browse the repository at this point in the history
…ited options.
  • Loading branch information
odygrd committed Sep 27, 2024
1 parent c73dd6d commit 0b6cadb
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@
- Introduced `SignalHandlerOptions` to simplify and unify the API. `Backend::start_with_signal_handler` is now
deprecated,
replaced by a new `Backend::start` overload that accepts `SignalHandlerOptions` for enabling signal handling.
- Added a new overload to `create_or_get_logger` to create a logger that inherits configuration options from a specified
logger. ([#596](https://github.com/odygrd/quill/issues/596))

## v7.2.2

Expand Down
17 changes: 17 additions & 0 deletions include/quill/Frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,23 @@ class FrontendImpl
pattern_formatter_options, clock_source, user_clock);
}

/**
* @brief Creates a new logger or retrieves an existing one that shares the same options as the specified logger.
*
* This function allows you to create or obtain a logger identified by `logger_name`. If a logger with the
* same name already exists, its configuration options will be used. If it does not exist, a new logger
* will be created with the same options as the provided `source_logger`.
*
* @param logger_name The name of the logger to create or retrieve.
* @param source_logger The logger from which to copy the configuration options.
* @return A pointer to the logger instance, either newly created or retrieved.
*/
static logger_t* create_or_get_logger(std::string const& logger_name, detail::LoggerBase* source_logger = nullptr)
{
return _cast_to_logger(
detail::LoggerManager::instance().create_or_get_logger<logger_t>(logger_name, source_logger));
}

/**
* @brief Asynchronously removes the specified logger.
* When a logger is removed, any files associated with its sinks are also closed.
Expand Down
2 changes: 2 additions & 0 deletions include/quill/core/LoggerBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ namespace detail
{
class BackendWorker;
class BacktraceStorage;
class LoggerManager;

/***/
class LoggerBase
Expand Down Expand Up @@ -124,6 +125,7 @@ class LoggerBase

protected:
friend class BackendWorker;
friend class LoggerManager;

static inline QUILL_THREAD_LOCAL ThreadContext* thread_context = nullptr; /* Set and accessed by the frontend */
std::shared_ptr<PatternFormatter> pattern_formatter; /* The backend thread will set this once, we never access it on the frontend */
Expand Down
13 changes: 13 additions & 0 deletions include/quill/core/LoggerManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,19 @@ class LoggerManager
return logger_ptr;
}

/***/
template <typename TLogger>
LoggerBase* create_or_get_logger(std::string const& logger_name, LoggerBase* source_logger)
{
if (!source_logger)
{
return get_logger(logger_name);
}

return create_or_get_logger<TLogger>(logger_name, source_logger->sinks, source_logger->pattern_formatter_options,
source_logger->clock_source, source_logger->user_clock);
}

/***/
void remove_logger(LoggerBase* logger)
{
Expand Down
1 change: 1 addition & 0 deletions test/integration_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ quill_add_test(TEST_RotatingSinkKeepOldest RotatingSinkKeepOldestTest.cpp)
quill_add_test(TEST_RotatingSinkOverwriteOldest RotatingSinkOverwriteOldestTest.cpp)
quill_add_test(TEST_SignalHandler SignalHandlerTest.cpp)
quill_add_test(TEST_SignalHandlerLogger SignalHandlerLoggerTest.cpp)
quill_add_test(TEST_SingleFrontendThreadMultipleLoggers SingleFrontendThreadMultipleLoggersTest.cpp)
quill_add_test(TEST_SingleFrontendThread SingleFrontendThreadTest.cpp)
quill_add_test(TEST_SinkFilter SinkFilterTest.cpp)
quill_add_test(TEST_StdArrayLogging StdArrayLoggingTest.cpp)
Expand Down
80 changes: 80 additions & 0 deletions test/integration_tests/SingleFrontendThreadMultipleLoggersTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#include "doctest/doctest.h"

#include "misc/TestUtilities.h"
#include "quill/Backend.h"
#include "quill/Frontend.h"
#include "quill/LogMacros.h"
#include "quill/sinks/FileSink.h"

#include <cstdio>
#include <string>
#include <vector>

using namespace quill;

/***/
TEST_CASE("single_frontend_thread_multiple_loggers")
{
static constexpr size_t number_of_messages = 500;
static constexpr char const* filename = "single_frontend_thread_multiple_loggers.log";
static std::string const logger_name_a = "logger_a";
static std::string const logger_name_b = "logger_b";

// Start the logging backend thread
Backend::start();

// Set writing logging to a file
auto file_sink = Frontend::create_or_get_sink<FileSink>(
filename,
[]()
{
FileSinkConfig cfg;
cfg.set_open_mode('w');

// For this test only we use the default buffer size, it should not make any difference it is just for testing the default behaviour and code coverage
cfg.set_write_buffer_size(0);

return cfg;
}(),
FileEventNotifier{});

Logger* logger_a = Frontend::create_or_get_logger(
logger_name_a, std::move(file_sink), quill::PatternFormatterOptions("[%(logger)] %(message)"));

// Take properties from logger_a
Logger* logger_b = Frontend::create_or_get_logger(logger_name_b, logger_a);

for (size_t i = 0; i < number_of_messages; ++i)
{
LOG_INFO(logger_a, "This is message {}", i);
LOG_INFO(logger_b, "This is message {}", i);
}

logger_a->flush_log();
Frontend::remove_logger(logger_a);
Frontend::remove_logger(logger_b);

// Wait until the backend thread stops for test stability
Backend::stop();

// Read file and check
std::vector<std::string> const file_contents = quill::testing::file_contents(filename);
REQUIRE_EQ(file_contents.size(), number_of_messages * 2);

for (size_t i = 0; i < number_of_messages; ++i)
{
{
std::string expected_string =
std::string("[") + logger_name_a + "] This is message " + std::to_string(i);
REQUIRE(quill::testing::file_contains(file_contents, expected_string));
}

{
std::string expected_string =
std::string("[") + logger_name_b + "] This is message " + std::to_string(i);
REQUIRE(quill::testing::file_contains(file_contents, expected_string));
}
}

testing::remove_file(filename);
}

0 comments on commit 0b6cadb

Please sign in to comment.