The Battle for Wesnoth  1.17.21+dev
log.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2004 - 2023
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: ...";
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 <iosfwd> // needed else all files including log.hpp need to do it.
55 #include <optional>
56 #include <sstream> // as above. iostream (actually, iosfwd) declares stringstream as an incomplete type, but does not define it
57 #include <string>
58 #include <utility>
59 #include <ctime>
60 #include <cstdint>
61 
62 #include "formatter.hpp"
63 
64 namespace lg {
65 
66 // Prefix and extension for log files. This is used both to generate the unique
67 // log file name during startup and to find old files to delete.
68 const std::string log_file_prefix = "wesnoth-";
69 const std::string log_file_suffix = ".log";
70 
71 // Maximum number of older log files to keep intact. Other files are deleted.
72 // Note that this count does not include the current log file!
73 const unsigned max_logs = 8;
74 
76 {
80  LG_DEBUG=3
81 };
82 
83 /**
84  * Helper class to redirect the output of the logger in a certain scope.
85  *
86  * The main usage of the redirection is for the unit tests to validate the
87  * output on the logger with the expected output.
88  */
90 {
91 public:
92 
93  /**
94  * Constructor.
95  *
96  * @param stream The stream to direct the output to.
97  */
98  explicit redirect_output_setter(std::ostream& stream);
99 
101 
102 private:
103 
104  /**
105  * The previously set redirection.
106  *
107  * This value is stored here to be restored in this destructor.
108  */
109  std::ostream* old_stream_;
110 };
111 
112 class logger;
113 
114 typedef std::pair<const std::string, int> logd;
115 
116 class log_domain {
118 public:
119  explicit log_domain(char const *name, int severity = 1);
120  friend class logger;
121 };
122 
123 bool set_log_domain_severity(const std::string& name, int severity);
124 bool set_log_domain_severity(const std::string& name, const logger &lg);
125 bool get_log_domain_severity(const std::string& name, int &severity);
126 std::string list_logdomains(const std::string& filter);
127 
128 void set_strict_severity(int severity);
129 void set_strict_severity(const logger &lg);
130 bool broke_strict();
131 void set_log_to_file();
133 std::optional<bool> log_dir_writable();
134 
135 bool is_not_log_file(const std::string& filename);
136 void rotate_logs(const std::string& log_dir);
137 std::string unique_log_filename();
138 
139 // A little "magic" to surround the logging operation in a mutex.
140 // This works by capturing the output first to a stringstream formatter, then
141 // locking a mutex and dumping it to the stream all in one go.
142 // By doing this we can avoid rare deadlocks if a function whose output is streamed
143 // calls logging of its own.
144 // We overload operator| only because it has lower precedence than operator<<
145 // Any other lower-precedence operator would have worked just as well.
147  std::ostream& stream_;
148  int indent_ = 0;
149  bool timestamp_ = false;
150  std::string prefix_;
151  bool auto_newline_ = true;
152 public:
153  log_in_progress(std::ostream& stream);
154  void operator|(formatter&& message);
155  void set_indent(int level);
156  void enable_timestamp();
157  void set_prefix(const std::string& prefix);
158  void set_auto_newline(bool enabled);
159 };
160 
161 class logger {
162  char const *name_;
164 public:
165  logger(char const *name, int severity): name_(name), severity_(severity) {}
166  log_in_progress operator()(const log_domain& domain,
167  bool show_names = true, bool do_indent = false, bool show_timestamps = true, bool break_strict = true, bool auto_newline = true) const;
168 
169  bool dont_log(const log_domain& domain) const
170  {
171  return severity_ > domain.domain_->second;
172  }
173 
174  /**
175  * Returns following values depending on the logger:
176  * error: 0
177  * warn: 1
178  * info: 2
179  * debug: 3
180  * See also the lg::severity enum.
181  */
182  int get_severity() const
183  {
184  return severity_;
185  }
186 
187  std::string get_name() const
188  {
189  return name_;
190  }
191 };
192 
193 void timestamps(bool);
194 void precise_timestamps(bool);
195 std::string get_timestamp(const std::time_t& t, const std::string& format="%Y%m%d %H:%M:%S ");
196 std::string get_timespan(const std::time_t& t);
197 std::string sanitize_log(const std::string& logstr);
198 std::string& get_log_file_path();
199 void set_log_file_path(const std::string& path);
200 
201 logger &err(), &warn(), &info(), &debug();
202 log_domain& general();
203 
205 {
206  int64_t ticks_;
208  std::string str_;
209 public:
210  scope_logger(const log_domain& domain, const char* str)
211  : ticks_(0)
212  , domain_(domain)
213  , str_()
214  {
215  if (!debug().dont_log(domain)) do_log_entry(str);
216  }
217  scope_logger(const log_domain& domain, const std::string& str)
218  : ticks_(0)
219  , domain_(domain)
220  , str_()
221  {
222  if (!debug().dont_log(domain)) do_log_entry(str);
223  }
225  {
226  if (!str_.empty()) do_log_exit();
227  }
228 private:
229  void do_log_entry(const std::string& str) noexcept;
230  void do_log_exit() noexcept;
231 };
232 
233 /**
234  * Use this to show WML errors in the ingame chat.
235  * After every WML event the errors are shown to the user so they can inform the campaign maintainer.
236  */
237 std::stringstream& log_to_chat();
238 
239 } // namespace lg
240 
241 #define log_scope(description) lg::scope_logger scope_logging_object__(lg::general(), description);
242 #define log_scope2(domain,description) lg::scope_logger scope_logging_object__(domain, description);
243 
244 #define LOG_STREAM(level, domain) if (lg::level().dont_log(domain)) ; else lg::level()(domain) | formatter()
245 
246 // Don't prefix the logdomain to messages on this stream
247 #define LOG_STREAM_NAMELESS(level, domain) if (lg::level().dont_log(domain)) ; else lg::level()(domain, false) | formatter()
248 
249 // Like LOG_STREAM_NAMELESS except doesn't add newlines automatically
250 #define LOG_STREAM_NAMELESS_STREAMING(level, domain) if (lg::level().dont_log(domain)) ; else lg::level()(domain, false, false, true, true, false) | formatter()
251 
252 // When using log_scope/log_scope2 it is nice to have all output indented.
253 #define LOG_STREAM_INDENT(level,domain) if (lg::level().dont_log(domain)) ; else lg::level()(domain, true, true) | formatter()
254 
255 // If you have an explicit logger object and want to ignore the logging level, use this.
256 // Meant for cases where you explicitly call dont_log to avoid an expensive operation if the logging is disabled.
257 #define FORCE_LOG_TO(logger, domain) logger(domain) | formatter()
258 
259 // always log (since it's at the error level) to the general log stream
260 // outputting the log domain and timestamp is disabled
261 // meant as a replacement to using cerr/cout, but that goes through the same logging infrastructure as everything else
262 #define PLAIN_LOG lg::err()(lg::general(), false, false, false, false, true) | formatter()
263 #define STREAMING_LOG lg::err()(lg::general(), false, false, false, false, false) | formatter()
double t
Definition: astarsearch.cpp:65
std::ostringstream wrapper.
Definition: formatter.hpp:40
logd * domain_
Definition: log.hpp:117
log_domain(char const *name, int severity=1)
Definition: log.cpp:257
void operator|(formatter &&message)
Definition: log.cpp:411
std::string prefix_
Definition: log.hpp:150
std::ostream & stream_
Definition: log.hpp:147
void set_auto_newline(bool enabled)
Definition: log.cpp:441
void set_prefix(const std::string &prefix)
Definition: log.cpp:437
void enable_timestamp()
Definition: log.cpp:433
bool auto_newline_
Definition: log.hpp:151
void set_indent(int level)
Definition: log.cpp:429
log_in_progress(std::ostream &stream)
Definition: log.cpp:407
char const * name_
Definition: log.hpp:162
std::string get_name() const
Definition: log.hpp:187
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:182
log_in_progress operator()(const log_domain &domain, bool show_names=true, bool do_indent=false, bool show_timestamps=true, bool break_strict=true, bool auto_newline=true) const
Definition: log.cpp:377
int severity_
Definition: log.hpp:163
logger(char const *name, int severity)
Definition: log.hpp:165
bool dont_log(const log_domain &domain) const
Definition: log.hpp:169
Helper class to redirect the output of the logger in a certain scope.
Definition: log.hpp:90
std::ostream * old_stream_
The previously set redirection.
Definition: log.hpp:109
redirect_output_setter(std::ostream &stream)
Constructor.
Definition: log.cpp:209
const log_domain & domain_
Definition: log.hpp:207
void do_log_entry(const std::string &str) noexcept
Definition: log.cpp:445
std::string str_
Definition: log.hpp:208
scope_logger(const log_domain &domain, const char *str)
Definition: log.hpp:210
void do_log_exit() noexcept
Definition: log.cpp:455
scope_logger(const log_domain &domain, const std::string &str)
Definition: log.hpp:217
int64_t ticks_
Definition: log.hpp:206
std::string path
Definition: filesystem.cpp:86
Definition: pump.hpp:41
logger & err()
Definition: log.cpp:226
severity
Definition: log.hpp:76
@ LG_INFO
Definition: log.hpp:79
@ LG_DEBUG
Definition: log.hpp:80
@ LG_WARN
Definition: log.hpp:78
@ LG_ERROR
Definition: log.hpp:77
log_domain & general()
Definition: log.cpp:252
bool broke_strict()
Definition: log.cpp:319
std::string get_timespan(const std::time_t &t)
Definition: log.cpp:330
logger & debug()
Definition: log.cpp:244
void set_strict_severity(int severity)
Definition: log.cpp:309
void rotate_logs(const std::string &log_dir)
Deletes old log files from the log directory.
Definition: log.cpp:95
std::string unique_log_filename()
Generates a unique log file name.
Definition: log.cpp:129
void set_log_to_file()
Definition: log.cpp:175
void set_log_file_path(const std::string &path)
Definition: log.cpp:204
logger & warn()
Definition: log.cpp:232
std::string list_logdomains(const std::string &filter)
Definition: log.cpp:299
bool is_not_log_file(const std::string &fn)
Helper function for rotate_logs.
Definition: log.cpp:86
void timestamps(bool t)
Definition: log.cpp:223
bool set_log_domain_severity(const std::string &name, int severity)
Definition: log.cpp:266
std::string sanitize_log(const std::string &logstr)
Definition: log.cpp:359
const unsigned max_logs
Definition: log.hpp:73
void check_log_dir_writable()
Definition: log.cpp:143
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp:468
const std::string log_file_prefix
Definition: log.hpp:68
std::string & get_log_file_path()
Definition: log.cpp:200
std::pair< const std::string, int > logd
Definition: log.hpp:112
std::string get_timestamp(const std::time_t &t, const std::string &format)
Definition: log.cpp:323
void precise_timestamps(bool pt)
Definition: log.cpp:224
std::optional< bool > log_dir_writable()
Definition: log.cpp:195
bool get_log_domain_severity(const std::string &name, int &severity)
Definition: log.cpp:290
logger & info()
Definition: log.cpp:238
const std::string log_file_suffix
Definition: log.hpp:69