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
170 virtual void OnOutput(LPCSTR szText)
\r
176 static tbb::enumerable_thread_specific<log_call_stack_walker> walkers;
\r
179 auto& walker = walkers.local();
\r
180 walker.ShowCallstack();
\r
181 return u16(walker.flush());
\r
191 void add_file_sink(const std::wstring& folder)
\r
193 boost::log::add_common_attributes<wchar_t>();
\r
194 typedef boost::log::aux::add_common_attributes_constants<wchar_t> traits_t;
\r
196 typedef boost::log::sinks::synchronous_sink<boost::log::sinks::wtext_file_backend> file_sink_type;
\r
200 if(!boost::filesystem::is_directory(folder))
\r
201 BOOST_THROW_EXCEPTION(directory_not_found());
\r
203 auto file_sink = boost::make_shared<file_sink_type>(
\r
204 boost::log::keywords::file_name = (folder + L"caspar_%Y-%m-%d.log"),
\r
205 boost::log::keywords::time_based_rotation = boost::log::sinks::file::rotation_at_time_point(0, 0, 0),
\r
206 boost::log::keywords::auto_flush = true,
\r
207 boost::log::keywords::open_mode = std::ios::app
\r
210 file_sink->locked_backend()->set_formatter(&my_formatter);
\r
213 // file_sink->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= debug);
\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 boost::log::wcore::get()->add_sink(file_sink);
\r
221 std::wcerr << L"Failed to Setup File Logging Sink" << std::endl << std::endl;
\r
225 void set_log_level(const std::wstring& lvl)
\r
227 if(boost::iequals(lvl, L"trace"))
\r
228 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= trace);
\r
229 else if(boost::iequals(lvl, L"debug"))
\r
230 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= debug);
\r
231 else if(boost::iequals(lvl, L"info"))
\r
232 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= info);
\r
233 else if(boost::iequals(lvl, L"warning"))
\r
234 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= warning);
\r
235 else if(boost::iequals(lvl, L"error"))
\r
236 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= error);
\r
237 else if(boost::iequals(lvl, L"fatal"))
\r
238 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= fatal);
\r