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
177 auto& walker = walkers.local();
\r
178 walker.ShowCallstack();
\r
179 return u16(walker.flush());
\r
184 void add_file_sink(const std::wstring& folder)
\r
186 boost::log::add_common_attributes<wchar_t>();
\r
187 typedef boost::log::aux::add_common_attributes_constants<wchar_t> traits_t;
\r
189 typedef boost::log::sinks::synchronous_sink<boost::log::sinks::wtext_file_backend> file_sink_type;
\r
193 if(!boost::filesystem::is_directory(folder))
\r
194 BOOST_THROW_EXCEPTION(directory_not_found());
\r
196 auto file_sink = boost::make_shared<file_sink_type>(
\r
197 boost::log::keywords::file_name = (folder + L"caspar_%Y-%m-%d.log"),
\r
198 boost::log::keywords::time_based_rotation = boost::log::sinks::file::rotation_at_time_point(0, 0, 0),
\r
199 boost::log::keywords::auto_flush = true,
\r
200 boost::log::keywords::open_mode = std::ios::app
\r
203 file_sink->locked_backend()->set_formatter(&my_formatter);
\r
206 // file_sink->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= debug);
\r
208 // file_sink->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= debug);
\r
210 boost::log::wcore::get()->add_sink(file_sink);
\r
214 std::wcerr << L"Failed to Setup File Logging Sink" << std::endl << std::endl;
\r
218 void set_log_level(const std::wstring& lvl)
\r
220 if(boost::iequals(lvl, L"trace"))
\r
221 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= trace);
\r
222 else if(boost::iequals(lvl, L"debug"))
\r
223 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= debug);
\r
224 else if(boost::iequals(lvl, L"info"))
\r
225 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= info);
\r
226 else if(boost::iequals(lvl, L"warning"))
\r
227 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= warning);
\r
228 else if(boost::iequals(lvl, L"error"))
\r
229 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= error);
\r
230 else if(boost::iequals(lvl, L"fatal"))
\r
231 boost::log::wcore::get()->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= fatal);
\r