1 /*!
2 * Copyright (c) 2015 by Contributors
3 * \file logging.h
4 * \brief defines logging macros of dmlc
5 * allows use of GLOG, fall back to internal
6 * implementation when disabled
7 */
8 #ifndef DMLC_LOGGING_H_
9 #define DMLC_LOGGING_H_
10 #include <cstdio>
11 #include <cstdlib>
12 #include <string>
13 #include <vector>
14 #include <stdexcept>
15 #include <memory>
16 #include "./base.h"
17
18 #if DMLC_LOG_STACK_TRACE
19 #include <cxxabi.h>
20 #include <sstream>
21 #include DMLC_EXECINFO_H
22 #endif
23
24 namespace dmlc {
25 /*!
26 * \brief exception class that will be thrown by
27 * default logger if DMLC_LOG_FATAL_THROW == 1
28 */
29 struct Error : public std::runtime_error {
30 /*!
31 * \brief constructor
32 * \param s the error message
33 */
ErrorError34 explicit Error(const std::string &s) : std::runtime_error(s) {}
35 };
36
37 #if DMLC_LOG_STACK_TRACE
38 // get stack trace logging depth from env variable.
LogStackTraceLevel()39 inline size_t LogStackTraceLevel() {
40 size_t level;
41 if (auto var = std::getenv("DMLC_LOG_STACK_TRACE_DEPTH")) {
42 if (1 == sscanf(var, "%zu", &level)) {
43 return level + 1;
44 }
45 }
46 return DMLC_LOG_STACK_TRACE_SIZE;
47 }
48
Demangle(char const * msg_str)49 inline std::string Demangle(char const *msg_str) {
50 using std::string;
51 string msg(msg_str);
52 size_t symbol_start = string::npos;
53 size_t symbol_end = string::npos;
54 if ( ((symbol_start = msg.find("_Z")) != string::npos)
55 && (symbol_end = msg.find_first_of(" +", symbol_start)) ) {
56 string left_of_symbol(msg, 0, symbol_start);
57 string symbol(msg, symbol_start, symbol_end - symbol_start);
58 string right_of_symbol(msg, symbol_end);
59
60 int status = 0;
61 size_t length = string::npos;
62 std::unique_ptr<char, void (*)(void *__ptr)> demangled_symbol =
63 {abi::__cxa_demangle(symbol.c_str(), 0, &length, &status), &std::free};
64 if (demangled_symbol && status == 0 && length > 0) {
65 string symbol_str(demangled_symbol.get());
66 std::ostringstream os;
67 os << left_of_symbol << symbol_str << right_of_symbol;
68 return os.str();
69 }
70 }
71 return string(msg_str);
72 }
73
74 // By default skip the first frame because
75 // that belongs to ~LogMessageFatal
76 inline std::string StackTrace(
77 size_t start_frame = 1,
78 const size_t stack_size = DMLC_LOG_STACK_TRACE_SIZE) {
79 using std::string;
80 std::ostringstream stacktrace_os;
81 std::vector<void*> stack(stack_size);
82 int nframes = backtrace(stack.data(), static_cast<int>(stack_size));
83 if (start_frame < static_cast<size_t>(nframes)) {
84 stacktrace_os << "Stack trace:\n";
85 }
86 char **msgs = backtrace_symbols(stack.data(), nframes);
87 if (msgs != nullptr) {
88 for (int frameno = start_frame; frameno < nframes; ++frameno) {
89 string msg = dmlc::Demangle(msgs[frameno]);
90 stacktrace_os << " [bt] (" << frameno - start_frame << ") " << msg << "\n";
91 }
92 }
93 free(msgs);
94 string stack_trace = stacktrace_os.str();
95 return stack_trace;
96 }
97
98 #else // DMLC_LOG_STACK_TRACE is off
99
LogStackTraceLevel()100 inline size_t LogStackTraceLevel() {
101 return 0;
102 }
103
demangle(char const * msg_str)104 inline std::string demangle(char const* msg_str) {
105 return std::string();
106 }
107
108 inline std::string StackTrace(size_t start_frame = 1,
109 const size_t stack_size = 0) {
110 return std::string("Stack trace not available when "
111 "DMLC_LOG_STACK_TRACE is disabled at compile time.");
112 }
113
114 #endif // DMLC_LOG_STACK_TRACE
115 } // namespace dmlc
116
117 #if DMLC_USE_GLOG
118 #include <glog/logging.h>
119
120 namespace dmlc {
121 /*!
122 * \brief optionally redirect to google's init log
123 * \param argv0 The arguments.
124 */
InitLogging(const char * argv0)125 inline void InitLogging(const char* argv0) {
126 google::InitGoogleLogging(argv0);
127 }
128 } // namespace dmlc
129
130 #else
131 // use a light version of glog
132 #include <assert.h>
133 #include <iostream>
134 #include <sstream>
135 #include <ctime>
136
137 #if defined(_MSC_VER)
138 #pragma warning(disable : 4722)
139 #pragma warning(disable : 4068)
140 #endif
141
142 namespace dmlc {
InitLogging(const char *)143 inline void InitLogging(const char*) {
144 // DO NOTHING
145 }
146
147 // get debug option from env variable.
DebugLoggingEnabled()148 inline bool DebugLoggingEnabled() {
149 static int state = 0;
150 if (state == 0) {
151 if (auto var = std::getenv("DMLC_LOG_DEBUG")) {
152 if (std::string(var) == "1") {
153 state = 1;
154 } else {
155 state = -1;
156 }
157 } else {
158 // by default hide debug logging.
159 state = -1;
160 }
161 }
162 return state == 1;
163 }
164
165 class LogCheckError {
166 public:
LogCheckError()167 LogCheckError() : str(nullptr) {}
LogCheckError(const std::string & str_)168 explicit LogCheckError(const std::string& str_) : str(new std::string(str_)) {}
169 LogCheckError(const LogCheckError& other) = delete;
LogCheckError(LogCheckError && other)170 LogCheckError(LogCheckError&& other) : str(other.str) {
171 other.str = nullptr;
172 }
~LogCheckError()173 ~LogCheckError() { if (str != nullptr) delete str; }
174 operator bool() const { return str != nullptr; }
175 LogCheckError& operator=(const LogCheckError& other) = delete;
176 LogCheckError& operator=(LogCheckError&& other) = delete;
177 std::string* str;
178 };
179
180 #ifndef DMLC_GLOG_DEFINED
181
182 #ifndef _LIBCPP_SGX_NO_IOSTREAMS
183 #define DEFINE_CHECK_FUNC(name, op) \
184 template <typename X, typename Y> \
185 inline LogCheckError LogCheck##name(const X& x, const Y& y) { \
186 if (x op y) return LogCheckError(); \
187 std::ostringstream os; \
188 os << " (" << x << " vs. " << y << ") "; /* CHECK_XX(x, y) requires x and y can be serialized to string. Use CHECK(x OP y) otherwise. NOLINT(*) */ \
189 return LogCheckError(os.str()); \
190 } \
191 inline LogCheckError LogCheck##name(int x, int y) { \
192 return LogCheck##name<int, int>(x, y); \
193 }
194 #else
195 #define DEFINE_CHECK_FUNC(name, op) \
196 template <typename X, typename Y> \
197 inline LogCheckError LogCheck##name(const X& x, const Y& y) { \
198 if (x op y) return LogCheckError(); \
199 return LogCheckError("Error."); \
200 } \
201 inline LogCheckError LogCheck##name(int x, int y) { \
202 return LogCheck##name<int, int>(x, y); \
203 }
204 #endif
205
206 #define CHECK_BINARY_OP(name, op, x, y) \
207 if (dmlc::LogCheckError _check_err = dmlc::LogCheck##name(x, y)) \
208 dmlc::LogMessageFatal(__FILE__, __LINE__).stream() \
209 << "Check failed: " << #x " " #op " " #y << *(_check_err.str) << ": "
210
211 #pragma GCC diagnostic push
212 #pragma GCC diagnostic ignored "-Wsign-compare"
213 DEFINE_CHECK_FUNC(_LT, <)
214 DEFINE_CHECK_FUNC(_GT, >)
215 DEFINE_CHECK_FUNC(_LE, <=)
216 DEFINE_CHECK_FUNC(_GE, >=)
217 DEFINE_CHECK_FUNC(_EQ, ==)
218 DEFINE_CHECK_FUNC(_NE, !=)
219 #pragma GCC diagnostic pop
220
221 // Always-on checking
222 #define CHECK(x) \
223 if (!(x)) \
224 dmlc::LogMessageFatal(__FILE__, __LINE__).stream() \
225 << "Check failed: " #x << ": "
226 #define CHECK_LT(x, y) CHECK_BINARY_OP(_LT, <, x, y)
227 #define CHECK_GT(x, y) CHECK_BINARY_OP(_GT, >, x, y)
228 #define CHECK_LE(x, y) CHECK_BINARY_OP(_LE, <=, x, y)
229 #define CHECK_GE(x, y) CHECK_BINARY_OP(_GE, >=, x, y)
230 #define CHECK_EQ(x, y) CHECK_BINARY_OP(_EQ, ==, x, y)
231 #define CHECK_NE(x, y) CHECK_BINARY_OP(_NE, !=, x, y)
232 #define CHECK_NOTNULL(x) \
233 ((x) == NULL ? dmlc::LogMessageFatal(__FILE__, __LINE__).stream() << "Check notnull: " #x << ' ', (x) : (x)) // NOLINT(*)
234
235 // Debug-only checking.
236 #if DMLC_LOG_DEBUG
237 #define DCHECK(x) \
238 while (false) CHECK(x)
239 #define DCHECK_LT(x, y) \
240 while (false) CHECK((x) < (y))
241 #define DCHECK_GT(x, y) \
242 while (false) CHECK((x) > (y))
243 #define DCHECK_LE(x, y) \
244 while (false) CHECK((x) <= (y))
245 #define DCHECK_GE(x, y) \
246 while (false) CHECK((x) >= (y))
247 #define DCHECK_EQ(x, y) \
248 while (false) CHECK((x) == (y))
249 #define DCHECK_NE(x, y) \
250 while (false) CHECK((x) != (y))
251 #else
252 #define DCHECK(x) CHECK(x)
253 #define DCHECK_LT(x, y) CHECK((x) < (y))
254 #define DCHECK_GT(x, y) CHECK((x) > (y))
255 #define DCHECK_LE(x, y) CHECK((x) <= (y))
256 #define DCHECK_GE(x, y) CHECK((x) >= (y))
257 #define DCHECK_EQ(x, y) CHECK((x) == (y))
258 #define DCHECK_NE(x, y) CHECK((x) != (y))
259 #endif // DMLC_LOG_DEBUG
260
261 #if DMLC_LOG_CUSTOMIZE
262 #define LOG_INFO dmlc::CustomLogMessage(__FILE__, __LINE__)
263 #else
264 #define LOG_INFO dmlc::LogMessage(__FILE__, __LINE__)
265 #endif
266 #define LOG_ERROR LOG_INFO
267 #define LOG_WARNING LOG_INFO
268 #define LOG_FATAL dmlc::LogMessageFatal(__FILE__, __LINE__)
269 #define LOG_QFATAL LOG_FATAL
270
271 // Poor man version of VLOG
272 #define VLOG(x) LOG_INFO.stream()
273
274 #define LOG(severity) LOG_##severity.stream()
275 #define LG LOG_INFO.stream()
276 #define LOG_IF(severity, condition) \
277 !(condition) ? (void)0 : dmlc::LogMessageVoidify() & LOG(severity)
278
279 #if DMLC_LOG_DEBUG
280
281 #define LOG_DFATAL LOG_FATAL
282 #define DFATAL FATAL
283 #define DLOG(severity) LOG_IF(severity, ::dmlc::DebugLoggingEnabled())
284 #define DLOG_IF(severity, condition) LOG_IF(severity, ::dmlc::DebugLoggingEnabled() && (condition))
285
286 #else
287
288 #define LOG_DFATAL LOG_ERROR
289 #define DFATAL ERROR
290 #define DLOG(severity) true ? (void)0 : dmlc::LogMessageVoidify() & LOG(severity)
291 #define DLOG_IF(severity, condition) \
292 (true || !(condition)) ? (void)0 : dmlc::LogMessageVoidify() & LOG(severity)
293 #endif
294
295 // Poor man version of LOG_EVERY_N
296 #define LOG_EVERY_N(severity, n) LOG(severity)
297
298 #endif // DMLC_GLOG_DEFINED
299
300 class DateLogger {
301 public:
DateLogger()302 DateLogger() {
303 #if defined(_MSC_VER)
304 _tzset();
305 #endif
306 }
HumanDate()307 const char* HumanDate() {
308 #ifndef _LIBCPP_SGX_CONFIG
309 #if defined(_MSC_VER)
310 _strtime_s(buffer_, sizeof(buffer_));
311 #else
312 time_t time_value = time(NULL);
313 struct tm *pnow;
314 #if !defined(_WIN32)
315 struct tm now;
316 pnow = localtime_r(&time_value, &now);
317 #else
318 pnow = localtime(&time_value); // NOLINT(*)
319 #endif
320 snprintf(buffer_, sizeof(buffer_), "%02d:%02d:%02d",
321 pnow->tm_hour, pnow->tm_min, pnow->tm_sec);
322 #endif
323 #endif // _LIBCPP_SGX_CONFIG
324 return buffer_;
325 }
326
327 private:
328 char buffer_[9];
329 };
330
331 #ifndef _LIBCPP_SGX_NO_IOSTREAMS
332 class LogMessage {
333 public:
LogMessage(const char * file,int line)334 LogMessage(const char* file, int line)
335 :
336 #ifdef __ANDROID__
337 log_stream_(std::cout)
338 #else
339 log_stream_(std::cerr)
340 #endif
341 {
342 log_stream_ << "[" << pretty_date_.HumanDate() << "] " << file << ":"
343 << line << ": ";
344 }
~LogMessage()345 ~LogMessage() { log_stream_ << '\n'; }
stream()346 std::ostream& stream() { return log_stream_; }
347
348 protected:
349 std::ostream& log_stream_;
350
351 private:
352 DateLogger pretty_date_;
353 LogMessage(const LogMessage&);
354 void operator=(const LogMessage&);
355 };
356
357 // customized logger that can allow user to define where to log the message.
358 class CustomLogMessage {
359 public:
CustomLogMessage(const char * file,int line)360 CustomLogMessage(const char* file, int line) {
361 log_stream_ << "[" << DateLogger().HumanDate() << "] " << file << ":"
362 << line << ": ";
363 }
~CustomLogMessage()364 ~CustomLogMessage() {
365 Log(log_stream_.str());
366 }
stream()367 std::ostream& stream() { return log_stream_; }
368 /*!
369 * \brief customized logging of the message.
370 * This function won't be implemented by libdmlc
371 * \param msg The message to be logged.
372 */
373 static void Log(const std::string& msg);
374
375 private:
376 std::ostringstream log_stream_;
377 };
378 #else
379 class DummyOStream {
380 public:
381 template <typename T>
382 DummyOStream& operator<<(T _) { return *this; }
str()383 inline std::string str() { return ""; }
384 };
385 class LogMessage {
386 public:
LogMessage(const char * file,int line)387 LogMessage(const char* file, int line) : log_stream_() {}
stream()388 DummyOStream& stream() { return log_stream_; }
389
390 protected:
391 DummyOStream log_stream_;
392
393 private:
394 LogMessage(const LogMessage&);
395 void operator=(const LogMessage&);
396 };
397 #endif
398
399
400 #if defined(_LIBCPP_SGX_NO_IOSTREAMS)
401 class LogMessageFatal : public LogMessage {
402 public:
LogMessageFatal(const char * file,int line)403 LogMessageFatal(const char* file, int line) : LogMessage(file, line) {}
~LogMessageFatal()404 ~LogMessageFatal() {
405 abort();
406 }
407 private:
408 LogMessageFatal(const LogMessageFatal&);
409 void operator=(const LogMessageFatal&);
410 };
411 #elif DMLC_LOG_FATAL_THROW == 0
412 class LogMessageFatal : public LogMessage {
413 public:
LogMessageFatal(const char * file,int line)414 LogMessageFatal(const char* file, int line) : LogMessage(file, line) {}
~LogMessageFatal()415 ~LogMessageFatal() {
416 log_stream_ << "\n" << StackTrace(1, LogStackTraceLevel()) << "\n";
417 abort();
418 }
419
420 private:
421 LogMessageFatal(const LogMessageFatal&);
422 void operator=(const LogMessageFatal&);
423 };
424 #else
425 class LogMessageFatal {
426 public:
LogMessageFatal(const char * file,int line)427 LogMessageFatal(const char* file, int line) {
428 log_stream_ << "[" << pretty_date_.HumanDate() << "] " << file << ":"
429 << line << ": ";
430 }
stream()431 std::ostringstream &stream() { return log_stream_; }
~LogMessageFatal()432 ~LogMessageFatal() DMLC_THROW_EXCEPTION {
433 #if DMLC_LOG_STACK_TRACE
434 log_stream_ << "\n" << StackTrace(1, LogStackTraceLevel()) << "\n";
435 #endif
436
437 // throwing out of destructor is evil
438 // hopefully we can do it here
439 // also log the message before throw
440 #if DMLC_LOG_BEFORE_THROW
441 LOG(ERROR) << log_stream_.str();
442 #endif
443 throw Error(log_stream_.str());
444 }
445
446 private:
447 std::ostringstream log_stream_;
448 DateLogger pretty_date_;
449 LogMessageFatal(const LogMessageFatal&);
450 void operator=(const LogMessageFatal&);
451 };
452 #endif
453
454 // This class is used to explicitly ignore values in the conditional
455 // logging macros. This avoids compiler warnings like "value computed
456 // is not used" and "statement has no effect".
457 class LogMessageVoidify {
458 public:
LogMessageVoidify()459 LogMessageVoidify() {}
460 // This has to be an operator with a precedence lower than << but
461 // higher than "?:". See its usage.
462 #if !defined(_LIBCPP_SGX_NO_IOSTREAMS)
463 void operator&(std::ostream&) {}
464 #endif
465 };
466
467 } // namespace dmlc
468
469 #endif
470 #endif // DMLC_LOGGING_H_
471