Merge pull request #5198 from wesnoth/thread_safe_logging

Thread-safe logging

Closes #5209
This commit is contained in:
Celtic Minstrel 2020-10-14 23:30:02 -04:00 committed by GitHub
commit 988bf973e9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 305 additions and 151 deletions

View file

@ -77,7 +77,7 @@ std::string deprecated_message(
if(log_ptr && !log_ptr->dont_log(log_deprecate)) {
const lg::logger& out_log = *log_ptr;
out_log(log_deprecate) << message << '\n';
FORCE_LOG_TO(out_log, log_deprecate) << message << '\n';
if(preferences::get("show_deprecation", false)) {
lg::wml_error() << message << '\n';

View file

@ -17,12 +17,8 @@
#include <pango/pango.h>
#include <ostream>
namespace font {
inline std::ostream& operator<<(std::ostream& s, const PangoRectangle &rect)
{
s << rect.x << ',' << rect.y << " x " << rect.width << ',' << rect.height;
return s;
}
} // end namespace font

View file

@ -67,6 +67,31 @@ public:
{
return stream_.str();
}
// Support manipulators
formatter& operator<<(std::ostream&(*fn)(std::ostream&)) &
{
fn(stream_);
return *this;
}
formatter&& operator<<(std::ostream&(*fn)(std::ostream&)) &&
{
fn(stream_);
return std::move(*this);
}
formatter& operator<<(std::ios_base&(*fn)(std::ios_base&)) &
{
fn(stream_);
return *this;
}
formatter&& operator<<(std::ios_base&(*fn)(std::ios_base&)) &&
{
fn(stream_);
return std::move(*this);
}
private:
std::ostringstream stream_;

View file

@ -134,7 +134,7 @@ void game_config_manager::load_game_config_with_loadscreen(FORCE_RELOAD_CONFIG f
boost::optional<std::set<std::string>> active_addons)
{
if (!lg::info().dont_log(log_config)) {
auto& out = lg::info()(log_config);
auto out = formatter();
out << "load_game_config: defines:";
for(const auto& pair : cache_.get_preproc_map()) {
out << pair.first << ",";
@ -149,7 +149,8 @@ void game_config_manager::load_game_config_with_loadscreen(FORCE_RELOAD_CONFIG f
out << "\n Everything:";
}
out << "\n";
}
FORCE_LOG_TO(lg::info(), log_config) << out.str();
}
game_config::scoped_preproc_define debug_mode("DEBUG_MODE",

View file

@ -402,7 +402,7 @@ void wml_event_pump::show_wml_messages()
void wml_event_pump::put_wml_message(
lg::logger& logger, const std::string& prefix, const std::string& message, bool in_chat)
{
logger(log_wml) << message << std::endl;
FORCE_LOG_TO(logger, log_wml) << message << std::endl;
if(in_chat) {
impl_->wml_messages_stream << prefix << message << std::endl;
}

View file

@ -357,47 +357,92 @@ void mouse_motion::stop_hover_timer()
#undef LOG_HEADER
#define LOG_HEADER \
"distributor mouse button " << name_ << " [" << owner_.id() << "]: "
"distributor mouse button " << events_.name << " [" << owner_.id() << "]: "
template<typename T>
mouse_button<T>::mouse_button(const std::string& name_, widget& owner,
mouse_button::mouse_button(const mouse_button_event_types& events, widget& owner,
const dispatcher::queue_position queue_position)
: mouse_motion(owner, queue_position)
, last_click_stamp_(0)
, last_clicked_widget_(nullptr)
, focus_(nullptr)
, name_(name_)
, events_(events)
, is_down_(false)
, signal_handler_sdl_button_down_entered_(false)
, signal_handler_sdl_button_up_entered_(false)
{
owner_.connect_signal<T::sdl_button_down_event>(
std::bind(&mouse_button<T>::signal_handler_sdl_button_down,
this,
_2,
_3,
_5),
queue_position);
owner_.connect_signal<T::sdl_button_up_event>(
std::bind(&mouse_button<T>::signal_handler_sdl_button_up,
this,
_2,
_3,
_5),
queue_position);
// The connect_signal framework is currently using SFINAE checking to ensure that we only
// register mouse button signal handlers for mouse buttons. That causes us to need this
// hardcoded (either directly or by making mouse_button a templated class), the manual handling
// of the three cases here is the current progress on refactoring.
switch(events_.sdl_button_down_event) {
case event::SDL_LEFT_BUTTON_DOWN: {
owner_.connect_signal<event::SDL_LEFT_BUTTON_DOWN>(
std::bind(&mouse_button::signal_handler_sdl_button_down,
this,
_2,
_3,
_5),
queue_position);
owner_.connect_signal<event::SDL_LEFT_BUTTON_UP>(
std::bind(&mouse_button::signal_handler_sdl_button_up,
this,
_2,
_3,
_5),
queue_position);
break;
}
case event::SDL_MIDDLE_BUTTON_DOWN: {
owner_.connect_signal<event::SDL_MIDDLE_BUTTON_DOWN>(
std::bind(&mouse_button::signal_handler_sdl_button_down,
this,
_2,
_3,
_5),
queue_position);
owner_.connect_signal<event::SDL_MIDDLE_BUTTON_UP>(
std::bind(&mouse_button::signal_handler_sdl_button_up,
this,
_2,
_3,
_5),
queue_position);
break;
}
case event::SDL_RIGHT_BUTTON_DOWN: {
owner_.connect_signal<event::SDL_RIGHT_BUTTON_DOWN>(
std::bind(&mouse_button::signal_handler_sdl_button_down,
this,
_2,
_3,
_5),
queue_position);
owner_.connect_signal<event::SDL_RIGHT_BUTTON_UP>(
std::bind(&mouse_button::signal_handler_sdl_button_up,
this,
_2,
_3,
_5),
queue_position);
break;
}
default: {
// There's exactly three instances of this class per instance of distributor, so this assert
// will be caught during the build-time-tests.
assert(!"Hardcoded assumption about button being LEFT / MIDDLE / RIGHT failed");
}
}
}
template<typename T>
void mouse_button<T>::initialize_state(const bool is_down)
void mouse_button::initialize_state(int32_t button_state)
{
last_click_stamp_ = 0;
last_clicked_widget_ = nullptr;
focus_ = 0;
is_down_ = is_down;
is_down_ = button_state & events_.mask;
}
template<typename T>
void mouse_button<T>::signal_handler_sdl_button_down(const event::ui_event event, bool& handled,
void mouse_button::signal_handler_sdl_button_down(const event::ui_event event, bool& handled,
const point& coordinate)
{
if(signal_handler_sdl_button_down_entered_) {
@ -420,10 +465,10 @@ void mouse_button<T>::signal_handler_sdl_button_down(const event::ui_event event
if(mouse_captured_) {
assert(mouse_focus_);
focus_ = mouse_focus_;
DBG_GUI_E << LOG_HEADER << "Firing: " << T::sdl_button_down_event << ".\n";
if(!owner_.fire(T::sdl_button_down_event, *focus_, coordinate)) {
DBG_GUI_E << LOG_HEADER << "Firing: " << T::button_down_event << ".\n";
owner_.fire(T::button_down_event, *mouse_focus_);
DBG_GUI_E << LOG_HEADER << "Firing: " << events_.sdl_button_down_event << ".\n";
if(!owner_.fire(events_.sdl_button_down_event, *focus_, coordinate)) {
DBG_GUI_E << LOG_HEADER << "Firing: " << events_.button_down_event << ".\n";
owner_.fire(events_.button_down_event, *mouse_focus_);
}
} else {
widget* mouse_over = owner_.find_at(coordinate, true);
@ -440,17 +485,16 @@ void mouse_button<T>::signal_handler_sdl_button_down(const event::ui_event event
}
focus_ = mouse_over;
DBG_GUI_E << LOG_HEADER << "Firing: " << T::sdl_button_down_event << ".\n";
if(!owner_.fire(T::sdl_button_down_event, *focus_, coordinate)) {
DBG_GUI_E << LOG_HEADER << "Firing: " << T::button_down_event << ".\n";
owner_.fire(T::button_down_event, *focus_);
DBG_GUI_E << LOG_HEADER << "Firing: " << events_.sdl_button_down_event << ".\n";
if(!owner_.fire(events_.sdl_button_down_event, *focus_, coordinate)) {
DBG_GUI_E << LOG_HEADER << "Firing: " << events_.button_down_event << ".\n";
owner_.fire(events_.button_down_event, *focus_);
}
}
handled = true;
}
template<typename T>
void mouse_button<T>::signal_handler_sdl_button_up(const event::ui_event event, bool& handled,
void mouse_button::signal_handler_sdl_button_up(const event::ui_event event, bool& handled,
const point& coordinate)
{
if(signal_handler_sdl_button_up_entered_) {
@ -470,13 +514,17 @@ void mouse_button<T>::signal_handler_sdl_button_up(const event::ui_event event,
is_down_ = false;
if(focus_) {
DBG_GUI_E << LOG_HEADER << "Firing: " << T::sdl_button_up_event << ".\n";
if(!owner_.fire(T::sdl_button_up_event, *focus_, coordinate)) {
DBG_GUI_E << LOG_HEADER << "Firing: " << T::button_up_event << ".\n";
owner_.fire(T::button_up_event, *focus_);
DBG_GUI_E << LOG_HEADER << "Firing: " << events_.sdl_button_up_event << ".\n";
if(!owner_.fire(events_.sdl_button_up_event, *focus_, coordinate)) {
DBG_GUI_E << LOG_HEADER << "Firing: " << events_.button_up_event << ".\n";
owner_.fire(events_.button_up_event, *focus_);
}
}
// FIXME: The block below is strange diamond inheritance - it's code that could be in
// mouse_motion which applies to all three buttons, but it will be run in one of the
// three mouse_button<T> subclasses, and then the other two mouse_button<T> subclasses
// will reach here with mouse_captured_ == false.
widget* mouse_over = owner_.find_at(coordinate, true);
if(mouse_captured_) {
const unsigned mask = SDL_BUTTON_LMASK | SDL_BUTTON_MMASK
@ -503,23 +551,22 @@ void mouse_button<T>::signal_handler_sdl_button_up(const event::ui_event event,
handled = true;
}
template<typename T>
void mouse_button<T>::mouse_button_click(widget* widget)
void mouse_button::mouse_button_click(widget* widget)
{
uint32_t stamp = SDL_GetTicks();
if(last_click_stamp_ + settings::double_click_time >= stamp
&& last_clicked_widget_ == widget) {
DBG_GUI_E << LOG_HEADER << "Firing: " << T::button_double_click_event << ".\n";
DBG_GUI_E << LOG_HEADER << "Firing: " << events_.button_double_click_event << ".\n";
owner_.fire(T::button_double_click_event, *widget);
owner_.fire(events_.button_double_click_event, *widget);
last_click_stamp_ = 0;
last_clicked_widget_ = nullptr;
} else {
DBG_GUI_E << LOG_HEADER << "Firing: " << T::button_click_event << ".\n";
owner_.fire(T::button_click_event, *widget);
DBG_GUI_E << LOG_HEADER << "Firing: " << events_.button_click_event << ".\n";
owner_.fire(events_.button_click_event, *widget);
last_click_stamp_ = stamp;
last_clicked_widget_ = widget;
}
@ -530,6 +577,40 @@ void mouse_button<T>::mouse_button_click(widget* widget)
#undef LOG_HEADER
#define LOG_HEADER "distributor mouse motion [" << owner_.id() << "]: "
namespace
{
const auto mouse_button_left_events = mouse_button_event_types {
SDL_LEFT_BUTTON_DOWN,
SDL_LEFT_BUTTON_UP,
LEFT_BUTTON_DOWN,
LEFT_BUTTON_UP,
LEFT_BUTTON_CLICK,
LEFT_BUTTON_DOUBLE_CLICK,
SDL_BUTTON_LMASK,
"left"};
const auto mouse_button_middle_events = mouse_button_event_types {
SDL_MIDDLE_BUTTON_DOWN,
SDL_MIDDLE_BUTTON_UP,
MIDDLE_BUTTON_DOWN,
MIDDLE_BUTTON_UP,
MIDDLE_BUTTON_CLICK,
MIDDLE_BUTTON_DOUBLE_CLICK,
SDL_BUTTON_MMASK,
"middle"};
const auto mouse_button_right_events = mouse_button_event_types {
SDL_RIGHT_BUTTON_DOWN,
SDL_RIGHT_BUTTON_UP,
RIGHT_BUTTON_DOWN,
RIGHT_BUTTON_UP,
RIGHT_BUTTON_CLICK,
RIGHT_BUTTON_DOUBLE_CLICK,
SDL_BUTTON_RMASK,
"right"};
} // anonymous namespace
/**
* @todo Test whether the state is properly tracked when an input blocker is
* used.
@ -537,9 +618,9 @@ void mouse_button<T>::mouse_button_click(widget* widget)
distributor::distributor(widget& owner,
const dispatcher::queue_position queue_position)
: mouse_motion(owner, queue_position)
, mouse_button_left("left", owner, queue_position)
, mouse_button_middle("middle", owner, queue_position)
, mouse_button_right("right", owner, queue_position)
, mouse_button_left(mouse_button_left_events, owner, queue_position)
, mouse_button_middle(mouse_button_middle_events, owner, queue_position)
, mouse_button_right(mouse_button_right_events, owner, queue_position)
, keyboard_focus_(nullptr)
, keyboard_focus_chain_()
{
@ -581,11 +662,11 @@ distributor::~distributor()
void distributor::initialize_state()
{
const uint8_t button_state = SDL_GetMouseState(nullptr, nullptr);
const uint32_t button_state = SDL_GetMouseState(nullptr, nullptr);
mouse_button_left::initialize_state((button_state & SDL_BUTTON(1)) != 0);
mouse_button_middle::initialize_state((button_state & SDL_BUTTON(2)) != 0);
mouse_button_right::initialize_state((button_state & SDL_BUTTON(3)) != 0);
mouse_button_left::initialize_state(button_state);
mouse_button_middle::initialize_state(button_state);
mouse_button_right::initialize_state(button_state);
init_mouse_location();
}

View file

@ -155,41 +155,36 @@ private:
/***** ***** ***** ***** mouse_button ***** ***** ***** ***** *****/
/**
* Small helper metastruct to specialize mouse_button with and provide ui_event type
* aliases without needing to make mouse_button take a million template types.
* Small helper metastruct to configure instances of mouse_button.
*/
template<
ui_event sdl_button_down,
ui_event sdl_button_up,
ui_event button_down,
ui_event button_up,
ui_event button_click,
ui_event button_double_click>
struct mouse_button_event_types_wrapper
struct mouse_button_event_types
{
static const ui_event sdl_button_down_event = sdl_button_down;
static const ui_event sdl_button_up_event = sdl_button_up;
static const ui_event button_down_event = button_down;
static const ui_event button_up_event = button_up;
static const ui_event button_click_event = button_click;
static const ui_event button_double_click_event = button_double_click;
ui_event sdl_button_down_event;
ui_event sdl_button_up_event;
ui_event button_down_event;
ui_event button_up_event;
ui_event button_click_event;
ui_event button_double_click_event;
/** Bitmask corresponding to this button's bit in SDL_GetMouseState's return value */
int32_t mask;
/** used for debug messages. */
std::string name;
};
template<typename T>
class mouse_button : public virtual mouse_motion
{
public:
mouse_button(const std::string& name_,
mouse_button(const mouse_button_event_types& events,
widget& owner,
const dispatcher::queue_position queue_position);
/**
* Initializes the state of the button.
*
* @param is_down The initial state of the button, if true down
* else initialized as up.
* @param button_state The initial state of all buttons, in which the bit corresponding to
mouse_button_event_types.mask will be set if the button is down, or unset if it is up.
*/
void initialize_state(const bool is_down);
void initialize_state(int32_t button_state);
protected:
/** The time of the last click used for double clicking. */
@ -206,8 +201,8 @@ protected:
widget* focus_;
private:
/** used for debug messages. */
const std::string name_;
/** Which set of SDL events correspond to this button. */
const mouse_button_event_types events_;
/** Is the button down? */
bool is_down_;
@ -226,38 +221,48 @@ private:
void mouse_button_click(widget* widget);
};
/**
* Three subclasses of mouse_button, so that the distributor class can inherit from them;
* C++ doesn't allow multiple inheritance to directly use more than one instance of a
* superclass.
*
* It's a diamond inheritance, as all of these have virtual base class mouse_motion;
* refactoring that would allow these multiple classes to be replaced with a simple
* (distributor has-a std::array<mouse_button, 3>) relationship.
*/
struct mouse_button_left : public mouse_button
{
mouse_button_left(const mouse_button_event_types& events,
widget& owner,
const dispatcher::queue_position queue_position)
: mouse_motion(owner, queue_position)
, mouse_button(events, owner, queue_position)
{
}
};
struct mouse_button_middle : public mouse_button
{
mouse_button_middle(const mouse_button_event_types& events,
widget& owner,
const dispatcher::queue_position queue_position)
: mouse_motion(owner, queue_position)
, mouse_button(events, owner, queue_position)
{
}
};
struct mouse_button_right : public mouse_button
{
mouse_button_right(const mouse_button_event_types& events,
widget& owner,
const dispatcher::queue_position queue_position)
: mouse_motion(owner, queue_position)
, mouse_button(events, owner, queue_position)
{
}
};
/***** ***** ***** ***** distributor ***** ***** ***** ***** *****/
using mouse_button_left = mouse_button<
mouse_button_event_types_wrapper<
SDL_LEFT_BUTTON_DOWN,
SDL_LEFT_BUTTON_UP,
LEFT_BUTTON_DOWN,
LEFT_BUTTON_UP,
LEFT_BUTTON_CLICK,
LEFT_BUTTON_DOUBLE_CLICK>
>;
using mouse_button_middle = mouse_button<
mouse_button_event_types_wrapper<
SDL_MIDDLE_BUTTON_DOWN,
SDL_MIDDLE_BUTTON_UP,
MIDDLE_BUTTON_DOWN,
MIDDLE_BUTTON_UP,
MIDDLE_BUTTON_CLICK,
MIDDLE_BUTTON_DOUBLE_CLICK>
>;
using mouse_button_right = mouse_button<
mouse_button_event_types_wrapper<
SDL_RIGHT_BUTTON_DOWN,
SDL_RIGHT_BUTTON_UP,
RIGHT_BUTTON_DOWN,
RIGHT_BUTTON_UP,
RIGHT_BUTTON_CLICK,
RIGHT_BUTTON_DOUBLE_CLICK>
>;
/** The event handler class for the widget library. */
class distributor :

View file

@ -43,11 +43,8 @@ extern lg::log_domain log_gui_general;
#define ERR_GUI_G LOG_STREAM_INDENT(err, gui2::log_gui_general)
extern lg::log_domain log_gui_iterator;
#define TST_GUI_I \
if(lg::debug().dont_log(gui2::log_gui_iterator)) \
; \
else \
lg::debug()(gui2::log_gui_iterator, false, false)
#define TST_GUI_I LOG_STREAM_NAMELESS(debug, gui2::log_gui_iterator)
#define DBG_GUI_I LOG_STREAM_INDENT(debug, gui2::log_gui_iterator)
#define LOG_GUI_I LOG_STREAM_INDENT(info, gui2::log_gui_iterator)
#define WRN_GUI_I LOG_STREAM_INDENT(warn, gui2::log_gui_iterator)

View file

@ -26,6 +26,7 @@
#include <map>
#include <sstream>
#include <ctime>
#include <mutex>
#include "global.hpp"
@ -44,6 +45,7 @@ static std::ostream null_ostream(new null_streambuf);
static int indent = 0;
static bool timestamp = true;
static bool precise_timestamp = false;
static std::mutex log_mutex;
static boost::posix_time::time_facet facet("%Y%m%d %H:%M:%S%F ");
static std::ostream *output_stream = nullptr;
@ -207,7 +209,7 @@ static void print_precise_timestamp(std::ostream & out) noexcept
} catch(...) {}
}
std::ostream &logger::operator()(const log_domain& domain, bool show_names, bool do_indent) const
log_in_progress logger::operator()(const log_domain& domain, bool show_names, bool do_indent) const
{
if (severity_ > domain.domain_->second) {
return null_ostream;
@ -218,33 +220,58 @@ std::ostream &logger::operator()(const log_domain& domain, bool show_names, bool
std::cerr << ss.str() << std::endl;
strict_threw_ = true;
}
std::ostream& stream = output();
log_in_progress stream = output();
if(do_indent) {
for(int i = 0; i != indent; ++i)
stream << " ";
}
stream.set_indent(indent);
}
if (timestamp) {
if(precise_timestamp) {
print_precise_timestamp(stream);
} else {
stream << get_timestamp(std::time(nullptr));
}
stream.enable_timestamp();
}
if (show_names) {
stream << name_ << ' ' << domain.domain_->first << ": ";
stream.set_prefix(formatter() << name_ << ' ' << domain.domain_->first << ": ");
}
return stream;
}
}
void scope_logger::do_log_entry(const log_domain& domain, const std::string& str) noexcept
log_in_progress::log_in_progress(std::ostream& stream)
: stream_(stream)
{}
void log_in_progress::operator|(formatter&& message)
{
std::lock_guard<std::mutex> lock(log_mutex);
for(int i = 0; i < indent; ++i)
stream_ << " ";
if(timestamp_) {
if(precise_timestamp) {
print_precise_timestamp(stream_);
} else {
stream_ << get_timestamp(std::time(nullptr));
}
}
stream_ << prefix_ << message.str();
}
void log_in_progress::set_indent(int level) {
indent_ = level;
}
void log_in_progress::enable_timestamp() {
timestamp_ = true;
}
void log_in_progress::set_prefix(const std::string& prefix) {
prefix_ = prefix;
}
void scope_logger::do_log_entry(const std::string& str) noexcept
{
output_ = &debug()(domain, false, true);
str_ = str;
try {
ticks_ = boost::posix_time::microsec_clock::local_time();
} catch(...) {}
(*output_) << "{ BEGIN: " << str_ << "\n";
debug()(domain_, false, true) | formatter() << "{ BEGIN: " << str_ << "\n";
++indent;
}
@ -255,15 +282,10 @@ void scope_logger::do_log_exit() noexcept
ticks = (boost::posix_time::microsec_clock::local_time() - ticks_).total_milliseconds();
} catch(...) {}
--indent;
do_indent();
if (timestamp) (*output_) << get_timestamp(std::time(nullptr));
(*output_) << "} END: " << str_ << " (took " << ticks << "ms)\n";
}
void scope_logger::do_indent() const
{
for(int i = 0; i != indent; ++i)
(*output_) << " ";
auto output = debug()(domain_, false, true);
output.set_indent(indent);
if(timestamp) output.enable_timestamp();
output | formatter() << "} END: " << str_ << " (took " << ticks << "ms)\n";
}
std::stringstream& wml_error()

View file

@ -58,6 +58,7 @@
#include <utility>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include "formatter.hpp"
using boost::posix_time::ptime;
@ -112,12 +113,32 @@ void set_strict_severity(int severity);
void set_strict_severity(const logger &lg);
bool broke_strict();
// A little "magic" to surround the logging operation in a mutex.
// This works by capturing the output first to a stringstream formatter, then
// locking a mutex and dumping it to the stream all in one go.
// By doing this we can avoid rare deadlocks if a function whose output is streamed
// calls logging of its own.
// We overload operator| only because it has lower precedence than operator<<
// Any other lower-precedence operator would have worked just as well.
class log_in_progress {
std::ostream& stream_;
int indent_ = 0;
bool timestamp_ = false;
std::string prefix_;
public:
log_in_progress(std::ostream& stream);
void operator|(formatter&& message);
void set_indent(int level);
void enable_timestamp();
void set_prefix(const std::string& prefix);
};
class logger {
char const *name_;
int severity_;
public:
logger(char const *name, int severity): name_(name), severity_(severity) {}
std::ostream &operator()(const log_domain& domain,
log_in_progress operator()(const log_domain& domain,
bool show_names = true, bool do_indent = false) const;
bool dont_log(const log_domain& domain) const
@ -147,26 +168,25 @@ log_domain& general();
class scope_logger
{
ptime ticks_;
std::ostream *output_;
const log_domain& domain_;
std::string str_;
public:
scope_logger(const log_domain& domain, const char* str) :
output_(nullptr)
domain_(domain)
{
if (!debug().dont_log(domain)) do_log_entry(domain, str);
if (!debug().dont_log(domain)) do_log_entry(str);
}
scope_logger(const log_domain& domain, const std::string& str) :
output_(nullptr)
domain_(domain)
{
if (!debug().dont_log(domain)) do_log_entry(domain, str);
if (!debug().dont_log(domain)) do_log_entry(str);
}
~scope_logger()
{
if (output_) do_log_exit();
if (!str_.empty()) do_log_exit();
}
void do_indent() const;
private:
void do_log_entry(const log_domain& domain, const std::string& str) noexcept;
void do_log_entry(const std::string& str) noexcept;
void do_log_exit() noexcept;
};
@ -186,7 +206,14 @@ std::stringstream& wml_error();
#define log_scope(description) lg::scope_logger scope_logging_object__(lg::general(), description);
#define log_scope2(domain,description) lg::scope_logger scope_logging_object__(domain, description);
#define LOG_STREAM(level, domain) if (lg::level().dont_log(domain)) ; else lg::level()(domain)
#define LOG_STREAM(level, domain) if (lg::level().dont_log(domain)) ; else lg::level()(domain) | formatter()
// Don't prefix the logdomain to messages on this stream
#define LOG_STREAM_NAMELESS(level, domain) if (lg::level().dont_log(domain)) ; else lg::level()(domain, false) | formatter()
// When using log_scope/log_scope2 it is nice to have all output indented.
#define LOG_STREAM_INDENT(level,domain) if (lg::level().dont_log(domain)) ; else lg::level()(domain, true, true)
#define LOG_STREAM_INDENT(level,domain) if (lg::level().dont_log(domain)) ; else lg::level()(domain, true, true) | formatter()
// If you have an explicit logger object and want to ignore the logging level, use this.
// Meant for cases where you explicitly call dont_log to avoid an expensive operation if the logging is disabled.
#define FORCE_LOG_TO(logger, domain) logger(domain) | formatter()

View file

@ -24,7 +24,7 @@
#include <boost/algorithm/string.hpp>
static lg::log_domain log_network("network");
#define LOG_CS if (lg::err().dont_log(log_network)) ; else lg::err()(log_network, false)
#define LOG_CS LOG_STREAM_NAMELESS(err, log_network)
namespace {