Merge pull request #5198 from wesnoth/thread_safe_logging
Thread-safe logging Closes #5209
This commit is contained in:
commit
988bf973e9
11 changed files with 305 additions and 151 deletions
|
@ -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';
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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 :
|
||||
|
|
|
@ -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)
|
||||
|
|
68
src/log.cpp
68
src/log.cpp
|
@ -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()
|
||||
|
|
49
src/log.hpp
49
src/log.hpp
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue