Update srslog to latest version.

master
faluco 4 years ago committed by Francisco Paisana
parent b0a7506a8a
commit 98a2c868b5

@ -13,12 +13,12 @@
#ifndef SRSLOG_DETAIL_LOG_BACKEND_H #ifndef SRSLOG_DETAIL_LOG_BACKEND_H
#define SRSLOG_DETAIL_LOG_BACKEND_H #define SRSLOG_DETAIL_LOG_BACKEND_H
#include "srslte/srslog/detail/log_entry.h"
namespace srslog { namespace srslog {
namespace detail { namespace detail {
struct log_entry;
/// The log backend receives generated log entries from the application. Each /// The log backend receives generated log entries from the application. Each
/// entry gets distributed to the corresponding sinks. /// entry gets distributed to the corresponding sinks.
/// NOTE: Thread safe class. /// NOTE: Thread safe class.
@ -32,7 +32,7 @@ public:
virtual void start() = 0; virtual void start() = 0;
/// Pushes a log entry into the backend. /// Pushes a log entry into the backend.
virtual void push(detail::log_entry&& entry) = 0; virtual void push(log_entry&& entry) = 0;
/// Returns true when the backend has been started, otherwise false. /// Returns true when the backend has been started, otherwise false.
virtual bool is_running() const = 0; virtual bool is_running() const = 0;

@ -13,9 +13,8 @@
#ifndef SRSLOG_DETAIL_LOG_ENTRY_H #ifndef SRSLOG_DETAIL_LOG_ENTRY_H
#define SRSLOG_DETAIL_LOG_ENTRY_H #define SRSLOG_DETAIL_LOG_ENTRY_H
#include "srslte/srslog/bundled/fmt/printf.h" #include "srslte/srslog/detail/log_entry_metadata.h"
#include "srslte/srslog/detail/support/thread_utils.h" #include "srslte/srslog/detail/support/thread_utils.h"
#include <chrono>
namespace srslog { namespace srslog {
@ -23,14 +22,6 @@ class sink;
namespace detail { namespace detail {
/// This structure gives the user a way to log generic information as a context.
struct log_context {
/// Generic contxt value.
uint32_t value;
/// When true, the context value will be printed in the log entry.
bool enabled;
};
/// This command flushes all the messages pending in the backend. /// This command flushes all the messages pending in the backend.
struct flush_backend_cmd { struct flush_backend_cmd {
shared_variable<bool>& completion_flag; shared_variable<bool>& completion_flag;
@ -39,16 +30,13 @@ struct flush_backend_cmd {
/// This structure packs all the required data required to create a log entry in /// This structure packs all the required data required to create a log entry in
/// the backend. /// the backend.
//:TODO: provide proper command objects when we have custom formatting. //:TODO: replace this object using a real command pattern when we have a raw
// memory queue for passing entries.
struct log_entry { struct log_entry {
sink* s; sink* s;
std::chrono::high_resolution_clock::time_point tp; std::function<void(log_entry_metadata&& metadata, fmt::memory_buffer& buffer)>
log_context context; format_func;
std::string fmtstring; log_entry_metadata metadata;
fmt::dynamic_format_arg_store<fmt::printf_context> store;
std::string log_name;
char log_tag;
std::vector<uint8_t> hex_dump;
std::unique_ptr<flush_backend_cmd> flush_cmd; std::unique_ptr<flush_backend_cmd> flush_cmd;
}; };

@ -14,7 +14,8 @@
#define SRSLOG_LOG_CHANNEL_H #define SRSLOG_LOG_CHANNEL_H
#include "srslte/srslog/detail/log_backend.h" #include "srslte/srslog/detail/log_backend.h"
#include <cassert> #include "srslte/srslog/detail/log_entry.h"
#include "srslte/srslog/sink.h"
namespace srslog { namespace srslog {
@ -22,9 +23,7 @@ namespace srslog {
struct log_channel_config { struct log_channel_config {
log_channel_config() = default; log_channel_config() = default;
log_channel_config(std::string n, char tag, bool should_print_context) : log_channel_config(std::string n, char tag, bool should_print_context) :
name(std::move(n)), name(std::move(n)), tag(tag), should_print_context(should_print_context)
tag(tag),
should_print_context(should_print_context)
{} {}
/// Optional log channel name. If set, will get printed for each log entry. /// Optional log channel name. If set, will get printed for each log entry.
@ -95,19 +94,25 @@ public:
if (!enabled()) { if (!enabled()) {
return; return;
} }
assert(&log_sink);
// Populate the store with all incoming arguments. // Populate the store with all incoming arguments.
fmt::dynamic_format_arg_store<fmt::printf_context> store; fmt::dynamic_format_arg_store<fmt::printf_context> store;
(void)std::initializer_list<int>{(store.push_back(args), 0)...}; (void)std::initializer_list<int>{(store.push_back(args), 0)...};
// Send the log entry to the backend. // Send the log entry to the backend.
detail::log_entry entry = {&log_sink, log_formatter& formatter = log_sink.get_formatter();
std::chrono::high_resolution_clock::now(), detail::log_entry entry = {
{ctx_value, should_print_context}, &log_sink,
fmtstr, [&formatter](detail::log_entry_metadata&& metadata,
std::move(store), fmt::memory_buffer& buffer) {
log_name, formatter.format(std::move(metadata), buffer);
log_tag}; },
{std::chrono::high_resolution_clock::now(),
{ctx_value, should_print_context},
fmtstr,
std::move(store),
log_name,
log_tag}};
backend.push(std::move(entry)); backend.push(std::move(entry));
} }
@ -123,7 +128,6 @@ public:
return; return;
} }
assert(&log_sink);
// Populate the store with all incoming arguments. // Populate the store with all incoming arguments.
fmt::dynamic_format_arg_store<fmt::printf_context> store; fmt::dynamic_format_arg_store<fmt::printf_context> store;
(void)std::initializer_list<int>{(store.push_back(args), 0)...}; (void)std::initializer_list<int>{(store.push_back(args), 0)...};
@ -133,14 +137,78 @@ public:
len = std::min<size_t>(len, hex_max_size); len = std::min<size_t>(len, hex_max_size);
// Send the log entry to the backend. // Send the log entry to the backend.
detail::log_entry entry = {&log_sink, log_formatter& formatter = log_sink.get_formatter();
std::chrono::high_resolution_clock::now(), detail::log_entry entry = {
{ctx_value, should_print_context}, &log_sink,
fmtstr, [&formatter](detail::log_entry_metadata&& metadata,
std::move(store), fmt::memory_buffer& buffer) {
log_name, formatter.format(std::move(metadata), buffer);
log_tag, },
std::vector<uint8_t>(buffer, buffer + len)}; {std::chrono::high_resolution_clock::now(),
{ctx_value, should_print_context},
fmtstr,
std::move(store),
log_name,
log_tag,
std::vector<uint8_t>(buffer, buffer + len)}};
backend.push(std::move(entry));
}
/// Builds the provided log entry and passes it to the backend. When the
/// channel is disabled the log entry will be discarded.
template <typename... Ts>
void operator()(const context<Ts...>& ctx)
{
if (!enabled()) {
return;
}
// Send the log entry to the backend.
log_formatter& formatter = log_sink.get_formatter();
detail::log_entry entry = {
&log_sink,
[&formatter, ctx](detail::log_entry_metadata&& metadata,
fmt::memory_buffer& buffer) {
formatter.format_ctx(ctx, std::move(metadata), buffer);
},
{std::chrono::high_resolution_clock::now(),
{ctx_value, should_print_context},
"",
{},
log_name,
log_tag}};
backend.push(std::move(entry));
}
/// Builds the provided log entry and passes it to the backend. When the
/// channel is disabled the log entry will be discarded.
template <typename... Ts, typename... Args>
void operator()(const context<Ts...>& ctx,
const std::string& fmtstr,
Args&&... args)
{
if (!enabled()) {
return;
}
// Populate the store with all incoming arguments.
fmt::dynamic_format_arg_store<fmt::printf_context> store;
(void)std::initializer_list<int>{(store.push_back(args), 0)...};
// Send the log entry to the backend.
log_formatter& formatter = log_sink.get_formatter();
detail::log_entry entry = {
&log_sink,
[&formatter, ctx](detail::log_entry_metadata&& metadata,
fmt::memory_buffer& buffer) {
formatter.format_ctx(ctx, std::move(metadata), buffer);
},
{std::chrono::high_resolution_clock::now(),
{ctx_value, should_print_context},
fmtstr,
std::move(store),
log_name,
log_tag}};
backend.push(std::move(entry)); backend.push(std::move(entry));
} }

@ -15,6 +15,8 @@
#include "srslte/srslog/detail/support/error_string.h" #include "srslte/srslog/detail/support/error_string.h"
#include "srslte/srslog/detail/support/memory_buffer.h" #include "srslte/srslog/detail/support/memory_buffer.h"
#include "srslte/srslog/formatter.h"
#include <cassert>
namespace srslog { namespace srslog {
@ -23,13 +25,25 @@ namespace srslog {
class sink class sink
{ {
public: public:
explicit sink(std::unique_ptr<log_formatter> f) : formatter(std::move(f))
{
assert(formatter && "Invalid formatter");
}
virtual ~sink() = default; virtual ~sink() = default;
/// Returns the formatter used by this sink.
log_formatter& get_formatter() { return *formatter; }
const log_formatter& get_formatter() const { return *formatter; }
/// Writes the provided memory buffer into the sink. /// Writes the provided memory buffer into the sink.
virtual detail::error_string write(detail::memory_buffer buffer) = 0; virtual detail::error_string write(detail::memory_buffer buffer) = 0;
/// Flushes any buffered contents to the backing store. /// Flushes any buffered contents to the backing store.
virtual detail::error_string flush() = 0; virtual detail::error_string flush() = 0;
private:
std::unique_ptr<log_formatter> formatter;
}; };
} // namespace srslog } // namespace srslog

@ -52,6 +52,10 @@ fetch_log_channel(const std::string& id, sink& s, log_channel_config config);
/// NOTE: Deprecated, use fetch_log_channel instead. /// NOTE: Deprecated, use fetch_log_channel instead.
log_channel* create_log_channel(const std::string& id, sink& s); log_channel* create_log_channel(const std::string& id, sink& s);
///
/// Logger management functions.
///
namespace detail { namespace detail {
/// Internal helper functions. /// Internal helper functions.
@ -60,10 +64,6 @@ detail::any* find_logger(const std::string& id);
} // namespace detail } // namespace detail
///
/// Logger management functions.
///
/// Finds a logger with the specified id string and type in the repository. On /// Finds a logger with the specified id string and type in the repository. On
/// success returns a pointer to the requested logger, otherwise nullptr. /// success returns a pointer to the requested logger, otherwise nullptr.
/// NOTE: T should be a type that is a logger. /// NOTE: T should be a type that is a logger.
@ -133,13 +133,30 @@ inline T* create_logger(const std::string& id, Args&&... args)
return detail::any_cast<T>(p); return detail::any_cast<T>(p);
} }
///
/// Formatter management functions.
///
/// Installs the specified formatter to be used as the default one by new sinks.
/// The initial default formatter formats plain text.
void set_default_log_formatter(std::unique_ptr<log_formatter> f);
/// Returns the instance of the default formatter being used.
std::unique_ptr<log_formatter> get_default_log_formatter();
/// Creates a new instance of a plain text formatter.
std::unique_ptr<log_formatter> create_text_formatter();
/// Creates a new instance of a JSON formatter.
std::unique_ptr<log_formatter> create_json_formatter();
/// ///
/// Sink management functions. /// Sink management functions.
/// ///
/// Installs the specified sink to be used as the default one by new log /// Installs the specified sink to be used as the default one by new log
/// channels and loggers. /// channels and loggers.
/// The initial default sink writes to stdout. /// The initial default sink writes to stdout with a text formatter.
void set_default_sink(sink& s); void set_default_sink(sink& s);
/// Returns the instance of the default sink being used. /// Returns the instance of the default sink being used.
@ -149,18 +166,35 @@ sink& get_default_sink();
/// success returns a pointer to the requested sink, otherwise nullptr. /// success returns a pointer to the requested sink, otherwise nullptr.
sink* find_sink(const std::string& id); sink* find_sink(const std::string& id);
/// Returns an instance of a sink that writes to the stdout stream. /// Returns an instance of a sink that writes to the stdout stream. You may use
sink& fetch_stdout_sink(); /// different ids if you need to create multiple stdout sinks with different
/// formatters.
sink& fetch_stdout_sink(
const std::string& id = "stdout",
std::unique_ptr<log_formatter> f = get_default_log_formatter());
/// Returns an instance of a sink that writes to the stderr stream. /// Returns an instance of a sink that writes to the stderr stream. You may use
sink& fetch_stderr_sink(); /// different ids if you need to create multiple stderr sinks with different
/// formatters.
sink& fetch_stderr_sink(
const std::string& id = "stderr",
std::unique_ptr<log_formatter> f = get_default_log_formatter());
/// Returns an instance of a sink that writes into a file in the specified path. /// Returns an instance of a sink that writes into a file in the specified path.
/// Specifying a max_size value different to zero will make the sink create a /// Specifying a max_size value different to zero will make the sink create a
/// new file each time the current file exceeds this value. The units of /// new file each time the current file exceeds this value. The units of
/// max_size are bytes. /// max_size are bytes.
/// NOTE: Any '#' characters in the id will get removed. /// NOTE: Any '#' characters in the path will get removed.
sink& fetch_file_sink(const std::string& path, size_t max_size = 0); sink& fetch_file_sink(
const std::string& path,
size_t max_size = 0,
std::unique_ptr<log_formatter> f = get_default_log_formatter());
/// Installs a custom user defined sink in the framework getting associated to
/// the specified id. Returns true on success, otherwise false.
/// WARNING: This function is an advanced feature and users should really know
/// what they are doing when using it.
bool install_custom_sink(const std::string& id, std::unique_ptr<sink> s);
/// Creates a new sink that writes into the a file in the specified path and /// Creates a new sink that writes into the a file in the specified path and
/// registers it into a sink repository so that it can be later retrieved in /// registers it into a sink repository so that it can be later retrieved in

@ -13,9 +13,9 @@ set(SOURCES
event_trace.cpp) event_trace.cpp)
add_subdirectory(bundled/fmt) add_subdirectory(bundled/fmt)
add_subdirectory(formatters)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
add_library(srslog STATIC ${SOURCES}) add_library(srslog STATIC ${SOURCES})
target_link_libraries(srslog fmt "${CMAKE_THREAD_LIBS_INIT}") target_link_libraries(srslog formatters fmt "${CMAKE_THREAD_LIBS_INIT}")
INSTALL(TARGETS srslog DESTINATION ${LIBRARY_DIR})

@ -11,9 +11,7 @@
*/ */
#include "backend_worker.h" #include "backend_worker.h"
#include "formatter.h"
#include "srslte/srslog/sink.h" #include "srslte/srslog/sink.h"
#include <cassert>
using namespace srslog; using namespace srslog;
@ -90,13 +88,14 @@ void backend_worker::process_log_entry(detail::log_entry&& entry)
return; return;
} }
// Save sink pointer before moving the entry. fmt::memory_buffer fmt_buffer;
sink* s = entry.s;
std::string result = format_log_entry_to_text(std::move(entry)); assert(entry.format_func && "Invalid format function");
detail::memory_buffer buffer(result); entry.format_func(std::move(entry.metadata), fmt_buffer);
if (auto err_str = s->write(buffer)) { const auto str = fmt::to_string(fmt_buffer);
detail::memory_buffer buffer(str);
if (auto err_str = entry.s->write(buffer)) {
err_handler(err_str.get_error()); err_handler(err_str.get_error());
} }
} }

@ -12,7 +12,6 @@
#include "srslte/srslog/event_trace.h" #include "srslte/srslog/event_trace.h"
#include "srslte/srslog/srslog.h" #include "srslte/srslog/srslog.h"
#include <cassert>
#include <ctime> #include <ctime>
#undef trace_duration_begin #undef trace_duration_begin

@ -1,87 +0,0 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2020 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#ifndef SRSLOG_FORMATTER_H
#define SRSLOG_FORMATTER_H
#include "srslte/srslog/bundled/fmt/chrono.h"
#include "srslte/srslog/bundled/fmt/ranges.h"
#include "srslte/srslog/detail/log_entry.h"
namespace srslog {
namespace detail {
/// Formats into a hex dump a range of elements, storing the result in the input
/// buffer.
inline void format_hex_dump(const std::vector<uint8_t>& v,
fmt::memory_buffer& buffer)
{
if (v.empty()) {
return;
}
const size_t elements_per_line = 16;
for (auto i = v.cbegin(), e = v.cend(); i != e;) {
auto num_elements =
std::min<size_t>(elements_per_line, std::distance(i, e));
fmt::format_to(buffer,
" {:04x}: {:02x}\n",
std::distance(v.cbegin(), i),
fmt::join(i, i + num_elements, " "));
std::advance(i, num_elements);
}
}
} // namespace detail
/// Formats to text all the fields of a log entry,
inline std::string format_log_entry_to_text(detail::log_entry&& entry)
{
fmt::memory_buffer buffer;
// Time stamp data preparation.
std::tm current_time =
fmt::gmtime(std::chrono::high_resolution_clock::to_time_t(entry.tp));
auto us_fraction = std::chrono::duration_cast<std::chrono::microseconds>(
entry.tp.time_since_epoch())
.count() %
1000000u;
fmt::format_to(buffer, "{:%H:%M:%S}.{:06} ", current_time, us_fraction);
// Format optional fields if present.
if (!entry.log_name.empty()) {
fmt::format_to(buffer, "[{: <4.4}] ", entry.log_name);
}
if (entry.log_tag != '\0') {
fmt::format_to(buffer, "[{}] ", entry.log_tag);
}
if (entry.context.enabled) {
fmt::format_to(buffer, "[{:5}] ", entry.context.value);
}
// Message formatting.
fmt::format_to(
buffer, "{}\n", fmt::vsprintf(entry.fmtstring, std::move(entry.store)));
// Optional hex dump formatting.
detail::format_hex_dump(entry.hex_dump, buffer);
return fmt::to_string(buffer);
}
} // namespace srslog
#endif // SRSLOG_FORMATTER_H

@ -1,114 +0,0 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2020 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#ifndef SRSLOG_SINK_REPOSITORY_H
#define SRSLOG_SINK_REPOSITORY_H
#include "object_repository.h"
#include "sinks/stream_sink.h"
namespace srslog {
/// The sink repository stores sink instances associated to an id. Both stdout
/// and stderr stream sinks are created on construction so they accessible
/// without the need of creating them previously.
/// NOTE: Thread safe class.
class sink_repository
{
/// Identifiers for stdout and stderr sinks.
static constexpr char stdout_id[] = "stdout#";
static constexpr char stderr_id[] = "stderr#";
object_repository<std::string, std::unique_ptr<sink> > repo;
public:
sink_repository()
{
//:TODO: GCC5 or lower versions emits an error if we use the new() expression directly, use redundant
//piecewise_construct instead.
repo.emplace(std::piecewise_construct,
std::forward_as_tuple(stdout_id),
std::forward_as_tuple(new stream_sink(sink_stream_type::stdout)));
repo.emplace(std::piecewise_construct,
std::forward_as_tuple(stderr_id),
std::forward_as_tuple(new stream_sink(sink_stream_type::stderr)));
}
/// Returns the instance of the sink that writes to stdout.
sink& get_stdout_sink()
{
auto s = repo.find(stdout_id);
assert(s && "stdout sink should always exist");
return *(s->get());
}
const sink& get_stdout_sink() const
{
const auto s = repo.find(stdout_id);
assert(s && "stdout sink should always exist");
return *(s->get());
}
/// Returns the instance of the sink that writes to stderr.
sink& get_stderr_sink()
{
auto s = repo.find(stderr_id);
assert(s && "stderr sink should always exist");
return *(s->get());
}
const sink& get_stderr_sink() const
{
const auto s = repo.find(stderr_id);
assert(s && "stderr sink should always exist");
return *(s->get());
}
/// Finds a sink with the specified id in the repository. Returns a pointer to
/// the sink, otherwise nullptr if not found.
sink* find(const std::string& id)
{
auto p = repo.find(id);
return (p) ? p->get() : nullptr;
}
const sink* find(const std::string& id) const
{
const auto p = repo.find(id);
return (p) ? p->get() : nullptr;
}
/// Returns an instance of a sink specified by the input arguments.
template <typename... Args>
sink& fetch_sink(Args&&... args)
{
return *repo.emplace(std::forward<Args>(args)...);
}
/// Returns a copy of the list of registered sinks.
std::vector<sink*> contents() const
{
auto repo_contents = repo.contents();
std::vector<sink*> data;
data.reserve(repo_contents.size());
for (const auto& s : repo_contents) {
data.push_back(s->get());
}
return data;
}
};
constexpr char sink_repository::stdout_id[];
constexpr char sink_repository::stderr_id[];
} // namespace srslog
#endif // SRSLOG_SINK_REPOSITORY_H

@ -15,7 +15,6 @@
#include "file_utils.h" #include "file_utils.h"
#include "srslte/srslog/sink.h" #include "srslte/srslog/sink.h"
#include <cassert>
namespace srslog { namespace srslog {
@ -25,7 +24,10 @@ namespace srslog {
class file_sink : public sink class file_sink : public sink
{ {
public: public:
file_sink(std::string name, size_t max_size) : file_sink(std::string name,
size_t max_size,
std::unique_ptr<log_formatter> f) :
sink(std::move(f)),
base_filename(std::move(name)), base_filename(std::move(name)),
max_size((max_size == 0) ? 0 : std::max<size_t>(max_size, 4 * 1024)) max_size((max_size == 0) ? 0 : std::max<size_t>(max_size, 4 * 1024))
{} {}
@ -33,8 +35,6 @@ public:
file_sink(const file_sink& other) = delete; file_sink(const file_sink& other) = delete;
file_sink& operator=(const file_sink& other) = delete; file_sink& operator=(const file_sink& other) = delete;
~file_sink() override { handler.close(); }
detail::error_string write(detail::memory_buffer buffer) override detail::error_string write(detail::memory_buffer buffer) override
{ {
// Create a new file the first time we hit this method. // Create a new file the first time we hit this method.

@ -85,7 +85,7 @@ public:
explicit operator bool() const { return handle; } explicit operator bool() const { return handle; }
/// Returns the handle of the underlying file. /// Returns the handle of the underlying file.
std::FILE* get_handle() { return handle; } std::FILE* get_handle() const { return handle; }
/// Returns the path of the file. /// Returns the path of the file.
const std::string& get_path() const { return path; } const std::string& get_path() const { return path; }
@ -101,8 +101,8 @@ public:
return {}; return {};
} }
return format_error(fmt::format("Unable to create log file \"{}\"", path), return format_error(
errno); fmt::format("Unable to create log file \"{}\"", new_path), errno);
} }
/// Writes the provided memory buffer into an open file, otherwise does /// Writes the provided memory buffer into an open file, otherwise does
@ -112,9 +112,10 @@ public:
if (handle && if (handle &&
std::fwrite(buffer.data(), sizeof(char), buffer.size(), handle) != std::fwrite(buffer.data(), sizeof(char), buffer.size(), handle) !=
buffer.size()) { buffer.size()) {
auto err_str = format_error(
fmt::format("Unable to write log file \"{}\"", path), errno);
close(); close();
return format_error(fmt::format("Unable to write log file \"{}\"", path), return err_str;
errno);
} }
return {}; return {};
@ -124,10 +125,11 @@ public:
detail::error_string flush() detail::error_string flush()
{ {
if (handle && ::fflush(handle) == EOF) { if (handle && ::fflush(handle) == EOF) {
close(); auto err_str = format_error(
return format_error(
fmt::format("Error encountered while flushing log file \"{}\"", path), fmt::format("Error encountered while flushing log file \"{}\"", path),
errno); errno);
close();
return err_str;
} }
return {}; return {};

@ -14,8 +14,6 @@
#define SRSLOG_STREAM_SINK_H #define SRSLOG_STREAM_SINK_H
#include "srslte/srslog/sink.h" #include "srslte/srslog/sink.h"
#include <cassert>
#include <cstdio>
namespace srslog { namespace srslog {
@ -26,7 +24,8 @@ enum class sink_stream_type { stdout, stderr };
class stream_sink : public sink class stream_sink : public sink
{ {
public: public:
explicit stream_sink(sink_stream_type s) : stream_sink(sink_stream_type s, std::unique_ptr<log_formatter> f) :
sink(std::move(f)),
handle((s == sink_stream_type::stdout) ? stdout : stderr) handle((s == sink_stream_type::stdout) ? stdout : stderr)
{} {}

@ -11,6 +11,7 @@
*/ */
#include "srslte/srslog/srslog.h" #include "srslte/srslog/srslog.h"
#include "formatters/json_formatter.h"
#include "sinks/file_sink.h" #include "sinks/file_sink.h"
#include "srslog_instance.h" #include "srslog_instance.h"
@ -27,10 +28,13 @@ static std::string remove_sharp_chars(const std::string& s)
/// Generic argument function that fetches a log channel from the repository. /// Generic argument function that fetches a log channel from the repository.
template <typename... Args> template <typename... Args>
static log_channel& fetch_log_channel_helper(const std::string& id, Args&&... args) static log_channel& fetch_log_channel_helper(const std::string& id,
Args&&... args)
{ {
return srslog_instance::get().get_channel_repo().emplace( return srslog_instance::get().get_channel_repo().emplace(
std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(id, std::forward<Args>(args)...)); std::piecewise_construct,
std::forward_as_tuple(id),
std::forward_as_tuple(id, std::forward<Args>(args)...));
} }
/// ///
@ -46,18 +50,46 @@ log_channel& srslog::fetch_log_channel(const std::string& id)
{ {
assert(!id.empty() && "Empty id string"); assert(!id.empty() && "Empty id string");
std::string clean_id = remove_sharp_chars(id); std::string clean_id = remove_sharp_chars(id);
srslog_instance& instance = srslog_instance::get(); srslog_instance& instance = srslog_instance::get();
return fetch_log_channel_helper(clean_id, instance.get_default_sink(), instance.get_backend()); return fetch_log_channel_helper(
clean_id, instance.get_default_sink(), instance.get_backend());
} }
log_channel& srslog::fetch_log_channel(const std::string& id, sink& s, log_channel_config config) log_channel& srslog::fetch_log_channel(const std::string& id,
sink& s,
log_channel_config config)
{ {
assert(!id.empty() && "Empty id string"); assert(!id.empty() && "Empty id string");
std::string clean_id = remove_sharp_chars(id); std::string clean_id = remove_sharp_chars(id);
srslog_instance& instance = srslog_instance::get(); srslog_instance& instance = srslog_instance::get();
return fetch_log_channel_helper(clean_id, s, instance.get_backend(), std::move(config)); return fetch_log_channel_helper(
clean_id, s, instance.get_backend(), std::move(config));
}
///
/// Formatter management functions.
///
void srslog::set_default_log_formatter(std::unique_ptr<log_formatter> f)
{
srslog_instance::get().set_default_formatter(std::move(f));
}
std::unique_ptr<log_formatter> srslog::get_default_log_formatter()
{
return srslog_instance::get().get_default_formatter();
}
std::unique_ptr<log_formatter> srslog::create_text_formatter()
{
return std::unique_ptr<log_formatter>(new text_formatter);
}
std::unique_ptr<log_formatter> srslog::create_json_formatter()
{
return std::unique_ptr<log_formatter>(new json_formatter);
} }
/// ///
@ -76,29 +108,65 @@ sink& srslog::get_default_sink()
sink* srslog::find_sink(const std::string& id) sink* srslog::find_sink(const std::string& id)
{ {
return srslog_instance::get().get_sink_repo().find(id); auto ptr = srslog_instance::get().get_sink_repo().find(id);
return (ptr) ? ptr->get() : nullptr;
} }
sink& srslog::fetch_stdout_sink() sink& srslog::fetch_stdout_sink(const std::string& id,
std::unique_ptr<log_formatter> f)
{ {
return srslog_instance::get().get_sink_repo().get_stdout_sink(); assert(!id.empty() && "Empty id string");
auto& s = srslog_instance::get().get_sink_repo().emplace(
std::piecewise_construct,
std::forward_as_tuple(id),
std::forward_as_tuple(
new stream_sink(sink_stream_type::stdout, std::move(f))));
return *s;
} }
sink& srslog::fetch_stderr_sink() sink& srslog::fetch_stderr_sink(const std::string& id,
std::unique_ptr<log_formatter> f)
{ {
return srslog_instance::get().get_sink_repo().get_stderr_sink(); assert(!id.empty() && "Empty id string");
auto& s = srslog_instance::get().get_sink_repo().emplace(
std::piecewise_construct,
std::forward_as_tuple(id),
std::forward_as_tuple(
new stream_sink(sink_stream_type::stderr, std::move(f))));
return *s;
} }
sink& srslog::fetch_file_sink(const std::string& path, size_t max_size) sink& srslog::fetch_file_sink(const std::string& path,
size_t max_size,
std::unique_ptr<log_formatter> f)
{ {
assert(!path.empty() && "Empty path string"); assert(!path.empty() && "Empty path string");
std::string clean_path = remove_sharp_chars(path); //:TODO: GCC5 or lower versions emits an error if we use the new() expression
//:TODO: GCC5 or lower versions emits an error if we use the new() expression directly, use redundant // directly, use redundant piecewise_construct instead.
// piecewise_construct instead. auto& s = srslog_instance::get().get_sink_repo().emplace(
return srslog_instance::get().get_sink_repo().fetch_sink(std::piecewise_construct, std::piecewise_construct,
std::forward_as_tuple(clean_path), std::forward_as_tuple(path),
std::forward_as_tuple(new file_sink(clean_path, max_size))); std::forward_as_tuple(new file_sink(path, max_size, std::move(f))));
return *s;
}
bool srslog::install_custom_sink(const std::string& id, std::unique_ptr<sink> s)
{
assert(!id.empty() && "Empty path string");
sink* input_sink = s.get();
sink* returned_sink =
srslog_instance::get().get_sink_repo().emplace(id, std::move(s)).get();
// Successful insertion occurs when the returned object is the same one as the
// input object.
return input_sink == returned_sink;
} }
/// ///
@ -122,9 +190,16 @@ void srslog::flush()
// The backend will set this shared variable when done. // The backend will set this shared variable when done.
detail::shared_variable<bool> completion_flag(false); detail::shared_variable<bool> completion_flag(false);
auto sink_ptrs = instance.get_sink_repo().contents();
std::vector<sink*> sinks;
sinks.reserve(sink_ptrs.size());
for (const auto& s : sink_ptrs) {
sinks.push_back(s->get());
}
detail::log_entry cmd; detail::log_entry cmd;
cmd.flush_cmd = std::unique_ptr<detail::flush_backend_cmd>( cmd.flush_cmd = std::unique_ptr<detail::flush_backend_cmd>(
new detail::flush_backend_cmd{completion_flag, instance.get_sink_repo().contents()}); new detail::flush_backend_cmd{completion_flag, std::move(sinks)});
instance.get_backend().push(std::move(cmd)); instance.get_backend().push(std::move(cmd));
@ -148,10 +223,12 @@ detail::any* srslog::detail::find_logger(const std::string& id)
return srslog_instance::get().get_logger_repo().find(id); return srslog_instance::get().get_logger_repo().find(id);
} }
detail::any* srslog::detail::fetch_logger(const std::string& id, detail::any&& logger) detail::any* srslog::detail::fetch_logger(const std::string& id,
detail::any&& logger)
{ {
assert(!id.empty() && "Empty id string"); assert(!id.empty() && "Empty id string");
return &srslog_instance::get().get_logger_repo().emplace(id, std::move(logger)); return &srslog_instance::get().get_logger_repo().emplace(id,
std::move(logger));
} }
/// Builds a logger name out of the id and tag. /// Builds a logger name out of the id and tag.
@ -161,7 +238,9 @@ static std::string build_logger_name(const std::string& id, char tag)
} }
/// Fetches a logger with all its log channels. /// Fetches a logger with all its log channels.
static basic_logger& fetch_basic_logger_helper(const std::string& id, sink& s, bool should_print_context) static basic_logger& fetch_basic_logger_helper(const std::string& id,
sink& s,
bool should_print_context)
{ {
static constexpr char basic_logger_chan_tags[] = {'E', 'W', 'I', 'D'}; static constexpr char basic_logger_chan_tags[] = {'E', 'W', 'I', 'D'};
@ -170,36 +249,41 @@ static basic_logger& fetch_basic_logger_helper(const std::string& id, sink& s, b
// User created log channels cannot have ids with a # character, encode the // User created log channels cannot have ids with a # character, encode the
// ids here with a # to ensure all channels are unique. // ids here with a # to ensure all channels are unique.
log_channel& error = log_channel& error = fetch_log_channel_helper(
fetch_log_channel_helper(build_logger_name(id, basic_logger_chan_tags[0]), build_logger_name(id, basic_logger_chan_tags[0]),
s, s,
instance.get_backend(), instance.get_backend(),
log_channel_config{id, basic_logger_chan_tags[0], should_print_context}); log_channel_config{id, basic_logger_chan_tags[0], should_print_context});
log_channel& warning = log_channel& warning = fetch_log_channel_helper(
fetch_log_channel_helper(build_logger_name(id, basic_logger_chan_tags[1]), build_logger_name(id, basic_logger_chan_tags[1]),
s, s,
instance.get_backend(), instance.get_backend(),
log_channel_config{id, basic_logger_chan_tags[1], should_print_context}); log_channel_config{id, basic_logger_chan_tags[1], should_print_context});
log_channel& info = fetch_log_channel_helper(build_logger_name(id, basic_logger_chan_tags[2]), log_channel& info = fetch_log_channel_helper(
s, build_logger_name(id, basic_logger_chan_tags[2]),
instance.get_backend(), s,
log_channel_config{id, basic_logger_chan_tags[2], should_print_context}); instance.get_backend(),
log_channel& debug = log_channel_config{id, basic_logger_chan_tags[2], should_print_context});
fetch_log_channel_helper(build_logger_name(id, basic_logger_chan_tags[3]), log_channel& debug = fetch_log_channel_helper(
s, build_logger_name(id, basic_logger_chan_tags[3]),
instance.get_backend(), s,
log_channel_config{id, basic_logger_chan_tags[3], should_print_context}); instance.get_backend(),
log_channel_config{id, basic_logger_chan_tags[3], should_print_context});
return fetch_logger<basic_logger>(id, error, warning, info, debug); return fetch_logger<basic_logger>(id, error, warning, info, debug);
} }
basic_logger& srslog::fetch_basic_logger(const std::string& id, bool should_print_context) basic_logger& srslog::fetch_basic_logger(const std::string& id,
bool should_print_context)
{ {
assert(!id.empty() && "Empty id string"); assert(!id.empty() && "Empty id string");
return fetch_basic_logger_helper(id, srslog_instance::get().get_default_sink(), should_print_context); return fetch_basic_logger_helper(
id, srslog_instance::get().get_default_sink(), should_print_context);
} }
basic_logger& srslog::fetch_basic_logger(const std::string& id, sink& s, bool should_print_context) basic_logger& srslog::fetch_basic_logger(const std::string& id,
sink& s,
bool should_print_context)
{ {
assert(!id.empty() && "Empty id string"); assert(!id.empty() && "Empty id string");
return fetch_basic_logger_helper(id, s, should_print_context); return fetch_basic_logger_helper(id, s, should_print_context);
@ -211,28 +295,33 @@ basic_logger& srslog::fetch_basic_logger(const std::string& id, sink& s, bool sh
/// Creates and registers a log channel. Returns a pointer to the newly created /// Creates and registers a log channel. Returns a pointer to the newly created
/// channel on success, otherwise nullptr. /// channel on success, otherwise nullptr.
static log_channel* create_and_register_log_channel(const std::string& id, sink& s) static log_channel* create_and_register_log_channel(const std::string& id,
sink& s)
{ {
assert(!id.empty() && "Empty id string"); assert(!id.empty() && "Empty id string");
srslog_instance& instance = srslog_instance::get(); srslog_instance& instance = srslog_instance::get();
auto& p = instance.get_channel_repo().emplace( auto& p = instance.get_channel_repo().emplace(
std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(id, s, instance.get_backend())); std::piecewise_construct,
std::forward_as_tuple(id),
std::forward_as_tuple(id, s, instance.get_backend()));
return &p; return &p;
} }
static log_channel* create_and_register_log_channel(const std::string& id, log_channel_config config, sink& s) static log_channel* create_and_register_log_channel(const std::string& id,
log_channel_config config,
sink& s)
{ {
assert(!id.empty() && "Empty id string"); assert(!id.empty() && "Empty id string");
srslog_instance& instance = srslog_instance::get(); srslog_instance& instance = srslog_instance::get();
auto& p = auto& p = instance.get_channel_repo().emplace(
instance.get_channel_repo().emplace(std::piecewise_construct, std::piecewise_construct,
std::forward_as_tuple(id), std::forward_as_tuple(id),
std::forward_as_tuple(id, s, instance.get_backend(), std::move(config))); std::forward_as_tuple(id, s, instance.get_backend(), std::move(config)));
return &p; return &p;
} }
@ -255,23 +344,32 @@ log_channel* srslog::create_log_channel(const std::string& id, sink& s)
sink* srslog::create_stdout_sink(const std::string& name) sink* srslog::create_stdout_sink(const std::string& name)
{ {
return &srslog_instance::get().get_sink_repo().get_stdout_sink(); return srslog_instance::get().get_sink_repo().find("stdout")->get();
} }
sink* srslog::create_stderr_sink(const std::string& name) sink* srslog::create_stderr_sink(const std::string& name)
{ {
return &srslog_instance::get().get_sink_repo().get_stderr_sink(); return srslog_instance::get().get_sink_repo().find("stderr")->get();
} }
sink* srslog::create_file_sink(const std::string& path, size_t max_size) sink* srslog::create_file_sink(const std::string& path, size_t max_size)
{ {
//:TODO: GCC5 or lower versions emits an error if we use the new() expression directly, use redundant //:TODO: GCC5 or lower versions emits an error if we use the new() expression
// piecewise_construct instead. // directly, use redundant piecewise_construct instead.
return &srslog_instance::get().get_sink_repo().fetch_sink( return srslog_instance::get()
std::piecewise_construct, std::forward_as_tuple(path), std::forward_as_tuple(new file_sink(path, max_size))); .get_sink_repo()
.emplace(std::piecewise_construct,
std::forward_as_tuple(path),
std::forward_as_tuple(new file_sink(
path,
max_size,
std::unique_ptr<log_formatter>(new text_formatter))))
.get();
} }
basic_logger* srslog::create_basic_logger(const std::string& id, sink& s, bool should_print_context) basic_logger* srslog::create_basic_logger(const std::string& id,
sink& s,
bool should_print_context)
{ {
assert(!id.empty() && "Empty id string"); assert(!id.empty() && "Empty id string");
@ -289,16 +387,24 @@ basic_logger* srslog::create_basic_logger(const std::string& id, sink& s, bool s
// without any id clashes. // without any id clashes.
log_channel* error = create_and_register_log_channel( log_channel* error = create_and_register_log_channel(
build_logger_name(id, basic_logger_chan_tags[0]), {id, basic_logger_chan_tags[0], should_print_context}, s); build_logger_name(id, basic_logger_chan_tags[0]),
{id, basic_logger_chan_tags[0], should_print_context},
s);
assert(error && "Could not create channel"); assert(error && "Could not create channel");
log_channel* warning = create_and_register_log_channel( log_channel* warning = create_and_register_log_channel(
build_logger_name(id, basic_logger_chan_tags[1]), {id, basic_logger_chan_tags[1], should_print_context}, s); build_logger_name(id, basic_logger_chan_tags[1]),
{id, basic_logger_chan_tags[1], should_print_context},
s);
assert(warning && "Could not create channel"); assert(warning && "Could not create channel");
log_channel* info = create_and_register_log_channel( log_channel* info = create_and_register_log_channel(
build_logger_name(id, basic_logger_chan_tags[2]), {id, basic_logger_chan_tags[2], should_print_context}, s); build_logger_name(id, basic_logger_chan_tags[2]),
{id, basic_logger_chan_tags[2], should_print_context},
s);
assert(info && "Could not create channel"); assert(info && "Could not create channel");
log_channel* debug = create_and_register_log_channel( log_channel* debug = create_and_register_log_channel(
build_logger_name(id, basic_logger_chan_tags[3]), {id, basic_logger_chan_tags[3], should_print_context}, s); build_logger_name(id, basic_logger_chan_tags[3]),
{id, basic_logger_chan_tags[3], should_print_context},
s);
assert(debug && "Could not create channel"); assert(debug && "Could not create channel");
return create_logger<basic_logger>(id, *error, *warning, *info, *debug); return create_logger<basic_logger>(id, *error, *warning, *info, *debug);

@ -12,7 +12,6 @@
#include "srslte/srslog/srslog_c.h" #include "srslte/srslog/srslog_c.h"
#include "srslte/srslog/srslog.h" #include "srslte/srslog/srslog.h"
#include <cassert>
#include <cstdarg> #include <cstdarg>
using namespace srslog; using namespace srslog;

@ -13,9 +13,10 @@
#ifndef SRSLOG_SRSLOG_INSTANCE_H #ifndef SRSLOG_SRSLOG_INSTANCE_H
#define SRSLOG_SRSLOG_INSTANCE_H #define SRSLOG_SRSLOG_INSTANCE_H
#include "formatters/text_formatter.h"
#include "log_backend_impl.h" #include "log_backend_impl.h"
#include "sink_repository.h" #include "object_repository.h"
#include "srslte/srslog/detail/support/any.h" #include "sinks/stream_sink.h"
#include "srslte/srslog/log_channel.h" #include "srslte/srslog/log_channel.h"
namespace srslog { namespace srslog {
@ -23,7 +24,29 @@ namespace srslog {
/// Singleton of the framework containing all the required classes. /// Singleton of the framework containing all the required classes.
class srslog_instance class srslog_instance
{ {
srslog_instance() { default_sink = &sink_repo.get_stdout_sink(); } srslog_instance()
{
// stdout and stderr sinks are always present.
auto& stdout_sink = sink_repo.emplace(
std::piecewise_construct,
std::forward_as_tuple("stdout"),
std::forward_as_tuple(new stream_sink(
sink_stream_type::stdout,
std::unique_ptr<log_formatter>(new text_formatter))));
default_sink = stdout_sink.get();
sink_repo.emplace(std::piecewise_construct,
std::forward_as_tuple("stderr"),
std::forward_as_tuple(new stream_sink(
sink_stream_type::stderr,
std::unique_ptr<log_formatter>(new text_formatter))));
// Initialize the default formatter pointer with a text formatter.
{
detail::scoped_lock lock(formatter_mutex);
default_formatter = std::unique_ptr<log_formatter>(new text_formatter);
}
}
public: public:
srslog_instance(const srslog_instance& other) = delete; srslog_instance(const srslog_instance& other) = delete;
@ -47,8 +70,9 @@ public:
const channel_repo_type& get_channel_repo() const { return channel_repo; } const channel_repo_type& get_channel_repo() const { return channel_repo; }
/// Sink repository accessor. /// Sink repository accessor.
sink_repository& get_sink_repo() { return sink_repo; } using sink_repo_type = object_repository<std::string, std::unique_ptr<sink>>;
const sink_repository& get_sink_repo() const { return sink_repo; } sink_repo_type& get_sink_repo() { return sink_repo; }
const sink_repo_type& get_sink_repo() const { return sink_repo; }
/// Backend accessor. /// Backend accessor.
detail::log_backend& get_backend() { return backend; } detail::log_backend& get_backend() { return backend; }
@ -64,16 +88,32 @@ public:
void set_default_sink(sink& s) { default_sink = &s; } void set_default_sink(sink& s) { default_sink = &s; }
/// Returns the default sink. /// Returns the default sink.
sink& get_default_sink() { return *default_sink; } sink& get_default_sink() const { return *default_sink; }
/// Set the specified formatter as the default one.
void set_default_formatter(std::unique_ptr<log_formatter> f)
{
detail::scoped_lock lock(formatter_mutex);
default_formatter = std::move(f);
}
/// Returns the default formatter.
std::unique_ptr<log_formatter> get_default_formatter() const
{
detail::scoped_lock lock(formatter_mutex);
return default_formatter->clone();
}
private: private:
/// NOTE: The order of declaration of each member is important here for proper /// NOTE: The order of declaration of each member is important here for proper
/// destruction. /// destruction.
sink_repository sink_repo; sink_repo_type sink_repo;
log_backend_impl backend; log_backend_impl backend;
channel_repo_type channel_repo; channel_repo_type channel_repo;
logger_repo_type logger_repo; logger_repo_type logger_repo;
detail::shared_variable<sink*> default_sink{nullptr}; detail::shared_variable<sink*> default_sink{nullptr};
mutable detail::mutex formatter_mutex;
std::unique_ptr<log_formatter> default_formatter;
}; };
} // namespace srslog } // namespace srslog

@ -42,7 +42,17 @@ add_definitions(-DENABLE_SRSLOG_EVENT_TRACE)
target_link_libraries(tracer_test srslog) target_link_libraries(tracer_test srslog)
add_test(tracer_test tracer_test) add_test(tracer_test tracer_test)
add_executable(formatter_test formatter_test.cpp) #:TODO: re-enable test.
target_include_directories(formatter_test PUBLIC ../../) #add_executable(text_formatter_test text_formatter_test.cpp)
target_link_libraries(formatter_test srslog) #target_include_directories(text_formatter_test PUBLIC ../../)
add_test(formatter_test formatter_test) #target_link_libraries(text_formatter_test srslog)
#add_test(text_formatter_test text_formatter_test)
add_executable(json_formatter_test json_formatter_test.cpp)
target_include_directories(json_formatter_test PUBLIC ../../)
target_link_libraries(json_formatter_test srslog)
add_test(json_formatter_test json_formatter_test)
add_executable(context_test context_test.cpp)
target_link_libraries(context_test srslog)
add_test(context_test context_test)

@ -12,25 +12,13 @@
#include "srslte/srslog/event_trace.h" #include "srslte/srslog/event_trace.h"
#include "srslte/srslog/log_channel.h" #include "srslte/srslog/log_channel.h"
#include "srslte/srslog/sink.h" #include "test_dummies.h"
#include "testing_helpers.h" #include "testing_helpers.h"
using namespace srslog; using namespace srslog;
namespace { namespace {
/// A Dummy implementation of a sink.
class sink_dummy : public sink
{
public:
detail::error_string write(detail::memory_buffer buffer) override
{
return {};
}
detail::error_string flush() override { return {}; }
};
/// A Spy implementation of a log backend. Tests can query if the push method /// A Spy implementation of a log backend. Tests can query if the push method
/// has been invoked. /// has been invoked.
class backend_spy : public detail::log_backend class backend_spy : public detail::log_backend
@ -38,11 +26,7 @@ class backend_spy : public detail::log_backend
public: public:
void start() override {} void start() override {}
void push(detail::log_entry&& entry) override void push(detail::log_entry&& entry) override { ++count; }
{
std::string result = fmt::vsprintf(entry.fmtstring, std::move(entry.store));
++count;
}
bool is_running() const override { return true; } bool is_running() const override { return true; }
@ -81,7 +65,7 @@ when_tracing_with_complete_event_then_one_event_is_generated(backend_spy& spy)
int main() int main()
{ {
sink_dummy s; test_dummies::sink_dummy s;
backend_spy backend; backend_spy backend;
log_channel c("test", s, backend); log_channel c("test", s, backend);

@ -12,6 +12,7 @@
#include "file_test_utils.h" #include "file_test_utils.h"
#include "src/srslog/sinks/file_sink.h" #include "src/srslog/sinks/file_sink.h"
#include "test_dummies.h"
#include "testing_helpers.h" #include "testing_helpers.h"
using namespace srslog; using namespace srslog;
@ -21,7 +22,10 @@ static constexpr char log_filename[] = "file_sink_test.log";
static bool when_data_is_written_to_file_then_contents_are_valid() static bool when_data_is_written_to_file_then_contents_are_valid()
{ {
file_test_utils::scoped_file_deleter deleter(log_filename); file_test_utils::scoped_file_deleter deleter(log_filename);
file_sink file(log_filename, 0); file_sink file(
log_filename,
0,
std::unique_ptr<log_formatter>(new test_dummies::log_formatter_dummy));
std::vector<std::string> entries; std::vector<std::string> entries;
for (unsigned i = 0; i != 10; ++i) { for (unsigned i = 0; i != 10; ++i) {
@ -45,7 +49,10 @@ class file_sink_subclass : public file_sink
{ {
public: public:
file_sink_subclass(std::string name, size_t max_size) : file_sink_subclass(std::string name, size_t max_size) :
file_sink(std::move(name), max_size) file_sink(
std::move(name),
max_size,
std::unique_ptr<log_formatter>(new test_dummies::log_formatter_dummy))
{} {}
uint32_t get_num_of_files() const { return get_file_index(); } uint32_t get_num_of_files() const { return get_file_index(); }

@ -1,112 +0,0 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2020 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "src/srslog/formatter.h"
#include "testing_helpers.h"
#include <numeric>
using namespace srslog;
/// Helper to build a log entry.
static detail::log_entry build_log_entry()
{
// Create a time point 50000us from epoch.
using tp_ty = std::chrono::time_point<std::chrono::high_resolution_clock>;
tp_ty tp(std::chrono::microseconds(50000));
fmt::dynamic_format_arg_store<fmt::printf_context> store;
store.push_back(88);
return {nullptr, tp, {10, true}, "Text %d", std::move(store), "ABC", 'Z'};
}
static bool when_fully_filled_log_entry_then_result_everything_is_formatted()
{
std::string result = format_log_entry_to_text(build_log_entry());
std::string expected = "00:00:00.050000 [ABC ] [Z] [ 10] Text 88\n";
ASSERT_EQ(result, expected);
return true;
}
static bool when_log_entry_without_name_is_passed_then_name_is_not_formatted()
{
auto entry = build_log_entry();
entry.log_name = "";
std::string result = format_log_entry_to_text(std::move(entry));
std::string expected = "00:00:00.050000 [Z] [ 10] Text 88\n";
ASSERT_EQ(result, expected);
return true;
}
static bool when_log_entry_without_tag_is_passed_then_tag_is_not_formatted()
{
auto entry = build_log_entry();
entry.log_tag = '\0';
std::string result = format_log_entry_to_text(std::move(entry));
std::string expected = "00:00:00.050000 [ABC ] [ 10] Text 88\n";
ASSERT_EQ(result, expected);
return true;
}
static bool
when_log_entry_without_context_is_passed_then_context_is_not_formatted()
{
auto entry = build_log_entry();
entry.context.enabled = false;
std::string result = format_log_entry_to_text(std::move(entry));
std::string expected = "00:00:00.050000 [ABC ] [Z] Text 88\n";
ASSERT_EQ(result, expected);
return true;
}
static bool when_log_entry_with_hex_dump_is_passed_then_hex_dump_is_formatted()
{
auto entry = build_log_entry();
entry.hex_dump.resize(20);
std::iota(entry.hex_dump.begin(), entry.hex_dump.end(), 0);
std::string result = format_log_entry_to_text(std::move(entry));
std::string expected =
"00:00:00.050000 [ABC ] [Z] [ 10] Text 88\n"
" 0000: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"
" 0010: 10 11 12 13\n";
ASSERT_EQ(result, expected);
return true;
}
int main()
{
TEST_FUNCTION(
when_fully_filled_log_entry_then_result_everything_is_formatted);
TEST_FUNCTION(
when_log_entry_without_name_is_passed_then_name_is_not_formatted);
TEST_FUNCTION(when_log_entry_without_tag_is_passed_then_tag_is_not_formatted);
TEST_FUNCTION(
when_log_entry_without_context_is_passed_then_context_is_not_formatted);
TEST_FUNCTION(
when_log_entry_with_hex_dump_is_passed_then_hex_dump_is_formatted);
return 0;
}

@ -11,7 +11,7 @@
*/ */
#include "src/srslog/log_backend_impl.h" #include "src/srslog/log_backend_impl.h"
#include "srslte/srslog/sink.h" #include "test_dummies.h"
#include "testing_helpers.h" #include "testing_helpers.h"
using namespace srslog; using namespace srslog;
@ -46,6 +46,10 @@ namespace {
class sink_spy : public sink class sink_spy : public sink
{ {
public: public:
sink_spy() :
sink(std::unique_ptr<log_formatter>(new test_dummies::log_formatter_dummy))
{}
detail::error_string write(detail::memory_buffer buffer) override detail::error_string write(detail::memory_buffer buffer) override
{ {
++count; ++count;
@ -88,7 +92,10 @@ static detail::log_entry build_log_entry(sink* s)
fmt::dynamic_format_arg_store<fmt::printf_context> store; fmt::dynamic_format_arg_store<fmt::printf_context> store;
store.push_back(88); store.push_back(88);
return {s, tp, {0, false}, "Text %d", std::move(store), "", '\0'}; return {
s,
[](detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer) {},
{tp, {0, false}, "Text %d", std::move(store), "", '\0'}};
} }
static bool when_backend_is_started_then_pushed_log_entries_are_sent_to_sink() static bool when_backend_is_started_then_pushed_log_entries_are_sent_to_sink()
@ -104,7 +111,27 @@ static bool when_backend_is_started_then_pushed_log_entries_are_sent_to_sink()
backend.stop(); backend.stop();
ASSERT_EQ(spy.write_invocation_count(), 1); ASSERT_EQ(spy.write_invocation_count(), 1);
ASSERT_NE(spy.received_buffer().find("Text 88"), std::string::npos);
return true;
}
static bool when_backend_is_started_then_backend_invokes_format_func()
{
test_dummies::sink_dummy s;
log_backend_impl backend;
backend.start();
auto entry = build_log_entry(&s);
unsigned counter = 0;
entry.format_func = [&counter](detail::log_entry_metadata&& metadata,
fmt::memory_buffer& buffer) { ++counter; };
backend.push(std::move(entry));
// Stop the backend to ensure the entry has been processed.
backend.stop();
ASSERT_EQ(counter, 1);
return true; return true;
} }
@ -117,7 +144,10 @@ namespace {
class sink_error_stub : public sink class sink_error_stub : public sink
{ {
public: public:
explicit sink_error_stub(std::string err) : err(std::move(err)) {} explicit sink_error_stub(std::string err) :
sink(std::unique_ptr<log_formatter>(new test_dummies::log_formatter_dummy)),
err(std::move(err))
{}
detail::error_string write(detail::memory_buffer buffer) override detail::error_string write(detail::memory_buffer buffer) override
{ {
@ -211,6 +241,7 @@ int main()
when_backend_is_not_started_then_pushed_log_entries_are_ignored); when_backend_is_not_started_then_pushed_log_entries_are_ignored);
TEST_FUNCTION( TEST_FUNCTION(
when_backend_is_started_then_pushed_log_entries_are_sent_to_sink); when_backend_is_started_then_pushed_log_entries_are_sent_to_sink);
TEST_FUNCTION(when_backend_is_started_then_backend_invokes_format_func);
TEST_FUNCTION(when_sink_write_fails_then_error_handler_is_invoked); TEST_FUNCTION(when_sink_write_fails_then_error_handler_is_invoked);
TEST_FUNCTION(when_handler_is_set_after_start_then_handler_is_not_used); TEST_FUNCTION(when_handler_is_set_after_start_then_handler_is_not_used);
TEST_FUNCTION(when_empty_handler_is_used_then_backend_does_not_crash); TEST_FUNCTION(when_empty_handler_is_used_then_backend_does_not_crash);

@ -11,42 +11,15 @@
*/ */
#include "srslte/srslog/log_channel.h" #include "srslte/srslog/log_channel.h"
#include "srslte/srslog/sink.h" #include "test_dummies.h"
#include "testing_helpers.h" #include "testing_helpers.h"
using namespace srslog; using namespace srslog;
namespace {
/// A Dummy implementation of a sink.
class sink_dummy : public sink
{
public:
detail::error_string write(detail::memory_buffer buffer) override
{
return {};
}
detail::error_string flush() override { return {}; }
};
/// A Dummy implementation of the log backend.
class backend_dummy : public detail::log_backend
{
public:
void start() override {}
void push(detail::log_entry&& entry) override {}
bool is_running() const override { return true; }
};
} // namespace
static bool when_log_channel_is_created_then_id_matches_expected_value() static bool when_log_channel_is_created_then_id_matches_expected_value()
{ {
backend_dummy backend; test_dummies::backend_dummy backend;
sink_dummy s; test_dummies::sink_dummy s;
log_channel log("id", s, backend); log_channel log("id", s, backend);
ASSERT_EQ(log.id(), "id"); ASSERT_EQ(log.id(), "id");
@ -56,8 +29,8 @@ static bool when_log_channel_is_created_then_id_matches_expected_value()
static bool when_log_channel_is_disabled_then_enabled_returns_false() static bool when_log_channel_is_disabled_then_enabled_returns_false()
{ {
backend_dummy backend; test_dummies::backend_dummy backend;
sink_dummy s; test_dummies::sink_dummy s;
log_channel log("id", s, backend); log_channel log("id", s, backend);
log.set_enabled(false); log.set_enabled(false);
@ -69,8 +42,8 @@ static bool when_log_channel_is_disabled_then_enabled_returns_false()
static bool when_log_channel_is_enabled_then_enabled_returns_true() static bool when_log_channel_is_enabled_then_enabled_returns_true()
{ {
backend_dummy backend; test_dummies::backend_dummy backend;
sink_dummy s; test_dummies::sink_dummy s;
log_channel log("id", s, backend); log_channel log("id", s, backend);
log.set_enabled(true); log.set_enabled(true);
@ -112,7 +85,7 @@ static bool
when_logging_in_log_channel_then_log_entry_is_pushed_into_the_backend() when_logging_in_log_channel_then_log_entry_is_pushed_into_the_backend()
{ {
backend_spy backend; backend_spy backend;
sink_dummy s; test_dummies::sink_dummy s;
log_channel log("id", s, backend); log_channel log("id", s, backend);
std::string fmtstring = "test"; std::string fmtstring = "test";
@ -126,7 +99,7 @@ when_logging_in_log_channel_then_log_entry_is_pushed_into_the_backend()
static bool when_logging_in_disabled_log_channel_then_log_entry_is_ignored() static bool when_logging_in_disabled_log_channel_then_log_entry_is_ignored()
{ {
backend_spy backend; backend_spy backend;
sink_dummy s; test_dummies::sink_dummy s;
log_channel log("id", s, backend); log_channel log("id", s, backend);
log.set_enabled(false); log.set_enabled(false);
@ -141,7 +114,7 @@ static bool when_logging_in_disabled_log_channel_then_log_entry_is_ignored()
static bool when_logging_then_filled_in_log_entry_is_pushed_into_the_backend() static bool when_logging_then_filled_in_log_entry_is_pushed_into_the_backend()
{ {
backend_spy backend; backend_spy backend;
sink_dummy s; test_dummies::sink_dummy s;
std::string name = "name"; std::string name = "name";
char tag = 'A'; char tag = 'A';
@ -157,12 +130,15 @@ static bool when_logging_then_filled_in_log_entry_is_pushed_into_the_backend()
ASSERT_EQ(backend.push_invocation_count(), 1); ASSERT_EQ(backend.push_invocation_count(), 1);
const detail::log_entry& entry = backend.last_entry(); const detail::log_entry& entry = backend.last_entry();
ASSERT_NE(entry.tp.time_since_epoch().count(), 0); ASSERT_EQ(&s, entry.s);
ASSERT_EQ(entry.context.value, ctx); ASSERT_NE(entry.format_func, nullptr);
ASSERT_EQ(entry.context.enabled, true); ASSERT_NE(entry.metadata.tp.time_since_epoch().count(), 0);
ASSERT_EQ(entry.fmtstring, fmtstring); ASSERT_EQ(entry.metadata.context.value, ctx);
ASSERT_EQ(entry.log_name, name); ASSERT_EQ(entry.metadata.context.enabled, true);
ASSERT_EQ(entry.log_tag, tag); ASSERT_EQ(entry.metadata.fmtstring, fmtstring);
ASSERT_EQ(entry.metadata.log_name, name);
ASSERT_EQ(entry.metadata.log_tag, tag);
ASSERT_EQ(entry.metadata.hex_dump.empty(), true);
return true; return true;
} }
@ -171,7 +147,7 @@ static bool
when_logging_with_hex_dump_then_filled_in_log_entry_is_pushed_into_the_backend() when_logging_with_hex_dump_then_filled_in_log_entry_is_pushed_into_the_backend()
{ {
backend_spy backend; backend_spy backend;
sink_dummy s; test_dummies::sink_dummy s;
std::string name = "name"; std::string name = "name";
char tag = 'A'; char tag = 'A';
@ -189,16 +165,19 @@ when_logging_with_hex_dump_then_filled_in_log_entry_is_pushed_into_the_backend()
ASSERT_EQ(backend.push_invocation_count(), 1); ASSERT_EQ(backend.push_invocation_count(), 1);
const detail::log_entry& entry = backend.last_entry(); const detail::log_entry& entry = backend.last_entry();
ASSERT_NE(entry.tp.time_since_epoch().count(), 0); ASSERT_EQ(&s, entry.s);
ASSERT_EQ(entry.context.value, ctx); ASSERT_NE(entry.format_func, nullptr);
ASSERT_EQ(entry.context.enabled, true); ASSERT_NE(entry.metadata.tp.time_since_epoch().count(), 0);
ASSERT_EQ(entry.fmtstring, fmtstring); ASSERT_EQ(entry.metadata.context.value, ctx);
ASSERT_EQ(entry.log_name, name); ASSERT_EQ(entry.metadata.context.enabled, true);
ASSERT_EQ(entry.log_tag, tag); ASSERT_EQ(entry.metadata.fmtstring, fmtstring);
ASSERT_EQ(entry.hex_dump.size(), 4); ASSERT_EQ(entry.metadata.log_name, name);
ASSERT_EQ( ASSERT_EQ(entry.metadata.log_tag, tag);
std::equal(entry.hex_dump.begin(), entry.hex_dump.end(), std::begin(hex)), ASSERT_EQ(entry.metadata.hex_dump.size(), 4);
true); ASSERT_EQ(std::equal(entry.metadata.hex_dump.begin(),
entry.metadata.hex_dump.end(),
std::begin(hex)),
true);
return true; return true;
} }
@ -207,10 +186,9 @@ static bool
when_hex_array_length_is_less_than_hex_log_max_size_then_array_length_is_used() when_hex_array_length_is_less_than_hex_log_max_size_then_array_length_is_used()
{ {
backend_spy backend; backend_spy backend;
sink_dummy s; test_dummies::sink_dummy s;
std::string name = "name"; std::string name = "name";
char tag = 'A';
log_channel log("id", s, backend); log_channel log("id", s, backend);
@ -223,10 +201,85 @@ when_hex_array_length_is_less_than_hex_log_max_size_then_array_length_is_used()
ASSERT_EQ(backend.push_invocation_count(), 1); ASSERT_EQ(backend.push_invocation_count(), 1);
const detail::log_entry& entry = backend.last_entry(); const detail::log_entry& entry = backend.last_entry();
ASSERT_EQ(entry.hex_dump.size(), 3); ASSERT_EQ(entry.metadata.hex_dump.size(), 3);
ASSERT_EQ( ASSERT_EQ(std::equal(entry.metadata.hex_dump.begin(),
std::equal(entry.hex_dump.begin(), entry.hex_dump.end(), std::begin(hex)), entry.metadata.hex_dump.end(),
true); std::begin(hex)),
true);
return true;
}
namespace {
DECLARE_METRIC("SNR", snr_t, int, "dB");
DECLARE_METRIC_SET("myset", my_set, snr_t);
using my_ctx = srslog::build_context_type<my_set>;
} // namespace
static bool
when_logging_with_context_then_filled_in_log_entry_is_pushed_into_the_backend()
{
backend_spy backend;
test_dummies::sink_dummy s;
std::string name = "name";
char tag = 'A';
log_channel log("id", s, backend, {name, tag, true});
uint32_t ctx_value = 4;
log.set_context(ctx_value);
my_ctx ctx("myctx");
log(ctx);
ASSERT_EQ(backend.push_invocation_count(), 1);
const detail::log_entry& entry = backend.last_entry();
ASSERT_EQ(&s, entry.s);
ASSERT_NE(entry.format_func, nullptr);
ASSERT_NE(entry.metadata.tp.time_since_epoch().count(), 0);
ASSERT_EQ(entry.metadata.context.value, ctx_value);
ASSERT_EQ(entry.metadata.context.enabled, true);
ASSERT_EQ(entry.metadata.log_name, name);
ASSERT_EQ(entry.metadata.log_tag, tag);
ASSERT_EQ(entry.metadata.hex_dump.empty(), true);
return true;
}
static bool
when_logging_with_context_and_message_then_filled_in_log_entry_is_pushed_into_the_backend()
{
backend_spy backend;
test_dummies::sink_dummy s;
std::string name = "name";
char tag = 'A';
log_channel log("id", s, backend, {name, tag, true});
uint32_t ctx_value = 4;
log.set_context(ctx_value);
std::string fmtstring = "test";
my_ctx ctx("myctx");
log(ctx, fmtstring, 10, 3.3);
ASSERT_EQ(backend.push_invocation_count(), 1);
const detail::log_entry& entry = backend.last_entry();
ASSERT_EQ(&s, entry.s);
ASSERT_NE(entry.format_func, nullptr);
ASSERT_NE(entry.metadata.tp.time_since_epoch().count(), 0);
ASSERT_EQ(entry.metadata.context.value, ctx_value);
ASSERT_EQ(entry.metadata.context.enabled, true);
ASSERT_EQ(entry.metadata.fmtstring, fmtstring);
ASSERT_EQ(entry.metadata.log_name, name);
ASSERT_EQ(entry.metadata.log_tag, tag);
ASSERT_EQ(entry.metadata.hex_dump.empty(), true);
return true; return true;
} }
@ -245,6 +298,10 @@ int main()
when_logging_with_hex_dump_then_filled_in_log_entry_is_pushed_into_the_backend); when_logging_with_hex_dump_then_filled_in_log_entry_is_pushed_into_the_backend);
TEST_FUNCTION( TEST_FUNCTION(
when_hex_array_length_is_less_than_hex_log_max_size_then_array_length_is_used); when_hex_array_length_is_less_than_hex_log_max_size_then_array_length_is_used);
TEST_FUNCTION(
when_logging_with_context_then_filled_in_log_entry_is_pushed_into_the_backend);
TEST_FUNCTION(
when_logging_with_context_and_message_then_filled_in_log_entry_is_pushed_into_the_backend);
return 0; return 0;
} }

@ -11,7 +11,7 @@
*/ */
#include "srslte/srslog/logger.h" #include "srslte/srslog/logger.h"
#include "srslte/srslog/sink.h" #include "test_dummies.h"
#include "testing_helpers.h" #include "testing_helpers.h"
using namespace srslog; using namespace srslog;
@ -20,29 +20,6 @@ static constexpr char logger_id[] = "TestLogger";
namespace { namespace {
/// A Dummy implementation of a sink.
class sink_dummy : public sink
{
public:
detail::error_string write(detail::memory_buffer buffer) override
{
return {};
}
detail::error_string flush() override { return {}; }
};
/// A Dummy implementation of the log backend.
class backend_dummy : public detail::log_backend
{
public:
void start() override {}
void push(detail::log_entry&& entry) override {}
bool is_running() const override { return true; }
};
/// Definition of a three level logger /// Definition of a three level logger
enum class test_logger_levels { error, warning, info, LAST }; enum class test_logger_levels { error, warning, info, LAST };
struct test_logger_channels { struct test_logger_channels {
@ -56,8 +33,8 @@ using test_logger = build_logger_type<test_logger_channels, test_logger_levels>;
static bool when_logger_is_created_then_id_matches_expected_value() static bool when_logger_is_created_then_id_matches_expected_value()
{ {
backend_dummy backend; test_dummies::backend_dummy backend;
sink_dummy s; test_dummies::sink_dummy s;
log_channel error("err", s, backend); log_channel error("err", s, backend);
log_channel warning("warning", s, backend); log_channel warning("warning", s, backend);
log_channel info("info", s, backend); log_channel info("info", s, backend);
@ -71,8 +48,8 @@ static bool when_logger_is_created_then_id_matches_expected_value()
static bool when_level_is_set_to_error_then_info_and_warning_is_disabled() static bool when_level_is_set_to_error_then_info_and_warning_is_disabled()
{ {
backend_dummy backend; test_dummies::backend_dummy backend;
sink_dummy s; test_dummies::sink_dummy s;
log_channel error("err", s, backend); log_channel error("err", s, backend);
log_channel warning("warning", s, backend); log_channel warning("warning", s, backend);
log_channel info("info", s, backend); log_channel info("info", s, backend);
@ -89,8 +66,8 @@ static bool when_level_is_set_to_error_then_info_and_warning_is_disabled()
static bool when_level_is_set_to_warning_then_info_is_disabled() static bool when_level_is_set_to_warning_then_info_is_disabled()
{ {
backend_dummy backend; test_dummies::backend_dummy backend;
sink_dummy s; test_dummies::sink_dummy s;
log_channel error("err", s, backend); log_channel error("err", s, backend);
log_channel warning("warning", s, backend); log_channel warning("warning", s, backend);
log_channel info("info", s, backend); log_channel info("info", s, backend);
@ -107,8 +84,8 @@ static bool when_level_is_set_to_warning_then_info_is_disabled()
static bool when_level_is_set_to_info_then_all_are_enabled() static bool when_level_is_set_to_info_then_all_are_enabled()
{ {
backend_dummy backend; test_dummies::backend_dummy backend;
sink_dummy s; test_dummies::sink_dummy s;
log_channel error("err", s, backend); log_channel error("err", s, backend);
log_channel warning("warning", s, backend); log_channel warning("warning", s, backend);
log_channel info("info", s, backend); log_channel info("info", s, backend);

@ -10,8 +10,8 @@
* *
*/ */
#include "srslte/srslog/sink.h"
#include "srslte/srslog/srslog.h" #include "srslte/srslog/srslog.h"
#include "test_dummies.h"
#include "testing_helpers.h" #include "testing_helpers.h"
using namespace srslog; using namespace srslog;
@ -26,6 +26,30 @@ static_assert(std::is_trivially_copyable<detail::memory_buffer>::value,
"Expected to be trivially copyable"); "Expected to be trivially copyable");
#endif #endif
static bool when_srslog_is_created_then_stdout_and_stderr_sinks_exist()
{
ASSERT_NE(find_sink("stdout"), nullptr);
ASSERT_NE(find_sink("stderr"), nullptr);
return true;
}
static bool when_no_installed_default_formatter_then_a_formatter_is_returned()
{
ASSERT_NE(get_default_log_formatter(), nullptr);
return true;
}
static bool when_no_installed_default_sink_then_stdout_sink_is_used()
{
sink& default_sink = get_default_sink();
ASSERT_EQ(&default_sink, find_sink("stdout"));
return true;
}
static bool when_fetching_channel_then_channel_instance_is_returned() static bool when_fetching_channel_then_channel_instance_is_returned()
{ {
log_channel& channel1 = fetch_log_channel(test_id1); log_channel& channel1 = fetch_log_channel(test_id1);
@ -70,6 +94,17 @@ when_id_with_sharps_is_passed_then_channel_is_fetched_with_clean_id()
return true; return true;
} }
static bool when_channel_already_exists_then_fetch_returns_channel()
{
log_channel& channel1 = fetch_log_channel("b1");
log_channel& channel2 = fetch_log_channel("b2", fetch_stdout_sink(), {});
ASSERT_EQ(&channel1, &fetch_log_channel("b1"));
ASSERT_EQ(&channel2, &fetch_log_channel("b2"));
return true;
}
static constexpr char logger_id[] = "TestLogger"; static constexpr char logger_id[] = "TestLogger";
static constexpr char basic_logger_id1[] = "BasicTestLogger"; static constexpr char basic_logger_id1[] = "BasicTestLogger";
static constexpr char basic_logger_id2[] = "BasicTestLogger2"; static constexpr char basic_logger_id2[] = "BasicTestLogger2";
@ -92,7 +127,8 @@ static bool when_fetching_logger_then_logger_instance_is_returned()
static bool when_fetching_basic_logger_then_basic_logger_instance_is_returned() static bool when_fetching_basic_logger_then_basic_logger_instance_is_returned()
{ {
basic_logger& logger1 = fetch_basic_logger(basic_logger_id1); basic_logger& logger1 = fetch_basic_logger(basic_logger_id1);
basic_logger& logger2 = fetch_basic_logger(basic_logger_id2, fetch_stdout_sink()); basic_logger& logger2 =
fetch_basic_logger(basic_logger_id2, fetch_stdout_sink());
ASSERT_EQ(logger1.id(), basic_logger_id1); ASSERT_EQ(logger1.id(), basic_logger_id1);
ASSERT_EQ(logger2.id(), basic_logger_id2); ASSERT_EQ(logger2.id(), basic_logger_id2);
@ -110,6 +146,17 @@ static bool when_valid_id_and_type_is_passed_then_logger_is_found()
return true; return true;
} }
static bool when_basic_logger_already_exists_then_fetch_returns_logger()
{
basic_logger& logger1 = fetch_basic_logger("b1");
basic_logger& logger2 = fetch_basic_logger("b2", fetch_stdout_sink());
ASSERT_EQ(&logger1, &fetch_basic_logger("b1"));
ASSERT_EQ(&logger2, &fetch_basic_logger("b2"));
return true;
}
static bool when_invalid_id_with_valid_type_is_passed_then_no_logger_is_found() static bool when_invalid_id_with_valid_type_is_passed_then_no_logger_is_found()
{ {
ASSERT_EQ(find_logger<basic_logger>("invalid"), nullptr); ASSERT_EQ(find_logger<basic_logger>("invalid"), nullptr);
@ -142,10 +189,11 @@ static constexpr char file_name[] = "file_fetch_test.txt";
static bool when_file_sink_is_fetched_then_sink_instance_is_returned() static bool when_file_sink_is_fetched_then_sink_instance_is_returned()
{ {
fetch_file_sink(file_name); sink& s1 = fetch_file_sink(file_name);
sink* s = find_sink(file_name); sink* s2 = find_sink(file_name);
ASSERT_NE(s, nullptr); ASSERT_EQ(&s1, s2);
ASSERT_NE(s2, nullptr);
return true; return true;
} }
@ -157,37 +205,93 @@ static bool when_invalid_id_is_passed_then_no_sink_is_found()
return true; return true;
} }
static bool when_no_installed_default_sink_then_stdout_sink_is_used() static bool
when_setting_stderr_as_default_then_get_default_returns_stderr_sink()
{ {
set_default_sink(fetch_stderr_sink());
sink& default_sink = get_default_sink(); sink& default_sink = get_default_sink();
ASSERT_EQ(&default_sink, &fetch_stdout_sink()); ASSERT_EQ(&default_sink, &fetch_stderr_sink());
return true; return true;
} }
static bool static bool
when_setting_stderr_as_default_then_get_default_returns_stderr_sink() when_creating_non_default_stdout_and_stderr_sinks_then_they_are_created()
{ {
set_default_sink(fetch_stderr_sink()); auto& out1 = fetch_stdout_sink("stdout1");
sink& default_sink = get_default_sink(); auto& err1 = fetch_stderr_sink("stderr1");
ASSERT_EQ(&default_sink, &fetch_stderr_sink()); ASSERT_EQ(&out1, find_sink("stdout1"));
ASSERT_EQ(&err1, find_sink("stderr1"));
return true;
}
static constexpr char custom_sink_name[] = "custom_sink";
static bool when_setting_custom_sink_first_time_then_insertion_succeeds()
{
std::unique_ptr<sink> custom(new test_dummies::sink_dummy);
sink* s = custom.get();
bool ret = install_custom_sink(custom_sink_name, std::move(custom));
ASSERT_EQ(ret, true);
sink* found_sink = find_sink(custom_sink_name);
ASSERT_NE(found_sink, nullptr);
ASSERT_EQ(found_sink, s);
return true;
}
static bool when_setting_custom_sink_twice_then_insertion_fails()
{
std::unique_ptr<sink> custom(new test_dummies::sink_dummy);
sink* s = custom.get();
bool ret = install_custom_sink(custom_sink_name, std::move(custom));
ASSERT_EQ(ret, false);
sink* found_sink = find_sink(custom_sink_name);
ASSERT_NE(found_sink, nullptr);
ASSERT_NE(found_sink, s);
return true;
}
static bool
when_default_formatter_is_installed_then_getter_returns_same_formatter()
{
auto f =
std::unique_ptr<log_formatter>(new test_dummies::log_formatter_dummy);
set_default_log_formatter(std::move(f));
// Dummy formatter does not implement the clone method, thus the unique
// pointer will be nullptr.
auto f2 = get_default_log_formatter();
ASSERT_EQ(f2.get(), nullptr);
return true; return true;
} }
int main() int main()
{ {
TEST_FUNCTION(when_srslog_is_created_then_stdout_and_stderr_sinks_exist);
TEST_FUNCTION(
when_no_installed_default_formatter_then_a_formatter_is_returned);
TEST_FUNCTION(when_no_installed_default_sink_then_stdout_sink_is_used);
TEST_FUNCTION(when_fetching_channel_then_channel_instance_is_returned); TEST_FUNCTION(when_fetching_channel_then_channel_instance_is_returned);
TEST_FUNCTION(when_valid_id_is_passed_then_channel_is_found); TEST_FUNCTION(when_valid_id_is_passed_then_channel_is_found);
TEST_FUNCTION(when_non_existent_id_is_passed_then_nothing_is_found); TEST_FUNCTION(when_non_existent_id_is_passed_then_nothing_is_found);
TEST_FUNCTION( TEST_FUNCTION(
when_id_with_sharps_is_passed_then_channel_is_fetched_with_clean_id); when_id_with_sharps_is_passed_then_channel_is_fetched_with_clean_id);
TEST_FUNCTION(when_channel_already_exists_then_fetch_returns_channel);
TEST_FUNCTION(when_fetching_logger_then_logger_instance_is_returned); TEST_FUNCTION(when_fetching_logger_then_logger_instance_is_returned);
TEST_FUNCTION( TEST_FUNCTION(
when_fetching_basic_logger_then_basic_logger_instance_is_returned); when_fetching_basic_logger_then_basic_logger_instance_is_returned);
TEST_FUNCTION(when_valid_id_and_type_is_passed_then_logger_is_found); TEST_FUNCTION(when_valid_id_and_type_is_passed_then_logger_is_found);
TEST_FUNCTION(when_basic_logger_already_exists_then_fetch_returns_logger);
TEST_FUNCTION( TEST_FUNCTION(
when_invalid_id_with_valid_type_is_passed_then_no_logger_is_found); when_invalid_id_with_valid_type_is_passed_then_no_logger_is_found);
TEST_FUNCTION(when_invalid_id_and_type_is_passed_then_no_logger_is_found); TEST_FUNCTION(when_invalid_id_and_type_is_passed_then_no_logger_is_found);
@ -195,9 +299,14 @@ int main()
when_valid_id_with_invalid_type_is_passed_then_no_logger_is_found); when_valid_id_with_invalid_type_is_passed_then_no_logger_is_found);
TEST_FUNCTION(when_file_sink_is_fetched_then_sink_instance_is_returned); TEST_FUNCTION(when_file_sink_is_fetched_then_sink_instance_is_returned);
TEST_FUNCTION(when_invalid_id_is_passed_then_no_sink_is_found); TEST_FUNCTION(when_invalid_id_is_passed_then_no_sink_is_found);
TEST_FUNCTION(when_no_installed_default_sink_then_stdout_sink_is_used);
TEST_FUNCTION( TEST_FUNCTION(
when_setting_stderr_as_default_then_get_default_returns_stderr_sink); when_setting_stderr_as_default_then_get_default_returns_stderr_sink);
TEST_FUNCTION(
when_creating_non_default_stdout_and_stderr_sinks_then_they_are_created);
TEST_FUNCTION(when_setting_custom_sink_first_time_then_insertion_succeeds);
TEST_FUNCTION(when_setting_custom_sink_twice_then_insertion_fails);
TEST_FUNCTION(
when_default_formatter_is_installed_then_getter_returns_same_formatter);
return 0; return 0;
} }

Loading…
Cancel
Save