OmniSciDB  340b00dbf6
 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 #ifndef __CUDACC__
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 extern bool g_enable_debug_timer;
57 
58 namespace logger {
59 
60 // Channel, ChannelNames, and ChannelSymbols must be updated together.
61 enum Channel { IR = 0, PTX, ASM, _NCHANNELS };
62 
63 constexpr std::array<char const*, 3> ChannelNames{"IR", "PTX", "ASM"};
64 
65 constexpr std::array<char, 3> ChannelSymbols{'R', 'P', 'A'};
66 
67 static_assert(Channel::_NCHANNELS == ChannelNames.size(),
68  "Size of ChannelNames must equal number of Channels.");
69 static_assert(Channel::_NCHANNELS == ChannelSymbols.size(),
70  "Size of ChannelSymbols must equal number of Channels.");
71 
72 // Severity, SeverityNames, and SeveritySymbols must be updated together.
73 enum Severity {
74  DEBUG4 = 0,
82  _NSEVERITIES // number of severity levels
83 };
84 
85 constexpr std::array<char const*, 8> SeverityNames{"DEBUG4",
86  "DEBUG3",
87  "DEBUG2",
88  "DEBUG1",
89  "INFO",
90  "WARNING",
91  "ERROR",
92  "FATAL"};
93 
94 constexpr std::array<char, 8> SeveritySymbols{'4', '3', '2', '1', 'I', 'W', 'E', 'F'};
95 
96 static_assert(Severity::_NSEVERITIES == SeverityNames.size(),
97  "Size of SeverityNames must equal number of Severity levels.");
98 static_assert(Severity::_NSEVERITIES == SeveritySymbols.size(),
99  "Size of SeveritySymbols must equal number of Severity levels.");
100 
101 #ifndef __CUDACC__
102 
103 using Channels = std::set<Channel>;
104 
105 // Filled by boost::program_options
106 class LogOptions {
107  std::string base_path_{"."}; // ignored if log_dir_ is absolute.
108  // boost::program_options::options_description is not copyable so unique_ptr
109  // allows for modification after initialization (e.g. changing default values.)
110  std::unique_ptr<boost::program_options::options_description> options_;
111 
112  public:
113  // Initialize to default values
114  boost::filesystem::path log_dir_{"mapd_log"};
115  // file_name_pattern and symlink are prepended with base_name.
116  std::string file_name_pattern_{".{SEVERITY}.%Y%m%d-%H%M%S.log"};
117  std::string symlink_{".{SEVERITY}"};
121  bool auto_flush_{true};
122  size_t max_files_{100};
123  size_t min_free_space_{20 << 20};
124  bool rotate_daily_{true};
125  size_t rotation_size_{10 << 20};
126 
127  LogOptions(char const* argv0);
128  boost::filesystem::path full_log_dir() const;
129  boost::program_options::options_description const& get_options() const;
130  void parse_command_line(int, char const* const*);
131  void set_base_path(std::string const& base_path);
132  void set_options();
133 };
134 
135 // Execute once in main() to initialize boost loggers.
136 void init(LogOptions const&);
137 
138 // Flush all sinks.
139 // https://www.boost.org/libs/log/doc/html/log/rationale/why_crash_on_term.html
140 void shutdown();
141 
142 struct LogShutdown {
143  inline ~LogShutdown() { shutdown(); }
144 };
145 
146 // Optional pointer to function to call on LOG(FATAL).
147 using FatalFunc = void (*)() noexcept;
149 
150 using ChannelLogger = boost::log::sources::channel_logger_mt<Channel>;
151 BOOST_LOG_GLOBAL_LOGGER(gChannelLogger, ChannelLogger)
152 
153 using SeverityLogger = boost::log::sources::severity_logger_mt<Severity>;
154 BOOST_LOG_GLOBAL_LOGGER(gSeverityLogger, SeverityLogger)
155 
156 // Lifetime of Logger is each call to LOG().
157 class Logger {
158  bool const is_channel_;
159  int const enum_value_;
160  // Pointers are used to minimize size of inline objects.
161  std::unique_ptr<boost::log::record> record_;
162  std::unique_ptr<boost::log::record_ostream> stream_;
163 
164  public:
165  Logger(Channel);
166  Logger(Severity);
167  Logger(Logger&&) = default;
168  ~Logger();
169  operator bool() const;
170  // Must check operator bool() first before calling stream().
171  boost::log::record_ostream& stream(char const* file, int line);
172 };
173 
175  extern bool g_any_active_channels;
176  return g_any_active_channels;
177 }
178 
179 inline bool fast_logging_check(Severity severity) {
181  return g_min_active_severity <= severity;
182 }
183 
184 // These macros risk inadvertent else-matching to the if statements,
185 // which are fortunately prevented by our clang-tidy requirements.
186 // These can be changed to for/while loops with slight performance degradation.
187 
188 #define LOG(tag) \
189  if (logger::fast_logging_check(logger::tag)) \
190  if (auto _omnisci_logger_ = logger::Logger(logger::tag)) \
191  _omnisci_logger_.stream(__FILE__, __LINE__)
192 
193 #define LOGGING(tag) logger::fast_logging_check(logger::tag)
194 
195 #define VLOGGING(n) logger::fast_logging_check(logger::DEBUG##n)
196 
197 #define CHECK(condition) \
198  if (BOOST_UNLIKELY(!(condition))) \
199  LOG(FATAL) << "Check failed: " #condition " "
200 
201 #define CHECK_OP(OP, x, y) \
202  if (std::string* fatal_msg = logger::Check##OP(x, y, #x, #y)) \
203  LOG(FATAL) << *std::unique_ptr<std::string>(fatal_msg)
204 
205 #define CHECK_EQ(x, y) CHECK_OP(EQ, x, y)
206 #define CHECK_NE(x, y) CHECK_OP(NE, x, y)
207 #define CHECK_LT(x, y) CHECK_OP(LT, x, y)
208 #define CHECK_LE(x, y) CHECK_OP(LE, x, y)
209 #define CHECK_GT(x, y) CHECK_OP(GT, x, y)
210 #define CHECK_GE(x, y) CHECK_OP(GE, x, y)
211 
212 template <typename X, typename Y>
213 BOOST_NOINLINE std::string* check_failed(X const& x,
214  Y const& y,
215  char const* xstr,
216  char const* ystr,
217  char const* op_str) {
218  std::stringstream ss;
219  ss << "Check failed: " << xstr << op_str << ystr << " (" << x << op_str << y << ") ";
220  return new std::string(ss.str()); // Deleted by CHECK_OP macro.
221 }
222 
223 // Complexity comes from requirement that x and y be evaluated only once.
224 #define OMINSCI_CHECKOP_FUNCTION(name, op) \
225  template <typename X, typename Y> \
226  inline std::string* Check##name( \
227  X const& x, Y const& y, char const* xstr, char const* ystr) { \
228  if (BOOST_LIKELY(x op y)) \
229  return nullptr; \
230  else \
231  return logger::check_failed(x, y, xstr, ystr, " " #op " "); \
232  }
239 #undef OMINSCI_CHECKOP_FUNCTION
240 
241 #define UNREACHABLE() LOG(FATAL) << "UNREACHABLE "
242 
243 #else // __CUDACC__
244 
245 // Provided for backward compatibility to allow code to compile.
246 // No logging is actually done, since cuda code should not log.
247 template <Severity severity>
248 class NullLogger {
249  public:
250  NullLogger() {
251  if /*constexpr*/ (severity == Severity::FATAL) {
252  abort();
253  }
254  }
255  template <typename T>
256 #ifndef SUPPRESS_NULL_LOGGER_DEPRECATION_WARNINGS
257  [[deprecated]]
258 #endif
259  // If you are here because of a deprecation warning, that is because the code
260  // is attempting to log something in cuda (e.g. CHECK macros). It should
261  // probably be fixed there.
262  NullLogger&
263  operator<<(const T&) {
264  return *this;
265  }
266 };
267 
268 #define LOG(severity) logger::NullLogger<logger::Severity::severity>()
269 
270 #define LOGGING(tag) false
271 
272 #define VLOGGING(n) false
273 
274 #define CHECK(condition) LOG_IF(FATAL, !(condition))
275 
276 #define CHECK_EQ(x, y) CHECK((x) == (y))
277 #define CHECK_NE(x, y) CHECK((x) != (y))
278 #define CHECK_LT(x, y) CHECK((x) < (y))
279 #define CHECK_LE(x, y) CHECK((x) <= (y))
280 #define CHECK_GT(x, y) CHECK((x) > (y))
281 #define CHECK_GE(x, y) CHECK((x) >= (y))
282 
283 #define UNREACHABLE() LOG(FATAL)
284 
285 #endif // __CUDACC__
286 
287 #define LOG_IF(severity, condition) \
288  if (condition) \
289  LOG(severity)
290 
291 #define VLOG(n) LOG(DEBUG##n)
292 
293 class Duration;
294 
295 class DebugTimer {
297 
298  public:
299  DebugTimer(Severity, char const* file, int line, char const* name);
300  ~DebugTimer();
301  void stop();
302  // json is returned only when called on the root DurationTree.
303  std::string stopAndGetJson();
304 };
305 
306 using ThreadId = uint64_t;
307 
308 void debug_timer_new_thread(ThreadId parent_thread_id);
309 
311 
312 // Typical usage: auto timer = DEBUG_TIMER(__func__);
313 #define DEBUG_TIMER(name) logger::DebugTimer(logger::INFO, __FILE__, __LINE__, name)
314 
315 // This MUST NOT be called more than once per thread, otherwise a failed CHECK() occurs.
316 // Best practice is to call it from the point where the new thread is spawned.
317 // Beware of threads that are re-used.
318 #define DEBUG_TIMER_NEW_THREAD(parent_thread_id) \
319  do { \
320  if (g_enable_debug_timer) \
321  logger::debug_timer_new_thread(parent_thread_id); \
322  } while (false)
323 
324 } // namespace logger
325 
326 #endif // SHARED_LOGGER_H
void debug_timer_new_thread(ThreadId parent_thread_id)
Definition: Logger.cpp:720
std::unique_ptr< boost::program_options::options_description > options_
Definition: Logger.h:110
int const enum_value_
Definition: Logger.h:159
constexpr std::array< char const *, 3 > ChannelNames
Definition: Logger.h:63
size_t min_free_space_
Definition: Logger.h:123
std::set< Channel > Channels
Definition: Logger.h:103
std::string stopAndGetJson()
Definition: Logger.cpp:707
boost::log::sources::channel_logger_mt< Channel > ChannelLogger
Definition: Logger.h:150
std::unique_ptr< boost::log::record > record_
Definition: Logger.h:161
LogOptions(char const *argv0)
Definition: Logger.cpp:66
tuple line
Definition: parse_ast.py:10
std::string base_path_
Definition: Logger.h:107
bool g_enable_debug_timer
Definition: Logger.cpp:17
Channel
Definition: Logger.h:61
Duration * duration_
Definition: Logger.h:296
void set_once_fatal_func(FatalFunc fatal_func)
Definition: Logger.cpp:307
constexpr std::array< char, 3 > ChannelSymbols
Definition: Logger.h:65
void(*)() noexcept FatalFunc
Definition: Logger.h:147
boost::program_options::options_description const & get_options() const
Definition: Logger.cpp:79
constexpr std::array< char, 8 > SeveritySymbols
Definition: Logger.h:94
Channels channels_
Definition: Logger.h:120
bool const is_channel_
Definition: Logger.h:158
bool fast_logging_check(Channel)
Definition: Logger.h:174
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:691
#define OMINSCI_CHECKOP_FUNCTION(name, op)
Definition: Logger.h:224
bool rotate_daily_
Definition: Logger.h:124
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:213
std::string symlink_
Definition: Logger.h:117
std::unique_ptr< boost::log::record_ostream > stream_
Definition: Logger.h:162
Severity
Definition: Logger.h:73
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:85
uint64_t ThreadId
Definition: Logger.h:306
Severity severity_
Definition: Logger.h:118
bool g_any_active_channels
Definition: Logger.cpp:277
ThreadId thread_id()
Definition: Logger.cpp:731
boost::log::sources::severity_logger_mt< Severity > SeverityLogger
Definition: Logger.h:153
std::string file_name_pattern_
Definition: Logger.h:116
size_t rotation_size_
Definition: Logger.h:125
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:122
string name
Definition: setup.py:35
Severity severity_clog_
Definition: Logger.h:119
boost::filesystem::path log_dir_
Definition: Logger.h:114
void set_options()
Definition: Logger.cpp:98
void parse_command_line(int, char const *const *)
Definition: Logger.cpp:84