X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=common%2Flog.cpp;h=31a34577617694555741f1e021b5d342bb45bca8;hb=ac181d6fde621f4b09bc6c98ee9115c17d8da526;hp=6368cb97da93a75e6c68f5101a91c694678ed61f;hpb=00c2a1f827f96e8a9bbb7f8f08a98fd7f8d91c24;p=casparcg diff --git a/common/log.cpp b/common/log.cpp index 6368cb97d..31a345776 100644 --- a/common/log.cpp +++ b/common/log.cpp @@ -1,183 +1,326 @@ -/* -* Copyright (c) 2011 Sveriges Television AB -* -* This file is part of CasparCG (www.casparcg.com). -* -* CasparCG is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* CasparCG is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with CasparCG. If not, see . -* -* Author: Robert Nagy, ronag89@gmail.com -*/ - -#include "stdafx.h" - -#if defined(_MSC_VER) -#pragma warning (disable : 4100) // 'identifier' : unreferenced formal parameter -#pragma warning (disable : 4512) // 'class' : assignment operator could not be generated -#endif - -#include "log.h" - -#include "except.h" -#include "utf.h" - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace caspar { namespace log { - -using namespace boost; - -void my_formatter(std::wostream& strm, boost::log::basic_record const& rec) -{ - namespace lambda = boost::lambda; - - #pragma warning(disable : 4996) - time_t rawtime; - struct tm* timeinfo; - time(&rawtime ); - timeinfo = localtime ( &rawtime ); - char buffer [80]; - strftime (buffer,80, "%c", timeinfo); - strm << L"[" << buffer << L"] "; - - boost::log::attributes::current_thread_id::held_type thread_id; - if(boost::log::extract(L"ThreadID", rec.attribute_values(), lambda::var(thread_id) = lambda::_1)) - strm << L"[" << thread_id << L"] "; - - - severity_level severity; - if(boost::log::extract(boost::log::sources::aux::severity_attribute_name::get(), rec.attribute_values(), lambda::var(severity) = lambda::_1)) - { - std::stringstream ss; - ss << severity; - strm << L"[" << severity << L"] "; - for(int n = 0; n < 7-static_cast(ss.str().size()); ++n) - strm << L" "; - } - - strm << rec.message(); -} - -namespace internal{ - -void init() -{ - boost::log::add_common_attributes(); - typedef boost::log::aux::add_common_attributes_constants traits_t; - - typedef boost::log::sinks::synchronous_sink file_sink_type; - - typedef boost::log::sinks::asynchronous_sink stream_sink_type; - - auto stream_backend = boost::make_shared(); - stream_backend->add_stream(boost::shared_ptr(&std::wcout, boost::log::empty_deleter())); - stream_backend->auto_flush(true); - - auto stream_sink = boost::make_shared(stream_backend); - -//#ifdef NDEBUG -// stream_sink->set_filter(boost::log::filters::attr(boost::log::sources::aux::severity_attribute_name::get()) >= debug); -//#else -// stream_sink->set_filter(boost::log::filters::attr(boost::log::sources::aux::severity_attribute_name::get()) >= debug); -//#endif - - stream_sink->locked_backend()->set_formatter(&my_formatter); - - boost::log::wcore::get()->add_sink(stream_sink); -} - -} - -void add_file_sink(const std::wstring& folder) -{ - boost::log::add_common_attributes(); - typedef boost::log::aux::add_common_attributes_constants traits_t; - - typedef boost::log::sinks::synchronous_sink file_sink_type; - - try - { - if(!boost::filesystem::is_directory(folder)) - BOOST_THROW_EXCEPTION(directory_not_found()); - - auto file_sink = boost::make_shared( - boost::log::keywords::file_name = (folder + L"caspar_%Y-%m-%d.log"), - boost::log::keywords::time_based_rotation = boost::log::sinks::file::rotation_at_time_point(0, 0, 0), - boost::log::keywords::auto_flush = true, - boost::log::keywords::open_mode = std::ios::app - ); - - file_sink->locked_backend()->set_formatter(&my_formatter); - -//#ifdef NDEBUG -// file_sink->set_filter(boost::log::filters::attr(boost::log::sources::aux::severity_attribute_name::get()) >= debug); -//#else -// file_sink->set_filter(boost::log::filters::attr(boost::log::sources::aux::severity_attribute_name::get()) >= debug); -//#endif - boost::log::wcore::get()->add_sink(file_sink); - } - catch(...) - { - std::wcerr << L"Failed to Setup File Logging Sink" << std::endl << std::endl; - } -} - -void set_log_level(const std::wstring& lvl) -{ - if(boost::iequals(lvl, L"trace")) - boost::log::wcore::get()->set_filter(boost::log::filters::attr(boost::log::sources::aux::severity_attribute_name::get()) >= trace); - else if(boost::iequals(lvl, L"debug")) - boost::log::wcore::get()->set_filter(boost::log::filters::attr(boost::log::sources::aux::severity_attribute_name::get()) >= debug); - else if(boost::iequals(lvl, L"info")) - boost::log::wcore::get()->set_filter(boost::log::filters::attr(boost::log::sources::aux::severity_attribute_name::get()) >= info); - else if(boost::iequals(lvl, L"warning")) - boost::log::wcore::get()->set_filter(boost::log::filters::attr(boost::log::sources::aux::severity_attribute_name::get()) >= warning); - else if(boost::iequals(lvl, L"error")) - boost::log::wcore::get()->set_filter(boost::log::filters::attr(boost::log::sources::aux::severity_attribute_name::get()) >= error); - else if(boost::iequals(lvl, L"fatal")) - boost::log::wcore::get()->set_filter(boost::log::filters::attr(boost::log::sources::aux::severity_attribute_name::get()) >= fatal); -} - -}} \ No newline at end of file +/* +* Copyright (c) 2011 Sveriges Television AB +* +* This file is part of CasparCG (www.casparcg.com). +* +* CasparCG is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* CasparCG is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with CasparCG. If not, see . +* +* Author: Robert Nagy, ronag89@gmail.com +*/ + +#include "stdafx.h" + +#include "log.h" + +#include "except.h" +#include "utf.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace caspar { namespace log { + +using namespace boost; + +template +void append_timestamp(Stream& stream) +{ + auto timestamp = boost::posix_time::microsec_clock::local_time(); + auto date = timestamp.date(); + auto time = timestamp.time_of_day(); + auto milliseconds = time.fractional_seconds() / 1000; // microseconds to milliseconds + + std::wstringstream buffer; + + buffer + << std::setfill(L'0') + << L"[" + << std::setw(4) << date.year() << L"-" << std::setw(2) << date.month().as_number() << "-" << std::setw(2) << date.day().as_number() + << L" " + << std::setw(2) << time.hours() << L":" << std::setw(2) << time.minutes() << L":" << std::setw(2) << time.seconds() + << L"." + << std::setw(3) << milliseconds + << L"] "; + + stream << buffer.str(); +} + +class column_writer +{ + tbb::atomic column_width_; +public: + column_writer(int initial_width = 0) + { + column_width_ = initial_width; + } + + template + void write(Stream& out, const Val& value) + { + std::wstring to_string = boost::lexical_cast(value); + int length = static_cast(to_string.size()); + int read_width; + + while ((read_width = column_width_) < length && column_width_.compare_and_swap(length, read_width) != read_width); + read_width = column_width_; + + out << L"[" << to_string << L"] "; + + for (int n = 0; n < read_width - length; ++n) + out << L" "; + } +}; + +template +void my_formatter(bool print_all_characters, const boost::log::record_view& rec, Stream& strm) +{ + static column_writer thread_id_column; + static column_writer severity_column(7); + namespace expr = boost::log::expressions; + + std::wstringstream pre_message_stream; + append_timestamp(pre_message_stream); + thread_id_column.write(pre_message_stream, boost::log::extract("ThreadID", rec).get().native_id()); + severity_column.write(pre_message_stream, boost::log::extract("Severity", rec)); + + auto pre_message = pre_message_stream.str(); + + strm << pre_message; + + auto line_break_replacement = L"\n" + pre_message; + + if (print_all_characters) + { + strm << boost::replace_all_copy(rec[expr::message].get(), "\n", line_break_replacement); + } + else + { + strm << boost::replace_all_copy(replace_nonprintable_copy(rec[expr::message].get(), L'?'), L"\n", line_break_replacement); + } +} + +namespace internal{ + +void init() +{ + boost::log::add_common_attributes(); + typedef boost::log::sinks::asynchronous_sink stream_sink_type; + + auto stream_backend = boost::make_shared(); + stream_backend->add_stream(boost::shared_ptr(&std::wcout, boost::null_deleter())); + stream_backend->auto_flush(true); + + auto stream_sink = boost::make_shared(stream_backend); + // Never log calltrace to console. The terminal is too slow, so the log queue will build up faster than consumed. + stream_sink->set_filter(category != log_category::calltrace); + + bool print_all_characters = false; + stream_sink->set_formatter(boost::bind(&my_formatter, print_all_characters, _1, _2)); + + boost::log::core::get()->add_sink(stream_sink); +} + +} + +void add_file_sink(const std::wstring& file, const boost::log::filter& filter) +{ + typedef boost::log::sinks::synchronous_sink file_sink_type; + + try + { + if (!boost::filesystem::is_directory(boost::filesystem::path(file).parent_path())) + CASPAR_THROW_EXCEPTION(directory_not_found()); + + auto file_sink = boost::make_shared( + boost::log::keywords::file_name = (file + L"_%Y-%m-%d.log"), + boost::log::keywords::time_based_rotation = boost::log::sinks::file::rotation_at_time_point(0, 0, 0), + boost::log::keywords::auto_flush = true, + boost::log::keywords::open_mode = std::ios::app + ); + + bool print_all_characters = true; + + file_sink->set_formatter(boost::bind(&my_formatter, print_all_characters, _1, _2)); + file_sink->set_filter(filter); + boost::log::core::get()->add_sink(file_sink); + } + catch (...) + { + std::wcerr << L"Failed to Setup File Logging Sink" << std::endl << std::endl; + } +} + +std::shared_ptr add_preformatted_line_sink(std::function formatted_line_sink) +{ + class sink_backend : public boost::log::sinks::basic_formatted_sink_backend + { + std::function formatted_line_sink_; + public: + sink_backend(std::function formatted_line_sink) + : formatted_line_sink_(std::move(formatted_line_sink)) + { + } + + void consume(const boost::log::record_view& rec, const std::string& formatted_message) + { + try + { + formatted_line_sink_(formatted_message); + } + catch (...) + { + std::cerr << "[sink_backend] Error while consuming formatted message: " << formatted_message << std::endl << std::endl; + } + } + }; + + typedef boost::log::sinks::synchronous_sink sink_type; + + auto sink = boost::make_shared(std::move(formatted_line_sink)); + bool print_all_characters = true; + + sink->set_formatter(boost::bind(&my_formatter, print_all_characters, _1, _2)); + sink->set_filter(category != log_category::calltrace); + + boost::log::core::get()->add_sink(sink); + + return std::shared_ptr(nullptr, [=](void*) + { + boost::log::core::get()->remove_sink(sink); + }); +} + +boost::mutex& get_filter_mutex() +{ + static boost::mutex instance; + + return instance; +} + +boost::log::trivial::severity_level& get_level() +{ + static boost::log::trivial::severity_level instance; + + return instance; +} + +log_category& get_disabled_categories() +{ + static log_category instance = log_category::calltrace; + + return instance; +} + +void set_log_filter() +{ + auto severity_filter = boost::log::trivial::severity >= get_level(); + auto disabled_categories = get_disabled_categories(); + + boost::log::core::get()->set_filter([=](const boost::log::attribute_value_set& attributes) + { + return severity_filter(attributes) + && static_cast(disabled_categories & attributes["Channel"].extract().get()) == 0; + }); +} + +void set_log_level(const std::wstring& lvl) +{ + boost::lock_guard lock(get_filter_mutex()); + + if (boost::iequals(lvl, L"trace")) + get_level() = boost::log::trivial::trace; + else if (boost::iequals(lvl, L"debug")) + get_level() = boost::log::trivial::debug; + else if (boost::iequals(lvl, L"info")) + get_level() = boost::log::trivial::info; + else if (boost::iequals(lvl, L"warning")) + get_level() = boost::log::trivial::warning; + else if (boost::iequals(lvl, L"error")) + get_level() = boost::log::trivial::error; + else if (boost::iequals(lvl, L"fatal")) + get_level() = boost::log::trivial::fatal; + + set_log_filter(); +} + +void set_log_category(const std::wstring& cat, bool enabled) +{ + log_category category_to_set; + + if (boost::iequals(cat, L"calltrace")) + category_to_set = log_category::calltrace; + else if (boost::iequals(cat, L"communication")) + category_to_set = log_category::communication; + else + return; // Ignore + + boost::lock_guard lock(get_filter_mutex()); + auto& disabled_categories = get_disabled_categories(); + + if (enabled) + disabled_categories &= ~category_to_set; + else + disabled_categories |= category_to_set; + + set_log_filter(); +} + +void print_child( + boost::log::trivial::severity_level level, + const std::wstring& indent, + const std::wstring& elem, + const boost::property_tree::wptree& tree) +{ + auto& data = tree.data(); + + if (!data.empty()) + BOOST_LOG_STREAM_WITH_PARAMS(log::logger::get(), (boost::log::keywords::severity = level)) << indent << elem << L" " << replace_nonprintable_copy(data, L'?'); + else if (tree.size() == 0) + BOOST_LOG_STREAM_WITH_PARAMS(log::logger::get(), (boost::log::keywords::severity = level)) << indent << elem; + + for (auto& child : tree) + print_child(level, indent + (elem.empty() ? L"" : elem + L"."), child.first, child.second); +} + +}}