Commit 51b1991a authored by Matthias Vallentin's avatar Matthias Vallentin

Make logger configurable

This patch adds support to configure the logger via the
actor_system_config of the corresponding system.
parent 57faf14c
......@@ -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;
std::string logger_verbosity;
bool logger_console;
bool logger_colorize;
// -- config parameters of the middleman -------------------------------------
atom_value middleman_network_backend;
......
......@@ -64,8 +64,10 @@ public:
struct event {
event* next;
event* prev;
int level;
std::string prefix;
std::string msg;
explicit event(std::string log_message = "");
explicit event(int l = 0, std::string p = "", std::string m = "");
};
template <class T>
......@@ -167,6 +169,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_;
......
......@@ -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_);
......
......@@ -19,12 +19,15 @@
#include "caf/actor_system_config.hpp"
#include <ctime>
#include <limits>
#include <thread>
#include <fstream>
#include <sstream>
#include "caf/message_builder.hpp"
#include "caf/detail/get_process_id.hpp"
#include "caf/detail/parse_ini.hpp"
namespace caf {
......@@ -95,6 +98,14 @@ 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;
std::ostringstream default_filename;
default_filename << "actor_log_" << detail::get_process_id()
<< "_" << time(0)
<< "_[NODE]" // dynamic placeholder for the node ID
<< ".log";
logger_filename = default_filename.str();
logger_console = false;
logger_colorize = false;
middleman_network_backend = atom("default");
middleman_enable_automatic_connections = false;
middleman_max_consecutive_reads = 50;
......@@ -128,6 +139,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_colorize, "colorize",
"colorizes console output (ignored on Windows)");
opt_group{options_, "middleman"}
.add(middleman_network_backend, "network-backend",
"sets the network backend to either 'default' or 'asio' (if available)")
......
......@@ -19,7 +19,6 @@
#include "caf/logger.hpp"
#include <ctime>
#include <thread>
#include <cstring>
#include <fstream>
......@@ -39,8 +38,8 @@
#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"
namespace caf {
......@@ -55,12 +54,35 @@ constexpr const char* log_level_name[] = {
"TRACE"
};
#ifndef CAF_MSVC
namespace color {
// UNIX terminal color codes
constexpr char reset[] = "\033[0m";
constexpr char black[] = "\033[30m";
constexpr char red[] = "\033[31m";
constexpr char green[] = "\033[32m";
constexpr char yellow[] = "\033[33m";
constexpr char blue[] = "\033[34m";
constexpr char magenta[] = "\033[35m";
constexpr char cyan[] = "\033[36m";
constexpr char white[] = "\033[37m";
constexpr char bold_black[] = "\033[1m\033[30m";
constexpr char bold_red[] = "\033[1m\033[31m";
constexpr char bold_green[] = "\033[1m\033[32m";
constexpr char bold_yellow[] = "\033[1m\033[33m";
constexpr char bold_blue[] = "\033[1m\033[34m";
constexpr char bold_magenta[] = "\033[1m\033[35m";
constexpr char bold_cyan[] = "\033[1m\033[36m";
constexpr char bold_white[] = "\033[1m\033[37m";
} // namespace color
#endif
#ifdef CAF_LOG_LEVEL
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 +147,12 @@ 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, std::string p, std::string m)
: next(nullptr),
prev(nullptr),
msg(std::move(x)) {
level(l),
prefix(std::move(p)),
msg(std::move(m)) {
// nop
}
......@@ -223,7 +247,9 @@ void logger::log(int level, const char* component,
const std::string& class_name, const char* function_name,
const char* c_full_file_name, int line_num,
const std::string& msg) {
CAF_ASSERT(level >= 0 && level <= 4);
CAF_ASSERT(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 +264,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] << " "
<< "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()});
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;
queue_.synchronized_enqueue(queue_mtx_, queue_cv_,
new event{level, prefix.str(), msg});
}
void logger::set_current_actor_system(actor_system* x) {
......@@ -273,11 +300,15 @@ 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 filename = system_.config().logger_filename;
// Replace node ID placeholder.
auto placeholder = std::string{"[NODE]"};
auto i = filename.find(placeholder);
if (i != std::string::npos) {
auto nid = to_string(system_.node());
filename.replace(i, placeholder.size(), nid);
}
std::fstream out(filename, std::ios::out | std::ios::app);
std::unique_ptr<event> ptr;
for (;;) {
// make sure we have data to read
......@@ -289,25 +320,71 @@ void logger::run() {
out.close();
return;
}
out << ptr->msg << std::flush;
out << ptr->prefix << ' ' << ptr->msg << std::endl;
if (system_.config().logger_console) {
#ifndef CAF_MSVC
if (system_.config().logger_colorize) {
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;
} else {
#endif
std::clog << ptr->msg << std::endl;
#ifndef CAF_MSVC
}
#endif
}
}
}
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)
const char* levels[] = {"ERROR", "WARNING", "INFO", "DEBUG", "TRACE"};
auto& lvl = system_.config().logger_verbosity;
if (lvl == "QUIET")
return;
if (lvl.empty()) {
level_ = CAF_LOG_LEVEL;
} else {
auto i = std::find(std::begin(levels), std::end(levels), lvl);
if (i == std::end(levels))
level_ = CAF_LOG_LEVEL; // ignore invalid log levels
else
level_ = static_cast<int>(std::distance(std::begin(levels), i));
CAF_ASSERT(level_ >= CAF_LOG_LEVEL_ERROR && level_ <= CAF_LOG_LEVEL_TRACE);
}
thread_ = std::thread{[this] { this->run(); }};
std::string msg = "ENTRY log level = ";
msg += log_level_table[global_log_level];
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
}
......
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