Include missing files.

This commit is contained in:
faluco 2020-12-10 18:13:06 +01:00 committed by Francisco Paisana
parent 98a2c868b5
commit f23fdf0639
13 changed files with 1798 additions and 0 deletions

View File

@ -0,0 +1,250 @@
/**
*
* \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_CONTEXT_H
#define SRSLOG_CONTEXT_H
#include "srslte/srslog/detail/support/tmp_utils.h"
#include <cassert>
#include <string>
namespace srslog {
/// Metric formatting kinds for textual conversion.
enum class metric_kind {
numeric, /// Metric represents a numeric value.
string /// Metric represents a string.
};
namespace detail {
/// This metrics container class is a wrapper to simplify access to the elements
/// of the underlying tuple that stores metrics and metric sets.
template <typename... Ts>
struct metrics_container {
/// Writes the arg value to metric T.
template <typename T, typename Arg>
void write(Arg&& arg)
{
constexpr std::size_t index = detail::get_type_index_in_tuple<T, Ts...>();
std::get<index>(metrics).value = std::forward<Arg>(arg);
}
/// Returns the value of metric T.
template <typename T>
auto read() const -> const decltype(T::value)&
{
constexpr std::size_t index = detail::get_type_index_in_tuple<T, Ts...>();
return std::get<index>(metrics).value;
}
/// Returns the element of type T.
template <typename T>
T& get()
{
constexpr std::size_t index = detail::get_type_index_in_tuple<T, Ts...>();
return std::get<index>(metrics);
}
/// Returns the element of type T.
template <typename T>
const T& get() const
{
constexpr std::size_t index = detail::get_type_index_in_tuple<T, Ts...>();
return std::get<index>(metrics);
}
/// Returns the element in the specified index of list T.
/// NOTE: T must have implemented the T operator.
template <typename T>
auto at(std::size_t i) -> typename T::value_type&
{
constexpr std::size_t index = detail::get_type_index_in_tuple<T, Ts...>();
auto& elem = std::get<index>(metrics);
assert(i < elem.size() && "Invalid index");
return elem[i];
}
/// Returns the element in the specified index of list T.
/// NOTE: T must have implemented the T operator.
template <typename T>
auto at(std::size_t i) const -> const typename T::value_type&
{
constexpr std::size_t index = detail::get_type_index_in_tuple<T, Ts...>();
const auto& elem = std::get<index>(metrics);
assert(i < elem.size() && "Invalid index");
return elem[i];
}
/// Returns the raw contents of the metric set as a tuple.
const std::tuple<Ts...>& contents() const { return metrics; }
private:
std::tuple<Ts...> metrics;
};
} // namespace detail
/// A generic list to store metric sets of the same type.
template <typename Name, typename T>
struct metric_list : public T {
/// Returns the name of the list.
static const char* name() { return Name::name(); }
};
/// Template specializations of this struct allow configuring what formatting
/// kind should be used for a concrete metric.
/// By default treat all metrics as strings.
template <typename T, typename = void>
struct metric_kind_selector {
static const metric_kind kind = metric_kind::string;
};
/// A metric is the most basic object that composes a context. It is generally
/// used to represent any kind of state of a program.
/// It stores a value of type T associated with a name and the units.
template <typename Ty, typename Name, typename Units>
struct metric {
/// Value of the metric.
Ty value{};
/// Returns the name of the metric.
static const char* name() { return Name::name(); }
/// Returns the units of the metric.
static const char* units() { return Units::units(); }
/// Returns the formatting kind of the metric.
static metric_kind kind()
{
return metric_kind_selector<metric<Ty, Name, Units>>::kind;
}
};
/// Template specialization that tags metrics with arithmetic values (integers
/// and floating point) as numeric.
template <typename Ty, typename Name, typename Units>
struct metric_kind_selector<
metric<Ty, Name, Units>,
typename std::enable_if<std::is_arithmetic<Ty>::value>::type> {
static const metric_kind kind = metric_kind::numeric;
};
/// A metric set is a group of metrics that share a logical relation. Allows
/// storing and mixing other metric sets and metrics for building complex
/// structures.
template <typename Name, typename... Ts>
struct metric_set : public detail::metrics_container<Ts...> {
/// Name of the metric set.
static const char* name() { return Name::name(); }
};
/// A context captures the state of different parts of a program grouping metric
/// sets. It is the root element of the metrics structure and allows mixing and
/// storing other metric sets and metrics.
template <typename... Ts>
struct context : public detail::metrics_container<Ts...> {
explicit context(std::string n) : name_str(std::move(n)) {}
/// Name of the context.
const std::string& name() const { return name_str; }
private:
const std::string name_str;
};
namespace detail {
/// Builds a metric set type using a list of metric, metric sets or list types.
/// eg: using my_metric_t = srslog::build_metric_set_type<m1_t, set1_t, m2_t>;
/// NOTE: Adding duplicated types into the list is not allowed.
template <typename Name, typename... Ts>
using build_metric_set_type =
metric_set<Name, typename std::decay<Ts>::type...>;
} // namespace detail
/// Builds a context type using a list of metric set types.
/// eg: using my_context_t = srslog::build_context_type<set1_t, set2_t>;
/// NOTE: Adding duplicated types into the list is not allowed.
template <typename... Ts>
using build_context_type = context<typename std::decay<Ts>::type...>;
/// This macro defines a new metric type using the following attributes:
/// a) name: encoded as a string.
/// b) Metric type: type identifier to create objects for this metric.
/// c) Value type: type of the underlying metric value.
/// d) Units: encoded as a string, leave as empty string for no units.
///
/// The following example declares a metric with the following attributes:
/// a) metric type: my_metric_t
/// b) metric value type: float
/// c) units: MB/s
/// d) name: Throughput
/// DECLARE_METRIC("Throughput", my_metric_t, float, "MB/s");
#define DECLARE_METRIC(_name_rep, _type, _value_type, _units) \
namespace metric_info { \
struct _type##__units { \
static const char* units() { return _units; } \
}; \
struct _type##__name_rep { \
static const char* name() { return _name_rep; } \
}; \
} \
using _type = srslog::metric<typename std::decay<_value_type>::type, \
metric_info::_type##__name_rep, \
metric_info::_type##__units>
/// This macro defines a new metric set type using the following attributes:
/// a) name: encoded as a string.
/// b) Metric set type: type identifier to create objects for this metric set.
/// c) Type list: list of types this set will hold (other sets, metrics,
/// lists).
///
/// The following example declares a metric set of three elements (two metrics
/// and one set) with the following attributes:
/// a) metric type: my_set_t
/// b) name: my_set
/// b) type list: metric1_t, metric2_t, set2_t
/// DECLARE_METRIC_SET("my_set", my_set_t, metric1_t, metric2_t, set2_t);
#define DECLARE_METRIC_SET(_name_rep, _type, ...) \
namespace metric_set_info { \
struct _type##__name_rep { \
static const char* name() { return _name_rep; } \
}; \
} \
using _type = srslog::detail:: \
build_metric_set_type<metric_set_info::_type##__name_rep, __VA_ARGS__>
/// This macro defines a list of metric sets of the same type:
/// a) name: encoded as a string.
/// b) List type: type identifier to create objects for this list.
/// c) Underlying type: type of the underlying list (vector, array, ...).
///
/// The following example declares a list of metrics sets of type set1_t with
/// the following attributes:
/// a) list type: my_list_t
/// b) name: my_list
/// b) underlying type: std::vector<set1_t>
/// DECLARE_METRIC_LIST("my_list", my_list_t, std::vector<set1_t>);
#define DECLARE_METRIC_LIST(_name_rep, _type, _list_type) \
namespace list_info { \
struct _type##__name_rep { \
static const char* name() { return _name_rep; } \
}; \
} \
using _type = srslog::metric_list<list_info::_type##__name_rep, \
typename std::decay<_list_type>::type>
} // namespace srslog
#endif // SRSLOG_CONTEXT_H

View File

@ -0,0 +1,47 @@
/**
*
* \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_DETAIL_LOG_ENTRY_METADATA_H
#define SRSLOG_DETAIL_LOG_ENTRY_METADATA_H
#include "srslte/srslog/bundled/fmt/printf.h"
#include <chrono>
namespace srslog {
namespace detail {
/// This structure gives the user a way to log generic information as a context.
//:TODO: legacy struct, will get replaced by the new context framework.
struct log_context {
/// Generic context value.
uint32_t value;
/// When true, the context value will be printed in the log entry.
bool enabled;
};
/// Metadata fields carried for each log entry.
struct log_entry_metadata {
std::chrono::high_resolution_clock::time_point tp;
log_context context;
std::string fmtstring;
fmt::dynamic_format_arg_store<fmt::printf_context> store;
std::string log_name;
char log_tag;
std::vector<uint8_t> hex_dump;
};
} // namespace detail
} // namespace srslog
#endif // SRSLOG_DETAIL_LOG_ENTRY_METADATA_H

View File

@ -0,0 +1,70 @@
/**
*
* \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_DETAIL_SUPPORT_TMP_UTILS_H
#define SRSLOG_DETAIL_SUPPORT_TMP_UTILS_H
#include <cstddef>
#include <tuple>
namespace srslog {
namespace detail {
///
/// Implementation of the std::index_sequence C++14 library utility.
///
template <std::size_t...>
struct index_sequence {};
template <std::size_t N, std::size_t... Next>
struct index_sequence_helper
: public index_sequence_helper<N - 1U, N - 1U, Next...> {};
template <std::size_t... Next>
struct index_sequence_helper<0U, Next...> {
using type = index_sequence<Next...>;
};
template <std::size_t N>
using make_index_sequence = typename index_sequence_helper<N>::type;
///
/// Implementation of the std::get<T> C++14 library utility.
///
template <typename T, typename Tuple>
struct tuple_index;
template <typename T, typename... Ts>
struct tuple_index<T, std::tuple<T, Ts...>> {
static constexpr std::size_t value = 0;
};
template <typename T, typename U, typename... Ts>
struct tuple_index<T, std::tuple<U, Ts...>> {
static constexpr std::size_t value =
1 + tuple_index<T, std::tuple<Ts...>>::value;
};
template <typename T, typename... Ts>
constexpr std::size_t get_type_index_in_tuple()
{
return tuple_index<T, std::tuple<Ts...>>::value;
}
} // namespace detail
} // namespace srslog
#endif // SRSLOG_DETAIL_SUPPORT_TMP_UTILS_H

View File

@ -0,0 +1,185 @@
/**
*
* \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/format.h"
#include "srslte/srslog/context.h"
namespace srslog {
namespace detail {
struct log_entry_metadata;
}
/// The generic metric value formatter.
template <typename T>
struct metric_value_formatter {
metric_value_formatter() = delete;
/// All specializations should implement the following method with signature:
/// template <typename T>
/// void format(const T& v, fmt::memory_buffer& buffer)
};
/// 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>> {
template <typename T>
void format(const T& v, fmt::memory_buffer& buffer)
{
fmt::format_to(buffer, "{}", v);
}
};
/// This is the base class that provides a common framework to format log
/// entries to different kinds of formats. User should implement two different
/// kinds of formats:
/// a) Basic log entry formatting.
/// b) Generic context formatting.
///
/// For context formatting, callbacks are provided so that derived classes
/// handle specific formatting rules.
class log_formatter
{
public:
virtual ~log_formatter() = default;
/// Returns a copy of the formatter.
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;
/// 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)
{
format_context_begin(metadata, ctx.name(), sizeof...(Ts), buffer);
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)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)
{
format_metric_set_begin(ms.name(), sizeof...(Ts), level, buffer);
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)
{
format_list_begin(list.name(), list.size(), level, buffer);
for (const auto& elem : list) {
process_element(elem, level + 1, buffer);
}
format_list_end(list.name(), level, buffer);
}
/// 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)
{
fmt::memory_buffer value;
metric_value_formatter<typename std::decay<decltype(t)>::type>{}.format(
t.value, value);
format_metric(
t.name(), fmt::to_string(value), t.units(), t.kind(), level, buffer);
}
private:
/// Derived classes should implement the following callbacks to format metric
/// objects. Each callback is invoked at a different place of the formatting
/// algorithm.
/// 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,
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;
/// 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;
/// 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;
/// 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;
/// 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;
/// 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,
metric_kind kind,
unsigned level,
fmt::memory_buffer& buffer) = 0;
};
} // namespace srslog
#endif // SRSLOG_FORMATTER_H

View File

@ -0,0 +1,13 @@
#
# 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.
#
set(SOURCES
json_formatter.cpp
text_formatter.cpp)
add_library(formatters STATIC ${SOURCES})

View File

@ -0,0 +1,149 @@
/**
*
* \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 "json_formatter.h"
#include "srslte/srslog/detail/log_entry_metadata.h"
using namespace srslog;
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)
{
fmt::format_to(buffer,
"{{\n"
" \"log_entry\": \"{}\"",
fmt::vsprintf(metadata.fmtstring, std::move(metadata.store)));
if (!metadata.hex_dump.empty()) {
fmt::format_to(
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,
unsigned size,
fmt::memory_buffer& buffer)
{
assert(scope_stack.empty() && "Stack should be empty");
assert(nest_level == 0 && "Nesting level should be 0");
fmt::format_to(buffer, "{{\n");
push_scope(size);
if (!md.fmtstring.empty()) {
fmt::format_to(buffer,
" \"log_entry\": \"{}\",\n",
fmt::vsprintf(md.fmtstring, std::move(md.store)));
}
}
void json_formatter::format_context_end(const detail::log_entry_metadata& md,
const std::string& ctx_name,
fmt::memory_buffer& buffer)
{
pop_scope();
fmt::format_to(buffer, "}}\n");
assert(scope_stack.empty() && "Stack should be empty");
assert(nest_level == 0 && "Nesting level should be 0");
}
void json_formatter::format_metric_set_begin(const std::string& set_name,
unsigned size,
unsigned level,
fmt::memory_buffer& buffer)
{
// Arrays in JSON require an additional nesting level before inserting the
// object.
// array: [
// {
// "obj: {}
// }
// ]
if (in_list_scope()) {
fmt::format_to(buffer, "{: <{}}{{\n", ' ', indents(level));
increment_nest_level();
}
consume_element();
fmt::format_to(buffer, "{: <{}}\"{}\": {{\n", ' ', indents(level), set_name);
push_scope(size);
}
void json_formatter::format_metric_set_end(const std::string& set_name,
unsigned level,
fmt::memory_buffer& buffer)
{
pop_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() ? "," : "");
}
}
void json_formatter::format_metric(const std::string& metric_name,
const std::string& metric_value,
const std::string& metric_units,
metric_kind kind,
unsigned level,
fmt::memory_buffer& buffer)
{
consume_element();
fmt::format_to(buffer,
"{: <{}}\"{}\": {}{}{}{}\n",
' ',
indents(level),
metric_name,
kind == metric_kind::string ? "\"" : "",
metric_value,
kind == metric_kind::string ? "\"" : "",
needs_comma() ? "," : "");
}
void json_formatter::format_list_begin(const std::string& list_name,
unsigned size,
unsigned level,
fmt::memory_buffer& buffer)
{
consume_element();
fmt::format_to(buffer, "{: <{}}\"{}\": [\n", ' ', indents(level), list_name);
push_list_scope(size);
}
void json_formatter::format_list_end(const std::string& list_name,
unsigned level,
fmt::memory_buffer& buffer)
{
pop_scope();
fmt::format_to(
buffer, "{: <{}}]{}\n", ' ', indents(level), needs_comma() ? "," : "");
}

View File

@ -0,0 +1,137 @@
/**
*
* \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_JSON_FORMATTER_H
#define SRSLOG_JSON_FORMATTER_H
#include "srslte/srslog/formatter.h"
namespace srslog {
/// JSON formatter class implementation.
/// Formats each log entry and context into its own JSON object making the
/// formatter stateless so that new entries do not depend on the state of
/// previous ones. The output is ready for JSON streaming following the
/// "Concatenated JSON" style.
class json_formatter : public log_formatter
{
public:
json_formatter() { scope_stack.reserve(16); }
std::unique_ptr<log_formatter> clone() const 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,
unsigned size,
fmt::memory_buffer& buffer) override;
void format_context_end(const 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::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_end(const std::string& 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,
metric_kind kind,
unsigned level,
fmt::memory_buffer& buffer) override;
/// Pushes a new entry in the scope stack.
void push_scope(unsigned size) { scope_stack.emplace_back(size, false); }
/// Pushes a new list entry in the scope stack.
void push_list_scope(unsigned size) { scope_stack.emplace_back(size, true); }
/// Pops the topmost entry in the scope stack.
void pop_scope()
{
assert(!scope_stack.empty() && "Popping scope in empty stack");
scope_stack.pop_back();
}
/// Consumes an element in the current scope.
void consume_element()
{
assert(!scope_stack.empty() && "Consuming element in void scope");
assert(scope_stack.back().size && "No more elements to consume");
--scope_stack.back().size;
}
/// Returns true if the current element needs a comma.
bool needs_comma() const
{
assert(!scope_stack.empty() && "No scope exists");
return scope_stack.back().size;
}
/// Returns true if the current scope is a list.
bool in_list_scope() const
{
assert(!scope_stack.empty() && "No scope exists");
return scope_stack.back().inside_list;
}
/// Increments the nesting level by one.
void increment_nest_level() { ++nest_level; }
/// Decrements the nesting level by one.
void decrement_nest_level()
{
assert(nest_level && "Expected the nesting level to greater than 0");
--nest_level;
}
/// Return the number of space chars to indent the specified level.
unsigned indents(unsigned level) const { return (nest_level + level) * 2; }
private:
/// Keeps track of some information about a JSON scope.
struct scope {
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.
const bool inside_list;
};
private:
unsigned nest_level = 0;
std::vector<scope> scope_stack;
};
} // namespace srslog
#endif // SRSLOG_JSON_FORMATTER_H

View File

@ -0,0 +1,160 @@
/**
*
* \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 "text_formatter.h"
#include "srslte/srslog/bundled/fmt/chrono.h"
#include "srslte/srslog/detail/log_entry_metadata.h"
using namespace srslog;
std::unique_ptr<log_formatter> text_formatter::clone() const
{
return std::unique_ptr<log_formatter>(new text_formatter(*this));
}
/// 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)
{
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);
}
}
/// Format the log metadata into the input 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;
fmt::format_to(buffer, "{:%H:%M:%S}.{:06} ", current_time, us_fraction);
// Format optional fields if present.
if (!metadata.log_name.empty()) {
fmt::format_to(buffer, "[{: <4.4}] ", metadata.log_name);
}
if (metadata.log_tag != '\0') {
fmt::format_to(buffer, "[{}] ", metadata.log_tag);
}
if (metadata.context.enabled) {
fmt::format_to(buffer, "[{:5}] ", metadata.context.value);
}
}
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)));
// 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,
unsigned size,
fmt::memory_buffer& buffer)
{
do_one_line_ctx_format = !md.fmtstring.empty();
format_metadata(md, buffer);
if (do_one_line_ctx_format) {
fmt::format_to(buffer, "[");
return;
}
fmt::format_to(buffer, "Context dump for \"{}\"\n", ctx_name);
}
void text_formatter::format_context_end(const detail::log_entry_metadata& md,
const std::string& ctx_name,
fmt::memory_buffer& buffer)
{
if (do_one_line_ctx_format) {
fmt::format_to(buffer, "]: {}\n", fmt::vsprintf(md.fmtstring, md.store));
return;
}
}
void text_formatter::format_metric_set_begin(const std::string& set_name,
unsigned size,
unsigned level,
fmt::memory_buffer& buffer)
{
/*if (do_one_line_ctx_format) {
fmt::format_to(buffer, "{}", is_first ? "[" : " [");
return;
}
fmt::format_to(buffer, " {}\n", set_name);*/
}
void text_formatter::format_metric_set_end(const std::string& set_name,
unsigned level,
fmt::memory_buffer& buffer)
{
if (do_one_line_ctx_format) {
fmt::format_to(buffer, "]");
return;
}
}
void text_formatter::format_metric(const std::string& metric_name,
const std::string& metric_value,
const std::string& metric_units,
metric_kind kind,
unsigned level,
fmt::memory_buffer& buffer)
{
//:TODO: re-enable
/*if (do_one_line_ctx_format) {
fmt::format_to(buffer,
"{}{}_{}: {}{}{}",
ctx.is_first_metric ? "" : ", ",
ctx.set_name,
ctx.metric_name,
ctx.metric_value,
ctx.metric_units.empty() ? "" : " ",
ctx.metric_units);
return;
}
fmt::format_to(buffer,
" {}: {}{}{}\n",
ctx.metric_name,
ctx.metric_value,
ctx.metric_units.empty() ? "" : " ",
ctx.metric_units);*/
}

