Unverified Commit 2c1e56f5 authored by Joseph Noir's avatar Joseph Noir Committed by GitHub

Merge pull request #795

Fix invalid class names in CAF log output
parents 28cd6b5d c083daa0
......@@ -106,6 +106,7 @@ set(LIBCAF_CORE_SRCS
src/stream_aborter.cpp
src/stream_manager.cpp
src/stream_priority.cpp
src/string_algorithms.cpp
src/string_view.cpp
src/stringification_inspector.cpp
src/sync_request_bouncer.cpp
......
......@@ -16,20 +16,6 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
// The rationale of this header is to provide a serialization API
// that is compatbile to boost.serialization. In particular, the
// design goals are:
// - allow users to integrate existing boost.serialization-based code easily
// - allow to switch out this header with the actual boost header in boost.actor
//
// Differences in semantics are:
// - CAF does *not* respect class versions
// - the `unsigned int` argument is always 0 and ignored by CAF
//
// Since CAF requires all runtime instances to have the same types
// announced, different class versions in a single actor system would
// cause inconsistencies that are not recoverable.
#pragma once
#include <string>
......@@ -46,4 +32,3 @@ std::string pretty_type_name(const std::type_info& x);
} // namespace detail
} // namespace caf
......@@ -27,14 +27,15 @@
#include <type_traits>
#include <unordered_map>
#include "caf/fwd.hpp"
#include "caf/config.hpp"
#include "caf/unifyn.hpp"
#include "caf/type_nr.hpp"
#include "caf/timestamp.hpp"
#include "caf/ref_counted.hpp"
#include "caf/abstract_actor.hpp"
#include "caf/config.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/fwd.hpp"
#include "caf/ref_counted.hpp"
#include "caf/string_view.hpp"
#include "caf/timestamp.hpp"
#include "caf/type_nr.hpp"
#include "caf/unifyn.hpp"
#include "caf/intrusive/drr_queue.hpp"
#include "caf/intrusive/fifo_inbox.hpp"
......@@ -71,20 +72,24 @@ public:
struct event : intrusive::singly_linked<event> {
event() = default;
event(int lvl, const char* cat, const char* fun, const char* fn, int line,
std::string msg, std::thread::id t, actor_id a, timestamp ts);
event(int lvl, string_view cat, string_view full_fun, string_view fun,
string_view fn, int line, std::string msg, std::thread::id t,
actor_id a, timestamp ts);
/// Level/priority of the event.
int level;
/// Name of the category (component) logging the event.
const char* category_name;
string_view category_name;
/// Name of the current context as reported by `__PRETTY_FUNCTION__`.
const char* pretty_fun;
/// Name of the current function as reported by `__PRETTY_FUNCTION__`.
string_view pretty_fun;
/// Name of the current function as reported by `__func__`.
string_view simple_fun;
/// Name of the current file.
const char* file_name;
string_view file_name;
/// Current line in the file.
int line_number;
......@@ -179,6 +184,8 @@ public:
line_builder& operator<<(const std::string& str);
line_builder& operator<<(string_view str);
line_builder& operator<<(const char* str);
line_builder& operator<<(char x);
......@@ -206,12 +213,7 @@ public:
static logger* current_logger();
bool accepts(int level, const char* cname_begin, const char* cname_end);
template <size_t N>
bool accepts(int level, const char (&component)[N]) {
return accepts(level, component, component + N - 1);
}
bool accepts(int level, string_view cname);
/** @endcond */
......@@ -226,10 +228,10 @@ public:
}
/// Renders the prefix (namespace and class) of a fully qualified function.
static void render_fun_prefix(std::ostream& out, const char* pretty_fun);
static void render_fun_prefix(std::ostream& out, const event& x);
/// Renders the name of a fully qualified function.
static void render_fun_name(std::ostream& out, const char* pretty_fun);
static void render_fun_name(std::ostream& out, const event& x);
/// Renders the difference between `t0` and `tn` in milliseconds.
static void render_time_diff(std::ostream& out, timestamp t0, timestamp tn);
......@@ -245,7 +247,7 @@ public:
static line_format parse_format(const std::string& format_str);
/// Skips path in `filename`.
static const char* skip_path(const char* filename);
static string_view skip_path(string_view filename);
/// Returns a string representation of the joined groups of `x` if `x` is an
/// actor with the `subscriber` mixin.
......@@ -364,6 +366,14 @@ bool operator==(const logger::field& x, const logger::field& y);
#define CAF_LOG_COMPONENT "caf"
#endif // CAF_LOG_COMPONENT
#define CAF_LOG_MAKE_EVENT(aid, component, loglvl, message) \
::caf::logger::event { \
loglvl, component, CAF_PRETTY_FUN, __func__, \
caf::logger::skip_path(__FILE__), __LINE__, \
(::caf::logger::line_builder{} << message).get(), \
::std::this_thread::get_id(), aid, ::caf::make_timestamp() \
}
#if CAF_LOG_LEVEL == -1
#define CAF_LOG_IMPL(unused1, unused2, unused3)
......@@ -389,12 +399,9 @@ inline caf::actor_id caf_set_aid_dummy() { return 0; }
if (CAF_UNIFYN(caf_logger) != nullptr \
&& CAF_UNIFYN(caf_logger)->accepts(loglvl, component)) \
CAF_UNIFYN(caf_logger) \
->log(new ::caf::logger::event{ \
loglvl, component, CAF_PRETTY_FUN, caf::logger::skip_path(__FILE__), \
__LINE__, (::caf::logger::line_builder{} << message).get(), \
::std::this_thread::get_id(), \
CAF_UNIFYN(caf_logger)->thread_local_aid(), \
::caf::make_timestamp()}); \
->log( \
new CAF_LOG_MAKE_EVENT(CAF_UNIFYN(caf_logger)->thread_local_aid(), \
component, loglvl, message)); \
} while (false)
#define CAF_PUSH_AID(aarg) \
......
......@@ -28,118 +28,66 @@
#include "caf/config.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/string_view.hpp"
namespace caf {
// provide boost::split compatible interface
inline std::string is_any_of(std::string arg) {
inline string_view is_any_of(string_view arg) {
return arg;
}
constexpr bool token_compress_on = false;
template <class Container, class Str, class Delim>
void split(Container& result, const Str& str, const Delim& delims,
bool keep_all = true) {
size_t pos = 0;
size_t prev = 0;
while ((pos = str.find_first_of(delims, prev)) != std::string::npos) {
if (pos > prev) {
auto substr = str.substr(prev, pos - prev);
if (!substr.empty() || keep_all) {
result.push_back(std::move(substr));
}
}
prev = pos + 1;
}
if (prev < str.size())
result.push_back(str.substr(prev, std::string::npos));
}
template <class Iterator>
class iterator_range {
public:
using iterator = Iterator;
iterator_range(iterator first, iterator last) : begin_(first), end_(last) {
// nop
}
void split(std::vector<std::string>& result, string_view str,
string_view delims, bool keep_all = true);
iterator begin() const {
return begin_;
}
void split(std::vector<string_view>& result, string_view str,
string_view delims, bool keep_all = true);
iterator end() const {
return end_;
}
void split(std::vector<std::string>& result, string_view str,
char delim, bool keep_all = true);
private:
iterator begin_;
iterator end_;
};
void split(std::vector<string_view>& result, string_view str,
char delim, bool keep_all = true);
template <class InputIterator>
std::string join(InputIterator first, InputIterator last, string_view glue) {
if (first == last)
return {};
std::ostringstream oss;
oss << *first++;
for (; first != last; ++first)
oss << glue << *first;
return oss.str();
}
template <class Container>
std::string join(const Container& c, const std::string& glue) {
auto begin = c.begin();
auto end = c.end();
bool first = true;
std::ostringstream oss;
for ( ; begin != end; ++begin) {
if (first)
first = false;
else
oss << glue;
oss << *begin;
}
return oss.str();
return join(c.begin(), c.end(), glue);
}
// end of recursion
inline void splice(std::string&, const std::string&) {
inline void splice(std::string&, string_view) {
// nop
}
template <class T, class... Ts>
void splice(std::string& str, const std::string& glue, T&& arg, Ts&&... xs) {
str += glue;
void splice(std::string& str, string_view glue, T&& arg, Ts&&... xs) {
str.insert(str.end(), glue.begin(), glue.end());
str += std::forward<T>(arg);
splice(str, glue, std::forward<Ts>(xs)...);
}
template <ptrdiff_t WhatSize, ptrdiff_t WithSize>
void replace_all(std::string& str,
const char (&what)[WhatSize],
const char (&with)[WithSize]) {
// end(what) - 1 points to the null-terminator
auto next = [&](std::string::iterator pos) -> std::string::iterator {
return std::search(pos, str.end(), std::begin(what), std::end(what) - 1);
};
auto i = next(std::begin(str));
while (i != std::end(str)) {
auto before = std::distance(std::begin(str), i);
CAF_ASSERT(before >= 0);
str.replace(i, i + WhatSize - 1, with);
// i became invalidated -> use new iterator pointing
// to the first character after the replaced text
i = next(str.begin() + before + (WithSize - 1));
}
}
/// Replaces all occurrences of `what` by `with` in `str`.
void replace_all(std::string& str, string_view what, string_view with);
template<size_t S>
bool starts_with(const std::string& str, const char (&prefix)[S]) {
return str.compare(0, S - 1, prefix) == 0;
}
/// Returns whether `str` begins with `prefix`.
bool starts_with(string_view str, string_view prefix);
template <size_t S>
bool ends_with(const std::string& str, const char (&suffix)[S]) {
auto n = str.size();
auto m = S - 1;
if (n >= m)
return str.compare(n - m, m, suffix) == 0;
return false;
}
/// Returns whether `str` ends with `suffix`.
bool ends_with(string_view str, string_view suffix);
template <class T>
typename std::enable_if<
......
This diff is collapsed.
......@@ -25,16 +25,15 @@ std::string replies_to_type_name(size_t input_size,
const std::string* input,
size_t output_opt1_size,
const std::string* output_opt1) {
using irange = iterator_range<const std::string*>;
std::string glue = ",";
string_view glue = ",";
std::string result;
// 'void' is not an announced type, hence we check whether uniform_typeid
// did return a valid pointer to identify 'void' (this has the
// possibility of false positives, but those will be catched anyways)
result = "caf::replies_to<";
result += join(irange{input, input + input_size}, glue);
result += join(input, input + input_size, glue);
result += ">::with<";
result += join(irange{output_opt1, output_opt1 + output_opt1_size}, glue);
result += join(output_opt1, output_opt1 + output_opt1_size, glue);
result += ">";
return result;
}
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/string_algorithms.hpp"
namespace caf {
namespace {
template <class F>
void split_impl(F consume, string_view str, string_view delims, bool keep_all) {
size_t pos = 0;
size_t prev = 0;
while ((pos = str.find_first_of(delims, prev)) != std::string::npos) {
if (pos > prev) {
auto substr = str.substr(prev, pos - prev);
if (!substr.empty() || keep_all)
consume(substr);
}
prev = pos + 1;
}
if (prev < str.size())
consume(str.substr(prev, std::string::npos));
}
} // namespace <anonymous>
void split(std::vector<std::string>& result, string_view str,
string_view delims, bool keep_all) {
auto f = [&](string_view sv) {
result.emplace_back(sv.begin(), sv.end());
};
return split_impl(f, str, delims, keep_all);
}
void split(std::vector<string_view>& result, string_view str,
string_view delims, bool keep_all) {
auto f = [&](string_view sv) {
result.emplace_back(sv);
};
return split_impl(f, str, delims, keep_all);
}
void split(std::vector<std::string>& result, string_view str, char delim,
bool keep_all) {
split(result, str, string_view{&delim, 1}, keep_all);
}
void split(std::vector<string_view>& result, string_view str, char delim,
bool keep_all) {
split(result, str, string_view{&delim, 1}, keep_all);
}
void replace_all(std::string& str, string_view what, string_view with) {
// end(what) - 1 points to the null-terminator
auto next = [&](std::string::iterator pos) -> std::string::iterator {
return std::search(pos, str.end(), what.begin(), what.end());
};
auto i = next(str.begin());
while (i != str.end()) {
auto before = std::distance(str.begin(), i);
CAF_ASSERT(before >= 0);
str.replace(i, i + what.size(), with.begin(), with.end());
// Iterator i became invalidated -> use new iterator pointing to the first
// character after the replaced text.
i = next(str.begin() + before + with.size());
}
}
bool starts_with(string_view str, string_view prefix) {
return str.compare(0, prefix.size(), prefix) == 0;
}
bool ends_with(string_view str, string_view suffix) {
auto n = str.size();
auto m = suffix.size();
return n >= m ? str.compare(n - m, m, suffix) == 0 : false;
}
} // namespace caf
......@@ -27,9 +27,35 @@
#include "caf/all.hpp"
using namespace caf;
using namespace std;
using namespace std::chrono;
using std::string;
#define CHECK_FUN_PREFIX(PrefixName) \
do { \
auto e = CAF_LOG_MAKE_EVENT(0, "caf", CAF_LOG_LEVEL_DEBUG, ""); \
std::ostringstream oss; \
::caf::logger::render_fun_prefix(oss, e); \
auto prefix = oss.str(); \
if (prefix != PrefixName) \
CAF_ERROR("rendering the prefix of " << e.pretty_fun << " produced " \
<< prefix << " instead of " \
<< PrefixName); \
else \
CAF_CHECK_EQUAL(prefix, PrefixName); \
} while (false)
void global_fun() {
CHECK_FUN_PREFIX("GLOBAL");
}
// Little helper that allows us to write a single check for all compilers.
// Clang expands template parameters in __PRETTY_FUNCTION__, while GCC does
// not. For example, Clang would produce "void foo<int>::bar()", while GCC
// would produce "void foo<T>::bar() [with T = int]". A type called T gives
// us always the same ouptut for the prefix.
struct T {};
namespace {
struct fixture {
......@@ -48,8 +74,8 @@ struct fixture {
template <class F, class... Ts>
string render(F f, Ts&&... xs) {
ostringstream oss;
f(oss, forward<Ts>(xs)...);
std::ostringstream oss;
f(oss, std::forward<Ts>(xs)...);
return oss.str();
}
......@@ -57,6 +83,52 @@ struct fixture {
logger::line_format lf;
};
void fun() {
CHECK_FUN_PREFIX("$");
}
const char* ptr_fun(const char* x) {
CHECK_FUN_PREFIX("$");
return x;
}
const int& ref_fun(const int& x) {
CHECK_FUN_PREFIX("$");
return x;
}
template <class T>
struct tpl {
static void run() {
CHECK_FUN_PREFIX("$.tpl<T>");
}
};
namespace foo {
void fun() {
CHECK_FUN_PREFIX("$.foo");
}
const char* ptr_fun(const char* x) {
CHECK_FUN_PREFIX("$.foo");
return x;
}
const int& ref_fun(const int& x) {
CHECK_FUN_PREFIX("$.foo");
return x;
}
template <class T>
struct tpl {
static void run() {
CHECK_FUN_PREFIX("$.foo.tpl<T>");
}
};
} // namespace foo
constexpr const char* file_format = "%r %c %p %a %t %C %M %F:%L %m%n";
} // namespace <anonymous>
......@@ -95,10 +167,6 @@ CAF_TEST(parse_default_format_strings) {
}
CAF_TEST(rendering) {
// Rendering of type names and function names.
const char* foobar = "void ns::foo::bar()";
CAF_CHECK_EQUAL(render(logger::render_fun_name, foobar), "bar");
CAF_CHECK_EQUAL(render(logger::render_fun_prefix, foobar), "ns.foo");
// Rendering of time points.
timestamp t0;
timestamp t1{timestamp::duration{5000000}}; // epoch + 5000000ns (5ms)
......@@ -113,13 +181,17 @@ CAF_TEST(rendering) {
CAF_LOG_LEVEL_WARNING,
"unit.test",
"void ns::foo::bar()",
"bar",
"foo.cpp",
42,
"hello world",
this_thread::get_id(),
std::this_thread::get_id(),
0,
t0
};
CAF_CHECK_EQUAL(render(logger::render_fun_name, e), string_view{"bar"});
CAF_CHECK_EQUAL(render(logger::render_fun_prefix, e),
string_view{"ns.foo"});
// Exclude %r and %t from rendering test because they are nondeterministic.
actor_system sys{cfg};
auto lf = logger::parse_format("%c %p %a %C %M %F:%L %m");
......@@ -130,4 +202,17 @@ CAF_TEST(rendering) {
"unit.test WARN actor0 ns.foo bar foo.cpp:42 hello world");
}
CAF_TEST(render_fun_prefix) {
int i = 42;
global_fun();
fun();
ptr_fun(nullptr);
ref_fun(i);
tpl<T>::run();
foo::fun();
foo::ptr_fun(nullptr);
foo::ref_fun(i);
foo::tpl<T>::run();
}
CAF_TEST_FIXTURE_SCOPE_END()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment