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