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