Treelite
logging.h
Go to the documentation of this file.
1 
7 #ifndef TREELITE_LOGGING_H_
8 #define TREELITE_LOGGING_H_
9 
10 #include <treelite/thread_local.h>
11 #include <iostream>
12 #include <stdexcept>
13 #include <string>
14 #include <sstream>
15 #include <memory>
16 #include <cstdio>
17 #include <ctime>
18 
19 namespace treelite {
20 
24 struct Error : public std::runtime_error {
25  explicit Error(const std::string& s) : std::runtime_error(s) {}
26 };
27 
28 template <typename X, typename Y>
29 std::unique_ptr<std::string> LogCheckFormat(const X& x, const Y& y) {
30  std::ostringstream os;
31  os << " (" << x << " vs. " << y << ") ";
32  /* CHECK_XX(x, y) requires x and y can be serialized to string. Use CHECK(x OP y) otherwise. */
33  return std::make_unique<std::string>(os.str());
34 }
35 
36 #if defined(__GNUC__) || defined(__clang__)
37 #define TREELITE_ALWAYS_INLINE inline __attribute__((__always_inline__))
38 #elif defined(_MSC_VER)
39 #define TREELITE_ALWAYS_INLINE __forceinline
40 #else
41 #define TREELITE_ALWAYS_INLINE inline
42 #endif
43 
44 #define DEFINE_CHECK_FUNC(name, op) \
45  template <typename X, typename Y> \
46  TREELITE_ALWAYS_INLINE std::unique_ptr<std::string> LogCheck##name(const X& x, const Y& y) { \
47  if (x op y) return nullptr; \
48  return LogCheckFormat(x, y); \
49  } \
50  TREELITE_ALWAYS_INLINE std::unique_ptr<std::string> LogCheck##name(int x, int y) { \
51  return LogCheck##name<int, int>(x, y); \
52  }
53 
54 #pragma GCC diagnostic push
55 #pragma GCC diagnostic ignored "-Wsign-compare"
56 DEFINE_CHECK_FUNC(_LT, <)
57 DEFINE_CHECK_FUNC(_GT, >)
58 DEFINE_CHECK_FUNC(_LE, <=)
59 DEFINE_CHECK_FUNC(_GE, >=)
60 DEFINE_CHECK_FUNC(_EQ, ==)
61 DEFINE_CHECK_FUNC(_NE, !=)
62 #pragma GCC diagnostic pop
63 
64 
65 #define TREELITE_CHECK_BINARY_OP(name, op, x, y) \
66  if (auto __treelite__log__err = ::treelite::LogCheck##name(x, y)) \
67  ::treelite::LogMessageFatal(__FILE__, __LINE__).stream() \
68  << "Check failed: " << #x " " #op " " #y << *__treelite__log__err << ": "
69 #define TREELITE_CHECK(x) \
70  if (!(x)) \
71  ::treelite::LogMessageFatal(__FILE__, __LINE__).stream() \
72  << "Check failed: " #x << ": "
73 #define TREELITE_CHECK_LT(x, y) TREELITE_CHECK_BINARY_OP(_LT, <, x, y)
74 #define TREELITE_CHECK_GT(x, y) TREELITE_CHECK_BINARY_OP(_GT, >, x, y)
75 #define TREELITE_CHECK_LE(x, y) TREELITE_CHECK_BINARY_OP(_LE, <=, x, y)
76 #define TREELITE_CHECK_GE(x, y) TREELITE_CHECK_BINARY_OP(_GE, >=, x, y)
77 #define TREELITE_CHECK_EQ(x, y) TREELITE_CHECK_BINARY_OP(_EQ, ==, x, y)
78 #define TREELITE_CHECK_NE(x, y) TREELITE_CHECK_BINARY_OP(_NE, !=, x, y)
79 
80 #define TREELITE_LOG_INFO ::treelite::LogMessage(__FILE__, __LINE__)
81 #define TREELITE_LOG_ERROR TREELITE_LOG_INFO
82 #define TREELITE_LOG_FATAL ::treelite::LogMessageFatal(__FILE__, __LINE__)
83 #define TREELITE_LOG(severity) TREELITE_LOG_##severity.stream()
84 
85 class DateLogger {
86  public:
87  DateLogger() {
88 #if defined(_MSC_VER)
89  _tzset();
90 #endif // defined(_MSC_VER)
91  }
92  const char* HumanDate() {
93 #if defined(_MSC_VER)
94  _strtime_s(buffer_, sizeof(buffer_));
95 #else // defined(_MSC_VER)
96  time_t time_value = std::time(nullptr);
97  struct tm* pnow;
98 #if !defined(_WIN32)
99  struct tm now;
100  pnow = localtime_r(&time_value, &now);
101 #else // !defined(_WIN32)
102  pnow = std::localtime(&time_value); // NOLINT(*)
103 #endif // !defined(_WIN32)
104  std::snprintf(buffer_, sizeof(buffer_), "%02d:%02d:%02d",
105  pnow->tm_hour, pnow->tm_min, pnow->tm_sec);
106 #endif // defined(_MSC_VER)
107  return buffer_;
108  }
109 
110  private:
111  char buffer_[9];
112 };
113 
115  public:
116  LogMessageFatal(const char* file, int line) {
117  log_stream_ << "[" << pretty_date_.HumanDate() << "] " << file << ":" << line << ": ";
118  }
119  LogMessageFatal(const LogMessageFatal&) = delete;
120  void operator=(const LogMessageFatal&) = delete;
121 
122  std::ostringstream& stream() {
123  return log_stream_;
124  }
125  ~LogMessageFatal() noexcept(false) {
126  throw Error(log_stream_.str());
127  }
128 
129  private:
130  std::ostringstream log_stream_;
131  DateLogger pretty_date_;
132 };
133 
134 class LogMessage {
135  public:
136  LogMessage(const char* file, int line) {
137  log_stream_ << "[" << DateLogger().HumanDate() << "] " << file << ":"
138  << line << ": ";
139  }
140  ~LogMessage() {
141  Log(log_stream_.str());
142  }
143  std::ostream& stream() { return log_stream_; }
144  static void Log(const std::string& msg);
145 
146  private:
147  std::ostringstream log_stream_;
148 };
149 
151  public:
152  using Callback = void (*)(const char*);
154  : log_callback_([] (const char* msg) { std::cerr << msg << std::endl; }) {}
155  inline void Register(Callback log_callback) {
156  this->log_callback_ = log_callback;
157  }
158  inline Callback Get() const {
159  return log_callback_;
160  }
161  private:
162  Callback log_callback_;
163 };
164 
166 
167 } // namespace treelite
168 
169 #endif // TREELITE_LOGGING_H_
Exception class that will be thrown by Treelite.
Definition: logging.h:24
Helper class for thread-local storage.
A thread-local storage.
Definition: thread_local.h:17