Commit a6b7d52c authored by Dominik Charousset's avatar Dominik Charousset Committed by GitHub

Merge pull request #523 from actor-framework/topic/logger-configuration

Make logger more configurable
parents b9ee7c0a 4c2a3a6f
......@@ -34,6 +34,7 @@ set (LIBCAF_CORE_SRCS
src/behavior_impl.cpp
src/blocking_actor.cpp
src/blocking_behavior.cpp
src/color.cpp
src/concatenated_tuple.cpp
src/config_option.cpp
src/continue_helper.cpp
......
......@@ -252,6 +252,13 @@ public:
size_t work_stealing_relaxed_steal_interval;
size_t work_stealing_relaxed_sleep_duration_us;
// -- config parameters for the logger ---------------------------------------
std::string logger_filename;
atom_value logger_verbosity;
atom_value logger_console;
std::string logger_filter;
// -- config parameters of the middleman -------------------------------------
atom_value middleman_network_backend;
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2016 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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. *
******************************************************************************/
#ifndef CAF_COLOR_HPP
#define CAF_COLOR_HPP
namespace caf {
enum color_face {
normal,
bold
};
enum color_value {
reset,
black,
red,
green,
yellow,
blue,
magenta,
cyan,
white
};
const char* color(color_value value, color_face face = normal);
} // namespace caf
#endif // CAF_COLOR_HPP
......@@ -17,8 +17,8 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_DETAIL_LOGGING_HPP
#define CAF_DETAIL_LOGGING_HPP
#ifndef CAF_LOGGER_HPP
#define CAF_LOGGER_HPP
#include <thread>
#include <cstring>
......@@ -64,8 +64,12 @@ public:
struct event {
event* next;
event* prev;
int level;
const char* component;
std::string prefix;
std::string msg;
explicit event(std::string log_message = "");
explicit event(int l = 0, char const* c = "", std::string p = "",
std::string m = "");
};
template <class T>
......@@ -167,6 +171,7 @@ private:
void stop();
actor_system& system_;
int level_;
detail::shared_spinlock aids_lock_;
std::unordered_map<std::thread::id, actor_id> aids_;
std::thread thread_;
......@@ -187,18 +192,6 @@ private:
#define CAF_LOG_LEVEL_DEBUG 3
#define CAF_LOG_LEVEL_TRACE 4
#define CAF_PRINT_ERROR_IMPL(nclass, nfun, message) \
do { \
caf::logger::line_builder lb; \
lb << message; \
if (nclass.empty()) \
printf("[ERROR] in %s:%d %s::%s: %s\n", __FILE__, __LINE__, \
nclass.c_str(), nfun, lb.get().c_str()); \
else \
printf("[ERROR] in %s:%d %s: %s\n", __FILE__, __LINE__, \
nfun, lb.get().c_str()); \
} while (false)
#define CAF_ARG(argument) caf::logger::make_arg_wrapper(#argument, argument)
#ifdef CAF_MSVC
......@@ -286,6 +279,8 @@ inline caf::actor_id caf_set_aid_dummy() { return 0; }
# define CAF_LOG_WARNING(output) CAF_LOG_IMPL(CAF_LOG_LEVEL_WARNING, output)
#endif
#define CAF_LOG_ERROR(output) CAF_LOG_IMPL(CAF_LOG_LEVEL_ERROR, output)
#endif // CAF_LOG_LEVEL
#ifndef CAF_LOG_INFO
......@@ -300,11 +295,9 @@ inline caf::actor_id caf_set_aid_dummy() { return 0; }
# define CAF_LOG_WARNING(output) CAF_VOID_STMT
#endif
#define CAF_LOG_ERROR(output) \
do { \
CAF_PRINT_ERROR_IMPL(CAF_GET_CLASS_NAME, __func__, output); \
CAF_LOG_IMPL(CAF_LOG_LEVEL_ERROR, output); \
} while (false)
#ifndef CAF_LOG_ERROR
# define CAF_LOG_ERROR(output) CAF_VOID_STMT
#endif
#ifdef CAF_LOG_LEVEL
......@@ -329,4 +322,4 @@ inline caf::actor_id caf_set_aid_dummy() { return 0; }
#endif // CAF_LOG_LEVEL
#endif // CAF_DETAIL_LOGGING_HPP
#endif // CAF_LOGGER_HPP
......@@ -242,12 +242,13 @@ actor_system::actor_system(actor_system_config& cfg)
if (mod)
mod->init(cfg);
groups_.init(cfg);
// start logger before spawning actors (since that uses the logger)
logger_.start();
// spawn config and spawn servers (lazily to not access the scheduler yet)
static constexpr auto Flags = hidden + lazy_init;
spawn_serv_ = actor_cast<strong_actor_ptr>(spawn<Flags>(spawn_serv_impl));
config_serv_ = actor_cast<strong_actor_ptr>(spawn<Flags>(config_serv_impl));
// fire up remaining modules
logger_.start();
registry_.start();
registry_.put(atom("SpawnServ"), spawn_serv_);
registry_.put(atom("ConfigServ"), config_serv_);
......
......@@ -22,6 +22,7 @@
#include <limits>
#include <thread>
#include <fstream>
#include <sstream>
#include "caf/message_builder.hpp"
......@@ -95,6 +96,8 @@ actor_system_config::actor_system_config()
work_stealing_moderate_sleep_duration_us = 50;
work_stealing_relaxed_steal_interval = 1;
work_stealing_relaxed_sleep_duration_us = 10000;
logger_filename = "actor_log_[PID]_[TIMESTAMP]_[NODE].log";
logger_console = atom("NONE");
middleman_network_backend = atom("default");
middleman_enable_automatic_connections = false;
middleman_max_consecutive_reads = 50;
......@@ -128,6 +131,15 @@ actor_system_config::actor_system_config()
"sets the frequency of steal attempts during relaxed polling")
.add(work_stealing_relaxed_sleep_duration_us, "relaxed-sleep-duration",
"sets the sleep interval between poll attempts during relaxed polling");
opt_group{options_, "logger"}
.add(logger_filename, "filename",
"sets the filesystem path of the log file")
.add(logger_verbosity, "verbosity",
"sets the verbosity (QUIET|ERROR|WARNING|INFO|DEBUG|TRACE)")
.add(logger_console, "console",
"enables logging to the console via std::clog")
.add(logger_filter, "filter",
"sets a component filter for console log messages");
opt_group{options_, "middleman"}
.add(middleman_network_backend, "network-backend",
"sets the network backend to either 'default' or 'asio' (if available)")
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2016 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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/color.hpp"
#include "caf/config.hpp"
namespace caf {
const char* color(color_value value, color_face face) {
#ifdef CAF_MSVC
return "";
#else
const char* colors[9][2] = {
{"\033[0m", "\033[0m"}, // reset
{"\033[30m", "\033[1m\033[30m"}, // black
{"\033[31m", "\033[1m\033[31m"}, // red
{"\033[32m", "\033[1m\033[32m"}, // green
{"\033[33m", "\033[1m\033[33m"}, // yellow
{"\033[34m", "\033[1m\033[34m"}, // blue
{"\033[35m", "\033[1m\033[35m"}, // magenta
{"\033[36m", "\033[1m\033[36m"}, // cyan
{"\033[37m", "\033[1m\033[37m"} // white
};
return colors[static_cast<size_t>(value)][static_cast<size_t>(face)];
#endif
}
} // namespace caf
......@@ -36,9 +36,11 @@
#include "caf/string_algorithms.hpp"
#include "caf/color.hpp"
#include "caf/locks.hpp"
#include "caf/actor_proxy.hpp"
#include "caf/actor_system.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/detail/get_process_id.hpp"
#include "caf/detail/single_reader_queue.hpp"
......@@ -59,8 +61,6 @@ constexpr const char* log_level_name[] = {
static_assert(CAF_LOG_LEVEL >= 0 && CAF_LOG_LEVEL <= 4,
"assertion: 0 <= CAF_LOG_LEVEL <= 4");
constexpr int global_log_level = CAF_LOG_LEVEL;
#ifdef CAF_MSVC
thread_local
#else
......@@ -125,10 +125,13 @@ void prettify_type_name(std::string& class_name, const char* c_class_name) {
} // namespace <anonymous>
logger::event::event(std::string x)
logger::event::event(int l, const char* c, std::string p, std::string m)
: next(nullptr),
prev(nullptr),
msg(std::move(x)) {
level(l),
component(c),
prefix(std::move(p)),
msg(std::move(m)) {
// nop
}
......@@ -224,6 +227,8 @@ void logger::log(int level, const char* component,
const char* c_full_file_name, int line_num,
const std::string& msg) {
CAF_ASSERT(level >= 0 && level <= 4);
if (level > level_)
return;
std::string file_name;
std::string full_file_name = c_full_file_name;
auto ri = find(full_file_name.rbegin(), full_file_name.rend(), '/');
......@@ -238,12 +243,13 @@ void logger::log(int level, const char* component,
}
auto t0 = std::chrono::high_resolution_clock::now().time_since_epoch();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(t0).count();
std::ostringstream line;
line << ms << " " << component << " " << log_level_name[level] << " "
std::ostringstream prefix;
prefix << ms << " " << component << " " << log_level_name[level] << " "
<< "actor" << thread_local_aid() << " " << std::this_thread::get_id()
<< " " << class_name << " " << function_name << " " << file_name << ":"
<< line_num << " " << msg << std::endl;
queue_.synchronized_enqueue(queue_mtx_, queue_cv_, new event{line.str()});
<< " " << class_name << " " << function_name
<< " " << file_name << ":" << line_num;
queue_.synchronized_enqueue(queue_mtx_, queue_cv_,
new event{level, component, prefix.str(), msg});
}
void logger::set_current_actor_system(actor_system* x) {
......@@ -273,11 +279,27 @@ logger::logger(actor_system& sys) : system_(sys) {
}
void logger::run() {
std::ostringstream fname;
fname << "actor_log_" << detail::get_process_id() << "_" << time(0)
<< "_" << to_string(system_.node())
<< ".log";
std::fstream out(fname.str().c_str(), std::ios::out | std::ios::app);
auto f = system_.config().logger_filename;
// Replace placeholders.
const char pid[] = "[PID]";
auto i = std::search(f.begin(), f.end(), std::begin(pid), std::end(pid) - 1);
if (i != f.end()) {
auto id = std::to_string(detail::get_process_id());
f.replace(i, i + sizeof(pid) - 1, id);
}
const char ts[] = "[TIMESTAMP]";
i = std::search(f.begin(), f.end(), std::begin(ts), std::end(ts) - 1);
if (i != f.end()) {
auto now = std::to_string(time(0));
f.replace(i, i + sizeof(ts) - 1, now);
}
const char node[] = "[NODE]";
i = std::search(f.begin(), f.end(), std::begin(node), std::end(node) - 1);
if (i != f.end()) {
auto nid = to_string(system_.node());
f.replace(i, i + sizeof(node) - 1, nid);
}
std::fstream file(f, std::ios::out | std::ios::app);
std::unique_ptr<event> ptr;
for (;;) {
// make sure we have data to read
......@@ -286,28 +308,74 @@ void logger::run() {
ptr.reset(queue_.try_pop());
CAF_ASSERT(ptr != nullptr);
if (ptr->msg.empty()) {
out.close();
file.close();
return;
}
out << ptr->msg << std::flush;
file << ptr->prefix << ' ' << ptr->msg << std::endl;
// TODO: once we've phased out GCC 4.8, we can upgarde this to a regex.
if (!system_.config().logger_filter.empty()
&& ptr->component != system_.config().logger_filter)
continue;
if (system_.config().logger_console == atom("UNCOLORED")) {
std::clog << ptr->msg << std::endl;
} else if (system_.config().logger_console == atom("COLORED")) {
switch (ptr->level) {
default:
break;
case CAF_LOG_LEVEL_ERROR:
std::clog << color(red);
break;
case CAF_LOG_LEVEL_WARNING:
std::clog << color(yellow);
break;
case CAF_LOG_LEVEL_INFO:
std::clog << color(green);
break;
case CAF_LOG_LEVEL_DEBUG:
std::clog << color(cyan);
break;
case CAF_LOG_LEVEL_TRACE:
std::clog << color(blue);
break;
}
std::clog << ptr->msg << color(reset) << std::endl;
}
}
}
void logger::start() {
#if defined(CAF_LOG_LEVEL) && CAF_LOG_LEVEL >= CAF_LOG_LEVEL_INFO
const char* log_level_table[] = {"ERROR", "WARN", "INFO", "DEBUG", "TRACE"};
#if defined(CAF_LOG_LEVEL)
auto& lvl = system_.config().logger_verbosity;
if (lvl == atom("QUIET"))
return;
if (lvl == atom("ERROR"))
level_ = CAF_LOG_LEVEL_ERROR;
else if (lvl == atom("WARNING"))
level_ = CAF_LOG_LEVEL_WARNING;
else if (lvl == atom("INFO"))
level_ = CAF_LOG_LEVEL_INFO;
else if (lvl == atom("DEBUG"))
level_ = CAF_LOG_LEVEL_DEBUG;
else if (lvl == atom("TRACE"))
level_ = CAF_LOG_LEVEL_TRACE;
else
level_ = CAF_LOG_LEVEL;
thread_ = std::thread{[this] { this->run(); }};
std::string msg = "ENTRY log level = ";
msg += log_level_table[global_log_level];
const char* levels[] = {"ERROR", "WARNING", "INFO", "DEBUG", "TRACE"};
msg += levels[level_];
log(CAF_LOG_LEVEL_INFO, "caf", "caf::logger", "run", __FILE__, __LINE__, msg);
#endif
}
void logger::stop() {
#if defined(CAF_LOG_LEVEL) && CAF_LOG_LEVEL >= CAF_LOG_LEVEL_INFO
log(CAF_LOG_LEVEL_INFO, "caf", "caf::logger", "run", __FILE__, __LINE__, "EXIT");
#if defined(CAF_LOG_LEVEL)
if (!thread_.joinable())
return;
log(CAF_LOG_LEVEL_INFO, "caf", "caf::logger", "run", __FILE__, __LINE__,
"EXIT");
// an empty string means: shut down
queue_.synchronized_enqueue(queue_mtx_, queue_cv_, new event{""});
queue_.synchronized_enqueue(queue_mtx_, queue_cv_, new event);
thread_.join();
#endif
}
......
......@@ -32,6 +32,7 @@
#include <iostream>
#include "caf/fwd.hpp"
#include "caf/color.hpp"
#include "caf/optional.hpp"
#include "caf/deep_to_string.hpp"
......@@ -213,23 +214,6 @@ private:
std::mutex file_mtx_;
};
enum color_face {
normal,
bold
};
enum color_value {
reset,
black,
red,
green,
yellow,
blue,
magenta,
cyan,
white
};
/// Drives unit test execution.
class engine {
public:
......@@ -310,17 +294,7 @@ private:
int argc_ = 0;
char** argv_ = nullptr;
char* path_ = nullptr;
const char* colors_[9][2] = {
{"\033[0m", "\033[0m"}, // reset
{"\033[30m", "\033[1m\033[30m"}, // black
{"\033[31m", "\033[1m\033[31m"}, // red
{"\033[32m", "\033[1m\033[32m"}, // green
{"\033[33m", "\033[1m\033[33m"}, // yellow
{"\033[34m", "\033[1m\033[34m"}, // blue
{"\033[35m", "\033[1m\033[35m"}, // magenta
{"\033[36m", "\033[1m\033[36m"}, // cyan
{"\033[37m", "\033[1m\033[37m"} // white
};
bool colorize_ = false;
const char* check_file_ = "<none>";
size_t check_line_ = 0;
test* current_test_ = nullptr;
......@@ -413,8 +387,8 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
#define CAF_TEST_PRINT(level, msg, colorcode) \
(::caf::test::logger::instance(). level () \
<< ::caf::test::engine::color(::caf::test:: colorcode ) \
<< " -> " << ::caf::test::engine::color(::caf::test::reset) << msg \
<< ::caf::test::engine::color(::caf:: colorcode ) \
<< " -> " << ::caf::test::engine::color(::caf::reset) << msg \
<< " [line " << __LINE__ << "]\n")
#define CAF_TEST_PRINT_ERROR(msg) CAF_TEST_PRINT(info, msg, red)
......
......@@ -316,13 +316,7 @@ bool engine::run(bool colorize,
// nothing to do
return true;
}
if (!colorize) {
for (size_t i = 0; i <= static_cast<size_t>(white); ++i) {
for (size_t j = 0; j <= static_cast<size_t>(bold); ++j) {
instance().colors_[i][j] = "";
}
}
}
instance().colorize_ = colorize;
if (!logger::init(verbosity_console, verbosity_file, log_file)) {
return false;
}
......@@ -475,7 +469,7 @@ bool engine::run(bool colorize,
}
const char* engine::color(color_value v, color_face t) {
return instance().colors_[static_cast<size_t>(v)][static_cast<size_t>(t)];
return instance().colorize_ ? caf::color(v, t) : "";
}
const char* engine::last_check_file() {
......
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