View File

@ -0,0 +1,79 @@
/**
*
* \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_TEXT_FORMATTER_H
#define SRSLOG_TEXT_FORMATTER_H
#include "srslte/srslog/formatter.h"
namespace srslog {
/// Plain text formatter implementation class.
//:TODO: this class needs refactoring to be compatible with multiple nesting of
// metrics.
class text_formatter : public log_formatter
{
public:
std::unique_ptr<log_formatter> clone() const 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,
unsigned size,
fmt::memory_buffer& buffer) override;
void format_context_end(const 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::memory_buffer& buffer) override;
void format_list_begin(const std::string& list_name,
unsigned size,
unsigned level,
fmt::memory_buffer& buffer) override
{
//:TODO: implement me
}
void format_list_end(const std::string& list_name,
unsigned level,
fmt::memory_buffer& buffer) override
{
//:TODO: implement me
}
void format_metric(const std::string& metric_name,
const std::string& metric_value,
const std::string& metric_units,
metric_kind kind,
unsigned level,
fmt::memory_buffer& buffer) override;
private:
/// Flags that the formatting should take place into a single line.
bool do_one_line_ctx_format = false;
};
} // namespace srslog
#endif // SRSLOG_TEXT_FORMATTER_H

View File

@ -0,0 +1,120 @@
/**
*
* \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 "srslte/srslog/context.h"
#include "testing_helpers.h"
#include <vector>
using namespace srslog;
namespace {
DECLARE_METRIC("SNR", snr_t, float, "dB");
DECLARE_METRIC("PWR", pwr_t, int, "dBm");
DECLARE_METRIC("CenterFreq", cfreq_t, unsigned, "MHz");
DECLARE_METRIC_SET("RF", myset1, snr_t, pwr_t, cfreq_t);
DECLARE_METRIC("Throughput", thr_t, float, "MB/s");
DECLARE_METRIC("Address", ip_addr_t, std::string, "");
DECLARE_METRIC_SET("Network", myset2, thr_t, ip_addr_t);
using ctx_t = srslog::build_context_type<myset1, myset2>;
} // namespace
/// Builds a context for testing.
static ctx_t build_context()
{
ctx_t ctx("Ctx");
return ctx;
}
static bool when_context_is_built_then_context_name_is_valid()
{
ctx_t ctx = build_context();
ASSERT_EQ(ctx.name(), "Ctx");
return true;
}
static bool when_context_is_built_then_metric_sets_names_are_valid()
{
ctx_t ctx = build_context();
ASSERT_EQ(ctx.get<myset1>().name(), std::string("RF"));
ASSERT_EQ(ctx.get<myset2>().name(), std::string("Network"));
return true;
}
static bool when_context_is_built_then_metric_names_are_valid()
{
ctx_t ctx = build_context();
ASSERT_EQ(ctx.get<myset1>().get<snr_t>().name(), std::string("SNR"));
ASSERT_EQ(ctx.get<myset1>().get<pwr_t>().name(), std::string("PWR"));
return true;
}
static bool when_context_is_built_then_metric_units_are_valid()
{
ctx_t ctx = build_context();
ASSERT_EQ(ctx.get<myset1>().get<snr_t>().units(), std::string("dB"));
ASSERT_EQ(ctx.get<myset1>().get<pwr_t>().units(), std::string("dBm"));
return true;
}
static bool when_metric_is_set_through_context_then_value_is_stored()
{
ctx_t ctx = build_context();
float value = 10;
ctx.get<myset1>().write<snr_t>(value);
ASSERT_EQ(ctx.get<myset1>().read<snr_t>(), value);
return true;
}
namespace {
DECLARE_METRIC("metric1", m1_t, float, "");
DECLARE_METRIC_SET("test_set_t", test_set_t, m1_t);
DECLARE_METRIC_LIST("vector", vector_metrics, std::vector<test_set_t>);
using ctx2_t = srslog::build_context_type<vector_metrics>;
} // namespace
static bool when_context_with_list_is_set_value_is_retrieved_correctly()
{
ctx2_t ctx("test");
float val = 2;
ctx.get<vector_metrics>().emplace_back();
ctx.at<vector_metrics>(0).write<m1_t>(val);
ASSERT_EQ(ctx.at<vector_metrics>(0).read<m1_t>(), val);
return true;
}
int main()
{
TEST_FUNCTION(when_context_is_built_then_context_name_is_valid);
TEST_FUNCTION(when_context_is_built_then_metric_sets_names_are_valid);
TEST_FUNCTION(when_context_is_built_then_metric_names_are_valid);
TEST_FUNCTION(when_context_is_built_then_metric_units_are_valid);
TEST_FUNCTION(when_metric_is_set_through_context_then_value_is_stored);
TEST_FUNCTION(when_context_with_list_is_set_value_is_retrieved_correctly);
return 0;
}

View File

@ -0,0 +1,295 @@
/**
*
* \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/formatters/json_formatter.h"
#include "srslte/srslog/detail/log_entry_metadata.h"
#include "testing_helpers.h"
#include <numeric>
using namespace srslog;
/// Helper to build a log entry.
static detail::log_entry_metadata build_log_entry_metadata()
{
// 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 {tp, {10, true}, "Text %d", std::move(store), "ABC", 'Z'};
}
static bool when_fully_filled_log_entry_then_everything_is_formatted()
{
fmt::memory_buffer buffer;
json_formatter{}.format(build_log_entry_metadata(), buffer);
std::string result = fmt::to_string(buffer);
std::string expected = "{\n"
" \"log_entry\": \"Text 88\"\n"
"}\n";
ASSERT_EQ(result, expected);
return true;
}
static bool
when_fully_filled_log_entry_with_hex_dump_then_everything_is_formatted()
{
auto entry = build_log_entry_metadata();
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"
" \"log_entry\": \"Text 88\",\n"
" \"hex_dump\": \"00 01 02 03 04 05 06 07 08 09 0a 0b\"\n"
"}\n";
ASSERT_EQ(result, expected);
return true;
}
namespace {
DECLARE_METRIC("SNR", snr_t, float, "dB");
DECLARE_METRIC("PWR", pwr_t, int, "dBm");
DECLARE_METRIC("CenterFreq", cfreq_t, unsigned, "MHz");
DECLARE_METRIC_SET("RF", myset1, snr_t, pwr_t, cfreq_t);
DECLARE_METRIC("Throughput", thr_t, float, "MB/s");
DECLARE_METRIC("Address", ip_addr_t, std::string, "");
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()
{
auto entry = build_log_entry_metadata();
entry.fmtstring = "";
basic_ctx_t ctx("UL Context");
ctx.get<myset1>().write<snr_t>(-55.1);
ctx.get<myset1>().write<pwr_t>(-10);
ctx.get<myset1>().write<cfreq_t>(1500);
ctx.get<myset2>().write<thr_t>(150.01);
ctx.get<myset2>().write<ip_addr_t>("192.168.1.0");
fmt::memory_buffer buffer;
json_formatter{}.format_ctx(ctx, std::move(entry), buffer);
std::string result = fmt::to_string(buffer);
std::string expected = "{\n"
" \"RF\": {\n"
" \"SNR\": -55.1,\n"
" \"PWR\": -10,\n"
" \"CenterFreq\": 1500\n"
" },\n"
" \"Network\": {\n"
" \"Throughput\": 150.01,\n"
" \"Address\": \"192.168.1.0\"\n"
" }\n"
"}\n";
ASSERT_EQ(result, expected);
return true;
}
static bool
when_log_entry_with_message_and_basic_context_is_passed_then_context_is_formatted()
{
auto entry = build_log_entry_metadata();
basic_ctx_t ctx("UL Context");
ctx.get<myset1>().write<snr_t>(-55.1);
ctx.get<myset1>().write<pwr_t>(-10);
ctx.get<myset1>().write<cfreq_t>(1500);
ctx.get<myset2>().write<thr_t>(150.01);
ctx.get<myset2>().write<ip_addr_t>("192.168.1.0");
fmt::memory_buffer buffer;
json_formatter{}.format_ctx(ctx, std::move(entry), buffer);
std::string result = fmt::to_string(buffer);
std::string expected = "{\n"
" \"log_entry\": \"Text 88\",\n"
" \"RF\": {\n"
" \"SNR\": -55.1,\n"
" \"PWR\": -10,\n"
" \"CenterFreq\": 1500\n"
" },\n"
" \"Network\": {\n"
" \"Throughput\": 150.01,\n"
" \"Address\": \"192.168.1.0\"\n"
" }\n"
"}\n";
ASSERT_EQ(result, expected);
return true;
}
namespace {
DECLARE_METRIC("bearer_id", bearer_id_t, unsigned, "");
DECLARE_METRIC("qci", qci_t, unsigned, "");
DECLARE_METRIC_SET("bearer_container", bearer_set, bearer_id_t, qci_t);
DECLARE_METRIC("ue_rnti", ue_rnti_t, unsigned, "");
DECLARE_METRIC("dl_cqi", dl_cqi_t, unsigned, "");
DECLARE_METRIC_LIST("bearer_list", bearer_list_t, std::vector<bearer_set>);
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_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()
{
complex_ctx_t ctx("UL Context");
auto entry = build_log_entry_metadata();
entry.fmtstring = "";
ctx.get<sector_list_t>().emplace_back();
ctx.at<sector_list_t>(0).get<ue_list_t>().emplace_back();
ctx.at<sector_list_t>(0).get<ue_list_t>().emplace_back();
ctx.at<sector_list_t>(0).at<ue_list_t>(0).get<bearer_list_t>().emplace_back();
ctx.at<sector_list_t>(0).at<ue_list_t>(0).get<bearer_list_t>().emplace_back();
ctx.at<sector_list_t>(0).at<ue_list_t>(1).get<bearer_list_t>().emplace_back();
ctx.at<sector_list_t>(0).at<ue_list_t>(1).get<bearer_list_t>().emplace_back();
fmt::memory_buffer buffer;
json_formatter{}.format_ctx(ctx, std::move(entry), buffer);
std::string result = fmt::to_string(buffer);
std::string expected = "{\n"
" \"sector_list\": [\n"
" {\n"
" \"sector_metrics\": {\n"
" \"type\": \"\",\n"
" \"sector_id\": 0,\n"
" \"ue_list\": [\n"
" {\n"
" \"ue_container\": {\n"
" \"ue_rnti\": 0,\n"
" \"dl_cqi\": 0,\n"
" \"bearer_list\": [\n"
" {\n"
" \"bearer_container\": {\n"
" \"bearer_id\": 0,\n"
" \"qci\": 0\n"
" }\n"
" },\n"
" {\n"
" \"bearer_container\": {\n"
" \"bearer_id\": 0,\n"
" \"qci\": 0\n"
" }\n"
" }\n"
" ]\n"
" }\n"
" },\n"
" {\n"
" \"ue_container\": {\n"
" \"ue_rnti\": 0,\n"
" \"dl_cqi\": 0,\n"
" \"bearer_list\": [\n"
" {\n"
" \"bearer_container\": {\n"
" \"bearer_id\": 0,\n"
" \"qci\": 0\n"
" }\n"
" },\n"
" {\n"
" \"bearer_container\": {\n"
" \"bearer_id\": 0,\n"
" \"qci\": 0\n"
" }\n"
" }\n"
" ]\n"
" }\n"
" }\n"
" ]\n"
" }\n"
" }\n"
" ]\n"
"}\n";
ASSERT_EQ(result, expected);
return true;
}
namespace {
DECLARE_METRIC("list_metric2", list_metric2, unsigned, "");
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>;
}; // 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();
entry.fmtstring = "";
fmt::memory_buffer buffer;
json_formatter{}.format_ctx(ctx, std::move(entry), buffer);
std::string result = fmt::to_string(buffer);
std::string expected = "{\n"
" \"list_metric3\": 0,\n"
" \"list_metric4\": 0,\n"
" \"metrics_list\": [\n"
" ]\n"
"}\n";
ASSERT_EQ(result, expected);
return true;
}
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);
return 0;
}

View File

@ -0,0 +1,98 @@
/**
*
* \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 TEST_DUMMIES
#define TEST_DUMMIES
#include "srslte/srslog/detail/log_backend.h"
#include "srslte/srslog/sink.h"
namespace test_dummies {
/// A Dummy implementation of a formatter.
class log_formatter_dummy : public srslog::log_formatter
{
public:
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,
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::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_end(const std::string& 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,
srslog::metric_kind kind,
unsigned level,
fmt::memory_buffer& buffer) override
{}
};
/// A Dummy implementation of a sink.
class sink_dummy : public srslog::sink
{
public:
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 flush() override { return {}; }
};
/// A Dummy implementation of the log backend.
class backend_dummy : public srslog::detail::log_backend
{
public:
void start() override {}
void push(srslog::detail::log_entry&& entry) override {}
bool is_running() const override { return true; }
};
} // namespace test_dummies
#endif // TEST_DUMMIES

View File

@ -0,0 +1,195 @@
/**
*
* \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/formatters/text_formatter.h"
#include "srslte/srslog/detail/log_entry_metadata.h"
#include "testing_helpers.h"
#include <numeric>
using namespace srslog;
/// Helper to build a log entry.
static detail::log_entry_metadata build_log_entry_metadata()
{
// 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 {tp, {10, true}, "Text %d", std::move(store), "ABC", 'Z'};
}
static bool when_fully_filled_log_entry_then_everything_is_formatted()
{
fmt::memory_buffer buffer;
text_formatter{}.format(build_log_entry_metadata(), buffer);
std::string result = fmt::to_string(buffer);
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_metadata();
entry.log_name = "";
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 [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_metadata();
entry.log_tag = '\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 ] [ 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_metadata();
entry.context.enabled = false;
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] 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_metadata();
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"
" 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;
}
namespace {
DECLARE_METRIC("SNR", snr_t, float, "dB");
DECLARE_METRIC("PWR", pwr_t, int, "dBm");
DECLARE_METRIC("CenterFreq", cfreq_t, unsigned, "MHz");
DECLARE_METRIC_SET("RF", myset1, snr_t, pwr_t, cfreq_t);
DECLARE_METRIC("Throughput", thr_t, float, "MB/s");
DECLARE_METRIC("Address", ip_addr_t, std::string, "");
DECLARE_METRIC_SET("Network", myset2, thr_t, ip_addr_t);
using ctx_t = srslog::build_context_type<myset1, myset2>;
} // namespace
static bool
when_log_entry_with_only_context_is_passed_then_context_is_formatted()
{
auto entry = build_log_entry_metadata();
entry.fmtstring = "";
ctx_t ctx("UL Context");
ctx.get<myset1>().write<snr_t>(-55.1);
ctx.get<myset1>().write<pwr_t>(-10);
ctx.get<myset1>().write<cfreq_t>(1500);
ctx.get<myset2>().write<thr_t>(150.01);
ctx.get<myset2>().write<ip_addr_t>("192.168.1.0");
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] Context dump for \"UL Context\"\n"
" RF\n"
" SNR: -55.1 dB\n"
" PWR: -10 dBm\n"
" CenterFreq: 1500 MHz\n"
" Network\n"
" Throughput: 150.01 MB/s\n"
" Address: 192.168.1.0\n";
ASSERT_EQ(result, expected);
return true;
}
static bool
when_log_entry_with_context_and_message_is_passed_then_context_is_formatted()
{
auto entry = build_log_entry_metadata();
ctx_t ctx("UL Context");
ctx.get<myset1>().write<snr_t>(-55.1);
ctx.get<myset1>().write<pwr_t>(-10);
ctx.get<myset1>().write<cfreq_t>(1500);
ctx.get<myset2>().write<thr_t>(150.01);
ctx.get<myset2>().write<ip_addr_t>("192.168.1.0");
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] [[RF_SNR: -55.1 dB, RF_PWR: -10 dBm, "
"RF_CenterFreq: 1500 MHz] [Network_Throughput: 150.01 MB/s, "
"Network_Address: 192.168.1.0]]: Text 88\n";
ASSERT_EQ(result, expected);
return true;
}
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_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);
return 0;
}