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 ...@@ -106,6 +106,7 @@ set(LIBCAF_CORE_SRCS
src/stream_aborter.cpp src/stream_aborter.cpp
src/stream_manager.cpp src/stream_manager.cpp
src/stream_priority.cpp src/stream_priority.cpp
src/string_algorithms.cpp
src/string_view.cpp src/string_view.cpp
src/stringification_inspector.cpp src/stringification_inspector.cpp
src/sync_request_bouncer.cpp src/sync_request_bouncer.cpp
......
...@@ -16,20 +16,6 @@ ...@@ -16,20 +16,6 @@
* http://www.boost.org/LICENSE_1_0.txt. * * 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 #pragma once
#include <string> #include <string>
...@@ -46,4 +32,3 @@ std::string pretty_type_name(const std::type_info& x); ...@@ -46,4 +32,3 @@ std::string pretty_type_name(const std::type_info& x);
} // namespace detail } // namespace detail
} // namespace caf } // namespace caf
...@@ -27,14 +27,15 @@ ...@@ -27,14 +27,15 @@
#include <type_traits> #include <type_traits>
#include <unordered_map> #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/abstract_actor.hpp"
#include "caf/config.hpp"
#include "caf/deep_to_string.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/drr_queue.hpp"
#include "caf/intrusive/fifo_inbox.hpp" #include "caf/intrusive/fifo_inbox.hpp"
...@@ -71,20 +72,24 @@ public: ...@@ -71,20 +72,24 @@ public:
struct event : intrusive::singly_linked<event> { struct event : intrusive::singly_linked<event> {
event() = default; event() = default;
event(int lvl, const char* cat, const char* fun, const char* fn, int line, event(int lvl, string_view cat, string_view full_fun, string_view fun,
std::string msg, std::thread::id t, actor_id a, timestamp ts); string_view fn, int line, std::string msg, std::thread::id t,
actor_id a, timestamp ts);
/// Level/priority of the event. /// Level/priority of the event.
int level; int level;
/// Name of the category (component) logging the event. /// 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__`. /// Name of the current function as reported by `__PRETTY_FUNCTION__`.
const char* pretty_fun; string_view pretty_fun;
/// Name of the current function as reported by `__func__`.
string_view simple_fun;
/// Name of the current file. /// Name of the current file.
const char* file_name; string_view file_name;
/// Current line in the file. /// Current line in the file.
int line_number; int line_number;
...@@ -179,6 +184,8 @@ public: ...@@ -179,6 +184,8 @@ public:
line_builder& operator<<(const std::string& str); line_builder& operator<<(const std::string& str);
line_builder& operator<<(string_view str);
line_builder& operator<<(const char* str); line_builder& operator<<(const char* str);
line_builder& operator<<(char x); line_builder& operator<<(char x);
...@@ -206,12 +213,7 @@ public: ...@@ -206,12 +213,7 @@ public:
static logger* current_logger(); static logger* current_logger();
bool accepts(int level, const char* cname_begin, const char* cname_end); bool accepts(int level, string_view cname);
template <size_t N>
bool accepts(int level, const char (&component)[N]) {
return accepts(level, component, component + N - 1);
}
/** @endcond */ /** @endcond */
...@@ -226,10 +228,10 @@ public: ...@@ -226,10 +228,10 @@ public:
} }
/// Renders the prefix (namespace and class) of a fully qualified function. /// 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. /// 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. /// Renders the difference between `t0` and `tn` in milliseconds.
static void render_time_diff(std::ostream& out, timestamp t0, timestamp tn); static void render_time_diff(std::ostream& out, timestamp t0, timestamp tn);
...@@ -245,7 +247,7 @@ public: ...@@ -245,7 +247,7 @@ public:
static line_format parse_format(const std::string& format_str); static line_format parse_format(const std::string& format_str);
/// Skips path in `filename`. /// 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 /// Returns a string representation of the joined groups of `x` if `x` is an
/// actor with the `subscriber` mixin. /// actor with the `subscriber` mixin.
...@@ -364,6 +366,14 @@ bool operator==(const logger::field& x, const logger::field& y); ...@@ -364,6 +366,14 @@ bool operator==(const logger::field& x, const logger::field& y);
#define CAF_LOG_COMPONENT "caf" #define CAF_LOG_COMPONENT "caf"
#endif // CAF_LOG_COMPONENT #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 #if CAF_LOG_LEVEL == -1
#define CAF_LOG_IMPL(unused1, unused2, unused3) #define CAF_LOG_IMPL(unused1, unused2, unused3)
...@@ -389,12 +399,9 @@ inline caf::actor_id caf_set_aid_dummy() { return 0; } ...@@ -389,12 +399,9 @@ inline caf::actor_id caf_set_aid_dummy() { return 0; }
if (CAF_UNIFYN(caf_logger) != nullptr \ if (CAF_UNIFYN(caf_logger) != nullptr \
&& CAF_UNIFYN(caf_logger)->accepts(loglvl, component)) \ && CAF_UNIFYN(caf_logger)->accepts(loglvl, component)) \
CAF_UNIFYN(caf_logger) \ CAF_UNIFYN(caf_logger) \
->log(new ::caf::logger::event{ \ ->log( \
loglvl, component, CAF_PRETTY_FUN, caf::logger::skip_path(__FILE__), \ new CAF_LOG_MAKE_EVENT(CAF_UNIFYN(caf_logger)->thread_local_aid(), \
__LINE__, (::caf::logger::line_builder{} << message).get(), \ component, loglvl, message)); \
::std::this_thread::get_id(), \
CAF_UNIFYN(caf_logger)->thread_local_aid(), \
::caf::make_timestamp()}); \
} while (false) } while (false)
#define CAF_PUSH_AID(aarg) \ #define CAF_PUSH_AID(aarg) \
......
...@@ -28,118 +28,66 @@ ...@@ -28,118 +28,66 @@
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
#include "caf/string_view.hpp"
namespace caf { namespace caf {
// provide boost::split compatible interface // 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; return arg;
} }
constexpr bool token_compress_on = false; constexpr bool token_compress_on = false;
template <class Container, class Str, class Delim> void split(std::vector<std::string>& result, string_view str,
void split(Container& result, const Str& str, const Delim& delims, string_view delims, bool keep_all = true);
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
}
iterator begin() const { void split(std::vector<string_view>& result, string_view str,
return begin_; string_view delims, bool keep_all = true);
}
iterator end() const { void split(std::vector<std::string>& result, string_view str,
return end_; char delim, bool keep_all = true);
}
private: void split(std::vector<string_view>& result, string_view str,
iterator begin_; char delim, bool keep_all = true);
iterator end_;
};
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> template <class Container>
std::string join(const Container& c, const std::string& glue) { std::string join(const Container& c, const std::string& glue) {
auto begin = c.begin(); return join(c.begin(), c.end(), glue);
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();
} }
// end of recursion // end of recursion
inline void splice(std::string&, const std::string&) { inline void splice(std::string&, string_view) {
// nop // nop
} }
template <class T, class... Ts> template <class T, class... Ts>
void splice(std::string& str, const std::string& glue, T&& arg, Ts&&... xs) { void splice(std::string& str, string_view glue, T&& arg, Ts&&... xs) {
str += glue; str.insert(str.end(), glue.begin(), glue.end());
str += std::forward<T>(arg); str += std::forward<T>(arg);
splice(str, glue, std::forward<Ts>(xs)...); splice(str, glue, std::forward<Ts>(xs)...);
} }
template <ptrdiff_t WhatSize, ptrdiff_t WithSize> /// Replaces all occurrences of `what` by `with` in `str`.
void replace_all(std::string& str, void replace_all(std::string& str, string_view what, string_view with);
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));
}
}
template<size_t S> /// Returns whether `str` begins with `prefix`.
bool starts_with(const std::string& str, const char (&prefix)[S]) { bool starts_with(string_view str, string_view prefix);
return str.compare(0, S - 1, prefix) == 0;
}
template <size_t S> /// Returns whether `str` ends with `suffix`.
bool ends_with(const std::string& str, const char (&suffix)[S]) { bool ends_with(string_view str, string_view suffix);
auto n = str.size();
auto m = S - 1;
if (n >= m)
return str.compare(n - m, m, suffix) == 0;
return false;
}
template <class T> template <class T>
typename std::enable_if< typename std::enable_if<
......
This diff is collapsed.
...@@ -25,16 +25,15 @@ std::string replies_to_type_name(size_t input_size, ...@@ -25,16 +25,15 @@ std::string replies_to_type_name(size_t input_size,
const std::string* input, const std::string* input,
size_t output_opt1_size, size_t output_opt1_size,
const std::string* output_opt1) { const std::string* output_opt1) {
using irange = iterator_range<const std::string*>; string_view glue = ",";
std::string glue = ",";
std::string result; std::string result;
// 'void' is not an announced type, hence we check whether uniform_typeid // 'void' is not an announced type, hence we check whether uniform_typeid
// did return a valid pointer to identify 'void' (this has the // did return a valid pointer to identify 'void' (this has the
// possibility of false positives, but those will be catched anyways) // possibility of false positives, but those will be catched anyways)
result = "caf::replies_to<"; result = "caf::replies_to<";
result += join(irange{input, input + input_size}, glue); result += join(input, input + input_size, glue);
result += ">::with<"; result += ">::with<";
result += join(irange{output_opt1, output_opt1 + output_opt1_size}, glue); result += join(output_opt1, output_opt1 + output_opt1_size, glue);
result += ">"; result += ">";
return 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 @@ ...@@ -27,9 +27,35 @@
#include "caf/all.hpp" #include "caf/all.hpp"
using namespace caf; using namespace caf;
using namespace std;
using namespace std::chrono; 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 { namespace {
struct fixture { struct fixture {
...@@ -48,8 +74,8 @@ struct fixture { ...@@ -48,8 +74,8 @@ struct fixture {
template <class F, class... Ts> template <class F, class... Ts>
string render(F f, Ts&&... xs) { string render(F f, Ts&&... xs) {
ostringstream oss; std::ostringstream oss;
f(oss, forward<Ts>(xs)...); f(oss, std::forward<Ts>(xs)...);
return oss.str(); return oss.str();
} }
...@@ -57,6 +83,52 @@ struct fixture { ...@@ -57,6 +83,52 @@ struct fixture {
logger::line_format lf; 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"; constexpr const char* file_format = "%r %c %p %a %t %C %M %F:%L %m%n";
} // namespace <anonymous> } // namespace <anonymous>
...@@ -95,10 +167,6 @@ CAF_TEST(parse_default_format_strings) { ...@@ -95,10 +167,6 @@ CAF_TEST(parse_default_format_strings) {
} }
CAF_TEST(rendering) { 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. // Rendering of time points.
timestamp t0; timestamp t0;
timestamp t1{timestamp::duration{5000000}}; // epoch + 5000000ns (5ms) timestamp t1{timestamp::duration{5000000}}; // epoch + 5000000ns (5ms)
...@@ -113,13 +181,17 @@ CAF_TEST(rendering) { ...@@ -113,13 +181,17 @@ CAF_TEST(rendering) {
CAF_LOG_LEVEL_WARNING, CAF_LOG_LEVEL_WARNING,
"unit.test", "unit.test",
"void ns::foo::bar()", "void ns::foo::bar()",
"bar",
"foo.cpp", "foo.cpp",
42, 42,
"hello world", "hello world",
this_thread::get_id(), std::this_thread::get_id(),
0, 0,
t0 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. // Exclude %r and %t from rendering test because they are nondeterministic.
actor_system sys{cfg}; actor_system sys{cfg};
auto lf = logger::parse_format("%c %p %a %C %M %F:%L %m"); auto lf = logger::parse_format("%c %p %a %C %M %F:%L %m");
...@@ -130,4 +202,17 @@ CAF_TEST(rendering) { ...@@ -130,4 +202,17 @@ CAF_TEST(rendering) {
"unit.test WARN actor0 ns.foo bar foo.cpp:42 hello world"); "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() 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