1 /* Logger.cpp */
2 
3 /* Copyright (C) 2011-2020 Michael Lugmair (Lucio Carreras)
4  *
5  * This file is part of sayonara player
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11 
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16 
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "Utils/Utils.h"
22 #include "Utils/Settings/Settings.h"
23 #include "Utils/Logger/Logger.h"
24 #include "Utils/Logger/LogListener.h"
25 
26 
27 #include <QString>
28 #include <QStringList>
29 #include <QByteArray>
30 #include <QPoint>
31 #include <QDateTime>
32 #include <QSize>
33 #include <QMargins>
34 #include <QRect>
35 #include <QRegion>
36 
37 #include "LoggerUtils.h"
38 
39 #include <ostream>
40 #include <iostream>
41 #include <fstream>
42 #include <sstream>
43 #include <iomanip>
44 
45 #ifdef Q_OS_UNIX
46 	#define LOG_RED "\x1B[31m"
47 	#define LOG_GREEN "\x1B[32m"
48 	#define LOG_BLUE "\x1B[34m"
49 	#define LOG_YELLOW "\x1B[33m"
50 	#define LOG_COL_END "\x1B[0m"
51 #else
52 	#define LOG_RED ""
53 	#define LOG_GREEN ""
54 	#define LOG_BLUE ""
55 	#define LOG_YELLOW ""
56 	#define LOG_COL_END ""
57 #endif
58 
59 #ifdef SAYONARA_HAS_CXX_ABI
60 	#include "cxxabi.h"
61 #endif
62 
63 using LogListeners=QList<LogListener*>;
64 Q_GLOBAL_STATIC(LogListeners, log_listeners)
65 
66 using LogEntries=QList<LogEntry>;
67 Q_GLOBAL_STATIC(LogEntries, log_buffer)
68 
69 struct Logger::Private
70 {
71 	QString				class_name;
72 	std::stringstream	msg;
73 	Log					type;
74 
PrivateLogger::Private75 	Private() {}
76 
~PrivateLogger::Private77 	~Private()
78 	{
79 		QString type_str;
80 		std::string color;
81 
82 		bool ignore=false;
83 
84 		Settings* s = Settings::instance();
85 		int logger_level = 0;
86 
87 		if(s->checkSettings()){
88 			logger_level = GetSetting(Set::Logger_Level);
89 		}
90 
91 		switch(type)
92 		{
93 			case Log::Info:
94 				color = LOG_GREEN;
95 				type_str = "Info";
96 				break;
97 			case Log::Warning:
98 				color = LOG_RED;
99 				type_str = "Warning";
100 				break;
101 			case Log::Error:
102 				color = LOG_RED;
103 				type_str = "Error";
104 				break;
105 			case Log::Debug:
106 				color = LOG_YELLOW;
107 				type_str = "Debug";
108 				if(logger_level < 1) {
109 					ignore = true;
110 				}
111 				break;
112 			case Log::Develop:
113 				color = LOG_YELLOW;
114 				type_str = "Dev";
115 				if(logger_level < 2){
116 					ignore = true;
117 				}
118 				break;
119 			case Log::Crazy:
120 				color = LOG_YELLOW;
121 				type_str = "CrazyLog";
122 				if(logger_level < 3){
123 					ignore = true;
124 				}
125 
126 				break;
127 			case Log::Always:
128 				[[fallthrough]];
129 			default:
130 				color = LOG_YELLOW;
131 				type_str = "";
132 				break;
133 		}
134 
135 		if(!ignore)
136 		{
137 			QString date_time = QDateTime::currentDateTime().toString("hh:mm:ss");
138 
139 			std::string str(msg.str());
140 			std::clog
141 					<< "[" << date_time.toStdString() << "] "
142 					<< color
143 					<< type_str.toStdString() << ": "
144 					<< LOG_COL_END;
145 
146 			if(!class_name.isEmpty()) {
147 				std::clog << LOG_BLUE << class_name.toStdString() << ": " << LOG_COL_END;
148 			}
149 
150 			std::clog << str;
151 			std::clog << std::endl;
152 
153 			LogEntry le;
154 				le.className = class_name;
155 				le.message = QString::fromStdString(str);
156 				le.type = type;
157 
158 			log_buffer->push_back(le);
159 
160 			for(auto it=log_listeners->begin(); it != log_listeners->end(); it++)
161 			{
162 				LogListener* log_listener = *it;
163 				if(log_listener)
164 				{
165 					log_listener->addLogLine(le);
166 				}
167 			}
168 		}
169 
170 		msg.clear();
171 	}
172 };
173 
174 
Logger(const Log & type,const QString & class_name)175 Logger::Logger(const Log& type, const QString& class_name)
176 {
177 	m = new Logger::Private();
178 
179 	m->type = type;
180 	m->class_name = class_name;
181 }
182 
~Logger()183 Logger::~Logger()
184 {
185 	delete m;
186 	m = nullptr;
187 }
188 
189 //static
registerLogListener(LogListener * log_listener)190 void Logger::registerLogListener(LogListener* log_listener)
191 {
192 	for(auto it=log_buffer->begin(); it != log_buffer->end(); it++)
193 	{
194 		log_listener->addLogLine(*it);
195 	}
196 
197 	log_listeners->push_back(log_listener);
198 }
199 
200 
operator <<(const QString & msg)201 Logger& Logger::operator << (const QString& msg)
202 {
203 	(*this) << msg.toLocal8Bit().constData();
204 	return *this;
205 }
206 
operator <<(const QStringList & lst)207 Logger& Logger::operator << (const QStringList& lst)
208 {
209 	(*this) << lst.join(",");
210 
211 	return *this;
212 }
213 
operator <<(const QChar & c)214 Logger& Logger::operator << (const QChar& c)
215 {
216 	(*this) << c.toLatin1();
217 
218 	return *this;
219 }
220 
operator <<(const QPoint & point)221 Logger& Logger::operator << (const QPoint& point)
222 {
223 	(*this) << "Point(" << point.x() << "," << point.y() << ")";
224 	return *this;
225 }
226 
operator <<(const QSize & size)227 Logger& Logger::operator <<(const QSize& size)
228 {
229 	(*this) << "Size(" << size.width() << "," << size.height() << ")";
230 	return *this;
231 }
232 
operator <<(const QRect & r)233 Logger& Logger::operator <<(const QRect& r)
234 {
235 	(*this) << "Rect("
236 			<< "left:" << r.left()
237 			<< ", right:" << r.right()
238 			<< ", top:" << r.top()
239 			<< ", bottom:" << r.bottom()
240 			<< ", width:" << r.width()
241 			<< ", heigh:" << r.height();
242 	return *this;
243 }
244 
245 
operator <<(const QByteArray & arr)246 Logger& Logger::operator << (const QByteArray& arr)
247 {
248 	m->msg << std::endl;
249 
250 	QString line_str;
251 
252 	for(int i=0; i<arr.size(); i++)
253 	{
254 		char c = arr[i];
255 
256 		QChar qc = QChar(c);
257 
258 		if(qc.isPrint()){
259 			line_str += qc;
260 		}
261 
262 		else{
263 			line_str += ".";
264 		}
265 
266 		m->msg << std::hex << (unsigned int) (c & (0xff)) << " ";
267 
268 		if(i % 8 == 7)
269 		{
270 			m->msg << "\t";
271 			m->msg << line_str.toLocal8Bit().constData() << std::endl;
272 
273 			line_str.clear();
274 		}
275 	}
276 
277 	if(!line_str.isEmpty())
278 	{
279 		for(int i=0; i<8-line_str.size(); i++)
280 		{
281 			m->msg << "   ";
282 		}
283 
284 		m->msg << "\t" << line_str.toLocal8Bit().constData() << std::endl;
285 	}
286 
287 	return *this;
288 }
289 
operator <<(const char * str)290 Logger& Logger::operator << (const char* str)
291 {
292 	m->msg << str;
293 
294 	return *this;
295 }
296 
operator <<(const std::string & str)297 Logger& Logger::operator << (const std::string& str)
298 {
299 	(*this) << str.c_str();
300 	return *this;
301 }
302 
303 
304 /*************************
305  * Static Log functions
306  * ***********************/
spLog(const Log & type,const std::string & data)307 Logger spLog(const Log& type, const std::string& data)
308 {
309 	QString class_name;
310 	if(!data.empty())
311 	{
312 #ifdef SAYONARA_HAS_CXX_ABI
313 		int status;
314 		char* content = abi::__cxa_demangle(data.c_str(), nullptr, nullptr, &status);
315 		if(content && strnlen(content, 3) > 1){
316 			class_name = QString(content);
317 			free(content);
318 		}
319 		else {
320 			class_name = QString::fromStdString(data);
321 		}
322 
323 #else
324 		class_name = QString::fromStdString(data);
325 #endif
326 	}
327 
328 	return Logger(type, class_name);
329 }
330 
spLog(const Log & type,const char * data)331 Logger spLog(const Log& type, const char* data)
332 {
333 	return spLog(type, std::string(data));
334 }
335