1 //////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2004-2021 musikcube team
4 //
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions are met:
9 //
10 // * Redistributions of source code must retain the above copyright notice,
11 // this list of conditions and the following disclaimer.
12 //
13 // * Redistributions in binary form must reproduce the above copyright
14 // notice, this list of conditions and the following disclaimer in the
15 // documentation and/or other materials provided with the distribution.
16 //
17 // * Neither the name of the author nor the names of other contributors may
18 // be used to endorse or promote products derived from this software
19 // without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
32 //
33 //////////////////////////////////////////////////////////////////////////////
34
35 #include <pch.hpp>
36 #include <musikcore/debug.h>
37 #include <musikcore/support/Common.h>
38 #include <functional>
39 #include <string>
40 #include <queue>
41 #include <thread>
42 #include <mutex>
43 #include <condition_variable>
44 #include <memory>
45 #include <iostream>
46 #include <time.h>
47
48 using namespace musik::core;
49
50 class log_queue;
51
52 static std::vector<std::unique_ptr<musik::debug::IBackend>> backends;
53 static std::thread* thread = nullptr;
54 static log_queue* queue = nullptr;
55 static std::recursive_mutex mutex;
56 static volatile bool cancel = true;
57
58 enum class debug_level {
59 verbose = 0,
60 info = 1,
61 warning = 2,
62 error = 3
63 };
64
65 class log_queue {
66 public:
67 struct log_entry {
log_entrylog_queue::log_entry68 log_entry(debug_level l, const std::string& t, const std::string& m) {
69 level = l;
70 tag = t;
71 message = m;
72 }
73
74 debug_level level;
75 std::string tag;
76 std::string message;
77 };
78
79 class stopped_exception {
80 };
81
log_queue()82 log_queue() {
83 active = true;
84 }
85
pop_top()86 log_entry* pop_top() {
87 std::unique_lock<std::mutex> lock(queue_mutex);
88 while ((queue.size() == 0) && (active == true)) {
89 wait_for_next_item_condition.wait(lock);
90 }
91
92 if (!active) {
93 return nullptr;
94 }
95
96 log_entry* top = queue.front();
97 queue.pop();
98 return top;
99 }
100
push(log_entry * f)101 bool push(log_entry* f) {
102 std::unique_lock<std::mutex> lock(queue_mutex);
103
104 if (active) {
105 bool was_empty = (queue.size() == 0);
106 queue.push(f);
107
108 if (was_empty) {
109 wait_for_next_item_condition.notify_one();
110 }
111
112 return true;
113 }
114
115 return false;
116 }
117
stop()118 void stop() {
119 std::unique_lock<std::mutex> lock(queue_mutex);
120 active = false;
121
122 while (queue.size() > 0) {
123 log_entry* top = queue.front();
124 queue.pop();
125 delete top;
126 }
127
128 wait_for_next_item_condition.notify_all();
129 }
130
131 private:
132 std::queue<log_entry*> queue;
133 std::condition_variable wait_for_next_item_condition;
134 std::mutex queue_mutex;
135 volatile bool active;
136 };
137
thread_proc()138 static void thread_proc() {
139 try {
140 while (!cancel) {
141 log_queue::log_entry* entry = queue->pop_top();
142 if (entry) {
143 for (auto& backend : backends) {
144 switch (entry->level) {
145 case debug_level::verbose:
146 backend->verbose(entry->tag, entry->message);
147 break;
148 case debug_level::info:
149 backend->info(entry->tag, entry->message);
150 break;
151 case debug_level::warning:
152 backend->warning(entry->tag, entry->message);
153 break;
154 case debug_level::error:
155 backend->error(entry->tag, entry->message);
156 break;
157 }
158 }
159 delete entry;
160 }
161 }
162 }
163 catch (log_queue::stopped_exception&) {
164 }
165 }
166
Start(std::vector<musik::debug::IBackend * > backends)167 void musik::debug::Start(std::vector<musik::debug::IBackend*> backends) {
168 std::unique_lock<std::recursive_mutex> lock(mutex);
169
170 if (queue || thread) {
171 return;
172 }
173
174 for (auto backend : backends) {
175 ::backends.push_back(std::unique_ptr<musik::debug::IBackend>(backend));
176 }
177
178 cancel = false;
179 queue = new log_queue();
180 thread = new std::thread(std::bind(&thread_proc));
181
182 info("LOG SESSION", "---------- START ----------");
183 }
184
Stop()185 void musik::debug::Stop() {
186 std::unique_lock<std::recursive_mutex> lock(mutex);
187
188 cancel = true;
189
190 if (thread && queue) {
191 queue->stop();
192 thread->join();
193
194 delete thread;
195 thread = nullptr;
196 delete queue;
197 queue = nullptr;
198 }
199
200 backends.clear();
201 }
202
enqueue(debug_level level,const std::string & tag,const std::string & string)203 static void enqueue(debug_level level, const std::string& tag, const std::string& string) noexcept {
204 try {
205 std::unique_lock<std::recursive_mutex> lock(mutex);
206
207 if (queue) {
208 queue->push(new log_queue::log_entry(level, tag, string));
209 }
210 }
211 catch (...) {
212 fprintf(stderr, "[%d] [%s] %s", static_cast<int>(level), tag.c_str(), string.c_str());
213 }
214 }
215
verbose(const std::string & tag,const std::string & string)216 void musik::debug::verbose(const std::string& tag, const std::string& string) noexcept {
217 enqueue(debug_level::verbose, tag, string);
218 }
219
v(const std::string & tag,const std::string & string)220 void musik::debug::v(const std::string& tag, const std::string& string) noexcept {
221 enqueue(debug_level::verbose, tag, string);
222 }
223
info(const std::string & tag,const std::string & string)224 void musik::debug::info(const std::string& tag, const std::string& string) noexcept {
225 enqueue(debug_level::info, tag, string);
226 }
227
i(const std::string & tag,const std::string & string)228 void musik::debug::i(const std::string& tag, const std::string& string) noexcept {
229 enqueue(debug_level::info, tag, string);
230 }
231
warning(const std::string & tag,const std::string & string)232 void musik::debug::warning(const std::string& tag, const std::string& string) noexcept {
233 enqueue(debug_level::warning, tag, string);
234 }
235
w(const std::string & tag,const std::string & string)236 void musik::debug::w(const std::string& tag, const std::string& string) noexcept {
237 enqueue(debug_level::warning, tag, string);
238 }
239
error(const std::string & tag,const std::string & string)240 void musik::debug::error(const std::string& tag, const std::string& string) noexcept {
241 enqueue(debug_level::error, tag, string);
242 }
243
e(const std::string & tag,const std::string & string)244 void musik::debug::e(const std::string& tag, const std::string& string) noexcept {
245 enqueue(debug_level::error, tag, string);
246 }
247
248 ////////// backend utils //////////
249
250 namespace musik {
251
timestamp()252 static std::string timestamp() {
253 time_t rawtime = { 0 };
254 struct tm * timeinfo;
255 char buffer [64];
256 time(&rawtime);
257 timeinfo = localtime (&rawtime);
258 strftime (buffer, sizeof(buffer), "%T", timeinfo);
259 return std::string(buffer);
260 }
261
writeTo(std::ostream & out,const std::string & level,const std::string & tag,const std::string & message)262 static void writeTo(
263 std::ostream& out,
264 const std::string& level,
265 const std::string& tag,
266 const std::string& message)
267 {
268 out << timestamp() << " [" << level << "] [" << tag << "] " << message << "\n";
269 out.flush();
270 }
271
272 }
273
274 ////////// FileBackend //////////
275
276 namespace musik {
277
FileBackend(const std::string & fn)278 debug::FileBackend::FileBackend(const std::string& fn)
279 : out(fn.c_str()) {
280
281 }
282
FileBackend(FileBackend && fn)283 debug::FileBackend::FileBackend(FileBackend&& fn) {
284 this->out.swap(fn.out);
285 }
286
~FileBackend()287 debug::FileBackend::~FileBackend() {
288 }
289
verbose(const std::string & tag,const std::string & string)290 void debug::FileBackend::verbose(const std::string& tag, const std::string& string) {
291 writeTo(this->out, "verbose", tag, string);
292 }
293
info(const std::string & tag,const std::string & string)294 void debug::FileBackend::info(const std::string& tag, const std::string& string) {
295 writeTo(this->out, "info", tag, string);
296 }
297
warning(const std::string & tag,const std::string & string)298 void debug::FileBackend::warning(const std::string& tag, const std::string& string) {
299 writeTo(this->out, "warning", tag, string);
300 }
301
error(const std::string & tag,const std::string & string)302 void debug::FileBackend::error(const std::string& tag, const std::string& string) {
303 writeTo(this->out, "error", tag, string);
304 }
305
306 }
307
308 ////////// SimpleFileBackend //////////
309
310 namespace musik {
311
SimpleFileBackend()312 debug::SimpleFileBackend::SimpleFileBackend()
313 : FileBackend(GetDataDirectory() + "log.txt") {
314 }
315
316 }
317
318 ////////// ConsoleBackend //////////
319
320 namespace musik {
321
ConsoleBackend()322 debug::ConsoleBackend::ConsoleBackend() {
323 }
324
~ConsoleBackend()325 debug::ConsoleBackend::~ConsoleBackend() {
326 }
327
verbose(const std::string & tag,const std::string & string)328 void debug::ConsoleBackend::verbose(const std::string& tag, const std::string& string) {
329 writeTo(std::cout, "verbose", tag, string);
330 }
331
info(const std::string & tag,const std::string & string)332 void debug::ConsoleBackend::info(const std::string& tag, const std::string& string) {
333 writeTo(std::cout, "info", tag, string);
334 }
335
warning(const std::string & tag,const std::string & string)336 void debug::ConsoleBackend::warning(const std::string& tag, const std::string& string) {
337 writeTo(std::cout, "warning", tag, string);
338 }
339
error(const std::string & tag,const std::string & string)340 void debug::ConsoleBackend::error(const std::string& tag, const std::string& string) {
341 writeTo(std::cerr, "error", tag, string);
342 }
343
344 }
345