OmniSciDB  8fa3bf436f
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Logger.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2019 OmniSci, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18  * @file Logger.h
19  * @author Matt Pulver <matt.pulver@omnisci.com>
20  * @description Use Boost.Log for logging data compatible with previous API.
21  *
22  * Usage:
23  * - Initialize a LogOptions object. E.g.
24  * logger::LogOptions log_options(argv[0]);
25  * - LogOptions can optionally be added to boost::program_options:
26  * help_desc.add(log_options.get_options());
27  * - Initialize global logger once per application:
28  * logger::init(log_options);
29  * - From anywhere in the program:
30  * - LOG(INFO) << "Nice query!";
31  * - LOG(DEBUG4) << "x = " << x;
32  * - CHECK(condition);
33  * - CHECK_LE(x, xmax);
34  * Newlines are automatically appended to log messages.
35  */
36 
37 #ifndef SHARED_LOGGER_H
38 #define SHARED_LOGGER_H
39 
40 #if !(defined(__CUDACC__) || defined(NO_BOOST))
41 
42 #include <boost/config.hpp>
43 #include <boost/filesystem.hpp>
44 #include <boost/log/common.hpp>
45 #include <boost/program_options.hpp>
46 
47 #include <memory>
48 #include <set>
49 
50 #endif
51 
52 #include <array>
53 #include <sstream>
54 #include <thread>
55 
56 #ifdef _WIN32
57 #if defined(ERROR) || defined(INFO) || defined(WARNING) || defined(FATAL)
59 #endif
60 #endif
61 
62 extern bool g_enable_debug_timer;
63 
64 namespace logger {
65 
66 // Channel, ChannelNames, and ChannelSymbols must be updated together.
67 enum Channel { IR = 0, PTX, ASM, _NCHANNELS };
68 
69 constexpr std::array<char const*, 3> ChannelNames{"IR", "PTX", "ASM"};
70 
71 constexpr std::array<char, 3> ChannelSymbols{'R', 'P', 'A'};
72 
73 static_assert(Channel::_NCHANNELS == ChannelNames.size(),
74  "Size of ChannelNames must equal number of Channels.");
75 static_assert(Channel::_NCHANNELS == ChannelSymbols.size(),
76  "Size of ChannelSymbols must equal number of Channels.");
77 
78 // Severity, SeverityNames, and SeveritySymbols must be updated together.
79 enum Severity {
80  DEBUG4 = 0,
88  _NSEVERITIES // number of severity levels
89 };
90 
91 constexpr std::array<char const*, 8> SeverityNames{"DEBUG4",
92  "DEBUG3",
93  "DEBUG2",
94  "DEBUG1",
95  "INFO",
96  "WARNING",
97  "ERROR",
98  "FATAL"};
99 
100 constexpr std::array<char, 8> SeveritySymbols{'4', '3', '2', '1', 'I', 'W', 'E', 'F'};
101 
102 static_assert(Severity::_NSEVERITIES == SeverityNames.size(),
103  "Size of SeverityNames must equal number of Severity levels.");
104 static_assert(Severity::_NSEVERITIES == SeveritySymbols.size(),
105  "Size of SeveritySymbols must equal number of Severity levels.");
106 
107 #if !(defined(__CUDACC__) || defined(NO_BOOST))
108 
109 using Channels = std::set<Channel>;
110 
111 // Filled by boost::program_options
112 class LogOptions {
113  std::string base_path_{"."}; // ignored if log_dir_ is absolute.
114  // boost::program_options::options_description is not copyable so unique_ptr
115  // allows for modification after initialization (e.g. changing default values.)
116  std::unique_ptr<boost::program_options::options_description> options_;
117 
118  public:
119  // Initialize to default values
120  boost::filesystem::path log_dir_{"mapd_log"};
121  // file_name_pattern and symlink are prepended with base_name.
122  std::string file_name_pattern_{".{SEVERITY}.%Y%m%d-%H%M%S.log"};
123  std::string symlink_{".{SEVERITY}"};
127  bool auto_flush_{true};
128  size_t max_files_{100};
129  size_t min_free_space_{20 << 20};
130  bool rotate_daily_{true};
131  size_t rotation_size_{10 << 20};
132 
133  LogOptions(char const* argv0);
134  boost::filesystem::path full_log_dir() const;
135  boost::program_options::options_description const& get_options() const;
136  void parse_command_line(int, char const* const*);
137  void set_base_path(std::string const& base_path);
138  void set_options();
139 };
140 
141 // Execute once in main() to initialize boost loggers.
142 void init(LogOptions const&);
143 
144 // Flush all sinks.
145 // https://www.boost.org/libs/log/doc/html/log/rationale/why_crash_on_term.html
146 void shutdown();
147 
148 struct LogShutdown {
149  inline ~LogShutdown() { shutdown(); }
150 };
151 
152 // Optional pointer to function to call on LOG(FATAL).
153 using FatalFunc = void (*)() noexcept;
155 
156 using ChannelLogger = boost::log::sources::channel_logger_mt<Channel>;
157 BOOST_LOG_GLOBAL_LOGGER(gChannelLogger, ChannelLogger)
158 
159 using SeverityLogger = boost::log::sources::severity_logger_mt<Severity>;
160 BOOST_LOG_GLOBAL_LOGGER(gSeverityLogger, SeverityLogger)
161 
162 // Lifetime of Logger is each call to LOG().
163 class Logger {
164  bool const is_channel_;
165  int const enum_value_;
166  // Pointers are used to minimize size of inline objects.
167  std::unique_ptr<boost::log::record> record_;
168  std::unique_ptr<boost::log::record_ostream> stream_;
169 
170  public:
171  Logger(Channel);
172  Logger(Severity);
173  Logger(Logger&&) = default;
174  ~Logger();
175  operator bool() const;
176  // Must check operator bool() first before calling stream().
177  boost::log::record_ostream& stream(char const* file, int line);
178 };
179 
181  extern bool g_any_active_channels;
182  return g_any_active_channels;
183 }
184 
185 inline bool fast_logging_check(Severity severity) {
187  return g_min_active_severity <= severity;
188 }
189 
190 // These macros risk inadvertent else-matching to the if statements,
191 // which are fortunately prevented by our clang-tidy requirements.
192 // These can be changed to for/while loops with slight performance degradation.
193 
194 #define LOG(tag) \
195  if (logger::fast_logging_check(logger::tag)) \
196  if (auto _omnisci_logger_ = logger::Logger(logger::tag)) \
197  _omnisci_logger_.stream(__FILE__, __LINE__)
198 
199 #define LOGGING(tag) logger::fast_logging_check(logger::tag)
200 
201 #define VLOGGING(n) logger::fast_logging_check(logger::DEBUG##n)
202 
203 #define CHECK(condition) \
204  if (BOOST_UNLIKELY(!(condition))) \
205  LOG(FATAL) << "Check failed: " #condition " "
206 
207 #define CHECK_OP(OP, x, y) \
208  if (std::string* fatal_msg = logger::Check##OP(x, y, #x, #y)) \
209  LOG(FATAL) << *std::unique_ptr<std::string>(fatal_msg)
210 
211 #define CHECK_EQ(x, y) CHECK_OP(EQ, x, y)
212 #define CHECK_NE(x, y) CHECK_OP(NE, x, y)
213 #define CHECK_LT(x, y) CHECK_OP(LT, x, y)
214 #define CHECK_LE(x, y) CHECK_OP(LE, x, y)
215 #define CHECK_GT(x, y) CHECK_OP(GT, x, y)
216 #define CHECK_GE(x, y) CHECK_OP(GE, x, y)
217 
218 template <typename X, typename Y>
219 BOOST_NOINLINE std::string* check_failed(X const& x,
220  Y const& y,
221  char const* xstr,
222  char const* ystr,
223  char const* op_str) {
224  std::stringstream ss;
225  ss << "Check failed: " << xstr << op_str << ystr << " (" << x << op_str << y << ") ";
226  return new std::string(ss.str()); // Deleted by CHECK_OP macro.
227 }
228 
229 // Complexity comes from requirement that x and y be evaluated only once.
230 #define OMINSCI_CHECKOP_FUNCTION(name, op) \
231  template <typename X, typename Y> \
232  inline std::string* Check##name( \
233  X const& x, Y const& y, char const* xstr, char const* ystr) { \
234  if (BOOST_LIKELY(x op y)) \
235  return nullptr; \
236  else \
237  return logger::check_failed(x, y, xstr, ystr, " " #op " "); \
238  }
245 #undef OMINSCI_CHECKOP_FUNCTION
246 
247 #define UNREACHABLE() LOG(FATAL) << "UNREACHABLE "
248 
249 #else // __CUDACC__
250 
251 // Provided for backward compatibility to allow code to compile.
252 // No logging is actually done, since cuda code should not log.
253 template <Severity severity>
254 class NullLogger {
255  public:
256  NullLogger() {
257  if /*constexpr*/ (severity == Severity::FATAL) {
258  abort();
259  }
260  }
261  template <typename T>
262 #ifndef SUPPRESS_NULL_LOGGER_DEPRECATION_WARNINGS
263  [[deprecated]]
264 #endif
265  // If you are here because of a deprecation warning, that is because the code
266  // is attempting to log something in cuda (e.g. CHECK macros). It should
267  // probably be fixed there.
268  NullLogger&
269  operator<<(const T&) {
270  return *this;
271  }
272 };
273 
274 #define LOG(severity) logger::NullLogger<logger::Severity::severity>()
275 
276 #define LOGGING(tag) false
277 
278 #define VLOGGING(n) false
279 
280 #define CHECK(condition) LOG_IF(FATAL, !(condition))
281 
282 #define CHECK_EQ(x, y) CHECK((x) == (y))
283 #define CHECK_NE(x, y) CHECK((x) != (y))
284 #define CHECK_LT(x, y) CHECK((x) < (y))
285 #define CHECK_LE(x, y) CHECK((x) <= (y))
286 #define CHECK_GT(x, y) CHECK((x) > (y))
287 #define CHECK_GE(x, y) CHECK((x) >= (y))
288 
289 #define UNREACHABLE() LOG(FATAL)
290 
291 #endif // __CUDACC__
292 
293 #define LOG_IF(severity, condition) \
294  if (condition) \
295  LOG(severity)
296 
297 #define VLOG(n) LOG(DEBUG##n)
298 
299 class Duration;
300 
301 class DebugTimer {
303 
304  public:
305  DebugTimer(Severity, char const* file, int line, char const* name);
306  ~DebugTimer();
307  void stop();
308  // json is returned only when called on the root DurationTree.
309  std::string stopAndGetJson();
310 };
311 
312 using ThreadId = uint64_t;
313 
314 void debug_timer_new_thread(ThreadId parent_thread_id);
315 
317 
318 // Typical usage: auto timer = DEBUG_TIMER(__func__);
319 #define DEBUG_TIMER(name) logger::DebugTimer(logger::INFO, __FILE__, __LINE__, name)
320 
321 // This MUST NOT be called more than once per thread, otherwise a failed CHECK() occurs.
322 // Best practice is to call it from the point where the new thread is spawned.
323 // Beware of threads that are re-used.
324 #define DEBUG_TIMER_NEW_THREAD(parent_thread_id) \
325  do { \
326  if (g_enable_debug_timer) \
327  logger::debug_timer_new_thread(parent_thread_id); \
328  } while (false)
329 
330 } // namespace logger
331 
332 #endif // SHARED_LOGGER_H
void debug_timer_new_thread(ThreadId parent_thread_id)
Definition: Logger.cpp:721
std::unique_ptr< boost::program_options::options_description > options_
Definition: Logger.h:116
int const enum_value_
Definition: Logger.h:165
constexpr std::array< char const *, 3 > ChannelNames
Definition: Logger.h:69
size_t min_free_space_
Definition: Logger.h:129
std::set< Channel > Channels
Definition: Logger.h:109
std::string stopAndGetJson()
Definition: Logger.cpp:708
boost::log::sources::channel_logger_mt< Channel > ChannelLogger
Definition: Logger.h:156
std::unique_ptr< boost::log::record > record_
Definition: Logger.h:167
LogOptions(char const *argv0)
Definition: Logger.cpp:66
std::string base_path_
Definition: Logger.h:113
bool g_enable_debug_timer
Definition: Logger.cpp:17
Channel
Definition: Logger.h:67
string name
Definition: setup.in.py:72
Duration * duration_
Definition: Logger.h:302
void set_once_fatal_func(FatalFunc fatal_func)
Definition: Logger.cpp:307
constexpr std::array< char, 3 > ChannelSymbols
Definition: Logger.h:71
void(*)() noexcept FatalFunc
Definition: Logger.h:153
boost::program_options::options_description const & get_options() const
Definition: Logger.cpp:79
constexpr std::array< char, 8 > SeveritySymbols
Definition: Logger.h:100
Channels channels_
Definition: Logger.h:126
bool const is_channel_
Definition: Logger.h:164
bool fast_logging_check(Channel)
Definition: Logger.h:180
void init(LogOptions const &log_opts)
Definition: Logger.cpp:280
Severity g_min_active_severity
Definition: Logger.cpp:278
DebugTimer(Severity, char const *file, int line, char const *name)
Definition: Logger.cpp:692
#define OMINSCI_CHECKOP_FUNCTION(name, op)
Definition: Logger.h:230
bool rotate_daily_
Definition: Logger.h:130
BOOST_NOINLINE std::string * check_failed(X const &x, Y const &y, char const *xstr, char const *ystr, char const *op_str)
Definition: Logger.h:219
std::string symlink_
Definition: Logger.h:123
std::unique_ptr< boost::log::record_ostream > stream_
Definition: Logger.h:168
Severity
Definition: Logger.h:79
boost::log::formatting_ostream & operator<<(boost::log::formatting_ostream &strm, boost::log::to_log_manip< Channel, tag::channel > const &manip)
Definition: Logger.cpp:193
void shutdown()
Definition: Logger.cpp:314
constexpr std::array< char const *, 8 > SeverityNames
Definition: Logger.h:91
uint64_t ThreadId
Definition: Logger.h:312
Severity severity_
Definition: Logger.h:124
bool g_any_active_channels
Definition: Logger.cpp:277
ThreadId thread_id()
Definition: Logger.cpp:732
boost::log::sources::severity_logger_mt< Severity > SeverityLogger
Definition: Logger.h:159
std::string file_name_pattern_
Definition: Logger.h:122
size_t rotation_size_
Definition: Logger.h:131
void set_base_path(std::string const &base_path)
Definition: Logger.cpp:93
boost::filesystem::path full_log_dir() const
Definition: Logger.cpp:75
size_t max_files_
Definition: Logger.h:128
Severity severity_clog_
Definition: Logger.h:125
boost::filesystem::path log_dir_
Definition: Logger.h:120
void set_options()
Definition: Logger.cpp:98
void parse_command_line(int, char const *const *)
Definition: Logger.cpp:84