OmniSciDB  04ee39c94c
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 
55 namespace logger {
56 
57 // Channel, ChannelNames, and ChannelSymbols must be updated together.
58 enum Channel { IR = 0, PTX, _NCHANNELS };
59 
60 constexpr std::array<char const*, 2> ChannelNames{"IR", "PTX"};
61 
62 constexpr std::array<char, 2> ChannelSymbols{'R', 'P'};
63 
64 static_assert(Channel::_NCHANNELS == ChannelNames.size(),
65  "Size of ChannelNames must equal number of Channels.");
66 static_assert(Channel::_NCHANNELS == ChannelSymbols.size(),
67  "Size of ChannelSymbols must equal number of Channels.");
68 
69 // Severity, SeverityNames, and SeveritySymbols must be updated together.
70 enum Severity {
71  DEBUG4 = 0,
79  _NSEVERITIES // number of severity levels
80 };
81 
82 constexpr std::array<char const*, 8> SeverityNames{"DEBUG4",
83  "DEBUG3",
84  "DEBUG2",
85  "DEBUG1",
86  "INFO",
87  "WARNING",
88  "ERROR",
89  "FATAL"};
90 
91 constexpr std::array<char, 8> SeveritySymbols{'4', '3', '2', '1', 'I', 'W', 'E', 'F'};
92 
93 static_assert(Severity::_NSEVERITIES == SeverityNames.size(),
94  "Size of SeverityNames must equal number of Severity levels.");
95 static_assert(Severity::_NSEVERITIES == SeveritySymbols.size(),
96  "Size of SeveritySymbols must equal number of Severity levels.");
97 
98 #ifndef __CUDACC__
99 
100 using Channels = std::set<Channel>;
101 
102 // Filled by boost::program_options
103 class LogOptions {
104  std::string base_path_{"."}; // ignored if log_dir_ is absolute.
105  boost::program_options::options_description options_;
106 
107  public:
108  // Initialize to default values
109  boost::filesystem::path log_dir_{"mapd_log"};
110  // file_name_pattern and symlink are prepended with base_name.
111  std::string file_name_pattern_{".{SEVERITY}.%Y%m%d-%H%M%S.log"};
112  std::string symlink_{".{SEVERITY}"};
116  bool auto_flush_{true};
117  size_t max_files_{100};
118  size_t min_free_space_{20 << 20};
119  bool rotate_daily_{true};
120  size_t rotation_size_{10 << 20};
121 
122  LogOptions(char const* argv0);
123  boost::filesystem::path full_log_dir() const;
124  boost::program_options::options_description const& get_options() const;
125  void parse_command_line(int, char const* const*);
126  void set_base_path(std::string const& base_path);
127 };
128 
129 // Execute once in main() to initialize boost loggers.
130 void init(LogOptions const&);
131 
132 // Flush all sinks.
133 // https://www.boost.org/libs/log/doc/html/log/rationale/why_crash_on_term.html
134 void shutdown();
135 
136 struct LogShutdown {
137  inline ~LogShutdown() { shutdown(); }
138 };
139 
140 // Optional pointer to function to call on LOG(FATAL).
141 using FatalFunc = void (*)();
143 
144 using ChannelLogger = boost::log::sources::channel_logger_mt<Channel>;
145 BOOST_LOG_GLOBAL_LOGGER(gChannelLogger, ChannelLogger)
146 
147 using SeverityLogger = boost::log::sources::severity_logger_mt<Severity>;
148 BOOST_LOG_GLOBAL_LOGGER(gSeverityLogger, SeverityLogger)
149 
150 // Lifetime of Logger is each call to LOG().
151 class Logger {
152  bool const is_channel_;
153  int const enum_value_;
154  // Pointers are used to minimize size of inline objects.
155  std::unique_ptr<boost::log::record> record_;
156  std::unique_ptr<boost::log::record_ostream> stream_;
157 
158  public:
159  Logger(Channel);
160  Logger(Severity);
161  Logger(Logger&&) = default;
162  ~Logger();
163  operator bool() const;
164  // Must check operator bool() first before calling stream().
165  boost::log::record_ostream& stream(char const* file, int line);
166 };
167 
169  extern bool g_any_active_channels;
170  return g_any_active_channels;
171 }
172 
173 inline bool fast_logging_check(Severity severity) {
175  return g_min_active_severity <= severity;
176 }
177 
178 // These macros risk inadvertent else-matching to the if statements,
179 // which are fortunately prevented by our clang-tidy requirements.
180 // These can be changed to for/while loops with slight performance degradation.
181 
182 #define LOG(tag) \
183  if (logger::fast_logging_check(logger::tag)) \
184  if (auto _omnisci_logger_ = logger::Logger(logger::tag)) \
185  _omnisci_logger_.stream(__FILE__, __LINE__)
186 
187 #define CHECK(condition) \
188  if (BOOST_UNLIKELY(!(condition))) \
189  LOG(FATAL) << "Check failed: " #condition " "
190 
191 #define CHECK_OP(OP, x, y) \
192  if (std::string* fatal_msg = logger::Check##OP(x, y, #x, #y)) \
193  LOG(FATAL) << *std::unique_ptr<std::string>(fatal_msg)
194 
195 #define CHECK_EQ(x, y) CHECK_OP(EQ, x, y)
196 #define CHECK_NE(x, y) CHECK_OP(NE, x, y)
197 #define CHECK_LT(x, y) CHECK_OP(LT, x, y)
198 #define CHECK_LE(x, y) CHECK_OP(LE, x, y)
199 #define CHECK_GT(x, y) CHECK_OP(GT, x, y)
200 #define CHECK_GE(x, y) CHECK_OP(GE, x, y)
201 
202 template <typename X, typename Y>
203 BOOST_NOINLINE std::string* check_failed(X const& x,
204  Y const& y,
205  char const* xstr,
206  char const* ystr,
207  char const* op_str) {
208  std::stringstream ss;
209  ss << "Check failed: " << xstr << op_str << ystr << " (" << x << op_str << y << ") ";
210  return new std::string(ss.str()); // Deleted by CHECK_OP macro.
211 }
212 
213 // Complexity comes from requirement that x and y be evaluated only once.
214 #define OMINSCI_CHECKOP_FUNCTION(name, op) \
215  template <typename X, typename Y> \
216  inline std::string* Check##name( \
217  X const& x, Y const& y, char const* xstr, char const* ystr) { \
218  if (BOOST_LIKELY(x op y)) \
219  return nullptr; \
220  else \
221  return logger::check_failed(x, y, xstr, ystr, " " #op " "); \
222  }
229 #undef OMINSCI_CHECKOP_FUNCTION
230 
231 #define UNREACHABLE() LOG(FATAL) << "UNREACHABLE "
232 
233 #else // __CUDACC__
234 
235 // Provided for backward compatibility to allow code to compile.
236 // No logging is actually done, since cuda code should not log.
237 template <Severity severity>
238 class NullLogger {
239  public:
240  NullLogger() {
241  if /*constexpr*/ (severity == Severity::FATAL) {
242  abort();
243  }
244  }
245  template <typename T>
246 #ifndef SUPPRESS_NULL_LOGGER_DEPRECATION_WARNINGS
247  [[deprecated]]
248 #endif
249  // If you are here because of a deprecation warning, that is because the code
250  // is attempting to log something in cuda (e.g. CHECK macros). It should
251  // probably be fixed there.
252  NullLogger&
253  operator<<(const T&) {
254  return *this;
255  }
256 };
257 
258 #define LOG(severity) logger::NullLogger<logger::Severity::severity>()
259 
260 #define CHECK(condition) LOG_IF(FATAL, !(condition))
261 
262 #define CHECK_EQ(x, y) CHECK((x) == (y))
263 #define CHECK_NE(x, y) CHECK((x) != (y))
264 #define CHECK_LT(x, y) CHECK((x) < (y))
265 #define CHECK_LE(x, y) CHECK((x) <= (y))
266 #define CHECK_GT(x, y) CHECK((x) > (y))
267 #define CHECK_GE(x, y) CHECK((x) >= (y))
268 
269 #define UNREACHABLE() LOG(FATAL)
270 
271 #endif // __CUDACC__
272 
273 #define LOG_IF(severity, condition) \
274  if (condition) \
275  LOG(severity)
276 
277 #define VLOG(n) LOG(DEBUG##n)
278 
279 } // namespace logger
280 
281 #endif // SHARED_LOGGER_H
int const enum_value_
Definition: Logger.h:153
size_t min_free_space_
Definition: Logger.h:118
std::set< Channel > Channels
Definition: Logger.h:100
boost::log::sources::channel_logger_mt< Channel > ChannelLogger
Definition: Logger.h:144
std::unique_ptr< boost::log::record > record_
Definition: Logger.h:155
LogOptions(char const *argv0)
Definition: Logger.cpp:54
std::string base_path_
Definition: Logger.h:104
Channel
Definition: Logger.h:58
boost::program_options::options_description const & get_options() const
Definition: Logger.cpp:112
void set_once_fatal_func(FatalFunc fatal_func)
Definition: Logger.cpp:287
constexpr std::array< char, 8 > SeveritySymbols
Definition: Logger.h:91
Channels channels_
Definition: Logger.h:115
bool const is_channel_
Definition: Logger.h:152
constexpr std::array< char, 2 > ChannelSymbols
Definition: Logger.h:62
bool fast_logging_check(Channel)
Definition: Logger.h:168
void init(LogOptions const &log_opts)
Definition: Logger.cpp:260
Severity g_min_active_severity
Definition: Logger.cpp:258
#define OMINSCI_CHECKOP_FUNCTION(name, op)
Definition: Logger.h:214
bool rotate_daily_
Definition: Logger.h:119
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:203
std::string symlink_
Definition: Logger.h:112
std::unique_ptr< boost::log::record_ostream > stream_
Definition: Logger.h:156
Severity
Definition: Logger.h:70
boost::log::formatting_ostream & operator<<(boost::log::formatting_ostream &strm, boost::log::to_log_manip< Channel, tag::channel > const &manip)
Definition: Logger.cpp:173
void shutdown()
Definition: Logger.cpp:294
constexpr std::array< char const *, 8 > SeverityNames
Definition: Logger.h:82
boost::filesystem::path full_log_dir() const
Definition: Logger.cpp:108
Severity severity_
Definition: Logger.h:113
constexpr std::array< char const *, 2 > ChannelNames
Definition: Logger.h:60
bool g_any_active_channels
Definition: Logger.cpp:257
boost::program_options::options_description options_
Definition: Logger.h:105
boost::log::sources::severity_logger_mt< Severity > SeverityLogger
Definition: Logger.h:147
std::string file_name_pattern_
Definition: Logger.h:111
size_t rotation_size_
Definition: Logger.h:120
void set_base_path(std::string const &base_path)
Definition: Logger.cpp:126
size_t max_files_
Definition: Logger.h:117
void(*)() FatalFunc
Definition: Logger.h:141
Severity severity_clog_
Definition: Logger.h:114
boost::filesystem::path log_dir_
Definition: Logger.h:109
void parse_command_line(int, char const *const *)
Definition: Logger.cpp:117