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
28 #include "compiler/vs/stack_walker.h"
35 #include <boost/shared_ptr.hpp>
36 #include <boost/make_shared.hpp>
37 #include <boost/filesystem/convenience.hpp>
38 #include <boost/date_time/posix_time/posix_time.hpp>
39 #include <boost/algorithm/string.hpp>
41 #include <boost/log/core.hpp>
42 #include <boost/log/trivial.hpp>
43 #include <boost/log/expressions.hpp>
45 #include <boost/log/sinks/text_file_backend.hpp>
46 #include <boost/log/sinks/text_ostream_backend.hpp>
47 #include <boost/log/sinks/sync_frontend.hpp>
48 #include <boost/log/sinks/async_frontend.hpp>
49 #include <boost/log/core/record.hpp>
50 #include <boost/log/attributes/attribute_value.hpp>
51 #include <boost/log/attributes/current_thread_id.hpp>
52 #include <boost/log/utility/setup/common_attributes.hpp>
54 #include <boost/core/null_deleter.hpp>
55 #include <boost/lambda/lambda.hpp>
56 #include <boost/bind.hpp>
57 #include <boost/lexical_cast.hpp>
59 #include <tbb/atomic.h>
60 #include <tbb/enumerable_thread_specific.h>
62 namespace caspar { namespace log {
64 using namespace boost;
66 template<typename Stream>
67 void append_timestamp(Stream& stream)
69 auto timestamp = boost::posix_time::microsec_clock::local_time();
70 auto date = timestamp.date();
71 auto time = timestamp.time_of_day();
72 auto milliseconds = time.fractional_seconds() / 1000; // microseconds to milliseconds
74 std::wstringstream buffer;
79 << std::setw(4) << date.year() << L"-" << std::setw(2) << date.month().as_number() << "-" << std::setw(2) << date.day().as_number()
81 << std::setw(2) << time.hours() << L":" << std::setw(2) << time.minutes() << L":" << std::setw(2) << time.seconds()
83 << std::setw(3) << milliseconds
86 stream << buffer.str();
91 tbb::atomic<int> column_width_;
93 column_writer(int initial_width = 0)
95 column_width_ = initial_width;
98 template<typename Stream, typename Val>
99 void write(Stream& out, const Val& value)
101 std::string to_string = boost::lexical_cast<std::string>(value);
102 int length = static_cast<int>(to_string.size());
105 while ((read_width = column_width_) < length && column_width_.compare_and_swap(length, read_width) != read_width);
106 read_width = column_width_;
108 out << L"[" << to_string << L"] ";
110 for (int n = 0; n < read_width - length; ++n)
115 template<typename Stream>
116 void my_formatter(bool print_all_characters, const boost::log::record_view& rec, Stream& strm)
118 static column_writer thread_id_column;
119 static column_writer severity_column(7);
120 namespace expr = boost::log::expressions;
122 append_timestamp(strm);
124 thread_id_column.write(strm, boost::log::extract<boost::log::attributes::current_thread_id::value_type>("ThreadID", rec).get().native_id());
125 severity_column.write(strm, boost::log::extract<boost::log::trivial::severity_level>("Severity", rec));
127 if (print_all_characters)
129 strm << rec[expr::message];
133 strm << replace_nonprintable_copy(rec[expr::message].get<std::wstring>(), L'?');
141 boost::log::add_common_attributes();
142 typedef boost::log::sinks::asynchronous_sink<boost::log::sinks::wtext_ostream_backend> stream_sink_type;
144 auto stream_backend = boost::make_shared<boost::log::sinks::wtext_ostream_backend>();
145 stream_backend->add_stream(boost::shared_ptr<std::wostream>(&std::wcout, boost::null_deleter()));
146 stream_backend->auto_flush(true);
148 auto stream_sink = boost::make_shared<stream_sink_type>(stream_backend);
150 bool print_all_characters = false;
151 stream_sink->set_formatter(boost::bind(&my_formatter<boost::log::wformatting_ostream>, print_all_characters, _1, _2));
153 boost::log::core::get()->add_sink(stream_sink);
156 std::wstring get_call_stack()
158 class log_call_stack_walker : public stack_walker
162 log_call_stack_walker() : stack_walker() {}
166 return std::move(str_);
169 virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName)
172 virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion)
175 virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr)
178 virtual void OnOutput(LPCSTR szText)
180 std::string str = szText;
182 if(str.find("internal::get_call_stack") == std::string::npos && str.find("stack_walker::ShowCallstack") == std::string::npos)
183 str_ += std::move(str);
187 static tbb::enumerable_thread_specific<log_call_stack_walker> walkers;
190 auto& walker = walkers.local();
191 walker.ShowCallstack();
192 return u16(walker.flush());
202 void add_file_sink(const std::wstring& folder)
204 typedef boost::log::sinks::synchronous_sink<boost::log::sinks::text_file_backend> file_sink_type;
208 if (!boost::filesystem::is_directory(folder))
209 BOOST_THROW_EXCEPTION(directory_not_found());
211 auto file_sink = boost::make_shared<file_sink_type>(
212 boost::log::keywords::file_name = (folder + L"caspar_%Y-%m-%d.log"),
213 boost::log::keywords::time_based_rotation = boost::log::sinks::file::rotation_at_time_point(0, 0, 0),
214 boost::log::keywords::auto_flush = true,
215 boost::log::keywords::open_mode = std::ios::app
218 bool print_all_characters = true;
220 file_sink->set_formatter(boost::bind(&my_formatter<boost::log::formatting_ostream>, print_all_characters, _1, _2));
221 boost::log::core::get()->add_sink(file_sink);
225 std::wcerr << L"Failed to Setup File Logging Sink" << std::endl << std::endl;
229 void set_log_level(const std::wstring& lvl)
231 if (boost::iequals(lvl, L"trace"))
232 boost::log::core::get()->set_filter(boost::log::trivial::severity >= boost::log::trivial::trace);
233 else if (boost::iequals(lvl, L"debug"))
234 boost::log::core::get()->set_filter(boost::log::trivial::severity >= boost::log::trivial::debug);
235 else if (boost::iequals(lvl, L"info"))
236 boost::log::core::get()->set_filter(boost::log::trivial::severity >= boost::log::trivial::info);
237 else if (boost::iequals(lvl, L"warning"))
238 boost::log::core::get()->set_filter(boost::log::trivial::severity >= boost::log::trivial::warning);
239 else if (boost::iequals(lvl, L"error"))
240 boost::log::core::get()->set_filter(boost::log::trivial::severity >= boost::log::trivial::error);
241 else if (boost::iequals(lvl, L"fatal"))
242 boost::log::core::get()->set_filter(boost::log::trivial::severity >= boost::log::trivial::fatal);