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