2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
\r
4 * This file is part of CasparCG (www.casparcg.com).
\r
6 * CasparCG is free software: you can redistribute it and/or modify
\r
7 * it under the terms of the GNU General Public License as published by
\r
8 * the Free Software Foundation, either version 3 of the License, or
\r
9 * (at your option) any later version.
\r
11 * CasparCG is distributed in the hope that it will be useful,
\r
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 * GNU General Public License for more details.
\r
16 * You should have received a copy of the GNU General Public License
\r
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
\r
19 * Author: Robert Nagy, ronag89@gmail.com
\r
24 #if defined(_MSC_VER)
\r
25 #pragma warning (disable : 4100) // 'identifier' : unreferenced formal parameter
\r
26 #pragma warning (disable : 4512) // 'class' : assignment operator could not be generated
\r
33 #include "compiler/vs/stack_walker.h"
\r
39 #include <boost/shared_ptr.hpp>
\r
40 #include <boost/make_shared.hpp>
\r
41 #include <boost/filesystem/convenience.hpp>
\r
42 #include <boost/date_time/posix_time/posix_time.hpp>
\r
43 #include <boost/algorithm/string.hpp>
\r
45 #include <boost/log/core/core.hpp>
\r
47 #include <boost/log/formatters/stream.hpp>
\r
48 #include <boost/log/formatters/attr.hpp>
\r
49 #include <boost/log/formatters/date_time.hpp>
\r
50 #include <boost/log/formatters/message.hpp>
\r
52 #include <boost/log/filters/attr.hpp>
\r
54 #include <boost/log/sinks/text_file_backend.hpp>
\r
56 #include <boost/log/detail/universal_path.hpp>
\r
58 #include <boost/log/sinks/text_file_backend.hpp>
\r
59 #include <boost/log/sinks/text_ostream_backend.hpp>
\r
60 #include <boost/log/sinks/sync_frontend.hpp>
\r
61 #include <boost/log/sinks/async_frontend.hpp>
\r
62 #include <boost/log/core/record.hpp>
\r
63 #include <boost/log/utility/attribute_value_extractor.hpp>
\r
65 #include <boost/log/utility/init/common_attributes.hpp>
\r
66 #include <boost/log/utility/empty_deleter.hpp>
\r
67 #include <boost/lambda/lambda.hpp>
\r
69 #include <tbb/enumerable_thread_specific.h>
\r
71 namespace caspar { namespace log {
\r
73 using namespace boost;
\r
75 template<typename T>
\r
76 inline void replace_nonprintable(std::basic_string<T, std::char_traits<T>, std::allocator<T>>& str, T with)
\r
79 std::replace_if(str.begin(), str.end(), [&](T c)->bool { return !std::isprint(c, loc) && c != '\r' && c != '\n'; }, with);
\r
82 template<typename T>
\r
83 inline std::basic_string<T> replace_nonprintable_copy(std::basic_string<T, std::char_traits<T>, std::allocator<T>> str, T with)
\r
85 replace_nonprintable(str, with);
\r
89 void my_formatter(std::wostream& strm, boost::log::basic_record<wchar_t> const& rec)
\r
91 namespace lambda = boost::lambda;
\r
93 #pragma warning(disable : 4996)
\r
95 struct tm* timeinfo;
\r
97 timeinfo = localtime ( &rawtime );
\r
99 strftime (buffer,80, "%c", timeinfo);
\r
100 strm << L"[" << buffer << L"] ";
\r
102 boost::log::attributes::current_thread_id::held_type thread_id;
\r
103 if(boost::log::extract<boost::log::attributes::current_thread_id::held_type>(L"ThreadID", rec.attribute_values(), lambda::var(thread_id) = lambda::_1))
\r
104 strm << L"[" << thread_id << L"] ";
\r
106 severity_level severity;
\r
107 if(boost::log::extract<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get(), rec.attribute_values(), lambda::var(severity) = lambda::_1))
\r
109 std::stringstream ss;
\r
111 strm << L"[" << severity << L"] ";
\r
112 for(int n = 0; n < 7-static_cast<int>(ss.str().size()); ++n)
\r
116 strm << replace_nonprintable_copy(rec.message(), L'?');
\r
119 namespace internal{
\r
123 boost::log::add_common_attributes<wchar_t>();
\r
124 typedef boost::log::aux::add_common_attributes_constants<wchar_t> traits_t;
\r
126 typedef boost::log::sinks::synchronous_sink<boost::log::sinks::wtext_file_backend> file_sink_type;
\r
128 typedef boost::log::sinks::asynchronous_sink<boost::log::sinks::wtext_ostream_backend> stream_sink_type;
\r
130 auto stream_backend = boost::make_shared<boost::log::sinks::wtext_ostream_backend>();
\r
131 stream_backend->add_stream(boost::shared_ptr<std::wostream>(&std::wcout, boost::log::empty_deleter()));
\r
132 stream_backend->auto_flush(true);
\r
134 auto stream_sink = boost::make_shared<stream_sink_type>(stream_backend);
\r
137 // stream_sink->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= debug);
\r
139 // stream_sink->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= debug);
\r
142 stream_sink->locked_backend()->set_formatter(&my_formatter);
\r
144 boost::log::wcore::get()->add_sink(stream_sink);
\r
147 std::wstring get_call_stack()
\r
149 class log_call_stack_walker : public stack_walker
\r
153 log_call_stack_walker() : stack_walker() {}
\r
155 std::string flush()
\r
157 return std::move(str_);
\r
160 virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName)
\r
163 virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion)
\r
166 virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr)
\r
169 virtual void OnOutput(LPCSTR szText)
\r
171 std::string str = szText;
\r
173 if(str.find("internal::get_call_stack") == std::string::npos && str.find("stack_walker::ShowCallstack") == std::string::npos)
\r
174 str_ += std::move(str);
\r
178 static tbb::enumerable_thread_specific<log_call_stack_walker> walkers;
\r
181 auto& walker = walkers.local();
\r
182 walker.ShowCallstack();
\r
183 return u16(walker.flush());
\r
193 void add_file_sink(const std::wstring& folder)
\r
195 boost::log::add_common_attributes<wchar_t>();
\r
196 typedef boost::log::aux::add_common_attributes_constants<wchar_t> traits_t;
\r
198 typedef boost::log::sinks::synchronous_sink<boost::log::sinks::wtext_file_backend> file_sink_type;
\r
202 if(!boost::filesystem::is_directory(folder))
\r
203 CASPAR_THROW_EXCEPTION(directory_not_found());
\r
205 auto file_sink = boost::make_shared<file_sink_type>(
\r
206 boost::log::keywords::file_name = (folder + L"caspar_%Y-%m-%d.log"),
\r
207 boost::log::keywords::time_based_rotation = boost::log::sinks::file::rotation_at_time_point(0, 0, 0),
\r
208 boost::log::keywords::auto_flush = true,
\r
209 boost::log::keywords::open_mode = std::ios::app
\r
212 file_sink->locked_backend()->set_formatter(&my_formatter);
\r
215 // file_sink->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= debug);
\r
217 // file_sink->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= debug);
\r
219 boost::log::wcore::get()->add_sink(file_sink);
\r
223 std::wcerr << L"Failed to Setup File Logging Sink" << std::endl << std::endl;
\r
227 void set_log_level(const std::wstring& lvl)
\r
229 if(boost::iequals(lvl, L"trace"))
\r
230 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= trace);
\r
231 else if(boost::iequals(lvl, L"debug"))
\r
232 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= debug);
\r
233 else if(boost::iequals(lvl, L"info"))
\r
234 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= info);
\r
235 else if(boost::iequals(lvl, L"warning"))
\r
236 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= warning);
\r
237 else if(boost::iequals(lvl, L"error"))
\r
238 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= error);
\r
239 else if(boost::iequals(lvl, L"fatal"))
\r
240 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= fatal);
\r