Commit 7ec4be04 authored by Dominik Charousset's avatar Dominik Charousset Committed by Dominik Charousset

Use bounded queue for the logger, cleanup code

- Use detail::ringbuffer as queue for the logger
- Limit queue size to 128 per default
- Consolidate various flags and parameters in a simple bitfield
- Re-assign log level integer values:
  * Log level QUIET is now 0
  * TRACE has now the new maximum value 15
  * Added 9 currently unused values for later extensions
- Use names, not integers for the CAF_LOG_LEVEL variable in CMake
- Remove several #ifdef-blocks in the logger implementation
  * A log level of QUIET already makes these code paths unreachable
  * Enables users to compile without CAF logging but still the logger
parent ce577346
......@@ -368,7 +368,22 @@ endif()
################################################################################
if(NOT CAF_LOG_LEVEL)
set(CAF_LOG_LEVEL "-1")
set(CAF_LOG_LEVEL "QUIET")
endif()
# set human-readable representation for log level
if(CAF_LOG_LEVEL STREQUAL "ERROR")
set(CAF_LOG_LEVEL_INT 3)
elseif(CAF_LOG_LEVEL STREQUAL "WARNING")
set(CAF_LOG_LEVEL_INT 6)
elseif(CAF_LOG_LEVEL STREQUAL "INFO")
set(CAF_LOG_LEVEL_INT 9)
elseif(CAF_LOG_LEVEL STREQUAL "DEBUG")
set(CAF_LOG_LEVEL_INT 12)
elseif(CAF_LOG_LEVEL STREQUAL "TRACE")
set(CAF_LOG_LEVEL_INT 15)
else()
set(CAF_LOG_LEVEL_INT 0)
endif()
################################################################################
......@@ -662,23 +677,6 @@ add_custom_target(gui_dummy SOURCES configure ${script_files})
# print summary #
################################################################################
# set human-readable representation for log level
set(LOG_LEVEL_STR "none")
if(CAF_LOG_LEVEL)
if(${CAF_LOG_LEVEL} EQUAL 0)
set(LOG_LEVEL_STR "ERROR")
elseif(${CAF_LOG_LEVEL} EQUAL 1)
set(LOG_LEVEL_STR "WARNING")
elseif(${CAF_LOG_LEVEL} EQUAL 2)
set(LOG_LEVEL_STR "INFO")
elseif(${CAF_LOG_LEVEL} EQUAL 3)
set(LOG_LEVEL_STR "DEBUG")
elseif(${CAF_LOG_LEVEL} EQUAL 4)
set(LOG_LEVEL_STR "TRACE")
else()
set(LOG_LEVEL_STR "invalid")
endif()
endif()
# little helper macro to invert a boolean
macro(invertYesNo in out)
if(${in})
......
......@@ -21,7 +21,7 @@
// this header is auto-generated by CMake
#define CAF_LOG_LEVEL @CAF_LOG_LEVEL@
#define CAF_LOG_LEVEL @CAF_LOG_LEVEL_INT@
#cmakedefine CAF_NO_MEM_MANAGEMENT
......
......@@ -283,27 +283,7 @@ while [ $# -ne 0 ]; do
;;
--with-log-level=*)
level=`echo "$optarg" | tr '[:lower:]' '[:upper:]'`
case $level in
ERROR)
append_cache_entry CAF_LOG_LEVEL STRING 0
;;
WARNING)
append_cache_entry CAF_LOG_LEVEL STRING 1
;;
INFO)
append_cache_entry CAF_LOG_LEVEL STRING 2
;;
DEBUG)
append_cache_entry CAF_LOG_LEVEL STRING 3
;;
TRACE)
append_cache_entry CAF_LOG_LEVEL STRING 4
;;
*)
echo "Invalid log level '$level'. Try '$0 --help' to see valid values."
exit 1
;;
esac
append_cache_entry CAF_LOG_LEVEL STRING "$level"
;;
--with-clang=*)
clang="$optarg"
......
......@@ -30,22 +30,21 @@
#include "caf/abstract_actor.hpp"
#include "caf/config.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/detail/arg_wrapper.hpp"
#include "caf/detail/pretty_type_name.hpp"
#include "caf/detail/ringbuffer.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/detail/shared_spinlock.hpp"
#include "caf/fwd.hpp"
#include "caf/intrusive/drr_queue.hpp"
#include "caf/intrusive/fifo_inbox.hpp"
#include "caf/intrusive/singly_linked.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/fifo_inbox.hpp"
#include "caf/intrusive/singly_linked.hpp"
#include "caf/detail/arg_wrapper.hpp"
#include "caf/detail/pretty_type_name.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/detail/shared_spinlock.hpp"
/*
* To enable logging, you have to define CAF_DEBUG. This enables
* CAF_LOG_ERROR messages. To enable more debugging output, you can
......@@ -66,16 +65,59 @@ namespace caf {
/// default, the logger generates log4j compatible output.
class logger : public ref_counted {
public:
// -- friends ----------------------------------------------------------------
friend class actor_system;
// -- constants --------------------------------------------------------------
/// Configures the size of the circular event queue.
static constexpr size_t queue_size = 128;
// -- member types -----------------------------------------------------------
/// Combines various logging-related flags and parameters into a bitfield.
struct config {
/// Stores `max(file_verbosity, console_verbosity)`.
unsigned verbosity : 4;
/// Configures the verbosity for file output.
unsigned file_verbosity : 4;
/// Configures the verbosity for console output.
unsigned console_verbosity : 4;
/// Configures whether the logger immediately writes its output in the
/// calling thread, bypassing its queue. Use this option only in
/// single-threaded test environments.
bool inline_output : 1;
/// Configures whether the logger generates colored output.
bool console_coloring : 1;
config();
};
/// Encapsulates a single logging event.
struct event : intrusive::singly_linked<event> {
struct event {
// -- constructors, destructors, and assignment operators ------------------
event() = default;
event(event&&) = default;
event(const event&) = default;
event& operator=(event&&) = default;
event& operator=(const event&) = default;
event(int lvl, string_view cat, string_view full_fun, string_view fun,
string_view fn, int line, std::string msg, std::thread::id t,
actor_id a, timestamp ts);
// -- member variables -----------------------------------------------------
/// Level/priority of the event.
int level;
......@@ -107,37 +149,6 @@ public:
timestamp tstamp;
};
struct policy {
// -- member types ---------------------------------------------------------
using mapped_type = event;
using task_size_type = long;
using deleter_type = std::default_delete<event>;
using unique_pointer = std::unique_ptr<event, deleter_type>;
using queue_type = intrusive::drr_queue<policy>;
using deficit_type = long;
// -- static member functions ----------------------------------------------
static inline void release(mapped_type* ptr) noexcept {
detail::disposer d;
d(ptr);
}
static inline task_size_type task_size(const mapped_type&) noexcept {
return 1;
}
static inline deficit_type quantum(const queue_type&, deficit_type x) {
return x;
}
};
/// Internal representation of format string entites.
enum field_type {
invalid_field,
......@@ -196,37 +207,52 @@ public:
std::string str_;
};
/// Returns the ID of the actor currently associated to the calling thread.
actor_id thread_local_aid();
/// Associates an actor ID to the calling thread and returns the last value.
actor_id thread_local_aid(actor_id aid);
/// Writes an entry to the log file.
void log(event* x);
// -- constructors, destructors, and assignment operators --------------------
~logger() override;
/** @cond PRIVATE */
// -- logging ----------------------------------------------------------------
static void set_current_actor_system(actor_system*);
/// Writes an entry to the event-queue of the logger.
/// @thread-safe
void log(event&& x);
static logger* current_logger();
// -- properties -------------------------------------------------------------
/// Returns the ID of the actor currently associated to the calling thread.
actor_id thread_local_aid();
bool accepts(int level, string_view cname);
/// Associates an actor ID to the calling thread and returns the last value.
actor_id thread_local_aid(actor_id aid);
/** @endcond */
/// Returns whether the logger is configured to accept input for given
/// component and log level.
bool accepts(int level, string_view component_name);
/// Returns the output format used for the log file.
inline const line_format& file_format() const {
const line_format& file_format() const {
return file_format_;
}
/// Returns the output format used for the console.
inline const line_format& console_format() const {
const line_format& console_format() const {
return console_format_;
}
int verbosity() const noexcept {
return static_cast<int>(cfg_.verbosity);
}
int file_verbosity() const noexcept {
return static_cast<int>(cfg_.file_verbosity);
}
int console_verbosity() const noexcept {
return static_cast<int>(cfg_.console_verbosity);
}
// -- static utility functions -----------------------------------------------
/// Renders the prefix (namespace and class) of a fully qualified function.
static void render_fun_prefix(std::ostream& out, const event& x);
......@@ -239,9 +265,6 @@ public:
/// Renders the date of `x` in ISO 8601 format.
static void render_date(std::ostream& out, timestamp x);
/// Renders `x` using the line format `lf` to `out`.
void render(std::ostream& out, const line_format& lf, const event& x) const;
/// Parses `format_str` into a format description vector.
/// @warning The returned vector can have pointers into `format_str`.
static line_format parse_format(const std::string& format_str);
......@@ -249,6 +272,11 @@ public:
/// Skips path in `filename`.
static string_view skip_path(string_view filename);
// -- utility functions ------------------------------------------------------
/// Renders `x` using the line format `lf` to `out`.
void render(std::ostream& out, const line_format& lf, const event& x) const;
/// Returns a string representation of the joined groups of `x` if `x` is an
/// actor with the `subscriber` mixin.
template <class T>
......@@ -272,30 +300,37 @@ public:
return "[]";
}
// -- individual flags -------------------------------------------------------
// -- thread-local properties ------------------------------------------------
/// Stores the actor system for the current thread.
static void set_current_actor_system(actor_system*);
/// Returns the logger for the current thread or `nullptr` if none is
/// registered.
static logger* current_logger();
private:
// -- constructors, destructors, and assignment operators --------------------
static constexpr int inline_output_flag = 0x01;
logger(actor_system& sys);
static constexpr int uncolored_console_flag = 0x02;
// -- initialization ---------------------------------------------------------
static constexpr int colored_console_flag = 0x04;
void init(actor_system_config& cfg);
// -- composed flags ---------------------------------------------------------
// -- event handling ---------------------------------------------------------
static constexpr int console_output_flag = 0x06;
void handle_event(const event& x);
private:
void handle_event(event& x);
void handle_file_event(event& x);
void handle_console_event(event& x);
void handle_file_event(const event& x);
void handle_console_event(const event& x);
void log_first_line();
void log_last_line();
logger(actor_system& sys);
void init(actor_system_config& cfg);
// -- thread management ------------------------------------------------------
void run();
......@@ -303,68 +338,98 @@ private:
void stop();
inline bool has(int flag) const noexcept {
return (flags_ & flag) != 0;
}
// -- member variables -------------------------------------------------------
inline void set(int flag) noexcept {
flags_ |= flag;
}
// Configures verbosity and output generation.
config cfg_;
// Filters events by component name.
std::string component_filter;
// References the parent system.
actor_system& system_;
int max_level_;
int console_level_;
int file_level_;
int flags_;
// Guards aids_.
detail::shared_spinlock aids_lock_;
// Maps thread IDs to actor IDs.
std::unordered_map<std::thread::id, actor_id> aids_;
std::thread thread_;
std::mutex queue_mtx_;
std::condition_variable queue_cv_;
intrusive::fifo_inbox<policy> queue_;
// Identifies the thread that called `logger::start`.
std::thread::id parent_thread_;
// Timestamp of the first log event.
timestamp t0_;
// Format for generating file output.
line_format file_format_;
// Format for generating console output.
line_format console_format_;
// Stream for file output.
std::fstream file_;
std::string component_filter;
// Filled with log events by other threads.
detail::ringbuffer<event, 128> queue_;
// Executes `logger::run`.
std::thread thread_;
};
/// @relates logger::field_type
std::string to_string(logger::field_type x);
/// @relates logger::field
std::string to_string(const logger::field& x);
/// @relates logger::field
bool operator==(const logger::field& x, const logger::field& y);
} // namespace caf
#define CAF_VOID_STMT static_cast<void>(0)
// -- macro constants ----------------------------------------------------------
#define CAF_CAT(a, b) a##b
/// Integer value for the QUIET log level.
#define CAF_LOG_LEVEL_QUIET 0
#define CAF_LOG_LEVEL_QUIET -1
#define CAF_LOG_LEVEL_ERROR 0
#define CAF_LOG_LEVEL_WARNING 1
#define CAF_LOG_LEVEL_INFO 2
#define CAF_LOG_LEVEL_DEBUG 3
#define CAF_LOG_LEVEL_TRACE 4
/// Integer value for the ERROR log level.
#define CAF_LOG_LEVEL_ERROR 3
#define CAF_ARG(argument) caf::detail::make_arg_wrapper(#argument, argument)
/// Integer value for the WARNING log level.
#define CAF_LOG_LEVEL_WARNING 6
#define CAF_ARG2(argname, argval) caf::detail::make_arg_wrapper(argname, argval)
/// Integer value for the INFO log level.
#define CAF_LOG_LEVEL_INFO 9
#define CAF_ARG3(argname, first, last) \
caf::detail::make_arg_wrapper(argname, first, last)
/// Integer value for the DEBUG log level.
#define CAF_LOG_LEVEL_DEBUG 12
/// Integer value for the TRACE log level.
#define CAF_LOG_LEVEL_TRACE 15
/// Expands to a no-op.
#define CAF_VOID_STMT static_cast<void>(0)
#ifndef CAF_LOG_COMPONENT
/// Name of the current component when logging.
#define CAF_LOG_COMPONENT "caf"
#endif // CAF_LOG_COMPONENT
// -- utility macros -----------------------------------------------------------
#ifdef CAF_MSVC
/// Expands to a string representation of the current funciton name that
/// includes the full function name and its signature.
#define CAF_PRETTY_FUN __FUNCSIG__
#else // CAF_MSVC
/// Expands to a string representation of the current funciton name that
/// includes the full function name and its signature.
#define CAF_PRETTY_FUN __PRETTY_FUNCTION__
#endif // CAF_MSVC
#ifndef CAF_LOG_COMPONENT
#define CAF_LOG_COMPONENT "caf"
#endif // CAF_LOG_COMPONENT
/// Concatenates `a` and `b` to a single preprocessor token.
#define CAF_CAT(a, b) a##b
#define CAF_LOG_MAKE_EVENT(aid, component, loglvl, message) \
::caf::logger::event { \
......@@ -374,7 +439,19 @@ bool operator==(const logger::field& x, const logger::field& y);
::std::this_thread::get_id(), aid, ::caf::make_timestamp() \
}
#if CAF_LOG_LEVEL == -1
/// Expands to `argument = <argument>` in log output.
#define CAF_ARG(argument) caf::detail::make_arg_wrapper(#argument, argument)
/// Expands to `argname = <argval>` in log output.
#define CAF_ARG2(argname, argval) caf::detail::make_arg_wrapper(argname, argval)
/// Expands to `argname = [argval, last)` in log output.
#define CAF_ARG3(argname, first, last) \
caf::detail::make_arg_wrapper(argname, first, last)
// -- logging macros -----------------------------------------------------------
#if CAF_LOG_LEVEL == CAF_LOG_LEVEL_QUIET
#define CAF_LOG_IMPL(unused1, unused2, unused3)
......@@ -391,7 +468,7 @@ inline caf::actor_id caf_set_aid_dummy() { return 0; }
#define CAF_LOG_TRACE(unused)
#else // CAF_LOG_LEVEL
#else // CAF_LOG_LEVEL == CAF_LOG_LEVEL_QUIET
#define CAF_LOG_IMPL(component, loglvl, message) \
do { \
......@@ -399,8 +476,7 @@ inline caf::actor_id caf_set_aid_dummy() { return 0; }
if (CAF_UNIFYN(caf_logger) != nullptr \
&& CAF_UNIFYN(caf_logger)->accepts(loglvl, component)) \
CAF_UNIFYN(caf_logger) \
->log( \
new CAF_LOG_MAKE_EVENT(CAF_UNIFYN(caf_logger)->thread_local_aid(), \
->log(CAF_LOG_MAKE_EVENT(CAF_UNIFYN(caf_logger)->thread_local_aid(), \
component, loglvl, message)); \
} while (false)
......@@ -458,60 +534,49 @@ inline caf::actor_id caf_set_aid_dummy() { return 0; }
#define CAF_LOG_ERROR(output) \
CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_ERROR, output)
#endif // CAF_LOG_LEVEL
#endif // CAF_LOG_LEVEL == CAF_LOG_LEVEL_QUIET
#ifndef CAF_LOG_INFO
# define CAF_LOG_INFO(output) CAF_VOID_STMT
#endif
#define CAF_LOG_INFO(output) CAF_VOID_STMT
#define CAF_LOG_INFO_IF(cond, output) CAF_VOID_STMT
#else // CAF_LOG_INFO
#define CAF_LOG_INFO_IF(cond, output) \
if (cond) \
CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_INFO, output); \
CAF_VOID_STMT
#endif // CAF_LOG_INFO
#ifndef CAF_LOG_DEBUG
# define CAF_LOG_DEBUG(output) CAF_VOID_STMT
#endif
#ifndef CAF_LOG_WARNING
# define CAF_LOG_WARNING(output) CAF_VOID_STMT
#endif
#ifndef CAF_LOG_ERROR
# define CAF_LOG_ERROR(output) CAF_VOID_STMT
#endif
#ifdef CAF_LOG_LEVEL
#define CAF_LOG_DEBUG(output) CAF_VOID_STMT
#define CAF_LOG_DEBUG_IF(cond, output) CAF_VOID_STMT
#else // CAF_LOG_DEBUG
#define CAF_LOG_DEBUG_IF(cond, output) \
if (cond) { \
if (cond) \
CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_DEBUG, output); \
} \
CAF_VOID_STMT
#define CAF_LOG_INFO_IF(cond, output) \
if (cond) { \
CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_INFO, output); \
} \
CAF_VOID_STMT
#endif // CAF_LOG_DEBUG
#ifndef CAF_LOG_WARNING
#define CAF_LOG_WARNING(output) CAF_VOID_STMT
#define CAF_LOG_WARNING_IF(cond, output) CAF_VOID_STMT
#else // CAF_LOG_WARNING
#define CAF_LOG_WARNING_IF(cond, output) \
if (cond) { \
if (cond) \
CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_WARNING, output); \
} \
CAF_VOID_STMT
#endif // CAF_LOG_WARNING
#ifndef CAF_LOG_ERROR
#define CAF_LOG_ERROR(output) CAF_VOID_STMT
#define CAF_LOG_ERROR_IF(cond, output) CAF_VOID_STMT
#else // CAF_LOG_ERROR
#define CAF_LOG_ERROR_IF(cond, output) \
if (cond) { \
if (cond) \
CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_ERROR, output); \
} \
CAF_VOID_STMT
#endif // CAF_LOG_ERROR
#else // CAF_LOG_LEVEL
#define CAF_LOG_DEBUG_IF(unused1, unused2)
#define CAF_LOG_INFO_IF(unused1, unused2)
#define CAF_LOG_WARNING_IF(unused1, unused2)
#define CAF_LOG_ERROR_IF(unused1, unused2)
#endif // CAF_LOG_LEVEL
// -- Standardized CAF events according to SE-0001.
// -- macros for logging CE-0001 events ----------------------------------------
/// The log component responsible for logging control flow events that are
/// crucial for understanding happens-before relations. See RFC SE-0001.
......
......@@ -48,11 +48,22 @@ namespace caf {
namespace {
constexpr string_view log_level_name[] = {
"QUIET",
"",
"",
"ERROR",
"",
"",
"WARN",
"",
"",
"INFO",
"",
"",
"DEBUG",
"TRACE"
"",
"",
"TRACE",
};
constexpr string_view fun_prefixes[] = {
......@@ -70,6 +81,26 @@ constexpr string_view anon_ns[] = {
"`anonymous-namespace'", // MSVC
};
/// Converts a verbosity atom to its integer counterpart.
unsigned to_level_int(atom_value x) {
switch (atom_uint(to_lowercase(x))) {
default:
return CAF_LOG_LEVEL_QUIET;
case atom_uint("quiet"):
return CAF_LOG_LEVEL_QUIET;
case atom_uint("error"):
return CAF_LOG_LEVEL_ERROR;
case atom_uint("warning"):
return CAF_LOG_LEVEL_WARNING;
case atom_uint("info"):
return CAF_LOG_LEVEL_INFO;
case atom_uint("debug"):
return CAF_LOG_LEVEL_DEBUG;
case atom_uint("trace"):
return CAF_LOG_LEVEL_TRACE;
}
}
// Reduces symbol by printing all prefixes to `out` and returning the
// remainder. For example, "ns::foo::bar" prints "ns.foo" to `out` and returns
// "bar".
......@@ -214,6 +245,15 @@ inline logger* get_current_logger() {
} // namespace <anonymous>
logger::config::config()
: verbosity(CAF_LOG_LEVEL),
file_verbosity(CAF_LOG_LEVEL),
console_verbosity(CAF_LOG_LEVEL),
inline_output(false),
console_coloring(false) {
// nop
}
logger::event::event(int lvl, string_view cat, string_view full_fun,
string_view fun, string_view fn, int line, std::string msg,
std::thread::id t, actor_id a, timestamp ts)
......@@ -292,14 +332,12 @@ actor_id logger::thread_local_aid(actor_id aid) {
return 0; // was empty before
}
void logger::log(event* x) {
void logger::log(event&& x) {
CAF_ASSERT(x->level >= 0 && x->level <= 4);
if (has(inline_output_flag)) {
std::unique_ptr<event> ptr{x};
handle_event(*ptr);
} else {
queue_.synchronized_push_back(queue_mtx_, queue_cv_,x);
}
if (cfg_.inline_output)
handle_event(x);
else
queue_.push_back(std::move(x));
}
void logger::set_current_actor_system(actor_system* x) {
......@@ -315,7 +353,7 @@ logger* logger::current_logger() {
bool logger::accepts(int level, string_view cname) {
CAF_ASSERT(level >= 0 && level <= 4);
if (level > max_level_)
if (level > cfg_.verbosity)
return false;
if (!component_filter.empty()) {
auto it = std::search(component_filter.begin(), component_filter.end(),
......@@ -325,6 +363,10 @@ bool logger::accepts(int level, string_view cname) {
return true;
}
logger::logger(actor_system& sys) : system_(sys) {
// nop
}
logger::~logger() {
stop();
// tell system our dtor is done
......@@ -333,46 +375,8 @@ logger::~logger() {
system_.logger_dtor_cv_.notify_one();
}
logger::logger(actor_system& sys)
: system_(sys),
max_level_(CAF_LOG_LEVEL),
flags_(0),
queue_(policy{}) {
// nop
}
namespace {
int to_level_int(const atom_value atom) {
switch (atom_uint(atom)) {
case atom_uint("quiet"):
case atom_uint("QUIET"):
return CAF_LOG_LEVEL_QUIET;
case atom_uint("error"):
case atom_uint("ERROR"):
return CAF_LOG_LEVEL_ERROR;
case atom_uint("warning"):
case atom_uint("WARNING"):
return CAF_LOG_LEVEL_WARNING;
case atom_uint("info"):
case atom_uint("INFO"):
return CAF_LOG_LEVEL_INFO;
case atom_uint("debug"):
case atom_uint("DEBUG"):
return CAF_LOG_LEVEL_DEBUG;
case atom_uint("trace"):
case atom_uint("TRACE"):
return CAF_LOG_LEVEL_TRACE;
default: {
// nop
}
}
return CAF_LOG_LEVEL_QUIET;
}
} // namespace
void logger::init(actor_system_config& cfg) {
CAF_IGNORE_UNUSED(cfg);
#if CAF_LOG_LEVEL >= 0
namespace lg = defaults::logger;
component_filter = get_or(cfg, "logger.component-filter",
lg::component_filter);
......@@ -383,9 +387,9 @@ void logger::init(actor_system_config& cfg) {
file_verbosity = get_or(cfg, "logger.file-verbosity", file_verbosity);
console_verbosity =
get_or(cfg, "logger.console-verbosity", console_verbosity);
file_level_ = to_level_int(file_verbosity);
console_level_ = to_level_int(console_verbosity);
max_level_ = std::max(file_level_, console_level_);
cfg_.file_verbosity = to_level_int(file_verbosity);
cfg_.console_verbosity = to_level_int(console_verbosity);
cfg_.verbosity = std::max(cfg_.file_verbosity, cfg_.console_verbosity);
// Parse the format string.
file_format_ =
parse_format(get_or(cfg, "logger.file-format", lg::file_format));
......@@ -393,13 +397,14 @@ void logger::init(actor_system_config& cfg) {
lg::console_format));
// Set flags.
if (get_or(cfg, "logger.inline-output", false))
set(inline_output_flag);
cfg_.inline_output = true;
auto con_atm = get_or(cfg, "logger.console", lg::console);
if (con_atm == atom("UNCOLORED"))
set(uncolored_console_flag);
else if (con_atm == atom("COLORED"))
set(colored_console_flag);
#endif
if (to_lowercase(con_atm) == atom("colored")) {
cfg_.console_coloring = true;
} else if (to_lowercase(con_atm) != atom("uncolored")) {
// Disable console output if neither 'colored' nor 'uncolored' are present.
cfg_.console_verbosity = CAF_LOG_LEVEL_QUIET;
}
}
void logger::render_fun_prefix(std::ostream& out, const event& x) {
......@@ -543,42 +548,29 @@ string_view logger::skip_path(string_view path) {
}
void logger::run() {
#if CAF_LOG_LEVEL >= 0
log_first_line();
// receive log entries from other threads and actors
bool stop = false;
auto f = [&](event& x) {
// empty message means: shut down
if (x.message.empty()) {
stop = true;
return intrusive::task_result::stop;
}
handle_event(x);
return intrusive::task_result::resume;
};
do {
// make sure we have data to read and consume events
queue_.synchronized_await(queue_mtx_, queue_cv_);
queue_.new_round(1000, f);
} while (!stop);
for (;;) {
queue_.wait_nonempty();
auto& e = queue_.front();
if (e.message.empty()) {
log_last_line();
#endif
return;
}
handle_event(e);
queue_.pop_front();
}
}
void logger::handle_file_event(event& x) {
void logger::handle_file_event(const event& x) {
// Print to file if available.
if (file_ && x.level <= file_level_)
if (file_ && x.level <= file_verbosity())
render(file_, file_format_, x);
}
void logger::handle_console_event(event& x) {
// Stop if no console output is configured.
if (!has(console_output_flag) || x.level > console_level_)
void logger::handle_console_event(const event& x) {
if (x.level > console_verbosity())
return;
if (has(uncolored_console_flag)) {
render(std::clog, console_format_, x);
std::clog << std::endl;
} else {
if (cfg_.console_coloring) {
switch (x.level) {
default:
break;
......@@ -600,63 +592,50 @@ void logger::handle_console_event(event& x) {
}
render(std::clog, console_format_, x);
std::clog << term::reset_endl;
} else {
render(std::clog, console_format_, x);
std::clog << std::endl;
}
}
void logger::handle_event(event& x) {
void logger::handle_event(const event& x) {
handle_file_event(x);
handle_console_event(x);
}
void logger::log_first_line() {
auto make_event = [&](string_view config_val,
const atom_value default_val) -> event {
auto e = CAF_LOG_MAKE_EVENT(0, CAF_LOG_COMPONENT, CAF_LOG_LEVEL_DEBUG, "");
auto make_message = [&](string_view config_name, atom_value default_value) {
std::string msg = "level = ";
msg += to_string(get_or(system_.config(), config_val, default_val));
msg += to_string(get_or(system_.config(), config_name, default_value));
msg += ", node = ";
msg += to_string(system_.node());
return {CAF_LOG_LEVEL_INFO,
CAF_LOG_COMPONENT,
CAF_PRETTY_FUN,
__func__,
__FILE__,
__LINE__,
std::move(msg),
std::this_thread::get_id(),
0,
make_timestamp()};
msg += ", component_filter = ";
msg += deep_to_string(component_filter);
return msg;
};
event file = make_event("logger.file-verbosity", defaults::logger::file_verbosity);
handle_file_event(file);
event console = make_event("logger.console-verbosity", defaults::logger::console_verbosity);
handle_console_event(console);
namespace lg = defaults::logger;
e.message = make_message("logger.file-verbosity", lg::file_verbosity);
handle_file_event(e);
e.message = make_message("logger.console-verbosity", lg::console_verbosity);
handle_console_event(e);
}
void logger::log_last_line() {
event tmp{CAF_LOG_LEVEL_INFO,
CAF_LOG_COMPONENT,
CAF_PRETTY_FUN,
__func__,
__FILE__,
__LINE__,
"EOF",
std::this_thread::get_id(),
0,
make_timestamp()};
handle_event(tmp);
auto e = CAF_LOG_MAKE_EVENT(0, CAF_LOG_COMPONENT, CAF_LOG_LEVEL_DEBUG, "");
handle_event(e);
}
void logger::start() {
#if CAF_LOG_LEVEL >= 0
parent_thread_ = std::this_thread::get_id();
if (max_level_ == CAF_LOG_LEVEL_QUIET)
if (verbosity() == CAF_LOG_LEVEL_QUIET)
return;
t0_ = make_timestamp();
auto f = get_or(system_.config(), "logger.file-name",
defaults::logger::file_name);
if (f.empty()) {
// No need to continue if console and log file are disabled.
if (has(console_output_flag))
if (console_verbosity() == CAF_LOG_LEVEL_QUIET)
return;
} else {
// Replace placeholders.
......@@ -685,7 +664,7 @@ void logger::start() {
return;
}
}
if (has(inline_output_flag))
if (cfg_.inline_output)
log_first_line();
else
thread_ = std::thread{[this] {
......@@ -694,21 +673,18 @@ void logger::start() {
this->run();
this->system_.thread_terminates();
}};
#endif
}
void logger::stop() {
#if CAF_LOG_LEVEL >= 0
if (has(inline_output_flag)) {
if (cfg_.inline_output) {
log_last_line();
return;
}
if (!thread_.joinable())
return;
// an empty string means: shut down
queue_.synchronized_push_back(queue_mtx_, queue_cv_, new event);
// A default-constructed event causes the logger to shutdown.
queue_.push_back(event{});
thread_.join();
#endif
}
std::string to_string(logger::field_type x) {
......
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