- Implement a pool to store dyn arg store objects in srslog to avoid allocations when passing arguments to the backend.

- Use string views inside the log formatters to avoid allocations.
- Avoid a std::string when using fmt::vsprintf, instead favour fmt::vprintf.
master
faluco 4 years ago committed by faluco
parent 1a0891df51
commit e8395c7474

@ -13,6 +13,8 @@
#ifndef SRSLOG_DETAIL_LOG_BACKEND_H
#define SRSLOG_DETAIL_LOG_BACKEND_H
#include "srsran/srslog/bundled/fmt/printf.h"
namespace srslog {
namespace detail {
@ -31,6 +33,9 @@ public:
/// NOTE: Calling this function more than once has no side effects.
virtual void start() = 0;
/// Allocates a dyn_arg_store and returns a pointer to it on success, otherwise returns nullptr.
virtual fmt::dynamic_format_arg_store<fmt::printf_context>* alloc_arg_store() = 0;
/// Pushes a log entry into the backend. Returns true on success, otherwise
/// false.
virtual bool push(log_entry&& entry) = 0;

@ -37,7 +37,7 @@ struct log_entry_metadata {
std::chrono::high_resolution_clock::time_point tp;
log_context context;
const char* fmtstring;
fmt::dynamic_format_arg_store<fmt::printf_context> store;
fmt::dynamic_format_arg_store<fmt::printf_context>* store;
std::string log_name;
char log_tag;
small_str_buffer small_str;

@ -0,0 +1,21 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 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_DETAIL_SUPPORT_BACKEND_CAPACITY_H
#define SRSLOG_DETAIL_SUPPORT_BACKEND_CAPACITY_H
/// Take this default value if users did not specify any custom size.
#ifndef SRSLOG_QUEUE_CAPACITY
#define SRSLOG_QUEUE_CAPACITY 8192
#endif
#endif // SRSLOG_DETAIL_SUPPORT_BACKEND_CAPACITY_H

@ -0,0 +1,78 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 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_DETAIL_SUPPORT_DYN_ARG_STORE_POOL_H
#define SRSLOG_DETAIL_SUPPORT_DYN_ARG_STORE_POOL_H
#include "srsran/srslog/bundled/fmt/printf.h"
#include "srsran/srslog/detail/support/backend_capacity.h"
namespace srslog {
namespace detail {
/// Keeps a pool of dynamic_format_arg_store objects. The main reason for this class is that the arg store objects are
/// implemented with std::vectors, so we want to avoid allocating memory each time we create a new object. Instead,
/// reserve memory for each vector during initialization and recycle the objects.
class dyn_arg_store_pool
{
public:
dyn_arg_store_pool()
{
pool.resize(SRSLOG_QUEUE_CAPACITY);
for (auto& elem : pool) {
// Reserve for 10 normal and 2 named arguments.
elem.reserve(10, 2);
}
free_list.reserve(SRSLOG_QUEUE_CAPACITY);
for (auto& elem : pool) {
free_list.push_back(&elem);
}
}
/// Returns a pointer to a free dyn arg store object, otherwise returns nullptr.
fmt::dynamic_format_arg_store<fmt::printf_context>* alloc()
{
scoped_lock lock(m);
if (free_list.empty()) {
return nullptr;
}
auto* p = free_list.back();
free_list.pop_back();
return p;
}
/// Deallocate the given dyn arg store object returning it to the pool.
void dealloc(fmt::dynamic_format_arg_store<fmt::printf_context>* p)
{
if (!p) {
return;
}
scoped_lock lock(m);
p->clear();
free_list.push_back(p);
}
private:
std::vector<fmt::dynamic_format_arg_store<fmt::printf_context> > pool;
std::vector<fmt::dynamic_format_arg_store<fmt::printf_context>*> free_list;
mutable mutex m;
};
} // namespace detail
} // namespace srslog
#endif // SRSLOG_DETAIL_SUPPORT_DYN_ARG_STORE_POOL_H

@ -13,13 +13,10 @@
#ifndef SRSLOG_DETAIL_SUPPORT_WORK_QUEUE_H
#define SRSLOG_DETAIL_SUPPORT_WORK_QUEUE_H
#include "srsran/srslog/detail/support/backend_capacity.h"
#include "srsran/srslog/detail/support/thread_utils.h"
#include <queue>
#ifndef SRSLOG_QUEUE_CAPACITY
#define SRSLOG_QUEUE_CAPACITY 8192
#endif
namespace srslog {
namespace detail {

@ -34,7 +34,7 @@ struct metric_value_formatter {
/// Default metric value formatter. Users that want to override this behaviour
/// should add an specialization of the metric they want to customize.
template <typename Ty, typename Name, typename Units>
struct metric_value_formatter<metric<Ty, Name, Units>> {
struct metric_value_formatter<metric<Ty, Name, Units> > {
template <typename T>
void format(const T& v, fmt::memory_buffer& buffer)
{
@ -59,54 +59,38 @@ public:
virtual std::unique_ptr<log_formatter> clone() const = 0;
/// Formats the log entry into the input buffer.
virtual void format(detail::log_entry_metadata&& metadata,
fmt::memory_buffer& buffer) = 0;
virtual void format(detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer) = 0;
/// Formats the context and log entry into the input buffer.
template <typename... Ts>
void format_ctx(const srslog::context<Ts...>& ctx,
detail::log_entry_metadata&& metadata,
fmt::memory_buffer& buffer)
void format_ctx(const srslog::context<Ts...>& ctx, detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer)
{
format_context_begin(metadata, ctx.name(), sizeof...(Ts), buffer);
iterate_tuple(ctx.contents(),
1,
buffer,
detail::make_index_sequence<sizeof...(Ts)>{});
iterate_tuple(ctx.contents(), 1, buffer, detail::make_index_sequence<sizeof...(Ts)>{});
format_context_end(metadata, ctx.name(), buffer);
}
private:
/// Processes all elements in a tuple.
template <typename... Ts, std::size_t... Is>
void iterate_tuple(const std::tuple<Ts...>& t,
unsigned level,
fmt::memory_buffer& buffer,
detail::index_sequence<Is...>)
void
iterate_tuple(const std::tuple<Ts...>& t, unsigned level, fmt::memory_buffer& buffer, detail::index_sequence<Is...>)
{
(void)std::initializer_list<int>{
(process_element(std::get<Is>(t), level, buffer), 0)...};
(void)std::initializer_list<int>{(process_element(std::get<Is>(t), level, buffer), 0)...};
}
/// Processes the input metric set.
template <typename Name, typename... Ts>
void process_element(const metric_set<Name, Ts...>& ms,
unsigned level,
fmt::memory_buffer& buffer)
void process_element(const metric_set<Name, Ts...>& ms, unsigned level, fmt::memory_buffer& buffer)
{
format_metric_set_begin(ms.name(), sizeof...(Ts), level, buffer);
iterate_tuple(ms.contents(),
level + 1,
buffer,
detail::make_index_sequence<sizeof...(Ts)>{});
iterate_tuple(ms.contents(), level + 1, buffer, detail::make_index_sequence<sizeof...(Ts)>{});
format_metric_set_end(ms.name(), level, buffer);
}
/// Processes the input metric list.
template <typename Name, typename T>
void process_element(const metric_list<Name, T>& list,
unsigned level,
fmt::memory_buffer& buffer)
void process_element(const metric_list<Name, T>& list, unsigned level, fmt::memory_buffer& buffer)
{
format_list_begin(list.name(), list.size(), level, buffer);
for (const auto& elem : list) {
@ -117,16 +101,13 @@ private:
/// Processes the input metric.
template <typename Ty, typename Name, typename Units>
void process_element(const metric<Ty, Name, Units>& t,
unsigned level,
fmt::memory_buffer& buffer)
void process_element(const metric<Ty, Name, Units>& t, unsigned level, fmt::memory_buffer& buffer)
{
fmt::memory_buffer value;
metric_value_formatter<typename std::decay<decltype(t)>::type>{}.format(
t.value, value);
metric_value_formatter<typename std::decay<decltype(t)>::type>{}.format(t.value, value);
value.push_back('\0');
format_metric(
t.name(), fmt::to_string(value), t.units(), t.kind(), level, buffer);
format_metric(t.name(), value.data(), t.units(), t.kind(), level, buffer);
}
private:
@ -137,44 +118,35 @@ private:
/// This callback gets called at the beginning of the context formatting
/// algorithm.
virtual void format_context_begin(const detail::log_entry_metadata& md,
const std::string& ctx_name,
fmt::string_view ctx_name,
unsigned size,
fmt::memory_buffer& buffer) = 0;
/// This callback gets called at the end of the context formatting algorithm.
virtual void format_context_end(const detail::log_entry_metadata& md,
const std::string& ctx_name,
fmt::memory_buffer& buffer) = 0;
virtual void
format_context_end(const detail::log_entry_metadata& md, fmt::string_view ctx_name, fmt::memory_buffer& buffer) = 0;
/// This callback gets called at the beginning of a metric set formatting
/// procedure.
virtual void format_metric_set_begin(const std::string& set_name,
unsigned size,
unsigned level,
fmt::memory_buffer& buffer) = 0;
virtual void
format_metric_set_begin(fmt::string_view set_name, unsigned size, unsigned level, fmt::memory_buffer& buffer) = 0;
/// This callback gets called at the beginning of a metric set formatting end.
virtual void format_metric_set_end(const std::string& set_name,
unsigned level,
fmt::memory_buffer& buffer) = 0;
virtual void format_metric_set_end(fmt::string_view set_name, unsigned level, fmt::memory_buffer& buffer) = 0;
/// This callback gets called at the beginning of a metric list formatting
/// procedure.
virtual void format_list_begin(const std::string& list_name,
unsigned size,
unsigned level,
fmt::memory_buffer& buffer) = 0;
virtual void
format_list_begin(fmt::string_view list_name, unsigned size, unsigned level, fmt::memory_buffer& buffer) = 0;
/// This callback gets called at the end of a metric list formatting
/// procedure.
virtual void format_list_end(const std::string& list_name,
unsigned level,
fmt::memory_buffer& buffer) = 0;
virtual void format_list_end(fmt::string_view list_name, unsigned level, fmt::memory_buffer& buffer) = 0;
/// This callback gets called for each metric.
virtual void format_metric(const std::string& metric_name,
const std::string& metric_value,
const std::string& metric_units,
virtual void format_metric(fmt::string_view metric_name,
fmt::string_view metric_value,
fmt::string_view metric_units,
metric_kind kind,
unsigned level,
fmt::memory_buffer& buffer) = 0;

@ -47,14 +47,9 @@ struct log_channel_config {
class log_channel
{
public:
log_channel(std::string id, sink& s, detail::log_backend& backend) :
log_channel(std::move(id), s, backend, {})
{}
log_channel(std::string id, sink& s, detail::log_backend& backend) : log_channel(std::move(id), s, backend, {}) {}
log_channel(std::string id,
sink& s,
detail::log_backend& backend,
log_channel_config config) :
log_channel(std::string id, sink& s, detail::log_backend& backend, log_channel_config config) :
log_id(std::move(id)),
log_sink(s),
backend(backend),
@ -96,22 +91,22 @@ public:
}
// Populate the store with all incoming arguments.
fmt::dynamic_format_arg_store<fmt::printf_context> store;
(void)std::initializer_list<int>{
(store.push_back(std::forward<Args>(args)), 0)...};
auto* store = backend.alloc_arg_store();
if (!store) {
return;
}
(void)std::initializer_list<int>{(store->push_back(std::forward<Args>(args)), 0)...};
// Send the log entry to the backend.
log_formatter& formatter = log_sink.get_formatter();
detail::log_entry entry = {
&log_sink,
[&formatter](detail::log_entry_metadata&& metadata,
fmt::memory_buffer& buffer) {
detail::log_entry entry = {&log_sink,
[&formatter](detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer) {
formatter.format(std::move(metadata), buffer);
},
{std::chrono::high_resolution_clock::now(),
{ctx_value, should_print_context},
fmtstr,
std::move(store),
store,
log_name,
log_tag,
small_str_buffer()}};
@ -120,7 +115,7 @@ public:
/// Builds the provided log entry and passes it to the backend. When the
/// channel is disabled the log entry will be discarded.
void operator()(small_str_buffer &&str)
void operator()(small_str_buffer&& str)
{
if (!enabled()) {
return;
@ -128,16 +123,14 @@ public:
// Send the log entry to the backend.
log_formatter& formatter = log_sink.get_formatter();
detail::log_entry entry = {
&log_sink,
[&formatter](detail::log_entry_metadata&& metadata,
fmt::memory_buffer& buffer) {
detail::log_entry entry = {&log_sink,
[&formatter](detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer) {
formatter.format(std::move(metadata), buffer);
},
{std::chrono::high_resolution_clock::now(),
{ctx_value, should_print_context},
nullptr,
{},
nullptr,
log_name,
log_tag,
std::move(str)}};
@ -147,36 +140,34 @@ public:
/// Builds the provided log entry and passes it to the backend. When the
/// channel is disabled the log entry will be discarded.
template <typename... Args>
void operator()(const uint8_t* buffer,
size_t len,
const char* fmtstr,
Args&&... args)
void operator()(const uint8_t* buffer, size_t len, const char* 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(std::forward<Args>(args)), 0)...};
auto* store = backend.alloc_arg_store();
if (!store) {
return;
}
(void)std::initializer_list<int>{(store->push_back(std::forward<Args>(args)), 0)...};
// Calculate the length to capture in the buffer.
if (hex_max_size >= 0)
if (hex_max_size >= 0) {
len = std::min<size_t>(len, hex_max_size);
}
// Send the log entry to the backend.
log_formatter& formatter = log_sink.get_formatter();
detail::log_entry entry = {
&log_sink,
[&formatter](detail::log_entry_metadata&& metadata,
fmt::memory_buffer& buffer) {
detail::log_entry entry = {&log_sink,
[&formatter](detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer) {
formatter.format(std::move(metadata), buffer);
},
{std::chrono::high_resolution_clock::now(),
{ctx_value, should_print_context},
fmtstr,
std::move(store),
store,
log_name,
log_tag,
small_str_buffer(),
@ -195,16 +186,14 @@ public:
// 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) {
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},
nullptr,
{},
nullptr,
log_name,
log_tag,
small_str_buffer()}};
@ -221,22 +210,22 @@ public:
}
// Populate the store with all incoming arguments.
fmt::dynamic_format_arg_store<fmt::printf_context> store;
(void)std::initializer_list<int>{
(store.push_back(std::forward<Args>(args)), 0)...};
auto* store = backend.alloc_arg_store();
if (!store) {
return;
}
(void)std::initializer_list<int>{(store->push_back(std::forward<Args>(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) {
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),
store,
log_name,
log_tag,
small_str_buffer()}};

@ -95,8 +95,14 @@ void backend_worker::process_log_entry(detail::log_entry&& entry)
if (entry.metadata.small_str.size()) {
entry.metadata.fmtstring = entry.metadata.small_str.data();
}
// Save the pointer before moving the entry.
auto* arg_store = entry.metadata.store;
entry.format_func(std::move(entry.metadata), fmt_buffer);
arg_pool.dealloc(arg_store);
if (auto err_str = entry.s->write({fmt_buffer.data(), fmt_buffer.size()})) {
err_handler(err_str.get_error());
}

@ -14,6 +14,7 @@
#define SRSLOG_BACKEND_WORKER_H
#include "srsran/srslog/detail/log_entry.h"
#include "srsran/srslog/detail/support/dyn_arg_store_pool.h"
#include "srsran/srslog/detail/support/work_queue.h"
#include "srsran/srslog/shared_types.h"
#include <mutex>
@ -31,8 +32,8 @@ class backend_worker
static constexpr unsigned sleep_period_ms = 500;
public:
explicit backend_worker(detail::work_queue<detail::log_entry>& queue) :
queue(queue), running_flag(false)
explicit backend_worker(detail::work_queue<detail::log_entry>& queue, detail::dyn_arg_store_pool& arg_pool) :
queue(queue), arg_pool(arg_pool), running_flag(false)
{}
backend_worker(const backend_worker&) = delete;
@ -94,8 +95,7 @@ private:
void report_queue_on_full_once()
{
if (queue.is_almost_full()) {
err_handler(
fmt::format("The backend queue size is about to reach its maximum "
err_handler(fmt::format("The backend queue size is about to reach its maximum "
"capacity of {} elements, new log entries will get "
"discarded.\nConsider increasing the queue capacity.",
queue.get_capacity()));
@ -105,10 +105,9 @@ private:
private:
detail::work_queue<detail::log_entry>& queue;
detail::dyn_arg_store_pool& arg_pool;
detail::shared_variable<bool> running_flag;
error_handler err_handler = [](const std::string& error) {
fmt::print(stderr, "srsLog error - {}\n", error);
};
error_handler err_handler = [](const std::string& error) { fmt::print(stderr, "srsLog error - {}\n", error); };
std::once_flag start_once_flag;
std::thread worker_thread;
fmt::memory_buffer fmt_buffer;

@ -20,26 +20,31 @@ std::unique_ptr<log_formatter> json_formatter::clone() const
return std::unique_ptr<log_formatter>(new json_formatter);
}
void json_formatter::format(detail::log_entry_metadata&& metadata,
fmt::memory_buffer& buffer)
void json_formatter::format(detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer)
{
fmt::format_to(buffer,
"{{\n"
" \"log_entry\": \"{}\"",
fmt::vsprintf(metadata.fmtstring, std::move(metadata.store)));
" \"log_entry\": \"");
if (metadata.fmtstring) {
if (metadata.store) {
fmt::basic_format_args<fmt::basic_printf_context_t<char> > args(*metadata.store);
fmt::vprintf(buffer, fmt::to_string_view(metadata.fmtstring), args);
fmt::format_to(buffer, fmt::to_string_view("\""));
} else {
fmt::format_to(buffer, "{}\"", metadata.fmtstring);
}
}
if (!metadata.hex_dump.empty()) {
fmt::format_to(
buffer,
",\n \"hex_dump\": \"{:02x}\"",
fmt::join(metadata.hex_dump.cbegin(), metadata.hex_dump.cend(), " "));
buffer, ",\n \"hex_dump\": \"{:02x}\"", fmt::join(metadata.hex_dump.cbegin(), metadata.hex_dump.cend(), " "));
}
fmt::format_to(buffer, "\n}}\n");
}
void json_formatter::format_context_begin(const detail::log_entry_metadata& md,
const std::string& ctx_name,
fmt::string_view ctx_name,
unsigned size,
fmt::memory_buffer& buffer)
{
@ -50,14 +55,19 @@ void json_formatter::format_context_begin(const detail::log_entry_metadata& md,
push_scope(size);
if (md.fmtstring) {
fmt::format_to(buffer,
" \"log_entry\": \"{}\",\n",
fmt::vsprintf(md.fmtstring, std::move(md.store)));
if (md.store) {
fmt::format_to(buffer, " \"log_entry\": \"");
fmt::basic_format_args<fmt::basic_printf_context_t<char> > args(*md.store);
fmt::vprintf(buffer, fmt::to_string_view(md.fmtstring), args);
fmt::format_to(buffer, "\",\n");
} else {
fmt::format_to(buffer, " \"log_entry\": \"{}\",\n", md.fmtstring);
}
}
}
void json_formatter::format_context_end(const detail::log_entry_metadata& md,
const std::string& ctx_name,
fmt::string_view ctx_name,
fmt::memory_buffer& buffer)
{
pop_scope();
@ -67,7 +77,7 @@ void json_formatter::format_context_end(const detail::log_entry_metadata& md,
assert(nest_level == 0 && "Nesting level should be 0");
}
void json_formatter::format_metric_set_begin(const std::string& set_name,
void json_formatter::format_metric_set_begin(fmt::string_view set_name,
unsigned size,
unsigned level,
fmt::memory_buffer& buffer)
@ -90,27 +100,20 @@ void json_formatter::format_metric_set_begin(const std::string& set_name,
push_scope(size);
}
void json_formatter::format_metric_set_end(const std::string& set_name,
unsigned level,
fmt::memory_buffer& buffer)
void json_formatter::format_metric_set_end(fmt::string_view set_name, unsigned level, fmt::memory_buffer& buffer)
{
pop_scope();
fmt::format_to(buffer,
"{: <{}}}}{}\n",
' ',
indents(level),
needs_comma() && !in_list_scope() ? "," : "");
fmt::format_to(buffer, "{: <{}}}}{}\n", ' ', indents(level), needs_comma() && !in_list_scope() ? "," : "");
if (in_list_scope()) {
decrement_nest_level();
fmt::format_to(
buffer, "{: <{}}}}{}\n", ' ', indents(level), needs_comma() ? "," : "");
fmt::format_to(buffer, "{: <{}}}}{}\n", ' ', indents(level), needs_comma() ? "," : "");
}
}
void json_formatter::format_metric(const std::string& metric_name,
const std::string& metric_value,
const std::string& metric_units,
void json_formatter::format_metric(fmt::string_view metric_name,
fmt::string_view metric_value,
fmt::string_view metric_units,
metric_kind kind,
unsigned level,
fmt::memory_buffer& buffer)
@ -128,7 +131,7 @@ void json_formatter::format_metric(const std::string& metric_name,
needs_comma() ? "," : "");
}
void json_formatter::format_list_begin(const std::string& list_name,
void json_formatter::format_list_begin(fmt::string_view list_name,
unsigned size,
unsigned level,
fmt::memory_buffer& buffer)
@ -139,11 +142,8 @@ void json_formatter::format_list_begin(const std::string& list_name,
push_list_scope(size);
}
void json_formatter::format_list_end(const std::string& list_name,
unsigned level,
fmt::memory_buffer& buffer)
void json_formatter::format_list_end(fmt::string_view list_name, unsigned level, fmt::memory_buffer& buffer)
{
pop_scope();
fmt::format_to(
buffer, "{: <{}}]{}\n", ' ', indents(level), needs_comma() ? "," : "");
fmt::format_to(buffer, "{: <{}}]{}\n", ' ', indents(level), needs_comma() ? "," : "");
}

@ -29,40 +29,33 @@ public:
std::unique_ptr<log_formatter> clone() const override;
void format(detail::log_entry_metadata&& metadata,
fmt::memory_buffer& buffer) override;
void format(detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer) override;
private:
void format_context_begin(const detail::log_entry_metadata& md,
const std::string& ctx_name,
fmt::string_view ctx_name,
unsigned size,
fmt::memory_buffer& buffer) override;
void format_context_end(const detail::log_entry_metadata& md,
const std::string& ctx_name,
fmt::string_view ctx_name,
fmt::memory_buffer& buffer) override;
void format_metric_set_begin(const std::string& set_name,
void format_metric_set_begin(fmt::string_view set_name,
unsigned size,
unsigned level,
fmt::memory_buffer& buffer) override;
void format_metric_set_end(const std::string& set_name,
unsigned level,
fmt::memory_buffer& buffer) override;
void format_metric_set_end(fmt::string_view set_name, unsigned level, fmt::memory_buffer& buffer) override;
void format_list_begin(const std::string& list_name,
unsigned size,
unsigned level,
fmt::memory_buffer& buffer) override;
void
format_list_begin(fmt::string_view list_name, unsigned size, unsigned level, fmt::memory_buffer& buffer) override;
void format_list_end(const std::string& list_name,
unsigned level,
fmt::memory_buffer& buffer) override;
void format_list_end(fmt::string_view list_name, unsigned level, fmt::memory_buffer& buffer) override;
void format_metric(const std::string& metric_name,
const std::string& metric_value,
const std::string& metric_units,
void format_metric(fmt::string_view metric_name,
fmt::string_view metric_value,
fmt::string_view metric_units,
metric_kind kind,
unsigned level,
fmt::memory_buffer& buffer) override;
@ -118,9 +111,7 @@ private:
private:
/// Keeps track of some information about a JSON scope.
struct scope {
scope(unsigned size, bool inside_list) :
size(size), inside_list(inside_list)
{}
scope(unsigned size, bool inside_list) : size(size), inside_list(inside_list) {}
/// Number of elements this scope holds.
unsigned size;
/// If true, indicates this scope belongs to a list.

@ -23,39 +23,26 @@ std::unique_ptr<log_formatter> text_formatter::clone() const
/// Formats into a hex dump a range of elements, storing the result in the input
/// buffer.
static void format_hex_dump(const std::vector<uint8_t>& v,
fmt::memory_buffer& buffer)
static 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));
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, " "));
fmt::format_to(buffer, " {:04x}: {:02x}\n", std::distance(v.cbegin(), i), fmt::join(i, i + num_elements, " "));
std::advance(i, num_elements);
}
}
/// Format the log metadata into the input buffer.
static void format_metadata(const detail::log_entry_metadata& metadata,
fmt::memory_buffer& buffer)
static void format_metadata(const detail::log_entry_metadata& metadata, fmt::memory_buffer& buffer)
{
// Time stamp data preparation.
std::tm current_time =
fmt::gmtime(std::chrono::high_resolution_clock::to_time_t(metadata.tp));
auto us_fraction = std::chrono::duration_cast<std::chrono::microseconds>(
metadata.tp.time_since_epoch())
.count() %
1000000u;
std::tm current_time = fmt::gmtime(std::chrono::high_resolution_clock::to_time_t(metadata.tp));
auto us_fraction =
std::chrono::duration_cast<std::chrono::microseconds>(metadata.tp.time_since_epoch()).count() % 1000000u;
fmt::format_to(buffer, "{:%H:%M:%S}.{:06} ", current_time, us_fraction);
// Format optional fields if present.
@ -70,23 +57,28 @@ static void format_metadata(const detail::log_entry_metadata& metadata,
}
}
void text_formatter::format(detail::log_entry_metadata&& metadata,
fmt::memory_buffer& buffer)
void text_formatter::format(detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer)
{
// Prefix first.
format_metadata(metadata, buffer);
// Message formatting.
fmt::format_to(buffer,
"{}\n",
fmt::vsprintf(metadata.fmtstring, std::move(metadata.store)));
if (metadata.fmtstring) {
if (metadata.store) {
fmt::basic_format_args<fmt::basic_printf_context_t<char> > args(*metadata.store);
fmt::vprintf(buffer, fmt::to_string_view(metadata.fmtstring), args);
fmt::format_to(buffer, "\n");
} else {
fmt::format_to(buffer, "{}\n", metadata.fmtstring);
}
}
// Optional hex dump formatting.
format_hex_dump(metadata.hex_dump, buffer);
}
void text_formatter::format_context_begin(const detail::log_entry_metadata& md,
const std::string& ctx_name,
fmt::string_view ctx_name,
unsigned size,
fmt::memory_buffer& buffer)
{
@ -104,35 +96,39 @@ void text_formatter::format_context_begin(const detail::log_entry_metadata& md,
}
void text_formatter::format_context_end(const detail::log_entry_metadata& md,
const std::string& ctx_name,
fmt::string_view ctx_name,
fmt::memory_buffer& buffer)
{
if (!do_one_line_ctx_format) {
return;
}
fmt::format_to(buffer, "]: {}\n", fmt::vsprintf(md.fmtstring, md.store));
if (md.store) {
fmt::format_to(buffer, "]: ");
fmt::basic_format_args<fmt::basic_printf_context_t<char> > args(*md.store);
fmt::vprintf(buffer, fmt::to_string_view(md.fmtstring), args);
fmt::format_to(buffer, "\n");
} else {
fmt::format_to(buffer, "]: {}\n", md.fmtstring);
}
assert(scope_stack.empty() && "Stack should be empty");
}
void text_formatter::format_metric_set_begin(const std::string& set_name,
void text_formatter::format_metric_set_begin(fmt::string_view set_name,
unsigned size,
unsigned level,
fmt::memory_buffer& buffer)
{
if (do_one_line_ctx_format) {
scope_stack.emplace_back(size, set_name);
scope_stack.emplace_back(size, set_name.data());
fmt::format_to(buffer, "[");
return;
}
fmt::format_to(
buffer, "{: <{}}> Set: {}\n", ' ', get_indents(level), set_name);
fmt::format_to(buffer, "{: <{}}> Set: {}\n", ' ', get_indents(level), set_name);
}
void text_formatter::format_metric_set_end(const std::string& set_name,
unsigned level,
fmt::memory_buffer& buffer)
void text_formatter::format_metric_set_end(fmt::string_view set_name, unsigned level, fmt::memory_buffer& buffer)
{
if (!do_one_line_ctx_format) {
return;
@ -142,9 +138,9 @@ void text_formatter::format_metric_set_end(const std::string& set_name,
fmt::format_to(buffer, "]");
}
void text_formatter::format_metric(const std::string& metric_name,
const std::string& metric_value,
const std::string& metric_units,
void text_formatter::format_metric(fmt::string_view metric_name,
fmt::string_view metric_value,
fmt::string_view metric_units,
metric_kind kind,
unsigned level,
fmt::memory_buffer& buffer)
@ -156,7 +152,7 @@ void text_formatter::format_metric(const std::string& metric_name,
get_current_set_name(),
metric_name,
metric_value,
metric_units.empty() ? "" : " ",
metric_units.size() == 0 ? "" : " ",
metric_units,
needs_comma() ? ", " : "");
return;
@ -168,11 +164,11 @@ void text_formatter::format_metric(const std::string& metric_name,
get_indents(level),
metric_name,
metric_value,
metric_units.empty() ? "" : " ",
metric_units.size() == 0 ? "" : " ",
metric_units);
}
void text_formatter::format_list_begin(const std::string& list_name,
void text_formatter::format_list_begin(fmt::string_view list_name,
unsigned size,
unsigned level,
fmt::memory_buffer& buffer)
@ -180,6 +176,5 @@ void text_formatter::format_list_begin(const std::string& list_name,
if (do_one_line_ctx_format) {
return;
}
fmt::format_to(
buffer, "{: <{}}> List: {}\n", ' ', get_indents(level), list_name);
fmt::format_to(buffer, "{: <{}}> List: {}\n", ' ', get_indents(level), list_name);
}

@ -25,41 +25,33 @@ public:
std::unique_ptr<log_formatter> clone() const override;
void format(detail::log_entry_metadata&& metadata,
fmt::memory_buffer& buffer) override;
void format(detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer) override;
private:
void format_context_begin(const detail::log_entry_metadata& md,
const std::string& ctx_name,
fmt::string_view ctx_name,
unsigned size,
fmt::memory_buffer& buffer) override;
void format_context_end(const detail::log_entry_metadata& md,
const std::string& ctx_name,
fmt::string_view ctx_name,
fmt::memory_buffer& buffer) override;
void format_metric_set_begin(const std::string& set_name,
void format_metric_set_begin(fmt::string_view set_name,
unsigned size,
unsigned level,
fmt::memory_buffer& buffer) override;
void format_metric_set_end(const std::string& set_name,
unsigned level,
fmt::memory_buffer& buffer) override;
void format_metric_set_end(fmt::string_view set_name, unsigned level, fmt::memory_buffer& buffer) override;
void format_list_begin(const std::string& list_name,
unsigned size,
unsigned level,
fmt::memory_buffer& buffer) override;
void
format_list_begin(fmt::string_view list_name, unsigned size, unsigned level, fmt::memory_buffer& buffer) override;
void format_list_end(const std::string& list_name,
unsigned level,
fmt::memory_buffer& buffer) override
{}
void format_list_end(fmt::string_view list_name, unsigned level, fmt::memory_buffer& buffer) override {}
void format_metric(const std::string& metric_name,
const std::string& metric_value,
const std::string& metric_units,
void format_metric(fmt::string_view metric_name,
fmt::string_view metric_value,
fmt::string_view metric_units,
metric_kind kind,
unsigned level,
fmt::memory_buffer& buffer) override;
@ -92,9 +84,7 @@ private:
private:
/// Keeps track of some state required for formatting.
struct scope {
scope(unsigned size, std::string set_name) :
size(size), set_name(std::move(set_name))
{}
scope(unsigned size, std::string set_name) : size(size), set_name(std::move(set_name)) {}
/// Number of elements this scope holds.
unsigned size;
/// Set name in this scope.

@ -33,23 +33,28 @@ public:
bool push(detail::log_entry&& entry) override
{
return queue.push(std::move(entry));
auto* arg_store = entry.metadata.store;
if (!queue.push(std::move(entry))) {
arg_pool.dealloc(arg_store);
return false;
}
return true;
}
fmt::dynamic_format_arg_store<fmt::printf_context>* alloc_arg_store() override { return arg_pool.alloc(); }
bool is_running() const override { return worker.is_running(); }
/// Installs the specified error handler into the backend worker.
void set_error_handler(error_handler err_handler)
{
worker.set_error_handler(std::move(err_handler));
}
void set_error_handler(error_handler err_handler) { worker.set_error_handler(std::move(err_handler)); }
/// Stops the backend worker thread.
void stop() { worker.stop(); }
private:
detail::work_queue<detail::log_entry> queue;
backend_worker worker{queue};
detail::dyn_arg_store_pool arg_pool;
backend_worker worker{queue, arg_pool};
};
} // namespace srslog

@ -198,6 +198,7 @@ void srslog::flush()
}
detail::log_entry cmd;
cmd.metadata.store = nullptr;
cmd.flush_cmd = std::unique_ptr<detail::flush_backend_cmd>(
new detail::flush_backend_cmd{completion_flag, std::move(sinks)});

@ -32,6 +32,8 @@ public:
return true;
}
fmt::dynamic_format_arg_store<fmt::printf_context>* alloc_arg_store() override { return &store; }
bool is_running() const override { return true; }
void reset() { count = 0; }
@ -40,12 +42,12 @@ public:
private:
unsigned count = 0;
fmt::dynamic_format_arg_store<fmt::printf_context> store;
};
} // namespace
static bool
when_tracing_with_duration_event_then_two_events_are_generated(backend_spy& spy)
static bool when_tracing_with_duration_event_then_two_events_are_generated(backend_spy& spy)
{
trace_duration_begin("a", "b");
ASSERT_EQ(spy.push_invocation_count(), 1);
@ -56,8 +58,7 @@ when_tracing_with_duration_event_then_two_events_are_generated(backend_spy& spy)
return true;
}
static bool
when_tracing_with_complete_event_then_one_event_is_generated(backend_spy& spy)
static bool when_tracing_with_complete_event_then_one_event_is_generated(backend_spy& spy)
{
{
trace_complete_event("a", "b");
@ -76,11 +77,9 @@ int main()
// Inject our spy into the framework.
event_trace_init(c);
TEST_FUNCTION(when_tracing_with_duration_event_then_two_events_are_generated,
backend);
TEST_FUNCTION(when_tracing_with_duration_event_then_two_events_are_generated, backend);
backend.reset();
TEST_FUNCTION(when_tracing_with_complete_event_then_one_event_is_generated,
backend);
TEST_FUNCTION(when_tracing_with_complete_event_then_one_event_is_generated, backend);
return 0;
}

@ -18,22 +18,24 @@
using namespace srslog;
/// Helper to build a log entry.
static detail::log_entry_metadata build_log_entry_metadata()
static detail::log_entry_metadata build_log_entry_metadata(fmt::dynamic_format_arg_store<fmt::printf_context>* store)
{
// 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);
if (store) {
store->push_back(88);
}
return {tp, {10, true}, "Text %d", std::move(store), "ABC", 'Z', small_str_buffer()};
return {tp, {10, true}, "Text %d", store, "ABC", 'Z', small_str_buffer()};
}
static bool when_fully_filled_log_entry_then_everything_is_formatted()
{
fmt::dynamic_format_arg_store<fmt::printf_context> store;
fmt::memory_buffer buffer;
json_formatter{}.format(build_log_entry_metadata(), buffer);
json_formatter{}.format(build_log_entry_metadata(&store), buffer);
std::string result = fmt::to_string(buffer);
std::string expected = "{\n"
" \"log_entry\": \"Text 88\"\n"
@ -44,18 +46,17 @@ static bool when_fully_filled_log_entry_then_everything_is_formatted()
return true;
}
static bool
when_fully_filled_log_entry_with_hex_dump_then_everything_is_formatted()
static bool when_fully_filled_log_entry_with_hex_dump_then_everything_is_formatted()
{
auto entry = build_log_entry_metadata();
fmt::dynamic_format_arg_store<fmt::printf_context> store;
auto entry = build_log_entry_metadata(&store);
entry.hex_dump.resize(12);
std::iota(entry.hex_dump.begin(), entry.hex_dump.end(), 0);
fmt::memory_buffer buffer;
json_formatter{}.format(std::move(entry), buffer);
std::string result = fmt::to_string(buffer);
std::string expected =
"{\n"
std::string expected = "{\n"
" \"log_entry\": \"Text 88\",\n"
" \"hex_dump\": \"00 01 02 03 04 05 06 07 08 09 0a 0b\"\n"
"}\n";
@ -78,10 +79,9 @@ DECLARE_METRIC_SET("Network", myset2, thr_t, ip_addr_t);
using basic_ctx_t = srslog::build_context_type<myset1, myset2>;
} // namespace
static bool
when_log_entry_with_only_basic_context_is_passed_then_context_is_formatted()
static bool when_log_entry_with_only_basic_context_is_passed_then_context_is_formatted()
{
auto entry = build_log_entry_metadata();
auto entry = build_log_entry_metadata(nullptr);
entry.fmtstring = nullptr;
basic_ctx_t ctx("UL Context");
@ -111,10 +111,10 @@ when_log_entry_with_only_basic_context_is_passed_then_context_is_formatted()
return true;
}
static bool
when_log_entry_with_message_and_basic_context_is_passed_then_context_is_formatted()
static bool when_log_entry_with_message_and_basic_context_is_passed_then_context_is_formatted()
{
auto entry = build_log_entry_metadata();
fmt::dynamic_format_arg_store<fmt::printf_context> store;
auto entry = build_log_entry_metadata(&store);
basic_ctx_t ctx("UL Context");
ctx.get<myset1>().write<snr_t>(-55.1);
@ -157,22 +157,17 @@ DECLARE_METRIC_SET("ue_container", ue_set, ue_rnti_t, dl_cqi_t, bearer_list_t);
DECLARE_METRIC("type", entry_type_t, std::string, "");
DECLARE_METRIC("sector_id", sector_id_t, unsigned, "");
DECLARE_METRIC_LIST("ue_list", ue_list_t, std::vector<ue_set>);
DECLARE_METRIC_SET("sector_metrics",
sector_set,
entry_type_t,
sector_id_t,
ue_list_t);
DECLARE_METRIC_SET("sector_metrics", sector_set, entry_type_t, sector_id_t, ue_list_t);
DECLARE_METRIC_LIST("sector_list", sector_list_t, std::vector<sector_set>);
using complex_ctx_t = srslog::build_context_type<sector_list_t>;
} // namespace
static bool
when_log_entry_with_only_complex_context_is_passed_then_context_is_formatted()
static bool when_log_entry_with_only_complex_context_is_passed_then_context_is_formatted()
{
complex_ctx_t ctx("UL Context");
auto entry = build_log_entry_metadata();
auto entry = build_log_entry_metadata(nullptr);
entry.fmtstring = nullptr;
ctx.get<sector_list_t>().emplace_back();
@ -252,14 +247,13 @@ DECLARE_METRIC_SET("metric_list_set", metric_list_set, list_metric2);
DECLARE_METRIC_LIST("metrics_list", metrics_list, std::vector<metric_list_set>);
DECLARE_METRIC("list_metric3", list_metric3, unsigned, "");
DECLARE_METRIC("list_metric4", list_metric4, unsigned, "");
using list_ctx_t =
srslog::build_context_type<list_metric3, list_metric4, metrics_list>;
using list_ctx_t = srslog::build_context_type<list_metric3, list_metric4, metrics_list>;
}; // namespace
static bool when_context_with_empty_list_is_passed_then_list_object_is_empty()
{
list_ctx_t ctx("UL Context");
auto entry = build_log_entry_metadata();
auto entry = build_log_entry_metadata(nullptr);
entry.fmtstring = nullptr;
fmt::memory_buffer buffer;
@ -280,16 +274,11 @@ static bool when_context_with_empty_list_is_passed_then_list_object_is_empty()
int main()
{
TEST_FUNCTION(when_fully_filled_log_entry_then_everything_is_formatted);
TEST_FUNCTION(
when_fully_filled_log_entry_with_hex_dump_then_everything_is_formatted);
TEST_FUNCTION(
when_log_entry_with_only_basic_context_is_passed_then_context_is_formatted);
TEST_FUNCTION(
when_log_entry_with_message_and_basic_context_is_passed_then_context_is_formatted);
TEST_FUNCTION(
when_log_entry_with_only_complex_context_is_passed_then_context_is_formatted);
TEST_FUNCTION(
when_context_with_empty_list_is_passed_then_list_object_is_empty);
TEST_FUNCTION(when_fully_filled_log_entry_with_hex_dump_then_everything_is_formatted);
TEST_FUNCTION(when_log_entry_with_only_basic_context_is_passed_then_context_is_formatted);
TEST_FUNCTION(when_log_entry_with_message_and_basic_context_is_passed_then_context_is_formatted);
TEST_FUNCTION(when_log_entry_with_only_complex_context_is_passed_then_context_is_formatted);
TEST_FUNCTION(when_context_with_empty_list_is_passed_then_list_object_is_empty);
return 0;
}

@ -46,9 +46,7 @@ namespace {
class sink_spy : public sink
{
public:
sink_spy() :
sink(std::unique_ptr<log_formatter>(new test_dummies::log_formatter_dummy))
{}
sink_spy() : sink(std::unique_ptr<log_formatter>(new test_dummies::log_formatter_dummy)) {}
detail::error_string write(detail::memory_buffer buffer) override
{
@ -71,18 +69,18 @@ private:
} // namespace
/// Builds a basic log entry.
static detail::log_entry build_log_entry(sink* s)
static detail::log_entry build_log_entry(sink* s, fmt::dynamic_format_arg_store<fmt::printf_context>* store)
{
using tp_ty = std::chrono::time_point<std::chrono::high_resolution_clock>;
tp_ty tp;
fmt::dynamic_format_arg_store<fmt::printf_context> store;
store.push_back(88);
if (store) {
store->push_back(88);
}
return {
s,
return {s,
[](detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer) {},
{tp, {0, false}, "Text %d", std::move(store), "", '\0', small_str_buffer()}};
{tp, {0, false}, "Text %d", store, "", '\0', small_str_buffer()}};
}
static bool when_backend_is_not_started_then_pushed_log_entries_are_ignored()
@ -90,7 +88,7 @@ static bool when_backend_is_not_started_then_pushed_log_entries_are_ignored()
sink_spy spy;
log_backend_impl backend;
backend.push(build_log_entry(&spy));
backend.push(build_log_entry(&spy, backend.alloc_arg_store()));
ASSERT_EQ(spy.write_invocation_count(), 0);
@ -104,7 +102,7 @@ static bool when_backend_is_started_then_pushed_log_entries_are_sent_to_sink()
log_backend_impl backend;
backend.start();
backend.push(build_log_entry(&spy));
backend.push(build_log_entry(&spy, backend.alloc_arg_store()));
// Stop the backend to ensure the entry has been processed.
backend.stop();
@ -121,10 +119,9 @@ static bool when_backend_is_started_then_backend_invokes_format_func()
log_backend_impl backend;
backend.start();
auto entry = build_log_entry(&s);
auto entry = build_log_entry(&s, backend.alloc_arg_store());
unsigned counter = 0;
entry.format_func = [&counter](detail::log_entry_metadata&& metadata,
fmt::memory_buffer& buffer) { ++counter; };
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.
@ -144,14 +141,10 @@ class sink_error_stub : public sink
{
public:
explicit sink_error_stub(std::string err) :
sink(std::unique_ptr<log_formatter>(new test_dummies::log_formatter_dummy)),
err(std::move(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
{
return err;
}
detail::error_string write(detail::memory_buffer buffer) override { return err; }
detail::error_string flush() override { return err; }
@ -179,7 +172,7 @@ static bool when_sink_write_fails_then_error_handler_is_invoked()
backend.set_error_handler(handler);
backend.start();
backend.push(build_log_entry(&s));
backend.push(build_log_entry(&s, backend.alloc_arg_store()));
// Stop the backend to ensure the entry has been processed.
backend.stop();
@ -204,7 +197,7 @@ static bool when_handler_is_set_after_start_then_handler_is_not_used()
backend.start();
backend.set_error_handler(handler);
backend.push(build_log_entry(&s));
backend.push(build_log_entry(&s, backend.alloc_arg_store()));
// Stop the backend to ensure the entry has been processed.
backend.stop();
@ -223,7 +216,7 @@ static bool when_empty_handler_is_used_then_backend_does_not_crash()
backend.set_error_handler({});
backend.start();
backend.push(build_log_entry(&s));
backend.push(build_log_entry(&s, backend.alloc_arg_store()));
// Stop the backend to ensure the entry has been processed.
backend.stop();
@ -234,12 +227,9 @@ static bool when_empty_handler_is_used_then_backend_does_not_crash()
int main()
{
TEST_FUNCTION(when_backend_is_started_then_is_started_returns_true);
TEST_FUNCTION(
when_backend_is_started_and_stopped_then_is_started_returns_false);
TEST_FUNCTION(
when_backend_is_not_started_then_pushed_log_entries_are_ignored);
TEST_FUNCTION(
when_backend_is_started_then_pushed_log_entries_are_sent_to_sink);
TEST_FUNCTION(when_backend_is_started_and_stopped_then_is_started_returns_false);
TEST_FUNCTION(when_backend_is_not_started_then_pushed_log_entries_are_ignored);
TEST_FUNCTION(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_handler_is_set_after_start_then_handler_is_not_used);

@ -71,6 +71,8 @@ public:
bool is_running() const override { return true; }
fmt::dynamic_format_arg_store<fmt::printf_context>* alloc_arg_store() override { return &store; }
unsigned push_invocation_count() const { return count; }
const detail::log_entry& last_entry() const { return e; }
@ -78,12 +80,12 @@ public:
private:
unsigned count = 0;
detail::log_entry e;
fmt::dynamic_format_arg_store<fmt::printf_context> store;
};
} // namespace
static bool
when_logging_in_log_channel_then_log_entry_is_pushed_into_the_backend()
static bool when_logging_in_log_channel_then_log_entry_is_pushed_into_the_backend()
{
backend_spy backend;
test_dummies::sink_dummy s;
@ -141,8 +143,7 @@ static bool when_logging_then_filled_in_log_entry_is_pushed_into_the_backend()
return true;
}
static bool
when_logging_with_hex_dump_then_filled_in_log_entry_is_pushed_into_the_backend()
static bool when_logging_with_hex_dump_then_filled_in_log_entry_is_pushed_into_the_backend()
{
backend_spy backend;
test_dummies::sink_dummy s;
@ -171,16 +172,12 @@ when_logging_with_hex_dump_then_filled_in_log_entry_is_pushed_into_the_backend()
ASSERT_EQ(entry.metadata.log_name, name);
ASSERT_EQ(entry.metadata.log_tag, tag);
ASSERT_EQ(entry.metadata.hex_dump.size(), 4);
ASSERT_EQ(std::equal(entry.metadata.hex_dump.begin(),
entry.metadata.hex_dump.end(),
std::begin(hex)),
true);
ASSERT_EQ(std::equal(entry.metadata.hex_dump.begin(), entry.metadata.hex_dump.end(), std::begin(hex)), true);
return true;
}
static bool
when_hex_array_length_is_less_than_hex_log_max_size_then_array_length_is_used()
static bool when_hex_array_length_is_less_than_hex_log_max_size_then_array_length_is_used()
{
backend_spy backend;
test_dummies::sink_dummy s;
@ -197,10 +194,7 @@ when_hex_array_length_is_less_than_hex_log_max_size_then_array_length_is_used()
const detail::log_entry& entry = backend.last_entry();
ASSERT_EQ(entry.metadata.hex_dump.size(), 3);
ASSERT_EQ(std::equal(entry.metadata.hex_dump.begin(),
entry.metadata.hex_dump.end(),
std::begin(hex)),
true);
ASSERT_EQ(std::equal(entry.metadata.hex_dump.begin(), entry.metadata.hex_dump.end(), std::begin(hex)), true);
return true;
}
@ -213,8 +207,7 @@ 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()
static bool when_logging_with_context_then_filled_in_log_entry_is_pushed_into_the_backend()
{
backend_spy backend;
test_dummies::sink_dummy s;
@ -245,8 +238,7 @@ when_logging_with_context_then_filled_in_log_entry_is_pushed_into_the_backend()
return true;
}
static bool
when_logging_with_context_and_message_then_filled_in_log_entry_is_pushed_into_the_backend()
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;
@ -278,8 +270,7 @@ when_logging_with_context_and_message_then_filled_in_log_entry_is_pushed_into_th
return true;
}
static bool
when_logging_with_small_string_then_filled_in_log_entry_is_pushed_into_the_backend()
static bool when_logging_with_small_string_then_filled_in_log_entry_is_pushed_into_the_backend()
{
backend_spy backend;
test_dummies::sink_dummy s;
@ -308,21 +299,14 @@ int main()
TEST_FUNCTION(when_log_channel_is_created_then_id_matches_expected_value);
TEST_FUNCTION(when_log_channel_is_disabled_then_enabled_returns_false);
TEST_FUNCTION(when_log_channel_is_enabled_then_enabled_returns_true);
TEST_FUNCTION(
when_logging_in_log_channel_then_log_entry_is_pushed_into_the_backend);
TEST_FUNCTION(when_logging_in_log_channel_then_log_entry_is_pushed_into_the_backend);
TEST_FUNCTION(when_logging_in_disabled_log_channel_then_log_entry_is_ignored);
TEST_FUNCTION(
when_logging_then_filled_in_log_entry_is_pushed_into_the_backend);
TEST_FUNCTION(
when_logging_with_hex_dump_then_filled_in_log_entry_is_pushed_into_the_backend);
TEST_FUNCTION(
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);
TEST_FUNCTION(
when_logging_with_small_string_then_filled_in_log_entry_is_pushed_into_the_backend);
TEST_FUNCTION(when_logging_then_filled_in_log_entry_is_pushed_into_the_backend);
TEST_FUNCTION(when_logging_with_hex_dump_then_filled_in_log_entry_is_pushed_into_the_backend);
TEST_FUNCTION(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);
TEST_FUNCTION(when_logging_with_small_string_then_filled_in_log_entry_is_pushed_into_the_backend);
return 0;
}

@ -22,43 +22,30 @@ namespace test_dummies {
class log_formatter_dummy : public srslog::log_formatter
{
public:
void format(srslog::detail::log_entry_metadata&& metadata,
fmt::memory_buffer& buffer) override
{}
void format(srslog::detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer) override {}
std::unique_ptr<log_formatter> clone() const override { return nullptr; }
private:
void format_context_begin(const srslog::detail::log_entry_metadata& md,
const std::string& ctx_name,
fmt::string_view ctx_name,
unsigned size,
fmt::memory_buffer& buffer) override
{}
void format_context_end(const srslog::detail::log_entry_metadata& md,
const std::string& ctx_name,
fmt::memory_buffer& buffer) override
{}
void format_metric_set_begin(const std::string& set_name,
unsigned size,
unsigned level,
fmt::memory_buffer& buffer) override
{}
void format_metric_set_end(const std::string& set_name,
unsigned level,
fmt::string_view ctx_name,
fmt::memory_buffer& buffer) override
{}
void format_list_begin(const std::string& list_name,
unsigned size,
unsigned level,
fmt::memory_buffer& buffer) override
void
format_metric_set_begin(fmt::string_view set_name, unsigned size, unsigned level, fmt::memory_buffer& buffer) override
{}
void format_list_end(const std::string& list_name,
unsigned level,
fmt::memory_buffer& buffer) override
void format_metric_set_end(fmt::string_view set_name, unsigned level, fmt::memory_buffer& buffer) override {}
void format_list_begin(fmt::string_view list_name, unsigned size, unsigned level, fmt::memory_buffer& buffer) override
{}
void format_metric(const std::string& metric_name,
const std::string& metric_value,
const std::string& metric_units,
void format_list_end(fmt::string_view list_name, unsigned level, fmt::memory_buffer& buffer) override {}
void format_metric(fmt::string_view metric_name,
fmt::string_view metric_value,
fmt::string_view metric_units,
srslog::metric_kind kind,
unsigned level,
fmt::memory_buffer& buffer) override
@ -69,15 +56,9 @@ private:
class sink_dummy : public srslog::sink
{
public:
sink_dummy() :
sink(std::unique_ptr<srslog::log_formatter>(new log_formatter_dummy))
{}
sink_dummy() : sink(std::unique_ptr<srslog::log_formatter>(new log_formatter_dummy)) {}
srslog::detail::error_string
write(srslog::detail::memory_buffer buffer) override
{
return {};
}
srslog::detail::error_string write(srslog::detail::memory_buffer buffer) override { return {}; }
srslog::detail::error_string flush() override { return {}; }
};
@ -91,6 +72,8 @@ public:
bool push(srslog::detail::log_entry&& entry) override { return true; }
bool is_running() const override { return true; }
fmt::dynamic_format_arg_store<fmt::printf_context>* alloc_arg_store() override { return nullptr; }
};
} // namespace test_dummies

@ -18,22 +18,24 @@
using namespace srslog;
/// Helper to build a log entry.
static detail::log_entry_metadata build_log_entry_metadata()
static detail::log_entry_metadata build_log_entry_metadata(fmt::dynamic_format_arg_store<fmt::printf_context>* store)
{
// 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);
if (store) {
store->push_back(88);
}
return {tp, {10, true}, "Text %d", std::move(store), "ABC", 'Z', small_str_buffer()};
return {tp, {10, true}, "Text %d", store, "ABC", 'Z', small_str_buffer()};
}
static bool when_fully_filled_log_entry_then_everything_is_formatted()
{
fmt::memory_buffer buffer;
text_formatter{}.format(build_log_entry_metadata(), buffer);
fmt::dynamic_format_arg_store<fmt::printf_context> store;
text_formatter{}.format(build_log_entry_metadata(&store), buffer);
std::string result = fmt::to_string(buffer);
std::string expected = "00:00:00.050000 [ABC ] [Z] [ 10] Text 88\n";
@ -44,7 +46,8 @@ static bool when_fully_filled_log_entry_then_everything_is_formatted()
static bool when_log_entry_without_name_is_passed_then_name_is_not_formatted()
{
auto entry = build_log_entry_metadata();
fmt::dynamic_format_arg_store<fmt::printf_context> store;
auto entry = build_log_entry_metadata(&store);
entry.log_name = "";
fmt::memory_buffer buffer;
@ -59,7 +62,8 @@ static bool when_log_entry_without_name_is_passed_then_name_is_not_formatted()
static bool when_log_entry_without_tag_is_passed_then_tag_is_not_formatted()
{
auto entry = build_log_entry_metadata();
fmt::dynamic_format_arg_store<fmt::printf_context> store;
auto entry = build_log_entry_metadata(&store);
entry.log_tag = '\0';
fmt::memory_buffer buffer;
@ -72,10 +76,10 @@ static bool when_log_entry_without_tag_is_passed_then_tag_is_not_formatted()
return true;
}
static bool
when_log_entry_without_context_is_passed_then_context_is_not_formatted()
static bool when_log_entry_without_context_is_passed_then_context_is_not_formatted()
{
auto entry = build_log_entry_metadata();
fmt::dynamic_format_arg_store<fmt::printf_context> store;
auto entry = build_log_entry_metadata(&store);
entry.context.enabled = false;
fmt::memory_buffer buffer;
@ -90,15 +94,15 @@ when_log_entry_without_context_is_passed_then_context_is_not_formatted()
static bool when_log_entry_with_hex_dump_is_passed_then_hex_dump_is_formatted()
{
auto entry = build_log_entry_metadata();
fmt::dynamic_format_arg_store<fmt::printf_context> store;
auto entry = build_log_entry_metadata(&store);
entry.hex_dump.resize(20);
std::iota(entry.hex_dump.begin(), entry.hex_dump.end(), 0);
fmt::memory_buffer buffer;
text_formatter{}.format(std::move(entry), buffer);
std::string result = fmt::to_string(buffer);
std::string expected =
"00:00:00.050000 [ABC ] [Z] [ 10] Text 88\n"
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";
@ -120,11 +124,7 @@ DECLARE_METRIC_SET("ue_container", ue_set, thr_t, ip_addr_t, antenna_list_t);
DECLARE_METRIC("type", entry_type_t, std::string, "");
DECLARE_METRIC("sector_id", sector_id_t, unsigned, "");
DECLARE_METRIC_LIST("ue_list", ue_list_t, std::vector<ue_set>);
DECLARE_METRIC_SET("sector_metrics",
sector_set,
entry_type_t,
sector_id_t,
ue_list_t);
DECLARE_METRIC_SET("sector_metrics", sector_set, entry_type_t, sector_id_t, ue_list_t);
DECLARE_METRIC_LIST("sector_list", sector_list_t, std::vector<sector_set>);
@ -149,49 +149,28 @@ static complex_ctx_t build_complex_context()
ctx.at<sector_list_t>(0).at<ue_list_t>(1).write<thr_t>(10.2);
ctx.at<sector_list_t>(0).at<ue_list_t>(1).write<ip_addr_t>("10.20.30.41");
ctx.at<sector_list_t>(0)
.at<ue_list_t>(0)
.get<antenna_list_t>()
.emplace_back();
ctx.at<sector_list_t>(0).at<ue_list_t>(0).at<antenna_list_t>(0).write<snr_t>(
5.1);
ctx.at<sector_list_t>(0).at<ue_list_t>(0).at<antenna_list_t>(0).write<pwr_t>(
-11.5);
ctx.at<sector_list_t>(0)
.at<ue_list_t>(0)
.get<antenna_list_t>()
.emplace_back();
ctx.at<sector_list_t>(0).at<ue_list_t>(0).at<antenna_list_t>(1).write<snr_t>(
10.1);
ctx.at<sector_list_t>(0).at<ue_list_t>(0).at<antenna_list_t>(1).write<pwr_t>(
-20.5);
ctx.at<sector_list_t>(0)
.at<ue_list_t>(1)
.get<antenna_list_t>()
.emplace_back();
ctx.at<sector_list_t>(0).at<ue_list_t>(1).at<antenna_list_t>(0).write<snr_t>(
20.1);
ctx.at<sector_list_t>(0).at<ue_list_t>(1).at<antenna_list_t>(0).write<pwr_t>(
-30.5);
ctx.at<sector_list_t>(0)
.at<ue_list_t>(1)
.get<antenna_list_t>()
.emplace_back();
ctx.at<sector_list_t>(0).at<ue_list_t>(1).at<antenna_list_t>(1).write<snr_t>(
30.1);
ctx.at<sector_list_t>(0).at<ue_list_t>(1).at<antenna_list_t>(1).write<pwr_t>(
-40.5);
ctx.at<sector_list_t>(0).at<ue_list_t>(0).get<antenna_list_t>().emplace_back();
ctx.at<sector_list_t>(0).at<ue_list_t>(0).at<antenna_list_t>(0).write<snr_t>(5.1);
ctx.at<sector_list_t>(0).at<ue_list_t>(0).at<antenna_list_t>(0).write<pwr_t>(-11.5);
ctx.at<sector_list_t>(0).at<ue_list_t>(0).get<antenna_list_t>().emplace_back();
ctx.at<sector_list_t>(0).at<ue_list_t>(0).at<antenna_list_t>(1).write<snr_t>(10.1);
ctx.at<sector_list_t>(0).at<ue_list_t>(0).at<antenna_list_t>(1).write<pwr_t>(-20.5);
ctx.at<sector_list_t>(0).at<ue_list_t>(1).get<antenna_list_t>().emplace_back();
ctx.at<sector_list_t>(0).at<ue_list_t>(1).at<antenna_list_t>(0).write<snr_t>(20.1);
ctx.at<sector_list_t>(0).at<ue_list_t>(1).at<antenna_list_t>(0).write<pwr_t>(-30.5);
ctx.at<sector_list_t>(0).at<ue_list_t>(1).get<antenna_list_t>().emplace_back();
ctx.at<sector_list_t>(0).at<ue_list_t>(1).at<antenna_list_t>(1).write<snr_t>(30.1);
ctx.at<sector_list_t>(0).at<ue_list_t>(1).at<antenna_list_t>(1).write<pwr_t>(-40.5);
return ctx;
}
static bool
when_log_entry_with_only_context_is_passed_then_context_is_formatted()
static bool when_log_entry_with_only_context_is_passed_then_context_is_formatted()
{
auto ctx = build_complex_context();
auto entry = build_log_entry_metadata();
auto entry = build_log_entry_metadata(nullptr);
entry.fmtstring = nullptr;
fmt::memory_buffer buffer;
@ -230,17 +209,16 @@ when_log_entry_with_only_context_is_passed_then_context_is_formatted()
return true;
}
static bool
when_log_entry_with_context_and_message_is_passed_then_context_is_formatted()
static bool when_log_entry_with_context_and_message_is_passed_then_context_is_formatted()
{
auto entry = build_log_entry_metadata();
fmt::dynamic_format_arg_store<fmt::printf_context> store;
auto entry = build_log_entry_metadata(&store);
auto ctx = build_complex_context();
fmt::memory_buffer buffer;
text_formatter{}.format_ctx(ctx, std::move(entry), buffer);
std::string result = fmt::to_string(buffer);
std::string expected =
"00:00:00.050000 [ABC ] [Z] [ 10] [[sector_metrics_type: event, "
std::string expected = "00:00:00.050000 [ABC ] [Z] [ 10] [[sector_metrics_type: event, "
"sector_metrics_sector_id: 1, [ue_container_Throughput: 1.2 MB/s, "
"ue_container_Address: 10.20.30.40, [RF_SNR: 5.1 dB, RF_PWR: -11 "
"dBm][RF_SNR: 10.1 dB, RF_PWR: -20 dBm]][ue_container_Throughput: 10.2 "
@ -255,17 +233,12 @@ when_log_entry_with_context_and_message_is_passed_then_context_is_formatted()
int main()
{
TEST_FUNCTION(when_fully_filled_log_entry_then_everything_is_formatted);
TEST_FUNCTION(
when_log_entry_without_name_is_passed_then_name_is_not_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);
TEST_FUNCTION(
when_log_entry_with_only_context_is_passed_then_context_is_formatted);
TEST_FUNCTION(
when_log_entry_with_context_and_message_is_passed_then_context_is_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);
TEST_FUNCTION(when_log_entry_with_only_context_is_passed_then_context_is_formatted);
TEST_FUNCTION(when_log_entry_with_context_and_message_is_passed_then_context_is_formatted);
return 0;
}

Loading…
Cancel
Save