1 // This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
2 // the main distribution directory for license terms and copyright or visit
3 // https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
4
5 #define CAF_SUITE logger
6
7 #include "caf/logger.hpp"
8
9 #include "core-test.hpp"
10
11 #include <ctime>
12 #include <string>
13
14 #include "caf/all.hpp"
15
16 using namespace caf;
17 using namespace std::chrono;
18
19 using std::string;
20
21 #define CHECK_FUN_PREFIX(PrefixName) \
22 do { \
23 auto e = CAF_LOG_MAKE_EVENT(0, "caf", CAF_LOG_LEVEL_DEBUG, ""); \
24 std::ostringstream oss; \
25 ::caf::logger::render_fun_prefix(oss, e); \
26 auto prefix = oss.str(); \
27 if (prefix != PrefixName) \
28 CAF_ERROR("rendering the prefix of " << e.pretty_fun << " produced " \
29 << prefix << " instead of " \
30 << PrefixName); \
31 else \
32 CAF_CHECK_EQUAL(prefix, PrefixName); \
33 } while (false)
34
global_fun()35 void global_fun() {
36 CHECK_FUN_PREFIX("GLOBAL");
37 }
38
39 // Little helper that allows us to write a single check for all compilers.
40 // Clang expands template parameters in __PRETTY_FUNCTION__, while GCC does
41 // not. For example, Clang would produce "void foo<int>::bar()", while GCC
42 // would produce "void foo<T>::bar() [with T = int]". A type called T gives
43 // us always the same output for the prefix.
44 struct T {};
45
46 namespace {
47
48 struct fixture {
fixture__anonaa285d940111::fixture49 fixture() {
50 cfg.set("caf.scheduler.policy", "testing");
51 cfg.set("caf.logger.file.verbosity", "debug");
52 cfg.set("caf.logger.file.path", "");
53 }
54
add__anonaa285d940111::fixture55 void add(logger::field_type kind) {
56 lf.emplace_back(logger::field{kind, std::string{}});
57 }
58
59 template <size_t N>
add__anonaa285d940111::fixture60 void add(logger::field_type kind, const char (&str)[N]) {
61 lf.emplace_back(logger::field{kind, std::string{str, str + (N - 1)}});
62 }
63
64 template <class F, class... Ts>
render__anonaa285d940111::fixture65 string render(F f, Ts&&... xs) {
66 std::ostringstream oss;
67 f(oss, std::forward<Ts>(xs)...);
68 return oss.str();
69 }
70
71 actor_system_config cfg;
72 logger::line_format lf;
73 };
74
fun()75 void fun() {
76 CHECK_FUN_PREFIX("$");
77 }
78
ptr_fun(const char * x)79 const char* ptr_fun(const char* x) {
80 CHECK_FUN_PREFIX("$");
81 return x;
82 }
83
ref_fun(const int & x)84 const int& ref_fun(const int& x) {
85 CHECK_FUN_PREFIX("$");
86 return x;
87 }
88
89 template <class T>
90 struct tpl {
run__anonaa285d940111::tpl91 static void run() {
92 CHECK_FUN_PREFIX("$.tpl<T>");
93 }
94 };
95
96 namespace foo {
97
fun()98 void fun() {
99 CHECK_FUN_PREFIX("$.foo");
100 }
101
ptr_fun(const char * x)102 const char* ptr_fun(const char* x) {
103 CHECK_FUN_PREFIX("$.foo");
104 return x;
105 }
106
ref_fun(const int & x)107 const int& ref_fun(const int& x) {
108 CHECK_FUN_PREFIX("$.foo");
109 return x;
110 }
111
112 template <class T>
113 struct tpl {
run__anonaa285d940111::foo::tpl114 static void run() {
115 CHECK_FUN_PREFIX("$.foo.tpl<T>");
116 }
117 };
118
119 } // namespace foo
120
121 constexpr const char* file_format = "%r %c %p %a %t %C %M %F:%L %m%n";
122
123 } // namespace
124
CAF_TEST_FIXTURE_SCOPE(logger_tests,fixture)125 CAF_TEST_FIXTURE_SCOPE(logger_tests, fixture)
126
127 // copy construction, copy assign, move construction, move assign
128 // and finally serialization round-trip
129 CAF_TEST(parse_default_format_strings) {
130 actor_system sys{cfg};
131 add(logger::runtime_field);
132 add(logger::plain_text_field, " ");
133 add(logger::category_field);
134 add(logger::plain_text_field, " ");
135 add(logger::priority_field);
136 add(logger::plain_text_field, " ");
137 add(logger::actor_field);
138 add(logger::plain_text_field, " ");
139 add(logger::thread_field);
140 add(logger::plain_text_field, " ");
141 add(logger::class_name_field);
142 add(logger::plain_text_field, " ");
143 add(logger::method_field);
144 add(logger::plain_text_field, " ");
145 add(logger::file_field);
146 add(logger::plain_text_field, ":");
147 add(logger::line_field);
148 add(logger::plain_text_field, " ");
149 add(logger::message_field);
150 add(logger::newline_field);
151 CAF_CHECK_EQUAL(logger::parse_format(file_format), lf);
152 CAF_CHECK_EQUAL(sys.logger().file_format(), lf);
153 }
154
CAF_TEST(rendering)155 CAF_TEST(rendering) {
156 // Rendering of time points.
157 timestamp t0;
158 time_t t0_t = 0;
159 char t0_buf[50];
160 CAF_REQUIRE(strftime(t0_buf, sizeof(t0_buf),
161 "%Y-%m-%dT%H:%M:%S.000", localtime(&t0_t)));
162 CAF_CHECK_EQUAL(render(logger::render_date, t0), t0_buf);
163 // Rendering of events.
164 logger::event e{
165 CAF_LOG_LEVEL_WARNING,
166 42,
167 "unit_test",
168 "void ns::foo::bar()",
169 "bar",
170 "foo.cpp",
171 "hello world",
172 std::this_thread::get_id(),
173 0,
174 t0,
175 };
176 CAF_CHECK_EQUAL(render(logger::render_fun_name, e), string_view{"bar"});
177 CAF_CHECK_EQUAL(render(logger::render_fun_prefix, e),
178 string_view{"ns.foo"});
179 // Exclude %r and %t from rendering test because they are nondeterministic.
180 actor_system sys{cfg};
181 auto lf = logger::parse_format("%c %p %a %C %M %F:%L %m");
182 auto& lg = sys.logger();
183 using namespace std::placeholders;
184 auto render_event = bind(&logger::render, &lg, _1, _2, _3);
185 CAF_CHECK_EQUAL(render(render_event, lf, e),
186 "unit_test WARN actor0 ns.foo bar foo.cpp:42 hello world");
187 }
188
CAF_TEST(render_fun_prefix)189 CAF_TEST(render_fun_prefix) {
190 int i = 42;
191 global_fun();
192 fun();
193 ptr_fun(nullptr);
194 ref_fun(i);
195 tpl<T>::run();
196 foo::fun();
197 foo::ptr_fun(nullptr);
198 foo::ref_fun(i);
199 foo::tpl<T>::run();
200 }
201
202 CAF_TEST_FIXTURE_SCOPE_END()
203