The Battle for Wesnoth  1.17.0-dev
log.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2004 - 2021
3  by Guillaume Melquiond <guillaume.melquiond@gmail.com>
4  Copyright (C) 2003 by David White <dave@whitevine.net>
5  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY.
13 
14  See the COPYING file for more details.
15 */
16 
17 /**
18  * @file
19  * Standard logging facilities (interface).
20  *
21  * To use one of the standard log channels, put something like the following at the start
22  * of your .cpp file:
23  *
24  * static lg::log_domain log_display("display");
25  * \#define ERR_DP LOG_STREAM(err, log_display)
26  * \#define LOG_DP LOG_STREAM(info, log_display)
27  *
28  * Then stream logging info to ERR_DP, or LOG_DP, as if it were an ostream like std::cerr.
29  * (In general it will actually be std::cerr at runtime when logging is enabled.)
30  *
31  * LOG_DP << "Found a window resize event: ...\n";
32  *
33  * Please do not use iomanip features like std::hex directly on the logger. Because of the
34  * design of the logger, this will result in all of the loggers (in fact std::cerr) being
35  * imbued with std::hex. Please use a formatter instead.
36  *
37  * \#include "formatter.hpp"
38  *
39  * LOG_DP << (formatter() << "The random seed is: '" << std::hex << seed << "'\n").str();
40  *
41  * It might be nice if somehow the logger class / macros could support using iomanip
42  * things directly, but right now it doesn't, and it seems that it would complicate the
43  * design greatly enough that it doesn't seem worth it.
44  */
45 
46 #pragma once
47 
48 #ifndef __func__
49  #ifdef __FUNCTION__
50  #define __func__ __FUNCTION__
51  #endif
52 #endif
53 
54 #include <iostream> // needed else all files including log.hpp need to do it.
55 #include <sstream> // as above. iostream (actually, iosfwd) declares stringstream as an incomplete type, but does not define it
56 #include <string>
57 #include <utility>
58 #include <ctime>
59 
60 #include "formatter.hpp"
61 
62 namespace lg {
63 
65 {
70 };
71 
72 /**
73  * Helper class to redirect the output of the logger in a certain scope.
74  *
75  * The main usage of the redirection is for the unit tests to validate the
76  * output on the logger with the expected output.
77  */
79 {
80 public:
81 
82  /**
83  * Constructor.
84  *
85  * @param stream The stream to direct the output to.
86  */
87  explicit redirect_output_setter(std::ostream& stream);
88 
90 
91 private:
92 
93  /**
94  * The previously set redirection.
95  *
96  * This value is stored here to be restored in this destructor.
97  */
98  std::ostream* old_stream_;
99 };
100 
101 class logger;
102 
103 typedef std::pair<const std::string, int> logd;
104 
105 class log_domain {
106  logd *domain_;
107 public:
108  explicit log_domain(char const *name, int severity = 1);
109  friend class logger;
110 };
111 
112 bool set_log_domain_severity(const std::string& name, int severity);
113 bool set_log_domain_severity(const std::string& name, const logger &lg);
114 bool get_log_domain_severity(const std::string& name, int &severity);
115 std::string list_logdomains(const std::string& filter);
116 
117 void set_strict_severity(int severity);
118 void set_strict_severity(const logger &lg);
119 bool broke_strict();
120 
121 // A little "magic" to surround the logging operation in a mutex.
122 // This works by capturing the output first to a stringstream formatter, then
123 // locking a mutex and dumping it to the stream all in one go.
124 // By doing this we can avoid rare deadlocks if a function whose output is streamed
125 // calls logging of its own.
126 // We overload operator| only because it has lower precedence than operator<<
127 // Any other lower-precedence operator would have worked just as well.
129  std::ostream& stream_;
130  int indent_ = 0;
131  bool timestamp_ = false;
132  std::string prefix_;
133 public:
134  log_in_progress(std::ostream& stream);
135  void operator|(formatter&& message);
136  void set_indent(int level);
137  void enable_timestamp();
138  void set_prefix(const std::string& prefix);
139 };
140 
141 class logger {
142  char const *name_;
144 public:
145  logger(char const *name, int severity): name_(name), severity_(severity) {}
146  log_in_progress operator()(const log_domain& domain,
147  bool show_names = true, bool do_indent = false) const;
148 
149  bool dont_log(const log_domain& domain) const
150  {
151  return severity_ > domain.domain_->second;
152  }
153 
154  /**
155  * Returns following values depending on the logger:
156  * error: 0
157  * warn: 1
158  * info: 2
159  * debug: 3
160  * See also the lg::severity enum.
161  */
162  int get_severity() const
163  {
164  return severity_;
165  }
166 
167  std::string get_name() const
168  {
169  return name_;
170  }
171 };
172 
173 void timestamps(bool);
174 void precise_timestamps(bool);
175 std::string get_timestamp(const std::time_t& t, const std::string& format="%Y%m%d %H:%M:%S ");
176 std::string get_timespan(const std::time_t& t);
177 
178 logger &err(), &warn(), &info(), &debug();
180 
182 {
183  int64_t ticks_;
185  std::string str_;
186 public:
187  scope_logger(const log_domain& domain, const char* str)
188  : ticks_(0)
189  , domain_(domain)
190  , str_()
191  {
192  if (!debug().dont_log(domain)) do_log_entry(str);
193  }
194  scope_logger(const log_domain& domain, const std::string& str)
195  : ticks_(0)
196  , domain_(domain)
197  , str_()
198  {
199  if (!debug().dont_log(domain)) do_log_entry(str);
200  }
202  {
203  if (!str_.empty()) do_log_exit();
204  }
205 private:
206  void do_log_entry(const std::string& str) noexcept;
207  void do_log_exit() noexcept;
208 };
209 
210 /**
211  * Use this to show WML errors in the ingame chat.
212  * After every WML event the errors are shown to the user so they can inform the campaign maintainer.
213  */
214 std::stringstream& log_to_chat();
215 
216 } // namespace lg
217 
218 #define log_scope(description) lg::scope_logger scope_logging_object__(lg::general(), description);
219 #define log_scope2(domain,description) lg::scope_logger scope_logging_object__(domain, description);
220 
221 #define LOG_STREAM(level, domain) if (lg::level().dont_log(domain)) ; else lg::level()(domain) | formatter()
222 
223 // Don't prefix the logdomain to messages on this stream
224 #define LOG_STREAM_NAMELESS(level, domain) if (lg::level().dont_log(domain)) ; else lg::level()(domain, false) | formatter()
225 
226 // When using log_scope/log_scope2 it is nice to have all output indented.
227 #define LOG_STREAM_INDENT(level,domain) if (lg::level().dont_log(domain)) ; else lg::level()(domain, true, true) | formatter()
228 
229 // If you have an explicit logger object and want to ignore the logging level, use this.
230 // Meant for cases where you explicitly call dont_log to avoid an expensive operation if the logging is disabled.
231 #define FORCE_LOG_TO(logger, domain) logger(domain) | formatter()
std::string get_timestamp(const std::time_t &t, const std::string &format)
Definition: log.cpp:174
char const * name_
Definition: log.hpp:142
int64_t ticks_
Definition: log.hpp:183
std::string get_timespan(const std::time_t &t)
Definition: log.cpp:181
logger(char const *name, int severity)
Definition: log.hpp:145
logger & info()
Definition: log.cpp:89
void timestamps(bool t)
Definition: log.cpp:74
std::string str_
Definition: log.hpp:185
redirect_output_setter(std::ostream &stream)
Constructor.
Definition: log.cpp:60
Helper class to redirect the output of the logger in a certain scope.
Definition: log.hpp:78
std::pair< const std::string, int > logd
Definition: log.hpp:101
bool broke_strict()
Definition: log.cpp:170
scope_logger(const log_domain &domain, const char *str)
Definition: log.hpp:187
bool get_log_domain_severity(const std::string &name, int &severity)
Definition: log.cpp:141
std::ostringstream wrapper.
Definition: formatter.hpp:39
int severity_
Definition: log.hpp:143
logger & debug()
Definition: log.cpp:95
bool dont_log(const log_domain &domain) const
Definition: log.hpp:149
severity
Definition: log.hpp:64
log_domain & general()
Definition: log.cpp:103
Definition: pump.hpp:40
const log_domain & domain_
Definition: log.hpp:184
std::ostream * old_stream_
The previously set redirection.
Definition: log.hpp:98
logger & err()
Definition: log.cpp:77
void set_strict_severity(int severity)
Definition: log.cpp:160
terrain_code operator|(const terrain_code &a, const terrain_code &b)
Definition: translation.hpp:73
std::string get_name() const
Definition: log.hpp:167
bool set_log_domain_severity(const std::string &name, int severity)
Definition: log.cpp:117
logger & warn()
Definition: log.cpp:83
double t
Definition: astarsearch.cpp:65
std::string list_logdomains(const std::string &filter)
Definition: log.cpp:150
std::string prefix_
Definition: log.hpp:132
int get_severity() const
Returns following values depending on the logger: error: 0 warn: 1 info: 2 debug: 3 See also the lg::...
Definition: log.hpp:162
std::ostream & stream_
Definition: log.hpp:129
scope_logger(const log_domain &domain, const std::string &str)
Definition: log.hpp:194
void precise_timestamps(bool pt)
Definition: log.cpp:75
logd * domain_
Definition: log.hpp:106
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp:289