OmniSciDB  a5dc49c757
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Logger.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2022 HEAVY.AI, 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 
18 
19 #ifndef __CUDACC__
20 
21 #include "Logger.h"
22 
23 #include <rapidjson/document.h>
24 #include <rapidjson/stringbuffer.h>
25 #include <rapidjson/writer.h>
26 
27 #include <boost/algorithm/string.hpp>
28 #include <boost/config.hpp>
29 #include <boost/filesystem.hpp>
30 #include <boost/log/common.hpp>
31 #include <boost/log/expressions.hpp>
32 #include <boost/log/sinks.hpp>
33 #include <boost/log/sources/global_logger_storage.hpp>
34 #include <boost/log/sources/logger.hpp>
35 #include <boost/log/sources/severity_feature.hpp>
36 #include <boost/log/support/date_time.hpp>
37 #include <boost/log/utility/setup.hpp>
38 #include <boost/phoenix.hpp>
39 #include <boost/program_options.hpp>
40 #include <boost/smart_ptr/weak_ptr.hpp>
41 #include <boost/variant.hpp>
42 
43 #include <atomic>
44 #include <cstdlib>
45 #include <iostream>
46 #include <mutex>
47 #include <regex>
48 
49 #include "Shared/SysDefinitions.h"
50 #include "Shared/nvtx_helpers.h"
51 
52 namespace logger {
53 using ChannelLogger = boost::log::sources::channel_logger_mt<Channel>;
54 BOOST_LOG_GLOBAL_LOGGER(gChannelLogger_IR, ChannelLogger)
55 BOOST_LOG_GLOBAL_LOGGER(gChannelLogger_PTX, ChannelLogger)
56 BOOST_LOG_GLOBAL_LOGGER(gChannelLogger_ASM, ChannelLogger)
57 BOOST_LOG_GLOBAL_LOGGER(gChannelLogger_EXECUTOR, ChannelLogger)
58 
59 using SeverityLogger = boost::log::sources::severity_logger_mt<Severity>;
60 BOOST_LOG_GLOBAL_LOGGER(gSeverityLogger, SeverityLogger)
61 
62 namespace attr = boost::log::attributes;
63 namespace expr = boost::log::expressions;
64 namespace fs = boost::filesystem;
65 namespace keywords = boost::log::keywords;
66 namespace sinks = boost::log::sinks;
67 namespace sources = boost::log::sources;
68 namespace po = boost::program_options;
69 
70 BOOST_LOG_ATTRIBUTE_KEYWORD(process_id, "ProcessID", attr::current_process_id::value_type)
71 BOOST_LOG_ATTRIBUTE_KEYWORD(channel, "Channel", Channel)
72 BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", Severity)
73 
74 BOOST_LOG_GLOBAL_LOGGER_CTOR_ARGS(gChannelLogger_IR,
76  (keywords::channel = IR))
77 BOOST_LOG_GLOBAL_LOGGER_CTOR_ARGS(gChannelLogger_PTX,
78  ChannelLogger,
79  (keywords::channel = PTX))
80 BOOST_LOG_GLOBAL_LOGGER_CTOR_ARGS(gChannelLogger_ASM,
81  ChannelLogger,
82  (keywords::channel = ASM))
83 BOOST_LOG_GLOBAL_LOGGER_CTOR_ARGS(gChannelLogger_EXECUTOR,
84  ChannelLogger,
85  (keywords::channel = EXECUTOR))
86 BOOST_LOG_GLOBAL_LOGGER_DEFAULT(gSeverityLogger, SeverityLogger)
87 
88 // Return last component of path
89 std::string filename(char const* path) {
90  return fs::path(path).filename().string();
91 }
92 
93 LogOptions::LogOptions(char const* argv0)
94  : log_dir_(std::make_unique<fs::path>(shared::kDefaultLogDirName)) {
95  // Log file base_name matches name of program.
96  std::string const base_name =
97  argv0 == nullptr ? std::string("heavydb") : filename(argv0);
98  file_name_pattern_ = base_name + file_name_pattern_;
99  symlink_ = base_name + symlink_;
100  set_options();
101 }
102 
103 // Needed to allow forward declarations within std::unique_ptr.
104 LogOptions::~LogOptions() {}
105 
106 fs::path LogOptions::full_log_dir() const {
107  return log_dir_->has_root_directory() ? *log_dir_ : base_path_ / *log_dir_;
108 }
109 
110 po::options_description const& LogOptions::get_options() const {
111  return *options_;
112 }
113 
114 // Typical usage calls either get_options() or parse_command_line() but not both.
115 void LogOptions::parse_command_line(int argc, char const* const* argv) {
116  po::variables_map vm;
117  po::store(
118  po::command_line_parser(argc, argv).options(*options_).allow_unregistered().run(),
119  vm);
120  po::notify(vm); // Sets LogOptions member variables.
121 }
122 
123 // Must be called before init() to take effect.
124 void LogOptions::set_base_path(std::string const& base_path) {
125  base_path_ = base_path;
126 }
127 
128 // May be called to update default values based on updated member variables.
129 void LogOptions::set_options() {
130  options_ = std::make_unique<boost::program_options::options_description>("Logging");
131  std::string const channels =
132  std::accumulate(ChannelNames.cbegin() + 1,
133  ChannelNames.cend(),
134  std::string(ChannelNames.front()),
135  [](auto a, auto channel) { return a + ' ' + channel; });
136  // Filter out DEBUG[1-4] severities from --help options
137  std::string severities;
138  for (auto const& name : SeverityNames) {
139  if (!boost::algorithm::starts_with(name, "DEBUG")) {
140  (severities += (severities.empty() ? "" : " ")) += name;
141  }
142  }
143  options_->add_options()(
144  "log-directory",
145  po::value<fs::path>(&*log_dir_)->default_value(*log_dir_),
146  "Logging directory. May be relative to data directory, or absolute.");
147  options_->add_options()(
148  "log-file-name",
149  po::value<std::string>(&file_name_pattern_)->default_value(file_name_pattern_),
150  "Log file name relative to log-directory.");
151  options_->add_options()("log-symlink",
152  po::value<std::string>(&symlink_)->default_value(symlink_),
153  "Symlink to active log.");
154  options_->add_options()("log-severity",
155  po::value<Severity>(&severity_)->default_value(severity_),
156  ("Log to file severity level: " + severities).c_str());
157  options_->add_options()(
158  "log-severity-clog",
159  po::value<Severity>(&severity_clog_)->default_value(severity_clog_),
160  ("Log to console severity level: " + severities).c_str());
161  options_->add_options()("log-channels",
162  po::value<Channels>(&channels_)->default_value(channels_),
163  ("Log channel debug info: " + channels).c_str());
164  options_->add_options()("log-auto-flush",
165  po::value<bool>(&auto_flush_)->default_value(auto_flush_),
166  "Flush logging buffer to file after each message.");
167  options_->add_options()("log-max-files",
168  po::value<size_t>(&max_files_)->default_value(max_files_),
169  "Maximum number of log files to keep.");
170  options_->add_options()(
171  "log-min-free-space",
172  po::value<size_t>(&min_free_space_)->default_value(min_free_space_),
173  "Minimum number of bytes left on device before oldest log files are deleted.");
174  options_->add_options()("log-rotate-daily",
175  po::value<bool>(&rotate_daily_)->default_value(rotate_daily_),
176  "Start new log files at midnight.");
177  options_->add_options()(
178  "log-rotation-size",
179  po::value<size_t>(&rotation_size_)->default_value(rotation_size_),
180  "Maximum file size in bytes before new log files are started.");
181 }
182 
183 template <typename TAG>
184 std::string replace_braces(std::string const& str, TAG const tag) {
185  constexpr std::regex::flag_type flags = std::regex::ECMAScript | std::regex::optimize;
186  static std::regex const regex(R"(\{SEVERITY\})", flags);
187  if constexpr (std::is_same<TAG, Channel>::value) {
188  return std::regex_replace(str, regex, ChannelNames[tag]);
189  } else {
190  return std::regex_replace(str, regex, SeverityNames[tag]);
191  }
192 }
193 
194 // Print decimal value for process_id (14620) instead of hex (0x0000391c)
195 boost::log::attributes::current_process_id::value_type::native_type get_native_process_id(
196  boost::log::value_ref<boost::log::attributes::current_process_id::value_type,
197  tag::process_id> const& pid) {
198  return pid ? pid->native_id() : 0;
199 }
200 
201 template <typename SINK>
202 sinks::text_file_backend::open_handler_type create_or_replace_symlink(
203  boost::weak_ptr<SINK> weak_ptr,
204  std::string&& symlink) {
205  return [weak_ptr,
206  symlink = std::move(symlink)](sinks::text_file_backend::stream_type& stream) {
207  if (boost::shared_ptr<SINK> sink = weak_ptr.lock()) {
209  fs::path const& file_name = sink->locked_backend()->get_current_file_name();
210  fs::path const symlink_path = file_name.parent_path() / symlink;
211  fs::remove(symlink_path, ec);
212  if (ec) {
213  stream << filename(__FILE__) << ':' << __LINE__ << ' ' << ec.message() << '\n';
214  }
215  fs::create_symlink(file_name.filename(), symlink_path, ec);
216  if (ec) {
217  stream << filename(__FILE__) << ':' << __LINE__ << ' ' << ec.message() << '\n';
218  }
219  }
220  };
221 }
222 
223 // Remove symlink if referent file does not exist.
224 struct RemoveDeadLink {
225  void operator()(fs::path const& symlink_path) const {
227  bool const exists = fs::exists(symlink_path, ec);
228  // If the symlink or referent file doesn't exist, ec.message() = "No such file or
229  // directory" even though it's not really an error, so we don't bother checking it.
230  if (!exists) {
231  fs::remove(symlink_path, ec);
232  if (ec) {
233  std::cerr << "Error removing " << symlink_path << ": " << ec.message()
234  << std::endl;
235  }
236  }
237  }
238 };
239 
240 // Custom file collector that also deletes invalid symlinks.
241 class Collector : public sinks::file::collector {
242  boost::shared_ptr<sinks::file::collector> collector_;
243  std::vector<fs::path> symlink_paths_;
244 
245  public:
246  Collector(fs::path const& full_log_dir, LogOptions const& log_opts)
247  : collector_(sinks::file::make_collector(
248  keywords::target = full_log_dir,
249  keywords::max_files = log_opts.max_files_,
250  keywords::min_free_space = log_opts.min_free_space_)) {}
251  // Remove dead symlinks after rotated files are deleted.
252  void store_file(fs::path const& path) override {
253  collector_->store_file(path); // Deletes files that exceed rotation limits.
254  std::for_each(symlink_paths_.begin(), symlink_paths_.end(), RemoveDeadLink{});
255  }
256 #if 107900 <= BOOST_VERSION
257  bool is_in_storage(fs::path const& path) const override {
258  return collector_->is_in_storage(path);
259  }
260  sinks::file::scan_result scan_for_files(sinks::file::scan_method method,
261  fs::path const& path = fs::path()) override {
262  return collector_->scan_for_files(method, path);
263  }
264 #else
265  uintmax_t scan_for_files(sinks::file::scan_method method,
266  fs::path const& path = fs::path(),
267  unsigned* counter = nullptr) override {
268  return collector_->scan_for_files(method, path, counter);
269  }
270 #endif
271  void track_symlink(fs::path symlink_path) {
272  symlink_paths_.emplace_back(std::move(symlink_path));
273  }
274 };
275 
276 boost::log::formatting_ostream& operator<<(
277  boost::log::formatting_ostream& strm,
278  boost::log::to_log_manip<Channel, tag::channel> const& manip) {
279  return strm << ChannelSymbols[manip.get()];
280 }
281 
282 boost::log::formatting_ostream& operator<<(
283  boost::log::formatting_ostream& strm,
284  boost::log::to_log_manip<Severity, tag::severity> const& manip) {
285  return strm << SeveritySymbols[manip.get()];
286 }
287 
288 template <typename TAG, typename SINK>
289 void set_formatter(SINK& sink) {
290  if constexpr (std::is_same<TAG, Channel>::value) {
291  sink->set_formatter(
292  expr::stream << expr::format_date_time<boost::posix_time::ptime>(
293  "TimeStamp", "%Y-%m-%dT%H:%M:%S.%f")
294  << ' ' << channel << ' '
295  << boost::phoenix::bind(&get_native_process_id, process_id.or_none())
296  << ' ' << expr::smessage);
297  } else {
298  sink->set_formatter(
299  expr::stream << expr::format_date_time<boost::posix_time::ptime>(
300  "TimeStamp", "%Y-%m-%dT%H:%M:%S.%f")
301  << ' ' << severity << ' '
302  << boost::phoenix::bind(&get_native_process_id, process_id.or_none())
303  << ' ' << expr::smessage);
304  }
305 }
306 
307 template <typename FILE_SINK, typename TAG>
308 boost::shared_ptr<FILE_SINK> make_sink(boost::shared_ptr<Collector>& collector,
309  LogOptions const& log_opts,
310  fs::path const& full_log_dir,
311  TAG const tag) {
312  auto sink = boost::make_shared<FILE_SINK>(
313  keywords::file_name =
314  full_log_dir / replace_braces(log_opts.file_name_pattern_, tag),
315  keywords::auto_flush = log_opts.auto_flush_,
316  keywords::rotation_size = log_opts.rotation_size_);
317  if constexpr (std::is_same<TAG, Channel>::value) {
318  sink->set_filter(channel == static_cast<Channel>(tag));
319  set_formatter<Channel>(sink);
320  } else {
321  // INFO sink logs all other levels. Other sinks only log at their level or higher.
322  Severity const min_filter_level = static_cast<Severity>(tag) == Severity::INFO
323  ? log_opts.severity_
324  : static_cast<Severity>(tag);
325  sink->set_filter(min_filter_level <= severity);
326  set_formatter<Severity>(sink);
327  }
328  typename FILE_SINK::locked_backend_ptr backend = sink->locked_backend();
329  if (log_opts.rotate_daily_) {
330  backend->set_time_based_rotation(sinks::file::rotation_at_time_point(0, 0, 0));
331  }
332  collector->track_symlink(full_log_dir / replace_braces(log_opts.symlink_, tag));
333  backend->set_file_collector(collector);
334  backend->set_open_handler(create_or_replace_symlink(
335  boost::weak_ptr<FILE_SINK>(sink), replace_braces(log_opts.symlink_, tag)));
336  backend->scan_for_files();
337  return sink;
338 }
339 
340 // Pointer to function to optionally call on LOG(FATAL).
341 std::atomic<FatalFunc> g_fatal_func{nullptr};
342 std::once_flag g_fatal_func_flag;
343 
344 using ClogSync = sinks::synchronous_sink<sinks::text_ostream_backend>;
345 using FileSync = sinks::synchronous_sink<sinks::text_file_backend>;
346 
347 template <typename CONSOLE_SINK>
348 boost::shared_ptr<CONSOLE_SINK> make_sink(LogOptions const& log_opts) {
349  auto sink = boost::make_shared<CONSOLE_SINK>();
350  boost::shared_ptr<std::ostream> clog(&std::clog, boost::null_deleter());
351  sink->locked_backend()->add_stream(clog);
352  sink->set_filter(log_opts.severity_clog_ <= severity);
353  set_formatter<Severity>(sink);
354  return sink;
355 }
356 
357 // Locking/atomicity not needed for g_any_active_channels or g_min_active_severity
358 // as they are modifed by init() once.
361 
362 static fs::path g_log_dir_path;
363 
364 void init(LogOptions const& log_opts) {
365  boost::shared_ptr<boost::log::core> core = boost::log::core::get();
366  // boost::log::add_common_attributes(); // LineID TimeStamp ProcessID ThreadID
367  core->add_global_attribute("TimeStamp", attr::local_clock());
368  core->add_global_attribute("ProcessID", attr::current_process_id());
369  if (0 < log_opts.max_files_) {
370  fs::path const full_log_dir = log_opts.full_log_dir();
371  auto collector = boost::make_shared<Collector>(full_log_dir, log_opts);
372  bool const log_dir_was_created = fs::create_directory(full_log_dir);
373  // Don't create separate log sinks for anything less than Severity::INFO.
374  Severity const min_sink_level = std::max(Severity::INFO, log_opts.severity_);
375  for (int i = min_sink_level; i < Severity::_NSEVERITIES; ++i) {
376  Severity const level = static_cast<Severity>(i);
377  core->add_sink(make_sink<FileSync>(collector, log_opts, full_log_dir, level));
378  }
380  if (log_dir_was_created) {
381  LOG(INFO) << "Log directory(" << full_log_dir.native() << ") created.";
382  }
383  for (auto const channel : log_opts.channels_) {
384  core->add_sink(make_sink<FileSync>(collector, log_opts, full_log_dir, channel));
385  }
386  g_any_active_channels = !log_opts.channels_.empty();
387  }
388  core->add_sink(make_sink<ClogSync>(log_opts));
391  g_log_dir_path = log_opts.full_log_dir();
392 }
393 
394 void set_once_fatal_func(FatalFunc fatal_func) {
395  if (g_fatal_func.exchange(fatal_func)) {
396  throw std::runtime_error(
397  "logger::set_once_fatal_func() should not be called more than once.");
398  }
399 }
400 
401 void shutdown() {
402  static std::once_flag logger_flag;
403  std::call_once(logger_flag, []() {
404  boost::log::core::get()->remove_all_sinks();
406  });
407 }
408 
409 namespace {
410 
411 // Remove quotes if they match from beginning and end of string.
412 // Does not check for escaped quotes within string.
413 void unquote(std::string& str) {
414  if (1 < str.size() && (str.front() == '\'' || str.front() == '"') &&
415  str.front() == str.back()) {
416  str.erase(str.size() - 1, 1);
417  str.erase(0, 1);
418  }
419 }
420 
421 } // namespace
422 
423 // Used by boost::program_options when parsing enum Channel.
424 std::istream& operator>>(std::istream& in, Channels& channels) {
425  std::string line;
426  std::getline(in, line);
427  unquote(line);
428  std::regex const rex(R"(\w+)");
429  using TokenItr = std::regex_token_iterator<std::string::iterator>;
430  TokenItr const end;
431  for (TokenItr tok(line.begin(), line.end(), rex); tok != end; ++tok) {
432  auto itr = std::find(ChannelNames.cbegin(), ChannelNames.cend(), *tok);
433  if (itr == ChannelNames.cend()) {
434  in.setstate(std::ios_base::failbit);
435  break;
436  } else {
437  channels.emplace(static_cast<Channel>(itr - ChannelNames.cbegin()));
438  }
439  }
440  return in;
441 }
442 
443 // Used by boost::program_options when stringifying Channels.
444 std::ostream& operator<<(std::ostream& out, Channels const& channels) {
445  int i = 0;
446  for (auto const channel : channels) {
447  out << (i++ ? " " : "") << ChannelNames.at(channel);
448  }
449  return out;
450 }
451 
452 // Used by boost::program_options when parsing enum Severity.
453 std::istream& operator>>(std::istream& in, Severity& sev) {
454  std::string token;
455  in >> token;
456  unquote(token);
457  auto itr = std::find(SeverityNames.cbegin(), SeverityNames.cend(), token);
458  if (itr == SeverityNames.cend()) {
459  in.setstate(std::ios_base::failbit);
460  } else {
461  sev = static_cast<Severity>(itr - SeverityNames.cbegin());
462  }
463  return in;
464 }
465 
466 // Used by boost::program_options when stringifying enum Severity.
467 std::ostream& operator<<(std::ostream& out, Severity const& sev) {
468  return out << SeverityNames.at(sev);
469 }
470 
471 namespace {
472 std::atomic<RequestId> g_next_request_id{1};
473 std::atomic<ThreadId> g_next_thread_id{1};
474 
475 thread_local ThreadLocalIds g_thread_local_ids(0, 0);
476 
478  switch (channel) {
479  default:
480  case IR:
481  return gChannelLogger_IR::get();
482  case PTX:
483  return gChannelLogger_PTX::get();
484  case ASM:
485  return gChannelLogger_ASM::get();
486  case EXECUTOR:
487  return gChannelLogger_EXECUTOR::get();
488  }
489 }
490 } // namespace
491 
492 Logger::Logger(Channel channel)
493  : is_channel_(true)
494  , enum_value_(channel)
495  , record_(std::make_unique<boost::log::record>(
496  get_channel_logger(channel).open_record())) {
497  if (*record_) {
498  stream_ = std::make_unique<boost::log::record_ostream>(*record_);
499  }
500 }
501 
503  : is_channel_(false)
504  , enum_value_(severity)
505  , record_(std::make_unique<boost::log::record>(
506  gSeverityLogger::get().open_record(keywords::severity = severity))) {
507  if (*record_) {
508  stream_ = std::make_unique<boost::log::record_ostream>(*record_);
509  }
510 }
511 
513  if (stream_) {
514  if (is_channel_) {
515  get_channel_logger(static_cast<Channel>(enum_value_))
516  .push_record(boost::move(stream_->get_record()));
517  } else {
518  gSeverityLogger::get().push_record(boost::move(stream_->get_record()));
519  }
520  }
521  if (!is_channel_ && static_cast<Severity>(enum_value_) == Severity::FATAL) {
522  if (FatalFunc fatal_func = g_fatal_func.load()) {
523  // set_once_fatal_func() prevents race condition.
524  // Exceptions thrown by (*fatal_func)() are propagated here.
525  std::call_once(g_fatal_func_flag, *fatal_func);
526  }
527 
528  abort();
529  }
530 }
531 
532 Logger::operator bool() const {
533  return static_cast<bool>(stream_);
534 }
535 
536 // Assume *this is from the parent thread. Set current g_thread_local_ids.
537 // The new thread is assumed to have the same request_id as the parent thread.
539  ThreadLocalIds const prev_thread_local_ids = g_thread_local_ids;
541  return {prev_thread_local_ids, g_thread_local_ids};
542 }
543 
545  if (enabled_) {
546 #ifndef NDEBUG
549 #endif
551  }
552 }
553 
554 boost::log::record_ostream& Logger::stream(char const* file, int line) {
555  return *stream_ << g_thread_local_ids.request_id_ << ' '
556  << g_thread_local_ids.thread_id_ << ' ' << filename(file) << ':' << line
557  << ' ';
558 }
559 
560 // DebugTimer-related classes and functions.
561 using Clock = std::chrono::steady_clock;
562 
563 class DurationTree;
564 
567  char const* file_;
568  int line_;
569  char const* name_;
570 };
571 
572 class Duration {
574  Clock::time_point const start_;
575  Clock::time_point stop_;
576 
577  public:
578  int const depth_;
580 
581  Duration(DurationTree* const duration_tree,
582  int const depth,
583  DebugTimerParams const debug_timer_params)
584  : duration_tree_(duration_tree)
585  , start_(Clock::now())
586  , depth_(depth)
587  , debug_timer_params_(debug_timer_params) {}
588  bool stop();
589  // Start time relative to parent DurationTree::start_.
590  template <typename Units = std::chrono::milliseconds>
591  typename Units::rep relative_start_time() const;
592  // Duration value = stop_ - start_.
593  template <typename Units = std::chrono::milliseconds>
594  typename Units::rep value() const;
595  DebugTimerParams const* operator->() const { return &debug_timer_params_; }
596 };
597 
598 using DurationTreeNode = boost::variant<Duration, DurationTree&>;
599 using DurationTreeNodes = std::deque<DurationTreeNode>;
600 
603  int current_depth_; //< Depth of next DurationTreeNode.
604 
605  public:
606  int const depth_; //< Depth of tree within parent tree, 0 for root tree.
607  Clock::time_point const start_;
609  DurationTree(ThreadId thread_id, int start_depth)
610  // Add +1 to current_depth_ for non-root DurationTrees for extra indentation.
611  : current_depth_(start_depth + bool(start_depth))
612  , depth_(start_depth)
613  , start_(Clock::now())
614  , thread_id_(thread_id) {}
615  void pushDurationTree(DurationTree& duration_tree) {
616  durations_.emplace_back(duration_tree);
617  }
618  const Duration& rootDuration() const {
619  CHECK(!durations_.empty());
620  return boost::get<Duration>(durations_.front());
621  }
622  int currentDepth() const { return current_depth_; }
624  DurationTreeNodes const& durations() const { return durations_; }
625  Duration* newDuration(DebugTimerParams const debug_timer_params) {
626  durations_.emplace_back(Duration(this, current_depth_++, debug_timer_params));
627  return boost::get<Duration>(&durations_.back());
628  }
629 };
630 
634  stop_ = Clock::now();
636  return depth_ == 0;
637 }
638 
639 template <typename Units>
640 typename Units::rep Duration::relative_start_time() const {
641  return std::chrono::duration_cast<Units>(start_ - duration_tree_->start_).count();
642 }
643 
644 template <typename Units>
645 typename Units::rep Duration::value() const {
646  return std::chrono::duration_cast<Units>(stop_ - start_).count();
647 }
648 
649 struct GetDepth : boost::static_visitor<int> {
650  int operator()(Duration const& duration) const { return duration.depth_; }
651  int operator()(DurationTree const& duration_tree) const { return duration_tree.depth_; }
652 };
653 
654 using DurationTreeMap = std::unordered_map<ThreadId, std::unique_ptr<DurationTree>>;
655 
658 
659 Duration* newDuration(DebugTimerParams const debug_timer_params) {
660  if (g_enable_debug_timer) {
662  std::lock_guard<std::mutex> lock_guard(g_duration_tree_map_mutex);
663  auto& duration_tree_ptr = g_duration_tree_map[g_thread_local_ids.thread_id_];
664  if (!duration_tree_ptr) {
665  duration_tree_ptr =
666  std::make_unique<DurationTree>(g_thread_local_ids.thread_id_, 0);
667  }
668  return duration_tree_ptr->newDuration(debug_timer_params);
669  }
670  LOG(ERROR) << "DEBUG_TIMER(" << debug_timer_params.name_
671  << ") is being called on a child thread nested within an existing "
672  "DEBUG_TIMER call at "
673  << debug_timer_params.file_ << ':' << debug_timer_params.line_
674  << ". New threads require DEBUG_TIMER_NEW_THREAD() to be called first to "
675  "guarantee accurate reporting.";
676  }
677  return nullptr; // Inactive - don't measure or report timing.
678 }
679 
680 std::ostream& operator<<(std::ostream& os, Duration const& duration) {
681  return os << std::setw(2 * duration.depth_) << ' ' << duration.value() << "ms start("
682  << duration.relative_start_time() << "ms) " << duration->name_ << ' '
683  << filename(duration->file_) << ':' << duration->line_;
684 }
685 
686 std::ostream& operator<<(std::ostream& os, DurationTree const& duration_tree) {
687  os << std::setw(2 * duration_tree.depth_) << ' ' << "New thread("
688  << duration_tree.thread_id_ << ')';
689  for (auto const& duration_tree_node : duration_tree.durations()) {
690  os << '\n' << duration_tree_node;
691  }
692  return os << '\n'
693  << std::setw(2 * duration_tree.depth_) << ' ' << "End thread("
694  << duration_tree.thread_id_ << ')';
695 }
696 
697 // Only called by logAndEraseDurationTree() on root tree
698 boost::log::record_ostream& operator<<(boost::log::record_ostream& os,
699  DurationTreeMap::const_reference kv_pair) {
700  auto itr = kv_pair.second->durations().cbegin();
701  auto const end = kv_pair.second->durations().cend();
702  auto const& root_duration = boost::get<Duration>(*itr);
703  os << "DEBUG_TIMER thread_id(" << kv_pair.first << ")\n"
704  << root_duration.value() << "ms total duration for " << root_duration->name_;
705  for (++itr; itr != end; ++itr) {
706  os << '\n' << *itr;
707  }
708  return os;
709 }
710 
711 // Encode DurationTree into json.
712 // DurationTrees encode parent/child relationships using a combination
713 // of ordered descendents, and an int depth member value for each node.
714 // Direct-child nodes are those which:
715 // * do not come after a node of depth <= parent_depth
716 // * have depth == parent_depth + 1
717 class JsonEncoder : boost::static_visitor<rapidjson::Value> {
718  std::shared_ptr<rapidjson::Document> doc_;
719  rapidjson::Document::AllocatorType& alloc_;
720  // Iterators are used to determine children when visiting a Duration node.
721  DurationTreeNodes::const_iterator begin_;
722  DurationTreeNodes::const_iterator end_;
723 
724  JsonEncoder(JsonEncoder& json_encoder,
725  DurationTreeNodes::const_iterator begin,
726  DurationTreeNodes::const_iterator end)
727  : doc_(json_encoder.doc_), alloc_(doc_->GetAllocator()), begin_(begin), end_(end) {}
728 
729  public:
731  : doc_(std::make_shared<rapidjson::Document>(rapidjson::kObjectType))
732  , alloc_(doc_->GetAllocator()) {}
733  rapidjson::Value operator()(Duration const& duration) {
734  rapidjson::Value retval(rapidjson::kObjectType);
735  retval.AddMember("type", "duration", alloc_);
736  retval.AddMember("duration_ms", rapidjson::Value(duration.value()), alloc_);
737  retval.AddMember(
738  "start_ms", rapidjson::Value(duration.relative_start_time()), alloc_);
739  retval.AddMember("name", rapidjson::StringRef(duration->name_), alloc_);
740  retval.AddMember("file", filename(duration->file_), alloc_);
741  retval.AddMember("line", rapidjson::Value(duration->line_), alloc_);
742  retval.AddMember("children", childNodes(duration.depth_), alloc_);
743  return retval;
744  }
745  rapidjson::Value operator()(DurationTree const& duration_tree) {
746  begin_ = duration_tree.durations().cbegin();
747  end_ = duration_tree.durations().cend();
748  rapidjson::Value retval(rapidjson::kObjectType);
749  retval.AddMember("type", "duration_tree", alloc_);
750  retval.AddMember("thread_id", std::to_string(duration_tree.thread_id_), alloc_);
751  retval.AddMember("children", childNodes(duration_tree.depth_), alloc_);
752  return retval;
753  }
754  rapidjson::Value childNodes(int const parent_depth) {
755  GetDepth const get_depth;
756  rapidjson::Value children(rapidjson::kArrayType);
757  for (auto itr = begin_; itr != end_; ++itr) {
758  int const depth = apply_visitor(get_depth, *itr);
759  if (depth <= parent_depth) {
760  break;
761  }
762  if (depth == parent_depth + 1) {
763  JsonEncoder json_encoder(*this, std::next(itr), end_);
764  children.PushBack(apply_visitor(json_encoder, *itr), alloc_);
765  }
766  }
767  return children;
768  }
769  // The root Duration is the "timer" node in the top level debug json object.
770  // Only root Duration has overall total_duration_ms.
771  rapidjson::Value timer(DurationTreeMap::const_reference kv_pair) {
772  begin_ = kv_pair.second->durations().cbegin();
773  end_ = kv_pair.second->durations().cend();
774  rapidjson::Value retval(rapidjson::kObjectType);
775  if (begin_ != end_) {
776  auto const& root_duration = boost::get<Duration>(*(begin_++));
777  retval.AddMember("type", "root", alloc_);
778  retval.AddMember("thread_id", std::to_string(kv_pair.first), alloc_);
779  retval.AddMember(
780  "total_duration_ms", rapidjson::Value(root_duration.value()), alloc_);
781  retval.AddMember("name", rapidjson::StringRef(root_duration->name_), alloc_);
782  retval.AddMember("children", childNodes(0), alloc_);
783  }
784  return retval;
785  }
786  // Assumes *doc_ is empty.
787  std::string str(DurationTreeMap::const_reference kv_pair) {
788  doc_->AddMember("timer", timer(kv_pair), alloc_);
789  rapidjson::StringBuffer buffer;
790  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
791  doc_->Accept(writer);
792  return {buffer.GetString(), buffer.GetSize()};
793  }
794 };
795 
797 struct EraseDurationTrees : boost::static_visitor<> {
798  void operator()(DurationTreeMap::const_iterator const& itr) const {
799  for (auto const& duration_tree_node : itr->second->durations()) {
800  apply_visitor(*this, duration_tree_node);
801  }
802  g_duration_tree_map.erase(itr);
803  }
804  void operator()(Duration const&) const {}
805  void operator()(DurationTree const& duration_tree) const {
806  for (auto const& duration_tree_node : duration_tree.durations()) {
807  apply_visitor(*this, duration_tree_node);
808  }
809  g_duration_tree_map.erase(duration_tree.thread_id_);
810  }
811 };
812 
813 void logAndEraseDurationTree(std::string* json_str) {
814  std::lock_guard<std::mutex> lock_guard(g_duration_tree_map_mutex);
815  DurationTreeMap::const_iterator const itr =
817  CHECK(itr != g_duration_tree_map.cend());
818  auto const& root_duration = itr->second->rootDuration();
819  if (auto log = Logger(root_duration->severity_)) {
820  log.stream(root_duration->file_, root_duration->line_) << *itr;
821  }
822  if (json_str) {
823  JsonEncoder json_encoder;
824  *json_str = json_encoder.str(*itr);
825  }
826  EraseDurationTrees erase_duration_trees;
827  erase_duration_trees(itr);
828 }
829 
830 DebugTimer::DebugTimer(Severity severity, char const* file, int line, char const* name)
831  : duration_(newDuration({severity, file, line, name})) {
833 }
834 
836  stop();
838 }
839 
841  if (duration_) {
842  if (duration_->stop()) {
843  logAndEraseDurationTree(nullptr);
844  }
845  duration_ = nullptr;
846  }
847 }
848 
850  std::string json_str;
851  if (duration_) {
852  if (duration_->stop()) {
853  logAndEraseDurationTree(&json_str);
854  }
855  duration_ = nullptr;
856  }
857  return json_str;
858 }
859 
863 void debug_timer_new_thread(ThreadId const parent_thread_id) {
864  std::lock_guard<std::mutex> lock_guard(g_duration_tree_map_mutex);
865  auto const parent_itr = g_duration_tree_map.find(parent_thread_id);
866  CHECK(parent_itr != g_duration_tree_map.end()) << parent_thread_id;
867  auto const current_depth = parent_itr->second->currentDepth();
868  auto const emplaced = g_duration_tree_map.emplace(
870  std::make_unique<DurationTree>(g_thread_local_ids.thread_id_, current_depth + 1));
871  CHECK(emplaced.second) << "ThreadId " << g_thread_local_ids.thread_id_
872  << " already in map.";
873  parent_itr->second->pushDurationTree(*emplaced.first->second);
874 }
875 
878 }
881 }
883  return g_thread_local_ids;
884 }
885 
886 // For example KafkaMgr::sql_execute() calls
887 // 1. db_handler_->internal_connect()
888 // 2. db_handler_->sql_execute()
889 // 3. db_handler_->disconnect()
890 // sequentially on the same thread. The thread_id is assigned only on the first call.
892  if (g_thread_local_ids.thread_id_ == 0) {
894  }
897 }
898 
900  if (g_thread_local_ids.thread_id_ == 0) {
902  }
904 }
905 
906 fs::path get_log_dir_path() {
907  return fs::canonical(g_log_dir_path);
908 }
909 } // namespace logger
910 
911 #endif // #ifndef __CUDACC__
std::istream & operator>>(std::istream &in, Channels &channels)
Definition: Logger.cpp:424
DurationTreeNodes::const_iterator end_
Definition: Logger.cpp:722
std::lock_guard< T > lock_guard
void track_symlink(fs::path symlink_path)
Definition: Logger.cpp:271
void pushDurationTree(DurationTree &duration_tree)
Definition: Logger.cpp:615
std::atomic< RequestId > g_next_request_id
Definition: Logger.cpp:472
int const enum_value_
Definition: Logger.h:250
#define CHECK_EQ(x, y)
Definition: Logger.h:301
std::unordered_map< ThreadId, std::unique_ptr< DurationTree >> DurationTreeMap
Definition: Logger.cpp:654
std::set< Channel > Channels
Definition: Logger.h:199
std::string stopAndGetJson()
Definition: Logger.cpp:849
std::mutex g_duration_tree_map_mutex
Definition: Logger.cpp:656
char const * file_
Definition: Logger.cpp:567
ThreadLocalIds thread_local_ids_
Definition: Logger.h:156
void set_formatter(SINK &sink)
Definition: Logger.cpp:289
std::unique_ptr< boost::log::record > record_
Definition: Logger.h:252
LogOptions(char const *argv0)
JsonEncoder(JsonEncoder &json_encoder, DurationTreeNodes::const_iterator begin, DurationTreeNodes::const_iterator end)
Definition: Logger.cpp:724
std::once_flag g_fatal_func_flag
Definition: Logger.cpp:342
void operator()(DurationTreeMap::const_iterator const &itr) const
Definition: Logger.cpp:798
void omnisci_range_pop()
RequestId request_id_
Definition: Logger.h:137
#define LOG(tag)
Definition: Logger.h:285
std::ostream & operator<<(std::ostream &os, const SessionInfo &session_info)
Definition: SessionInfo.cpp:57
Clock::time_point const start_
Definition: Logger.cpp:574
bool g_enable_debug_timer
Definition: Logger.cpp:17
void operator()(Duration const &) const
Definition: Logger.cpp:804
Channel
Definition: Logger.h:91
const std::string kDefaultLogDirName
const std::string json_str(const rapidjson::Value &obj) noexcept
Definition: JsonAccessors.h:46
ThreadLocalIds prev_local_ids_
Definition: Logger.h:153
DurationTreeNodes durations_
Definition: Logger.cpp:602
std::vector< fs::path > symlink_paths_
Definition: Logger.cpp:243
void operator()(DurationTree const &duration_tree) const
Definition: Logger.cpp:805
fs::path get_log_dir_path()
Definition: Logger.cpp:906
Units::rep relative_start_time() const
Definition: Logger.cpp:640
BOOST_LOG_GLOBAL_LOGGER_CTOR_ARGS(gChannelLogger_IR, ChannelLogger,(keywords::channel=IR)) BOOST_LOG_GLOBAL_LOGGER_CTOR_ARGS(gChannelLogger_PTX
thread_local ThreadLocalIds g_thread_local_ids(0, 0)
Clock::time_point const start_
Definition: Logger.cpp:607
Duration * duration_
Definition: Logger.h:393
rapidjson::Value operator()(Duration const &duration)
Definition: Logger.cpp:733
Depth-first search and erase all DurationTrees. Not thread-safe.
Definition: Logger.cpp:797
DurationTreeNodes const & durations() const
Definition: Logger.cpp:624
rapidjson::Value childNodes(int const parent_depth)
Definition: Logger.cpp:754
int currentDepth() const
Definition: Logger.cpp:622
void set_once_fatal_func(FatalFunc fatal_func)
Definition: Logger.cpp:394
std::string to_string(char const *&&v)
boost::log::sources::channel_logger_mt< Channel > ChannelLogger
Definition: Logger.cpp:53
sinks::synchronous_sink< sinks::text_ostream_backend > ClogSync
Definition: Logger.cpp:344
void(*)() noexcept FatalFunc
Definition: Logger.h:244
constexpr double a
Definition: Utm.h:32
constexpr std::array< char, 8 > SeveritySymbols
Definition: Logger.h:124
void omnisci_range_push(Category c, const char *name, const char *file)
Units::rep value() const
Definition: Logger.cpp:645
Channels channels_
Definition: Logger.h:216
bool const is_channel_
Definition: Logger.h:249
DurationTreeNodes::const_iterator begin_
Definition: Logger.cpp:721
int const depth_
Definition: Logger.cpp:578
DebugTimerParams const * operator->() const
Definition: Logger.cpp:595
void logAndEraseDurationTree(std::string *json_str)
Definition: Logger.cpp:813
sinks::synchronous_sink< sinks::text_file_backend > FileSync
Definition: Logger.cpp:345
rapidjson::Value operator()(DurationTree const &duration_tree)
Definition: Logger.cpp:745
void init(LogOptions const &log_opts)
Definition: Logger.cpp:364
std::deque< DurationTreeNode > DurationTreeNodes
Definition: Logger.cpp:599
Duration(DurationTree *const duration_tree, int const depth, DebugTimerParams const debug_timer_params)
Definition: Logger.cpp:581
Severity g_min_active_severity
Definition: Logger.cpp:360
void store_file(fs::path const &path) override
Definition: Logger.cpp:252
std::atomic< FatalFunc > g_fatal_func
Definition: Logger.cpp:341
void debug_timer_new_thread(ThreadId const parent_thread_id)
Definition: Logger.cpp:863
int operator()(Duration const &duration) const
Definition: Logger.cpp:650
RequestId set_new_request_id()
Definition: Logger.cpp:891
ChannelLogger & get_channel_logger(Channel const channel)
Definition: Logger.cpp:477
DEVICE auto accumulate(ARGS &&...args)
Definition: gpu_enabled.h:42
constexpr std::array< char const *, 4 > ChannelNames
Definition: Logger.h:93
bool rotate_daily_
Definition: Logger.h:220
void set_request_id(RequestId const request_id)
Definition: Logger.cpp:899
DurationTree *const duration_tree_
Definition: Logger.cpp:573
boost::log::sources::severity_logger_mt< Severity > SeverityLogger
Definition: Logger.cpp:59
bool g_enable_smem_group_by true
std::string symlink_
Definition: Logger.h:213
ThreadId const thread_id_
Definition: Logger.cpp:608
DebugTimer(DebugTimer const &)=delete
std::atomic< ThreadId > g_next_thread_id
Definition: Logger.cpp:473
uintmax_t scan_for_files(sinks::file::scan_method method, fs::path const &path=fs::path(), unsigned *counter=nullptr) override
Definition: Logger.cpp:265
static fs::path g_log_dir_path
Definition: Logger.cpp:362
std::unique_ptr< boost::log::record_ostream > stream_
Definition: Logger.h:253
boost::shared_ptr< FILE_SINK > make_sink(boost::shared_ptr< Collector > &collector, LogOptions const &log_opts, fs::path const &full_log_dir, TAG const tag)
Definition: Logger.cpp:308
Duration * newDuration(DebugTimerParams const debug_timer_params)
Definition: Logger.cpp:625
tuple line
Definition: parse_ast.py:10
LocalIdsScopeGuard setNewThreadId() const
Definition: Logger.cpp:538
Severity
Definition: Logger.h:103
boost::log::formatting_ostream & operator<<(boost::log::formatting_ostream &strm, boost::log::to_log_manip< Channel, tag::channel > const &manip)
Definition: Logger.cpp:276
void shutdown()
Definition: Logger.cpp:401
constexpr std::array< char const *, 8 > SeverityNames
Definition: Logger.h:115
char const * name_
Definition: Logger.cpp:569
rapidjson::Value timer(DurationTreeMap::const_reference kv_pair)
Definition: Logger.cpp:771
constexpr std::array< char, 4 > ChannelSymbols
Definition: Logger.h:95
uint64_t ThreadId
Definition: Logger.h:132
const Duration & rootDuration() const
Definition: Logger.cpp:618
Severity severity_
Definition: Logger.h:214
std::string str(DurationTreeMap::const_reference kv_pair)
Definition: Logger.cpp:787
bool g_any_active_channels
Definition: Logger.cpp:359
ThreadId thread_id()
Definition: Logger.cpp:879
RequestId request_id()
Definition: Logger.cpp:876
std::string file_name_pattern_
Definition: Logger.h:212
bool g_enable_watchdog false
Definition: Execute.cpp:80
def error_code
Definition: report.py:234
#define CHECK(condition)
Definition: Logger.h:291
size_t rotation_size_
Definition: Logger.h:221
int operator()(DurationTree const &duration_tree) const
Definition: Logger.cpp:651
DurationTreeMap g_duration_tree_map
Definition: Logger.cpp:657
boost::filesystem::path full_log_dir() const
int const depth_
Definition: Logger.cpp:606
Duration * newDuration(DebugTimerParams const debug_timer_params)
Definition: Logger.cpp:659
std::chrono::steady_clock Clock
Definition: Logger.cpp:561
size_t max_files_
Definition: Logger.h:218
Logger(Channel)
Definition: Logger.cpp:492
ThreadId thread_id_
Definition: Logger.h:138
string name
Definition: setup.in.py:72
void unquote(std::string &str)
Definition: Logger.cpp:413
Collector(fs::path const &full_log_dir, LogOptions const &log_opts)
Definition: Logger.cpp:246
std::shared_ptr< rapidjson::Document > doc_
Definition: Logger.cpp:718
boost::variant< Duration, DurationTree & > DurationTreeNode
Definition: Logger.cpp:598
Severity severity_clog_
Definition: Logger.h:215
boost::shared_ptr< sinks::file::collector > collector_
Definition: Logger.cpp:242
ThreadLocalIds thread_local_ids()
Definition: Logger.cpp:882
uint64_t RequestId
Definition: Logger.h:131
rapidjson::Document::AllocatorType & alloc_
Definition: Logger.cpp:719
DebugTimerParams const debug_timer_params_
Definition: Logger.cpp:579
Clock::time_point stop_
Definition: Logger.cpp:575
boost::log::record_ostream & stream(char const *file, int line)
Definition: Logger.cpp:554