The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
log.hpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2004 - 2025
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.
56#include <string>
57#include <utility>
58#include <chrono>
59#include <cstdint>
60
61#include "formatter.hpp"
62
63namespace lg {
64
65// Prefix and extension for log files.
66// This is used to find old files to delete.
67const std::string log_file_prefix = "wesnoth-";
68const std::string log_file_suffix = ".log";
69// stdout file for Windows; needs to end in log_file_suffix so log rotation works for both
70const std::string out_log_file_suffix = ".out" + log_file_suffix;
71
72// Maximum number of older log files to keep intact. Other files are deleted.
73// Note that this count does not include the current log file!
74// double for Windows due to the separate .log and .out.log files
75const unsigned max_logs = 8
76#ifdef _WIN32
77*2
78#endif
79;
80
81enum class severity
82{
83 LG_NONE=-1,
84 LG_ERROR=0,
85 LG_WARN=1,
86 LG_INFO=2,
87 LG_DEBUG=3
88};
89std::ostringstream& operator<<(std::ostringstream& oss, lg::severity severity);
90
91/**
92 * Helper class to redirect the output of the logger in a certain scope.
93 *
94 * The main usage of the redirection is for the unit tests to validate the
95 * output on the logger with the expected output.
96 */
98{
99public:
100
101 /**
102 * Constructor.
103 *
104 * @param stream The stream to direct the output to.
105 */
106 explicit redirect_output_setter(std::ostream& stream);
107
109
110private:
111
112 /**
113 * The previously set redirection.
114 *
115 * This value is stored here to be restored in this destructor.
116 */
117 std::ostream* old_stream_;
118};
119
120class logger;
121
122typedef std::pair<const std::string, severity> logd;
123
126public:
127 explicit log_domain(char const *name, severity severity = severity::LG_WARN);
128 friend class logger;
129};
130
131bool set_log_domain_severity(const std::string& name, severity severity);
132bool set_log_domain_severity(const std::string& name, const logger &lg);
133bool get_log_domain_severity(const std::string& name, severity &severity);
134std::string list_log_domains(const std::string& filter);
135
137void set_strict_severity(const logger &lg);
138bool broke_strict();
139
140/** toggle log sanitization */
141void set_log_sanitize(bool sanitize);
142
143/**
144 * Do the initial redirection to a log file if the logs directory is writable.
145 * Also performs log rotation to delete old logs.
146 * NOTE: This runs before command line arguments are processed.
147 * Therefore the log file is initially written under the default userdata directory
148 */
149void set_log_to_file();
150/**
151 * Move the log file to another directory.
152 * Used if a custom userdata directory is given as a command line option to move it to the new location.
153 */
154void move_log_file();
155/**
156 * Checks that a dummy file can be written to and deleted from the logs directory.
157 */
159/**
160 * Returns the result set by check_log_dir_writable().
161 * Will not be set if called before log redirection is done.
162 *
163 * @return true if the log directory is writable, false otherwise.
164 */
165utils::optional<bool> log_dir_writable();
166
167/**
168 * Use the defined prefix and suffix to determine if a filename is a log file.
169 *
170 * @return true if it's a log file, false otherwise
171 */
172bool is_not_log_file(const std::string& filename);
173/**
174 * Check how many log files exist and delete the oldest when there's too many.
175 */
176void rotate_logs(const std::string& log_dir);
177/**
178 * Generate a unique file name using the current timestamp and a randomly generated number.
179 *
180 * @return A unique file name to use for the current log file.
181 */
182std::string unique_log_filename();
183
184// A little "magic" to surround the logging operation in a mutex.
185// This works by capturing the output first to a stringstream formatter, then
186// locking a mutex and dumping it to the stream all in one go.
187// By doing this we can avoid rare deadlocks if a function whose output is streamed
188// calls logging of its own.
189// We overload operator| only because it has lower precedence than operator<<
190// Any other lower-precedence operator would have worked just as well.
192 std::ostream& stream_;
193 int indent_ = 0;
194 bool timestamp_ = false;
195 std::string prefix_;
196 bool auto_newline_ = true;
197public:
198 log_in_progress(std::ostream& stream);
199 void operator|(const formatter& message);
200 void set_indent(int level);
201 void enable_timestamp();
202 void set_prefix(const std::string& prefix);
203 void set_auto_newline(bool enabled);
204};
205
206class logger {
207 char const *name_;
209public:
210 logger(char const *name, severity severity): name_(name), severity_(severity) {}
212 bool show_names = true, bool do_indent = false, bool show_timestamps = true, bool break_strict = true, bool auto_newline = true) const;
213
214 bool dont_log(const log_domain& domain) const
215 {
216 return severity_ > domain.domain_->second;
217 }
218
220 {
221 return severity_;
222 }
223
224 std::string get_name() const
225 {
226 return name_;
227 }
228};
229
230void timestamps(bool);
231void precise_timestamps(bool);
232std::string sanitize_log(const std::string& logstr);
233std::string get_log_file_path();
234
235logger &err(), &warn(), &info(), &debug();
236log_domain& general();
237
239{
240 std::chrono::steady_clock::time_point start_;
242 std::string str_;
243public:
244 scope_logger(const log_domain& domain, const char* str)
245 : start_()
246 , domain_(domain)
247 , str_()
248 {
249 if (!debug().dont_log(domain)) do_log_entry(str);
250 }
251 scope_logger(const log_domain& domain, const std::string& str)
252 : start_()
253 , domain_(domain)
254 , str_()
255 {
256 if (!debug().dont_log(domain)) do_log_entry(str);
257 }
259 {
260 if (!str_.empty()) do_log_exit();
261 }
262private:
263 void do_log_entry(const std::string& str) noexcept;
264 void do_log_exit() noexcept;
265};
266
267/**
268 * Use this to show WML errors in the ingame chat.
269 * After every WML event the errors are shown to the user so they can inform the campaign maintainer.
270 */
271std::stringstream& log_to_chat();
272
273} // namespace lg
274
275#define log_scope(description) lg::scope_logger scope_logging_object__(lg::general(), description);
276#define log_scope2(domain,description) lg::scope_logger scope_logging_object__(domain, description);
277
278#define LOG_STREAM(level, domain) if (lg::level().dont_log(domain)) ; else lg::level()(domain) | formatter()
279
280// Don't prefix the logdomain to messages on this stream
281#define LOG_STREAM_NAMELESS(level, domain) if (lg::level().dont_log(domain)) ; else lg::level()(domain, false) | formatter()
282
283// Like LOG_STREAM_NAMELESS except doesn't add newlines automatically
284#define LOG_STREAM_NAMELESS_STREAMING(level, domain) if (lg::level().dont_log(domain)) ; else lg::level()(domain, false, false, true, true, false) | formatter()
285
286// When using log_scope/log_scope2 it is nice to have all output indented.
287#define LOG_STREAM_INDENT(level,domain) if (lg::level().dont_log(domain)) ; else lg::level()(domain, true, true) | formatter()
288
289// If you have an explicit logger object and want to ignore the logging level, use this.
290// Meant for cases where you explicitly call dont_log to avoid an expensive operation if the logging is disabled.
291#define FORCE_LOG_TO(logger, domain) logger(domain) | formatter()
292
293// always log (since it's at the error level) to the general log stream
294// outputting the log domain and timestamp is disabled
295// meant as a replacement to using cerr/cout, but that goes through the same logging infrastructure as everything else
296#define PLAIN_LOG lg::err()(lg::general(), false, false, false, false, true) | formatter()
297#define STREAMING_LOG lg::err()(lg::general(), false, false, false, false, false) | formatter()
std::ostringstream wrapper.
Definition formatter.hpp:40
logd * domain_
Definition log.hpp:125
std::string prefix_
Definition log.hpp:195
std::ostream & stream_
Definition log.hpp:192
void set_auto_newline(bool enabled)
Definition log.cpp:527
void set_prefix(const std::string &prefix)
Definition log.cpp:523
void enable_timestamp()
Definition log.cpp:519
void operator|(const formatter &message)
Definition log.cpp:494
void set_indent(int level)
Definition log.cpp:515
char const * name_
Definition log.hpp:207
std::string get_name() const
Definition log.hpp:224
logger(char const *name, severity severity)
Definition log.hpp:210
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:460
severity get_severity() const
Definition log.hpp:219
severity severity_
Definition log.hpp:208
bool dont_log(const log_domain &domain) const
Definition log.hpp:214
Helper class to redirect the output of the logger in a certain scope.
Definition log.hpp:98
std::ostream * old_stream_
The previously set redirection.
Definition log.hpp:117
const log_domain & domain_
Definition log.hpp:241
void do_log_entry(const std::string &str) noexcept
Definition log.cpp:531
std::string str_
Definition log.hpp:242
scope_logger(const log_domain &domain, const char *str)
Definition log.hpp:244
std::chrono::steady_clock::time_point start_
Definition log.hpp:240
void do_log_exit() noexcept
Definition log.cpp:539
scope_logger(const log_domain &domain, const std::string &str)
Definition log.hpp:251
Definition pump.hpp:41
bool get_log_domain_severity(const std::string &name, severity &severity)
Definition log.cpp:401
logger & err()
Definition log.cpp:339
severity
Definition log.hpp:82
std::string list_log_domains(const std::string &filter)
Definition log.cpp:410
log_domain & general()
Definition log.cpp:365
bool broke_strict()
Definition log.cpp:430
logger & debug()
Definition log.cpp:357
const std::string out_log_file_suffix
Definition log.hpp:70
void rotate_logs(const std::string &log_dir)
Check how many log files exist and delete the oldest when there's too many.
Definition log.cpp:134
std::string unique_log_filename()
Generate a unique file name using the current timestamp and a randomly generated number.
Definition log.cpp:165
void set_log_to_file()
Do the initial redirection to a log file if the logs directory is writable.
Definition log.cpp:266
void set_log_sanitize(bool sanitize)
toggle log sanitization
Definition log.cpp:434
utils::optional< bool > log_dir_writable()
Returns the result set by check_log_dir_writable().
Definition log.cpp:312
void move_log_file()
Move the log file to another directory.
Definition log.cpp:210
logger & warn()
Definition log.cpp:345
void timestamps(bool t)
Definition log.cpp:336
std::string sanitize_log(const std::string &logstr)
Definition log.cpp:438
const unsigned max_logs
Definition log.hpp:75
void check_log_dir_writable()
Checks that a dummy file can be written to and deleted from the logs directory.
Definition log.cpp:178
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition log.cpp:550
const std::string log_file_prefix
Definition log.hpp:67
std::pair< const std::string, severity > logd
Definition log.hpp:122
bool is_not_log_file(const std::string &fn)
Use the defined prefix and suffix to determine if a filename is a log file.
Definition log.cpp:128
void precise_timestamps(bool pt)
Definition log.cpp:337
logger & info()
Definition log.cpp:351
bool set_log_domain_severity(const std::string &name, severity severity)
Definition log.cpp:379
void set_strict_severity(severity severity)
Definition log.cpp:420
std::string get_log_file_path()
Definition log.cpp:317
const std::string log_file_suffix
Definition log.hpp:68
std::ostringstream & operator<<(std::ostringstream &oss, const lg::severity severity)
Definition log.cpp:122
STL namespace.
std::string filename
Filename.