Electroneum
mlog.cpp
Go to the documentation of this file.
1 // Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above copyright
9 // notice, this list of conditions and the following disclaimer in the
10 // documentation and/or other materials provided with the distribution.
11 // * Neither the name of the Andrey N. Sabelnikov nor the
12 // names of its contributors may be used to endorse or promote products
13 // derived from this software without specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
19 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 //
26 
27 
28 #ifndef _MLOG_H_
29 #define _MLOG_H_
30 
31 #ifdef _WIN32
32 #include <windows.h>
33 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
34 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
35 #endif
36 #endif
37 
38 #include <time.h>
39 #include <atomic>
40 #include <boost/filesystem.hpp>
41 #include <boost/algorithm/string.hpp>
42 #include "string_tools.h"
43 #include "misc_os_dependent.h"
44 #include "misc_log_ex.h"
45 
46 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
47 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "logging"
48 
49 #define MLOG_BASE_FORMAT "%datetime{%Y-%M-%d %H:%m:%s.%g}\t%thread\t%level\t%logger\t%loc\t%msg"
50 
51 #define MLOG_LOG(x) CINFO(el::base::Writer,el::base::DispatchAction::FileOnlyLog,ELECTRONEUM_DEFAULT_LOG_CATEGORY) << x
52 
53 using namespace epee;
54 
55 static std::string generate_log_filename(const char *base)
56 {
57  std::string filename(base);
58  static unsigned int fallback_counter = 0;
59  char tmp[200];
60  struct tm tm;
61  time_t now = time(NULL);
62  if (!epee::misc_utils::get_gmt_time(now, tm))
63  snprintf(tmp, sizeof(tmp), "part-%u", ++fallback_counter);
64  else
65  strftime(tmp, sizeof(tmp), "%Y-%m-%d-%H-%M-%S", &tm);
66  tmp[sizeof(tmp) - 1] = 0;
67  filename += "-";
68  filename += tmp;
69  return filename;
70 }
71 
72 std::string mlog_get_default_log_path(const char *default_filename)
73 {
76  std::string default_log_file = process_name;
77  std::string::size_type a = default_log_file.rfind('.');
78  if ( a != std::string::npos )
79  default_log_file.erase( a, default_log_file.size());
80  if ( ! default_log_file.empty() )
81  default_log_file += ".log";
82  else
83  default_log_file = default_filename;
84 
85  return (boost::filesystem::path(default_log_folder) / boost::filesystem::path(default_log_file)).string();
86 }
87 
88 static void mlog_set_common_prefix()
89 {
90  static const char * const expected_filename = "contrib/epee/src/mlog.cpp";
91  const char *path = __FILE__, *expected_ptr = strstr(path, expected_filename);
92  if (!expected_ptr)
93  return;
94  el::Loggers::setFilenameCommonPrefix(std::string(path, expected_ptr - path));
95 }
96 
97 static const char *get_default_categories(int level)
98 {
99  const char *categories = "";
100  switch (level)
101  {
102  case 0:
103  categories = "*:WARNING,net:FATAL,net.http:FATAL,net.ssl:FATAL,net.p2p:FATAL,net.cn:FATAL,global:INFO,verify:FATAL,serialization:FATAL,stacktrace:INFO,logging:INFO,msgwriter:INFO";
104  break;
105  case 1:
106  categories = "*:INFO,global:INFO,stacktrace:INFO,logging:INFO,msgwriter:INFO,perf.*:DEBUG";
107  break;
108  case 2:
109  categories = "*:DEBUG";
110  break;
111  case 3:
112  categories = "*:TRACE";
113  break;
114  case 4:
115  categories = "*:TRACE";
116  break;
117  default:
118  break;
119  }
120  return categories;
121 }
122 
123 #ifdef WIN32
124 bool EnableVTMode()
125 {
126  // Set output mode to handle virtual terminal sequences
127  HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
128  if (hOut == INVALID_HANDLE_VALUE)
129  {
130  return false;
131  }
132 
133  DWORD dwMode = 0;
134  if (!GetConsoleMode(hOut, &dwMode))
135  {
136  return false;
137  }
138 
139  dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
140  if (!SetConsoleMode(hOut, dwMode))
141  {
142  return false;
143  }
144  return true;
145 }
146 #endif
147 
148 void mlog_configure(const std::string &filename_base, bool console, const std::size_t max_log_file_size, const std::size_t max_log_files)
149 {
153  const char *log_format = getenv("ELECTRONEUM_LOG_FORMAT");
154  if (!log_format)
155  log_format = MLOG_BASE_FORMAT;
157  c.setGlobally(el::ConfigurationType::ToStandardOutput, console ? "true" : "false");
160 
166  el::Helpers::installPreRollOutCallback([filename_base, max_log_files](const char *name, size_t){
167  std::string rname = generate_log_filename(filename_base.c_str());
168  int ret = rename(name, rname.c_str());
169  if (ret < 0)
170  {
171  // can't log a failure, but don't do the file removal below
172  return;
173  }
174  if (max_log_files != 0)
175  {
176  std::vector<boost::filesystem::path> found_files;
177  const boost::filesystem::directory_iterator end_itr;
178  const boost::filesystem::path filename_base_path(filename_base);
179  const boost::filesystem::path parent_path = filename_base_path.has_parent_path() ? filename_base_path.parent_path() : ".";
180  for (boost::filesystem::directory_iterator iter(parent_path); iter != end_itr; ++iter)
181  {
182  const std::string filename = iter->path().string();
183  if (filename.size() >= filename_base.size() && std::memcmp(filename.data(), filename_base.data(), filename_base.size()) == 0)
184  {
185  found_files.push_back(iter->path());
186  }
187  }
188  if (found_files.size() >= max_log_files)
189  {
190  std::sort(found_files.begin(), found_files.end(), [](const boost::filesystem::path &a, const boost::filesystem::path &b) {
191  boost::system::error_code ec;
192  std::time_t ta = boost::filesystem::last_write_time(boost::filesystem::path(a), ec);
193  if (ec)
194  {
195  MERROR("Failed to get timestamp from " << a << ": " << ec);
196  ta = std::time(nullptr);
197  }
198  std::time_t tb = boost::filesystem::last_write_time(boost::filesystem::path(b), ec);
199  if (ec)
200  {
201  MERROR("Failed to get timestamp from " << b << ": " << ec);
202  tb = std::time(nullptr);
203  }
204  static_assert(std::is_integral<time_t>(), "bad time_t");
205  return ta < tb;
206  });
207  for (size_t i = 0; i <= found_files.size() - max_log_files; ++i)
208  {
209  try
210  {
211  boost::system::error_code ec;
212  boost::filesystem::remove(found_files[i], ec);
213  if (ec)
214  {
215  MERROR("Failed to remove " << found_files[i] << ": " << ec);
216  }
217  }
218  catch (const std::exception &e)
219  {
220  MERROR("Failed to remove " << found_files[i] << ": " << e.what());
221  }
222  }
223  }
224  }
225  });
226  mlog_set_common_prefix();
227  const char *electroneum_log = getenv("ELECTRONEUM_LOGS");
228  if (!electroneum_log)
229  {
230  electroneum_log = get_default_categories(0);
231  }
232  mlog_set_log(electroneum_log);
233 #ifdef WIN32
234  EnableVTMode();
235 #endif
236 }
237 
238 void mlog_set_categories(const char *categories)
239 {
240  std::string new_categories;
241  if (*categories)
242  {
243  if (*categories == '+')
244  {
245  ++categories;
246  new_categories = mlog_get_categories();
247  if (*categories)
248  {
249  if (!new_categories.empty())
250  new_categories += ",";
251  new_categories += categories;
252  }
253  }
254  else if (*categories == '-')
255  {
256  ++categories;
257  new_categories = mlog_get_categories();
258  std::vector<std::string> single_categories;
259  boost::split(single_categories, categories, boost::is_any_of(","), boost::token_compress_on);
260  for (const std::string &s: single_categories)
261  {
262  size_t pos = new_categories.find(s);
263  if (pos != std::string::npos)
264  new_categories = new_categories.erase(pos, s.size());
265  }
266  }
267  else
268  {
269  new_categories = categories;
270  }
271  }
272  el::Loggers::setCategories(new_categories.c_str(), true);
273  MLOG_LOG("New log categories: " << el::Loggers::getCategories());
274 }
275 
277 {
279 }
280 
281 // maps epee style log level to new logging system
282 void mlog_set_log_level(int level)
283 {
284  const char *categories = get_default_categories(level);
285  mlog_set_categories(categories);
286 }
287 
288 void mlog_set_log(const char *log)
289 {
290  long level;
291  char *ptr = NULL;
292 
293  if (!*log)
294  {
295  mlog_set_categories(log);
296  return;
297  }
298  level = strtol(log, &ptr, 10);
299  if (ptr && *ptr)
300  {
301  // we can have a default level, eg, 2,foo:ERROR
302  if (*ptr == ',') {
303  std::string new_categories = std::string(get_default_categories(level)) + ptr;
304  mlog_set_categories(new_categories.c_str());
305  }
306  else {
307  mlog_set_categories(log);
308  }
309  }
310  else if (level >= 0 && level <= 4)
311  {
312  mlog_set_log_level(level);
313  }
314  else
315  {
316  MERROR("Invalid numerical log level: " << log);
317  }
318 }
319 
320 namespace epee
321 {
322 
324 {
325  static std::atomic<bool> initialized(false);
326  static std::atomic<bool> is_a_tty(false);
327 
328  if (!initialized.load(std::memory_order_acquire))
329  {
330 #if defined(WIN32)
331  is_a_tty.store(0 != _isatty(_fileno(stdout)), std::memory_order_relaxed);
332 #else
333  is_a_tty.store(0 != isatty(fileno(stdout)), std::memory_order_relaxed);
334 #endif
335  initialized.store(true, std::memory_order_release);
336  }
337 
338  return is_a_tty.load(std::memory_order_relaxed);
339 }
340 
341 void set_console_color(int color, bool bright)
342 {
343  if (!is_stdout_a_tty())
344  return;
345 
346  switch(color)
347  {
349  {
350 #ifdef WIN32
351  HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
352  SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE| (bright ? FOREGROUND_INTENSITY:0));
353 #else
354  if(bright)
355  std::cout << "\033[1;37m";
356  else
357  std::cout << "\033[0m";
358 #endif
359  }
360  break;
361  case console_color_white:
362  {
363 #ifdef WIN32
364  HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
365  SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | (bright ? FOREGROUND_INTENSITY:0));
366 #else
367  if(bright)
368  std::cout << "\033[1;37m";
369  else
370  std::cout << "\033[0;37m";
371 #endif
372  }
373  break;
374  case console_color_red:
375  {
376 #ifdef WIN32
377  HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
378  SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | (bright ? FOREGROUND_INTENSITY:0));
379 #else
380  if(bright)
381  std::cout << "\033[1;31m";
382  else
383  std::cout << "\033[0;31m";
384 #endif
385  }
386  break;
387  case console_color_green:
388  {
389 #ifdef WIN32
390  HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
391  SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | (bright ? FOREGROUND_INTENSITY:0));
392 #else
393  if(bright)
394  std::cout << "\033[1;32m";
395  else
396  std::cout << "\033[0;32m";
397 #endif
398  }
399  break;
400 
401  case console_color_blue:
402  {
403 #ifdef WIN32
404  HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
405  SetConsoleTextAttribute(h_stdout, FOREGROUND_BLUE | FOREGROUND_INTENSITY);//(bright ? FOREGROUND_INTENSITY:0));
406 #else
407  if(bright)
408  std::cout << "\033[1;34m";
409  else
410  std::cout << "\033[0;34m";
411 #endif
412  }
413  break;
414 
415  case console_color_cyan:
416  {
417 #ifdef WIN32
418  HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
419  SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | FOREGROUND_BLUE | (bright ? FOREGROUND_INTENSITY:0));
420 #else
421  if(bright)
422  std::cout << "\033[1;36m";
423  else
424  std::cout << "\033[0;36m";
425 #endif
426  }
427  break;
428 
430  {
431 #ifdef WIN32
432  HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
433  SetConsoleTextAttribute(h_stdout, FOREGROUND_BLUE | FOREGROUND_RED | (bright ? FOREGROUND_INTENSITY:0));
434 #else
435  if(bright)
436  std::cout << "\033[1;35m";
437  else
438  std::cout << "\033[0;35m";
439 #endif
440  }
441  break;
442 
444  {
445 #ifdef WIN32
446  HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
447  SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | (bright ? FOREGROUND_INTENSITY:0));
448 #else
449  if(bright)
450  std::cout << "\033[1;33m";
451  else
452  std::cout << "\033[0;33m";
453 #endif
454  }
455  break;
456 
457  }
458 }
459 
461  if (!is_stdout_a_tty())
462  return;
463 
464 #ifdef WIN32
465  HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
466  SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
467 #else
468  std::cout << "\033[0m";
469  std::cout.flush();
470 #endif
471 }
472 
473 }
474 
475 #endif //_MLOG_H_
#define MERROR(x)
Definition: misc_log_ex.h:73
static void setDefaultConfigurations(const Configurations &configurations, bool reconfigureExistingLoggers=false)
Sets default configurations. This configuration is used for future (and conditionally for existing) l...
Creates logger automatically when not available.
void mlog_set_log(const char *log)
Definition: mlog.cpp:288
::std::string string
Definition: gtest-port.h:1097
static void addFlag(LoggingFlag flag)
Adds logging flag used internally.
std::string mlog_get_categories()
Definition: mlog.cpp:276
#define MLOG_BASE_FORMAT
Definition: mlog.cpp:49
const char * name
Thread-safe Configuration repository.
Determines log file (full path) to write logs to for correponding level and logger.
Determines format of logging corresponding level and logger.
void reset_console_color()
Definition: mlog.cpp:460
void setGlobally(ConfigurationType configurationType, const std::string &value)
Sets configuration for all levels.
static void installPreRollOutCallback(const PreRollOutCallback &callback)
Installs pre rollout callback, this callback is triggered when log file is about to be rolled out (ca...
time_t time
Definition: blockchain.cpp:93
void mlog_set_log_level(int level)
Definition: mlog.cpp:282
bool get_gmt_time(time_t t, struct tm &tm)
std::string & get_current_module_name()
Definition: string_tools.h:227
#define MLOG_LOG(x)
Definition: mlog.cpp:51
static void setCategories(const char *categories, bool clear=true)
Sets categories as specified (on the fly)
Make terminal output colorful for supported terminals.
std::string & get_current_module_folder()
Definition: string_tools.h:233
Enables strict file rolling.
void set_console_color(int color, bool bright)
Definition: mlog.cpp:341
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
Definition: pointer.h:1124
static std::string getCategories()
Gets current categories.
bool is_stdout_a_tty()
Definition: mlog.cpp:323
Whether or not to write corresponding log to log file.
std::string to_string(t_connection_type type)
std::string mlog_get_default_log_path(const char *default_filename)
Definition: mlog.cpp:72
void mlog_set_categories(const char *categories)
Definition: mlog.cpp:238
static void setFilenameCommonPrefix(const std::string &prefix)
Sets filename common prefix.
Whether or not to write corresponding level and logger log to standard output. By standard output mea...
void mlog_configure(const std::string &filename_base, bool console, const std::size_t max_log_file_size, const std::size_t max_log_files)
Definition: mlog.cpp:148
Enables hierarchical logging.
Specifies log file max size.
Allows to disable application abortion when logged using FATAL level.