]> git.sesse.net Git - casparcg/blob - common/log.cpp
Manually merged 0471dfe74a9f61e2fc2c03a963d71ba2bb68adc3 from master
[casparcg] / common / log.cpp
1 /*
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
3 *
4 * This file is part of CasparCG (www.casparcg.com).
5 *
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.
10 *
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.
15 *
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/>.
18 *
19 * Author: Robert Nagy, ronag89@gmail.com
20 */
21
22 #include "stdafx.h"
23
24 #if defined(_MSC_VER)
25 #pragma warning (disable : 4100) // 'identifier' : unreferenced formal parameter
26 #pragma warning (disable : 4512) // 'class' : assignment operator could not be generated
27 #endif
28
29 #include "log.h"
30
31 #include "except.h"
32 #include "utf.h"
33 #include "compiler/vs/stack_walker.h"
34
35 #include <ios>
36 #include <iomanip>
37 #include <string>
38 #include <ostream>
39
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>
46
47 #include <boost/log/core/core.hpp>
48
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>
53
54 #include <boost/log/filters/attr.hpp>
55
56 #include <boost/log/sinks/text_file_backend.hpp>
57
58 #include <boost/log/detail/universal_path.hpp>
59
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>
66
67 #include <boost/log/utility/init/common_attributes.hpp>
68 #include <boost/log/utility/empty_deleter.hpp>
69 #include <boost/lambda/lambda.hpp>
70
71 #include <tbb/enumerable_thread_specific.h>
72
73 namespace caspar { namespace log {
74
75 using namespace boost;
76
77 void append_timestamp(std::wostream& stream)
78 {
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
83
84         std::wstringstream buffer;
85
86         buffer
87                 << std::setfill(L'0')
88                 << L"["
89                 << std::setw(4) << date.year() << L"-" << std::setw(2) << date.month().as_number() << "-" << std::setw(2) << date.day().as_number()
90                 << L" "
91                 << std::setw(2) << time.hours() << L":" << std::setw(2) << time.minutes() << L":" << std::setw(2) << time.seconds()
92                 << L"."
93                 << std::setw(3) << milliseconds
94                 << L"] ";
95
96         stream << buffer.str();
97 }
98
99 void my_formatter(bool print_all_characters, std::wostream& strm, boost::log::basic_record<wchar_t> const& rec)
100 {
101     namespace lambda = boost::lambda;
102         
103         #pragma warning(disable : 4996)
104         
105         append_timestamp(strm);
106                 
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"] ";
110         
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))
113         {
114                 std::stringstream ss;
115                 ss << severity;
116         strm << L"[" << severity << L"] ";
117                 for(int n = 0; n < 7-static_cast<int>(ss.str().size()); ++n)
118                         strm << L" ";
119         }
120
121         if (print_all_characters)
122         {
123                 strm << rec.message();
124         }
125         else
126         {
127             strm << replace_nonprintable_copy(rec.message(), L'?');
128         }
129 }
130
131 namespace internal{
132         
133 void init()
134 {       
135         boost::log::add_common_attributes<wchar_t>();
136         typedef boost::log::aux::add_common_attributes_constants<wchar_t> traits_t;
137
138         typedef boost::log::sinks::synchronous_sink<boost::log::sinks::wtext_file_backend> file_sink_type;
139
140         typedef boost::log::sinks::asynchronous_sink<boost::log::sinks::wtext_ostream_backend> stream_sink_type;
141
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);
145
146         auto stream_sink = boost::make_shared<stream_sink_type>(stream_backend);
147         
148 //#ifdef NDEBUG
149 //      stream_sink->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= debug);
150 //#else
151 //      stream_sink->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= debug);
152 //#endif
153
154         stream_sink->locked_backend()->set_formatter(boost::bind(my_formatter, false, _1, _2));
155
156         boost::log::wcore::get()->add_sink(stream_sink);
157 }
158
159 std::wstring get_call_stack()
160 {
161         class log_call_stack_walker : public stack_walker
162         {
163                 std::string str_;
164         public:
165                 log_call_stack_walker() : stack_walker() {}
166
167                 std::string flush()
168                 {
169                         return std::move(str_);
170                 }
171         protected:              
172                 virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName)
173                 {
174                 }
175                 virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion)
176                 {
177                 }
178                 virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr)
179                 {
180                 }
181                 virtual void OnOutput(LPCSTR szText)
182                 {
183                         std::string str = szText;
184
185                         if(str.find("internal::get_call_stack") == std::string::npos && str.find("stack_walker::ShowCallstack") == std::string::npos)
186                                 str_ += std::move(str);
187                 }
188         };
189
190         static tbb::enumerable_thread_specific<log_call_stack_walker> walkers;
191         try
192         {
193                 auto& walker = walkers.local();
194                 walker.ShowCallstack();
195                 return u16(walker.flush());
196         }
197         catch(...)
198         {
199                 return L"!!!";
200         }
201 }
202
203 }
204
205 void add_file_sink(const std::wstring& folder)
206 {       
207         boost::log::add_common_attributes<wchar_t>();
208         typedef boost::log::aux::add_common_attributes_constants<wchar_t> traits_t;
209
210         typedef boost::log::sinks::synchronous_sink<boost::log::sinks::wtext_file_backend> file_sink_type;
211
212         try
213         {
214                 if(!boost::filesystem::is_directory(folder))
215                         CASPAR_THROW_EXCEPTION(directory_not_found());
216
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
222                 );
223                 
224                 file_sink->locked_backend()->set_formatter(boost::bind(my_formatter, true, _1, _2));
225
226 //#ifdef NDEBUG
227 //              file_sink->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= debug);
228 //#else
229 //              file_sink->set_filter(boost::log::filters::attr<severity_level>(boost::log::sources::aux::severity_attribute_name<wchar_t>::get()) >= debug);
230 //#endif
231                 boost::log::wcore::get()->add_sink(file_sink);
232         }
233         catch(...)
234         {
235                 std::wcerr << L"Failed to Setup File Logging Sink" << std::endl << std::endl;
236         }
237 }
238
239 void set_log_level(const std::wstring& lvl)
240 {       
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);
253 }
254
255 }}