1 // Copyright (c) 2010, Amar Takhar <verm@aegisub.org>
2 //
3 // Permission to use, copy, modify, and distribute this software for any
4 // purpose with or without fee is hereby granted, provided that the above
5 // copyright notice and this permission notice appear in all copies.
6 //
7 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 
15 #include "libaegisub/log.h"
16 
17 #include "libaegisub/cajun/elements.h"
18 #include "libaegisub/cajun/writer.h"
19 #include "libaegisub/dispatch.h"
20 #include "libaegisub/util.h"
21 
22 #include <boost/filesystem/fstream.hpp>
23 #include <boost/filesystem/operations.hpp>
24 #include <boost/filesystem/path.hpp>
25 #include <boost/range/algorithm/remove_if.hpp>
26 #include <chrono>
27 
28 namespace agi { namespace log {
29 
30 /// Global log sink.
31 LogSink *log;
32 
33 /// Short Severity ID
34 /// Keep this ordered the same as Severity
35 const char *Severity_ID = "EAWID";
36 
LogSink()37 LogSink::LogSink() : queue(dispatch::Create()) { }
38 
~LogSink()39 LogSink::~LogSink() {
40 	// The destructor for emitters may try to log messages, so disable all the
41 	// emitters before destructing any
42 	decltype(emitters) emitters_temp;
43 	queue->Sync([&]{ swap(emitters_temp, emitters); });
44 }
45 
Log(SinkMessage const & sm)46 void LogSink::Log(SinkMessage const& sm) {
47 	queue->Async([=] {
48 		if (messages.size() < 250)
49 			messages.push_back(sm);
50 		else {
51 			messages[next_idx] = sm;
52 			if (++next_idx == 250)
53 				next_idx = 0;
54 		}
55 		for (auto& em : emitters) em->log(sm);
56 	});
57 }
58 
Subscribe(std::unique_ptr<Emitter> em)59 void LogSink::Subscribe(std::unique_ptr<Emitter> em) {
60 	LOG_D("agi/log/emitter/subscribe") << "Subscribe: " << this;
61 	auto tmp = em.release();
62 	queue->Sync([=] { emitters.emplace_back(tmp); });
63 }
64 
Unsubscribe(Emitter * em)65 void LogSink::Unsubscribe(Emitter *em) {
66 	queue->Sync([=] {
67 		emitters.erase(
68 			boost::remove_if(emitters, [=](std::unique_ptr<Emitter> const& e) { return e.get() == em; }),
69 			emitters.end());
70 	});
71 	LOG_D("agi/log/emitter/unsubscribe") << "Un-Subscribe: " << this;
72 }
73 
GetMessages() const74 decltype(LogSink::messages) LogSink::GetMessages() const {
75 	decltype(messages) ret;
76 	queue->Sync([&] {
77 		ret.reserve(messages.size());
78 		ret.insert(ret.end(), messages.begin() + next_idx, messages.end());
79 		ret.insert(ret.end(), messages.begin(), messages.begin() + next_idx);
80 	});
81 	return ret;
82 }
83 
Message(const char * section,Severity severity,const char * file,const char * func,int line)84 Message::Message(const char *section, Severity severity, const char *file, const char *func, int line)
85 : msg(buffer, sizeof buffer)
86 {
87 	using namespace std::chrono;
88 	sm.section = section;
89 	sm.severity = severity;
90 	sm.file = file;
91 	sm.func = func;
92 	sm.line = line;
93 	sm.time = duration_cast<nanoseconds>(steady_clock::now().time_since_epoch()).count();
94 }
95 
~Message()96 Message::~Message() {
97 	sm.message = std::string(buffer, (std::string::size_type)msg.tellp());
98 	agi::log::log->Log(sm);
99 }
100 
JsonEmitter(fs::path const & directory)101 JsonEmitter::JsonEmitter(fs::path const& directory)
102 : fp(new boost::filesystem::ofstream(unique_path(directory/util::strftime("%Y-%m-%d-%H-%M-%S-%%%%%%%%.json"))))
103 {
104 }
105 
log(SinkMessage const & sm)106 void JsonEmitter::log(SinkMessage const& sm) {
107 	json::Object entry;
108 	entry["sec"]      = sm.time / 1000000000;
109 	entry["usec"]     = sm.time % 1000000000;
110 	entry["severity"] = sm.severity;
111 	entry["section"]  = sm.section;
112 	entry["file"]     = sm.file;
113 	entry["func"]     = sm.func;
114 	entry["line"]     = sm.line;
115 	entry["message"]  = sm.message;
116 	agi::JsonWriter::Write(entry, *fp);
117 	fp->flush();
118 }
119 
120 } }
121