Electroneum
Loading...
Searching...
No Matches
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
53using namespace epee;
54
55static 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);
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
72std::string mlog_get_default_log_path(const char *default_filename)
73{
74 std::string process_name = epee::string_tools::get_current_module_name();
75 std::string default_log_folder = epee::string_tools::get_current_module_folder();
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
88static 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
97static 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
124bool 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
148void 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");
158 c.setGlobally(el::ConfigurationType::MaxLogFileSize, std::to_string(max_log_file_size));
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
238void 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
282void mlog_set_log_level(int level)
283{
284 const char *categories = get_default_categories(level);
285 mlog_set_categories(categories);
286}
287
288void mlog_set_log(const char *log)
289{
290 long level;
291 char *ptr = NULL;
292
293 if (!*log)
294 {
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 {
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
320namespace 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
341void 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;
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;
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;
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
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
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_
else if(0==res)
time_t time
Thread-safe Configuration repository.
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...
static std::string getCategories()
Gets current categories.
static void setCategories(const char *categories, bool clear=true)
Sets categories as specified (on the fly).
static void setDefaultConfigurations(const Configurations &configurations, bool reconfigureExistingLoggers=false)
Sets default configurations. This configuration is used for future (and conditionally for existing) l...
static void setFilenameCommonPrefix(const std::string &prefix)
Sets filename common prefix.
static void addFlag(LoggingFlag flag)
Adds logging flag used internally.
#define MERROR(x)
Definition misc_log_ex.h:73
std::string mlog_get_default_log_path(const char *default_filename)
Definition mlog.cpp:72
std::string mlog_get_categories()
Definition mlog.cpp:276
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
void mlog_set_log(const char *log)
Definition mlog.cpp:288
void mlog_set_log_level(int level)
Definition mlog.cpp:282
#define MLOG_LOG(x)
Definition mlog.cpp:51
#define MLOG_BASE_FORMAT
Definition mlog.cpp:49
void mlog_set_categories(const char *categories)
Definition mlog.cpp:238
@ CreateLoggerAutomatically
Creates logger automatically when not available.
@ HierarchicalLogging
Enables hierarchical logging.
@ StrictLogFileSizeCheck
Enables strict file rolling.
@ DisableApplicationAbortOnFatalLog
Allows to disable application abortion when logged using FATAL level.
@ ColoredTerminalOutput
Make terminal output colorful for supported terminals.
@ Filename
Determines log file (full path) to write logs to for correponding level and logger.
@ MaxLogFileSize
Specifies log file max size.
@ Format
Determines format of logging corresponding level and logger.
@ ToStandardOutput
Whether or not to write corresponding level and logger log to standard output. By standard output mea...
@ ToFile
Whether or not to write corresponding log to log file.
bool get_gmt_time(time_t t, struct tm &tm)
std::string & get_current_module_name()
std::string & get_current_module_folder()
void set_console_color(int color, bool bright)
Definition mlog.cpp:341
void reset_console_color()
Definition mlog.cpp:460
bool is_stdout_a_tty()
Definition mlog.cpp:323
@ console_color_red
@ console_color_magenta
@ console_color_yellow
@ console_color_default
@ console_color_green
@ console_color_blue
@ console_color_white
@ console_color_cyan
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
Definition pointer.h:1124