23 #include <rapidjson/document.h>
24 #include <rapidjson/stringbuffer.h>
25 #include <rapidjson/writer.h>
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>
57 BOOST_LOG_GLOBAL_LOGGER(gChannelLogger_EXECUTOR,
ChannelLogger)
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;
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)
76 (keywords::channel =
IR))
79 (keywords::channel =
PTX))
82 (keywords::channel =
ASM))
89 std::
string filename(
char const* path) {
90 return fs::path(path).filename().string();
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_;
104 LogOptions::~LogOptions() {}
106 fs::path LogOptions::full_log_dir()
const {
107 return log_dir_->has_root_directory() ? *log_dir_ : base_path_ / *log_dir_;
110 po::options_description
const& LogOptions::get_options()
const {
115 void LogOptions::parse_command_line(
int argc,
char const*
const* argv) {
116 po::variables_map vm;
118 po::command_line_parser(argc, argv).options(*options_).allow_unregistered().run(),
124 void LogOptions::set_base_path(std::string
const& base_path) {
125 base_path_ = base_path;
129 void LogOptions::set_options() {
130 options_ = std::make_unique<boost::program_options::options_description>(
"Logging");
131 std::string
const channels =
135 [](
auto a,
auto channel) {
return a +
' ' + channel; });
137 std::string severities;
139 if (!boost::algorithm::starts_with(
name,
"DEBUG")) {
140 (severities += (severities.empty() ?
"" :
" ")) +=
name;
143 options_->add_options()(
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()(
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()(
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()(
179 po::value<size_t>(&rotation_size_)->default_value(rotation_size_),
180 "Maximum file size in bytes before new log files are started.");
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]);
190 return std::regex_replace(str, regex, SeverityNames[tag]);
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;
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) {
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);
213 stream <<
filename(__FILE__) <<
':' << __LINE__ <<
' ' << ec.message() <<
'\n';
215 fs::create_symlink(file_name.filename(), symlink_path, ec);
217 stream <<
filename(__FILE__) <<
':' << __LINE__ <<
' ' << ec.message() <<
'\n';
224 struct RemoveDeadLink {
225 void operator()(fs::path
const& symlink_path)
const {
227 bool const exists = fs::exists(symlink_path, ec);
231 fs::remove(symlink_path, ec);
233 std::cerr <<
"Error removing " << symlink_path <<
": " << ec.message()
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_)) {}
253 collector_->store_file(path);
254 std::for_each(symlink_paths_.begin(), symlink_paths_.end(), RemoveDeadLink{});
256 #if 107900 <= BOOST_VERSION
257 bool is_in_storage(fs::path
const& path)
const override {
258 return collector_->is_in_storage(path);
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);
266 fs::path
const& path = fs::path(),
267 unsigned* counter =
nullptr)
override {
268 return collector_->scan_for_files(method, path, counter);
272 symlink_paths_.emplace_back(std::move(symlink_path));
277 boost::log::formatting_ostream& strm,
278 boost::log::to_log_manip<Channel, tag::channel>
const& manip) {
283 boost::log::formatting_ostream& strm,
284 boost::log::to_log_manip<Severity, tag::severity>
const& manip) {
288 template <
typename TAG,
typename SINK>
290 if constexpr (std::is_same<TAG, Channel>::value) {
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);
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);
307 template <
typename FILE_SINK,
typename TAG>
308 boost::shared_ptr<FILE_SINK>
make_sink(boost::shared_ptr<Collector>& collector,
310 fs::path
const& full_log_dir,
312 auto sink = boost::make_shared<FILE_SINK>(
313 keywords::file_name =
317 if constexpr (std::is_same<TAG, Channel>::value) {
318 sink->set_filter(channel == static_cast<Channel>(tag));
319 set_formatter<Channel>(sink);
324 : static_cast<Severity>(tag);
325 sink->set_filter(min_filter_level <= severity);
326 set_formatter<Severity>(sink);
328 typename FILE_SINK::locked_backend_ptr backend = sink->locked_backend();
330 backend->set_time_based_rotation(sinks::file::rotation_at_time_point(0, 0, 0));
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();
344 using ClogSync = sinks::synchronous_sink<sinks::text_ostream_backend>;
345 using FileSync = sinks::synchronous_sink<sinks::text_file_backend>;
347 template <
typename CONSOLE_SINK>
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);
353 set_formatter<Severity>(sink);
365 boost::shared_ptr<boost::log::core> core = boost::log::core::get();
367 core->add_global_attribute(
"TimeStamp", attr::local_clock());
368 core->add_global_attribute(
"ProcessID", attr::current_process_id());
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);
377 core->add_sink(make_sink<FileSync>(collector, log_opts, full_log_dir, level));
380 if (log_dir_was_created) {
381 LOG(
INFO) <<
"Log directory(" << full_log_dir.native() <<
") created.";
383 for (
auto const channel : log_opts.
channels_) {
384 core->add_sink(make_sink<FileSync>(collector, log_opts, full_log_dir, channel));
388 core->add_sink(make_sink<ClogSync>(log_opts));
396 throw std::runtime_error(
397 "logger::set_once_fatal_func() should not be called more than once.");
402 static std::once_flag logger_flag;
403 std::call_once(logger_flag, []() {
404 boost::log::core::get()->remove_all_sinks();
414 if (1 < str.size() && (str.front() ==
'\'' || str.front() ==
'"') &&
415 str.front() == str.back()) {
416 str.erase(str.size() - 1, 1);
426 std::getline(in, line);
428 std::regex
const rex(R
"(\w+)");
429 using TokenItr = std::regex_token_iterator<std::string::iterator>;
431 for (TokenItr tok(line.begin(), line.end(), rex); tok != end; ++tok) {
434 in.setstate(std::ios_base::failbit);
437 channels.emplace(static_cast<Channel>(itr -
ChannelNames.cbegin()));
446 for (
auto const channel : channels) {
457 auto itr = std::find(SeverityNames.cbegin(), SeverityNames.cend(), token);
458 if (itr == SeverityNames.cend()) {
459 in.setstate(std::ios_base::failbit);
461 sev =
static_cast<Severity>(itr - SeverityNames.cbegin());
468 return out << SeverityNames.at(sev);
481 return gChannelLogger_IR::get();
483 return gChannelLogger_PTX::get();
485 return gChannelLogger_ASM::get();
487 return gChannelLogger_EXECUTOR::get();
494 , enum_value_(channel)
495 , record_(std::make_unique<boost::log::record>(
498 stream_ = std::make_unique<boost::log::record_ostream>(*record_);
504 , enum_value_(severity)
505 , record_(std::make_unique<boost::log::record>(
506 gSeverityLogger::get().open_record(keywords::severity = severity))) {
508 stream_ = std::make_unique<boost::log::record_ostream>(*record_);
516 .push_record(boost::move(
stream_->get_record()));
518 gSeverityLogger::get().push_record(boost::move(
stream_->get_record()));
532 Logger::operator bool()
const {
533 return static_cast<bool>(stream_);
561 using Clock = std::chrono::steady_clock;
590 template <
typename Units = std::chrono::milliseconds>
593 template <
typename Units = std::chrono::milliseconds>
594 typename Units::rep
value()
const;
620 return boost::get<Duration>(
durations_.front());
627 return boost::get<Duration>(&
durations_.back());
634 stop_ = Clock::now();
639 template <
typename Units>
644 template <
typename Units>
646 return std::chrono::duration_cast<Units>(
stop_ -
start_).count();
664 if (!duration_tree_ptr) {
668 return duration_tree_ptr->newDuration(debug_timer_params);
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.";
681 return os << std::setw(2 * duration.
depth_) <<
' ' << duration.
value() <<
"ms start("
687 os << std::setw(2 * duration_tree.
depth_) <<
' ' <<
"New thread("
689 for (
auto const& duration_tree_node : duration_tree.
durations()) {
690 os <<
'\n' << duration_tree_node;
693 << std::setw(2 * duration_tree.
depth_) <<
' ' <<
"End thread("
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) {
718 std::shared_ptr<rapidjson::Document>
doc_;
719 rapidjson::Document::AllocatorType&
alloc_;
721 DurationTreeNodes::const_iterator
begin_;
722 DurationTreeNodes::const_iterator
end_;
725 DurationTreeNodes::const_iterator begin,
726 DurationTreeNodes::const_iterator end)
731 :
doc_(std::make_shared<rapidjson::Document>(rapidjson::kObjectType))
734 rapidjson::Value retval(rapidjson::kObjectType);
735 retval.AddMember(
"type",
"duration",
alloc_);
736 retval.AddMember(
"duration_ms", rapidjson::Value(duration.
value()),
alloc_);
739 retval.AddMember(
"name", rapidjson::StringRef(duration->
name_),
alloc_);
741 retval.AddMember(
"line", rapidjson::Value(duration->
line_),
alloc_);
748 rapidjson::Value retval(rapidjson::kObjectType);
749 retval.AddMember(
"type",
"duration_tree",
alloc_);
756 rapidjson::Value children(rapidjson::kArrayType);
758 int const depth = apply_visitor(get_depth, *itr);
759 if (depth <= parent_depth) {
762 if (depth == parent_depth + 1) {
764 children.PushBack(apply_visitor(json_encoder, *itr),
alloc_);
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);
776 auto const& root_duration = boost::get<Duration>(*(
begin_++));
777 retval.AddMember(
"type",
"root",
alloc_);
780 "total_duration_ms", rapidjson::Value(root_duration.value()),
alloc_);
781 retval.AddMember(
"name", rapidjson::StringRef(root_duration->name_),
alloc_);
787 std::string
str(DurationTreeMap::const_reference kv_pair) {
789 rapidjson::StringBuffer buffer;
790 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
791 doc_->Accept(writer);
792 return {buffer.GetString(), buffer.GetSize()};
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);
806 for (
auto const& duration_tree_node : duration_tree.
durations()) {
807 apply_visitor(*
this, duration_tree_node);
815 DurationTreeMap::const_iterator
const itr =
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;
824 *json_str = json_encoder.
str(*itr);
827 erase_duration_trees(itr);
867 auto const current_depth = parent_itr->second->currentDepth();
872 <<
" already in map.";
873 parent_itr->second->pushDurationTree(*emplaced.first->second);
911 #endif // #ifndef __CUDACC__
std::istream & operator>>(std::istream &in, Channels &channels)
DurationTreeNodes::const_iterator end_
std::lock_guard< T > lock_guard
void track_symlink(fs::path symlink_path)
void pushDurationTree(DurationTree &duration_tree)
std::atomic< RequestId > g_next_request_id
std::unordered_map< ThreadId, std::unique_ptr< DurationTree >> DurationTreeMap
std::set< Channel > Channels
std::string stopAndGetJson()
std::mutex g_duration_tree_map_mutex
ThreadLocalIds thread_local_ids_
void set_formatter(SINK &sink)
std::unique_ptr< boost::log::record > record_
LogOptions(char const *argv0)
JsonEncoder(JsonEncoder &json_encoder, DurationTreeNodes::const_iterator begin, DurationTreeNodes::const_iterator end)
std::once_flag g_fatal_func_flag
void operator()(DurationTreeMap::const_iterator const &itr) const
std::ostream & operator<<(std::ostream &os, const SessionInfo &session_info)
Clock::time_point const start_
bool g_enable_debug_timer
void operator()(Duration const &) const
const std::string kDefaultLogDirName
const std::string json_str(const rapidjson::Value &obj) noexcept
ThreadLocalIds prev_local_ids_
DurationTreeNodes durations_
std::vector< fs::path > symlink_paths_
void operator()(DurationTree const &duration_tree) const
fs::path get_log_dir_path()
Units::rep relative_start_time() const
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_
rapidjson::Value operator()(Duration const &duration)
Depth-first search and erase all DurationTrees. Not thread-safe.
DurationTreeNodes const & durations() const
rapidjson::Value childNodes(int const parent_depth)
void set_once_fatal_func(FatalFunc fatal_func)
boost::log::sources::channel_logger_mt< Channel > ChannelLogger
sinks::synchronous_sink< sinks::text_ostream_backend > ClogSync
void(*)() noexcept FatalFunc
constexpr std::array< char, 8 > SeveritySymbols
void omnisci_range_push(Category c, const char *name, const char *file)
DurationTreeNodes::const_iterator begin_
DebugTimerParams const * operator->() const
void logAndEraseDurationTree(std::string *json_str)
sinks::synchronous_sink< sinks::text_file_backend > FileSync
rapidjson::Value operator()(DurationTree const &duration_tree)
void init(LogOptions const &log_opts)
std::deque< DurationTreeNode > DurationTreeNodes
Duration(DurationTree *const duration_tree, int const depth, DebugTimerParams const debug_timer_params)
Severity g_min_active_severity
void store_file(fs::path const &path) override
std::atomic< FatalFunc > g_fatal_func
void debug_timer_new_thread(ThreadId const parent_thread_id)
int operator()(Duration const &duration) const
RequestId set_new_request_id()
ChannelLogger & get_channel_logger(Channel const channel)
DEVICE auto accumulate(ARGS &&...args)
constexpr std::array< char const *, 4 > ChannelNames
void set_request_id(RequestId const request_id)
DurationTree *const duration_tree_
boost::log::sources::severity_logger_mt< Severity > SeverityLogger
bool g_enable_smem_group_by true
ThreadId const thread_id_
DebugTimer(DebugTimer const &)=delete
std::atomic< ThreadId > g_next_thread_id
uintmax_t scan_for_files(sinks::file::scan_method method, fs::path const &path=fs::path(), unsigned *counter=nullptr) override
static fs::path g_log_dir_path
std::unique_ptr< boost::log::record_ostream > stream_
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)
Duration * newDuration(DebugTimerParams const debug_timer_params)
LocalIdsScopeGuard setNewThreadId() const
boost::log::formatting_ostream & operator<<(boost::log::formatting_ostream &strm, boost::log::to_log_manip< Channel, tag::channel > const &manip)
constexpr std::array< char const *, 8 > SeverityNames
rapidjson::Value timer(DurationTreeMap::const_reference kv_pair)
constexpr std::array< char, 4 > ChannelSymbols
const Duration & rootDuration() const
std::string str(DurationTreeMap::const_reference kv_pair)
bool g_any_active_channels
std::string filename(char const *path)
std::string file_name_pattern_
bool g_enable_watchdog false
int operator()(DurationTree const &duration_tree) const
DurationTreeMap g_duration_tree_map
boost::filesystem::path full_log_dir() const
Duration * newDuration(DebugTimerParams const debug_timer_params)
std::chrono::steady_clock Clock
void unquote(std::string &str)
Collector(fs::path const &full_log_dir, LogOptions const &log_opts)
std::shared_ptr< rapidjson::Document > doc_
boost::variant< Duration, DurationTree & > DurationTreeNode
boost::shared_ptr< sinks::file::collector > collector_
ThreadLocalIds thread_local_ids()
rapidjson::Document::AllocatorType & alloc_
DebugTimerParams const debug_timer_params_
boost::log::record_ostream & stream(char const *file, int line)