The Battle for Wesnoth  1.17.0-dev
log.cpp
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 (implementation).
20  * See also the command line switches --logdomains and --log-@<level@>="domain".
21  */
22 
23 #include "log.hpp"
24 
25 #include <map>
26 #include <sstream>
27 #include <ctime>
28 #include <mutex>
29 #include <iomanip>
30 
31 namespace {
32 
33 class null_streambuf : public std::streambuf
34 {
35  virtual int overflow(int c) { return std::char_traits< char >::not_eof(c); }
36 public:
37  null_streambuf() {}
38 };
39 
40 } // end anonymous namespace
41 
42 static std::ostream null_ostream(new null_streambuf);
43 static int indent = 0;
44 static bool timestamp = true;
45 static bool precise_timestamp = false;
46 static std::mutex log_mutex;
47 
48 static std::ostream *output_stream = nullptr;
49 
50 static std::ostream& output()
51 {
52  if(output_stream) {
53  return *output_stream;
54  }
55  return std::cerr;
56 }
57 
58 namespace lg {
59 
60 redirect_output_setter::redirect_output_setter(std::ostream& stream)
61  : old_stream_(output_stream)
62 {
63  output_stream = &stream;
64 }
65 
67 {
69 }
70 
71 typedef std::map<std::string, int> domain_map;
72 static domain_map *domains;
73 static int strict_level_ = -1;
74 void timestamps(bool t) { timestamp = t; }
75 void precise_timestamps(bool pt) { precise_timestamp = pt; }
76 
78 {
79  static logger lg("error", 0);
80  return lg;
81 }
82 
84 {
85  static logger lg("warning", 1);
86  return lg;
87 }
88 
90 {
91  static logger lg("info", 2);
92  return lg;
93 }
94 
96 {
97  static logger lg("debug", 3);
98  return lg;
99 }
100 
101 static log_domain dom("general");
102 
104 {
105  return dom;
106 }
107 
108 log_domain::log_domain(char const *name, int severity)
109  : domain_(nullptr)
110 {
111  // Indirection to prevent initialization depending on link order.
112  if (!domains) domains = new domain_map;
113  domain_ = &*domains->insert(logd(name, severity)).first;
114  domain_->second = severity;
115 }
116 
117 bool set_log_domain_severity(const std::string& name, int severity)
118 {
119  std::string::size_type s = name.size();
120  if (name == "all") {
121  for(logd &l : *domains) {
122  l.second = severity;
123  }
124  } else if (s > 2 && name.compare(s - 2, 2, "/*") == 0) {
125  for(logd &l : *domains) {
126  if (l.first.compare(0, s - 1, name, 0, s - 1) == 0)
127  l.second = severity;
128  }
129  } else {
130  domain_map::iterator it = domains->find(name);
131  if (it == domains->end())
132  return false;
133  it->second = severity;
134  }
135  return true;
136 }
137 bool set_log_domain_severity(const std::string& name, const logger &lg) {
138  return set_log_domain_severity(name, lg.get_severity());
139 }
140 
141 bool get_log_domain_severity(const std::string& name, int &severity)
142 {
143  domain_map::iterator it = domains->find(name);
144  if (it == domains->end())
145  return false;
146  severity = it->second;
147  return true;
148 }
149 
150 std::string list_logdomains(const std::string& filter)
151 {
152  std::ostringstream res;
153  for(logd &l : *domains) {
154  if(l.first.find(filter) != std::string::npos)
155  res << l.first << "\n";
156  }
157  return res.str();
158 }
159 
161  strict_level_ = severity;
162 }
163 
166 }
167 
168 static bool strict_threw_ = false;
169 
170 bool broke_strict() {
171  return strict_threw_;
172 }
173 
174 std::string get_timestamp(const std::time_t& t, const std::string& format) {
175  std::ostringstream ss;
176 
177  ss << std::put_time(std::localtime(&t), format.c_str());
178 
179  return ss.str();
180 }
181 std::string get_timespan(const std::time_t& t) {
182  std::ostringstream sout;
183  // There doesn't seem to be any library function for this
184  const std::time_t minutes = t / 60;
185  const std::time_t days = minutes / 60 / 24;
186  if(t <= 0) {
187  sout << "expired";
188  } else if(minutes == 0) {
189  sout << t << " seconds";
190  } else if(days == 0) {
191  sout << minutes / 60 << " hours, " << minutes % 60 << " minutes";
192  } else {
193  sout << days << " days, " << (minutes / 60) % 24 << " hours, " << minutes % 60 << " minutes";
194  }
195  return sout.str();
196 }
197 
198 static void print_precise_timestamp(std::ostream& out) noexcept
199 {
200  try {
201  int64_t micros = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
202  std::time_t seconds = micros/1'000'000;
203  int fractional = micros-(seconds*1'000'000);
204  char c = out.fill('0');
205  out << std::put_time(std::localtime(&seconds), "%Y%m%d %H:%M:%S") << "." << std::setw(6) << fractional << ' ';
206  out.fill(c);
207  } catch(...) {}
208 }
209 
210 log_in_progress logger::operator()(const log_domain& domain, bool show_names, bool do_indent) const
211 {
212  if (severity_ > domain.domain_->second) {
213  return null_ostream;
214  } else {
215  if (!strict_threw_ && (severity_ <= strict_level_)) {
216  std::stringstream ss;
217  ss << "Error (strict mode, strict_level = " << strict_level_ << "): wesnoth reported on channel " << name_ << " " << domain.domain_->first;
218  std::cerr << ss.str() << std::endl;
219  strict_threw_ = true;
220  }
221  log_in_progress stream = output();
222  if(do_indent) {
223  stream.set_indent(indent);
224  }
225  if (timestamp) {
226  stream.enable_timestamp();
227  }
228  if (show_names) {
229  stream.set_prefix(formatter() << name_ << ' ' << domain.domain_->first << ": ");
230  }
231  return stream;
232  }
233 }
234 
235 log_in_progress::log_in_progress(std::ostream& stream)
236  : stream_(stream)
237 {}
238 
240 {
241  std::scoped_lock lock(log_mutex);
242  for(int i = 0; i < indent; ++i)
243  stream_ << " ";
244  if(timestamp_) {
245  if(precise_timestamp) {
247  } else {
248  stream_ << get_timestamp(std::time(nullptr));
249  }
250  }
251  stream_ << prefix_ << message.str();
252 }
253 
255  indent_ = level;
256 }
257 
259  timestamp_ = true;
260 }
261 
262 void log_in_progress::set_prefix(const std::string& prefix) {
263  prefix_ = prefix;
264 }
265 
266 void scope_logger::do_log_entry(const std::string& str) noexcept
267 {
268  str_ = str;
269  try {
270  ticks_ = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
271  } catch(...) {}
272  debug()(domain_, false, true) | formatter() << "{ BEGIN: " << str_ << "\n";
273  ++indent;
274 }
275 
277 {
278  long ticks = 0;
279  try {
280  ticks = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count() - ticks_;
281  } catch(...) {}
282  --indent;
283  auto output = debug()(domain_, false, true);
284  output.set_indent(indent);
285  if(timestamp) output.enable_timestamp();
286  output | formatter() << "} END: " << str_ << " (took " << ticks << "ms)\n";
287 }
288 
289 std::stringstream& log_to_chat()
290 {
291  static std::stringstream lg;
292  return lg;
293 }
294 
295 } // end namespace lg
static log_domain dom("general")
static int strict_level_
Definition: log.cpp:73
std::string get_timestamp(const std::time_t &t, const std::string &format)
Definition: log.cpp:174
static std::mutex log_mutex
Definition: log.cpp:46
static domain_map * domains
Definition: log.cpp:72
std::string get_timespan(const std::time_t &t)
Definition: log.cpp:181
void do_log_exit() noexcept
Definition: log.cpp:276
logger & info()
Definition: log.cpp:89
void timestamps(bool t)
Definition: log.cpp:74
log_in_progress(std::ostream &stream)
Definition: log.cpp:235
log_domain(char const *name, int severity=1)
Definition: log.cpp:108
static std::ostream * output_stream
Definition: log.cpp:48
std::pair< const std::string, int > logd
Definition: log.hpp:101
bool broke_strict()
Definition: log.cpp:170
static std::ostream & output()
Definition: log.cpp:50
bool get_log_domain_severity(const std::string &name, int &severity)
Definition: log.cpp:141
std::ostringstream wrapper.
Definition: formatter.hpp:39
log_in_progress operator()(const log_domain &domain, bool show_names=true, bool do_indent=false) const
Definition: log.cpp:210
static bool precise_timestamp
Definition: log.cpp:45
logger & debug()
Definition: log.cpp:95
severity
Definition: log.hpp:64
static int indent
Definition: log.cpp:43
log_domain & general()
Definition: log.cpp:103
void do_log_entry(const std::string &str) noexcept
Definition: log.cpp:266
std::map< std::string, int > domain_map
Definition: log.cpp:71
Definition: pump.hpp:40
std::ostream * old_stream_
The previously set redirection.
Definition: log.hpp:98
std::size_t i
Definition: function.cpp:967
logger & err()
Definition: log.cpp:77
static void print_precise_timestamp(std::ostream &out) noexcept
Definition: log.cpp:198
void set_strict_severity(int severity)
Definition: log.cpp:160
static map_location::DIRECTION s
bool set_log_domain_severity(const std::string &name, int severity)
Definition: log.cpp:117
void operator|(formatter &&message)
Definition: log.cpp:239
static bool strict_threw_
Definition: log.cpp:168
logger & warn()
Definition: log.cpp:83
double t
Definition: astarsearch.cpp:65
void enable_timestamp()
Definition: log.cpp:258
static bool timestamp
Definition: log.cpp:44
std::string list_logdomains(const std::string &filter)
Definition: log.cpp:150
std::string prefix_
Definition: log.hpp:132
Standard logging facilities (interface).
static std::ostream null_ostream(new null_streambuf)
void set_prefix(const std::string &prefix)
Definition: log.cpp:262
void set_indent(int level)
Definition: log.cpp:254
mock_char c
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
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
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