mirror of https://github.com/pvnis/srsRAN_4G.git
Include missing files.
parent
98a2c868b5
commit
f23fdf0639
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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})
|
@ -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() ? "," : "");
|
||||||
|
}
|
@ -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
|
@ -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);*/
|
||||||
|
}
|
@ -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
|
@ -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;
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
@ -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
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue