2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
4 * This file is part of CasparCG (www.casparcg.com).
6 * CasparCG is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * CasparCG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
19 * Author: Robert Nagy, ronag89@gmail.com
25 #pragma warning (disable : 4100) // 'identifier' : unreferenced formal parameter
26 #pragma warning (disable : 4512) // 'class' : assignment operator could not be generated
33 #include "compiler/vs/stack_walker.h"
40 #include <boost/bind.hpp>
41 #include <boost/shared_ptr.hpp>
42 #include <boost/make_shared.hpp>
43 #include <boost/filesystem/convenience.hpp>
44 #include <boost/date_time/posix_time/posix_time.hpp>
45 #include <boost/algorithm/string.hpp>
47 #include <boost/log/core/core.hpp>
49 #include <boost/log/formatters/stream.hpp>
50 #include <boost/log/formatters/attr.hpp>
51 #include <boost/log/formatters/date_time.hpp>
52 #include <boost/log/formatters/message.hpp>
54 #include <boost/log/filters/attr.hpp>
56 #include <boost/log/sinks/text_file_backend.hpp>
58 #include <boost/log/detail/universal_path.hpp>
60 #include <boost/log/sinks/text_file_backend.hpp>
61 #include <boost/log/sinks/text_ostream_backend.hpp>
62 #include <boost/log/sinks/sync_frontend.hpp>
63 #include <boost/log/sinks/async_frontend.hpp>
64 #include <boost/log/core/record.hpp>
65 #include <boost/log/utility/attribute_value_extractor.hpp>
67 #include <boost/log/utility/init/common_attributes.hpp>
68 #include <boost/log/utility/empty_deleter.hpp>
69 #include <boost/lambda/lambda.hpp>
71 #include <tbb/enumerable_thread_specific.h>
73 namespace caspar { namespace log {
75 using namespace boost;
77 void append_timestamp(std::wostream& stream)
79 auto timestamp = boost::posix_time::microsec_clock::local_time();
80 auto date = timestamp.date();
81 auto time = timestamp.time_of_day();
82 auto milliseconds = time.fractional_seconds() / 1000; // microseconds to milliseconds
84 std::wstringstream buffer;
89 << std::setw(4) << date.year() << L"-" << std::setw(2) << date.month().as_number() << "-" << std::setw(2) << date.day().as_number()
91 << std::setw(2) << time.hours() << L":" << std::setw(2) << time.minutes() << L":" << std::setw(2) << time.seconds()
93 << std::setw(3) << milliseconds
96 stream << buffer.str();
99 void my_formatter(bool print_all_characters, std::wostream& strm, boost::log::basic_record<wchar_t> const& rec)
101 namespace lambda = boost::lambda;
103 #pragma warning(disable : 4996)
105 append_timestamp(strm);
107 boost::log::attributes::current_thread_id::held_type thread_id;
108 if(boost::log::extract<boost::log::attributes::current_thread_id::held_type>(L"ThreadID", rec.attribute_values(), lambda::var(thread_id) = lambda::_1))
109 strm << L"[" << thread_id << L"] ";
111 severity_level severity;
112 if(boost::log::extract<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get(), rec.attribute_values(), lambda::var(severity) = lambda::_1))
114 std::stringstream ss;
116 strm << L"[" << severity << L"] ";
117 for(int n = 0; n < 7-static_cast<int>(ss.str().size()); ++n)
121 if (print_all_characters)
123 strm << rec.message();
127 strm << replace_nonprintable_copy(rec.message(), L'?');
135 boost::log::add_common_attributes<wchar_t>();
136 typedef boost::log::aux::add_common_attributes_constants<wchar_t> traits_t;
138 typedef boost::log::sinks::synchronous_sink<boost::log::sinks::wtext_file_backend> file_sink_type;
140 typedef boost::log::sinks::asynchronous_sink<boost::log::sinks::wtext_ostream_backend> stream_sink_type;
142 auto stream_backend = boost::make_shared<boost::log::sinks::wtext_ostream_backend>();
143 stream_backend->add_stream(boost::shared_ptr<std::wostream>(&std::wcout, boost::log::empty_deleter()));
144 stream_backend->auto_flush(true);
146 auto stream_sink = boost::make_shared<stream_sink_type>(stream_backend);
149 // stream_sink->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= debug);
151 // stream_sink->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= debug);
154 stream_sink->locked_backend()->set_formatter(boost::bind(my_formatter, false, _1, _2));
156 boost::log::wcore::get()->add_sink(stream_sink);
159 std::wstring get_call_stack()
161 class log_call_stack_walker : public stack_walker
165 log_call_stack_walker() : stack_walker() {}
169 return std::move(str_);
172 virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName)
175 virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion)
178 virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr)
181 virtual void OnOutput(LPCSTR szText)
183 std::string str = szText;
185 if(str.find("internal::get_call_stack") == std::string::npos && str.find("stack_walker::ShowCallstack") == std::string::npos)
186 str_ += std::move(str);
190 static tbb::enumerable_thread_specific<log_call_stack_walker> walkers;
193 auto& walker = walkers.local();
194 walker.ShowCallstack();
195 return u16(walker.flush());
205 void add_file_sink(const std::wstring& folder)
207 boost::log::add_common_attributes<wchar_t>();
208 typedef boost::log::aux::add_common_attributes_constants<wchar_t> traits_t;
210 typedef boost::log::sinks::synchronous_sink<boost::log::sinks::wtext_file_backend> file_sink_type;
214 if(!boost::filesystem::is_directory(folder))
215 CASPAR_THROW_EXCEPTION(directory_not_found());
217 auto file_sink = boost::make_shared<file_sink_type>(
218 boost::log::keywords::file_name = (folder + L"caspar_%Y-%m-%d.log"),
219 boost::log::keywords::time_based_rotation = boost::log::sinks::file::rotation_at_time_point(0, 0, 0),
220 boost::log::keywords::auto_flush = true,
221 boost::log::keywords::open_mode = std::ios::app
224 file_sink->locked_backend()->set_formatter(boost::bind(my_formatter, true, _1, _2));
227 // file_sink->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= debug);
229 // file_sink->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= debug);
231 boost::log::wcore::get()->add_sink(file_sink);
235 std::wcerr << L"Failed to Setup File Logging Sink" << std::endl << std::endl;
239 void set_log_level(const std::wstring& lvl)
241 if(boost::iequals(lvl, L"trace"))
242 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= trace);
243 else if(boost::iequals(lvl, L"debug"))
244 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= debug);
245 else if(boost::iequals(lvl, L"info"))
246 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= info);
247 else if(boost::iequals(lvl, L"warning"))
248 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= warning);
249 else if(boost::iequals(lvl, L"error"))
250 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= error);
251 else if(boost::iequals(lvl, L"fatal"))
252 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= fatal);