diff options
Diffstat (limited to 'rbutil/rbutilqt/logger/Logger.cpp')
-rw-r--r-- | rbutil/rbutilqt/logger/Logger.cpp | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/rbutil/rbutilqt/logger/Logger.cpp b/rbutil/rbutilqt/logger/Logger.cpp new file mode 100644 index 0000000000..33ba50ec58 --- /dev/null +++ b/rbutil/rbutilqt/logger/Logger.cpp | |||
@@ -0,0 +1,370 @@ | |||
1 | /* | ||
2 | Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com) | ||
3 | |||
4 | This program is free software: you can redistribute it and/or modify | ||
5 | it under the terms of the GNU Lesser General Public License version 2.1 | ||
6 | as published by the Free Software Foundation and appearing in the file | ||
7 | LICENSE.LGPL included in the packaging of this file. | ||
8 | |||
9 | This program is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | GNU Lesser General Public License for more details. | ||
13 | */ | ||
14 | // Local | ||
15 | #include "Logger.h" | ||
16 | #include "AbstractAppender.h" | ||
17 | |||
18 | // Qt | ||
19 | #include <QCoreApplication> | ||
20 | #include <QReadWriteLock> | ||
21 | #include <QSemaphore> | ||
22 | #include <QDateTime> | ||
23 | #include <QIODevice> | ||
24 | #include <QTextCodec> | ||
25 | |||
26 | // STL | ||
27 | #include <iostream> | ||
28 | |||
29 | |||
30 | class LogDevice : public QIODevice | ||
31 | { | ||
32 | public: | ||
33 | LogDevice() | ||
34 | : m_semaphore(1) | ||
35 | {} | ||
36 | |||
37 | void lock(Logger::LogLevel logLevel, const char* file, int line, const char* function) | ||
38 | { | ||
39 | m_semaphore.acquire(); | ||
40 | |||
41 | if (!isOpen()) | ||
42 | open(QIODevice::WriteOnly); | ||
43 | |||
44 | m_logLevel = logLevel; | ||
45 | m_file = file; | ||
46 | m_line = line; | ||
47 | m_function = function; | ||
48 | } | ||
49 | |||
50 | protected: | ||
51 | qint64 readData(char*, qint64) | ||
52 | { | ||
53 | return 0; | ||
54 | } | ||
55 | |||
56 | qint64 writeData(const char* data, qint64 maxSize) | ||
57 | { | ||
58 | if (maxSize > 0) | ||
59 | Logger::write(m_logLevel, m_file, m_line, m_function, QString::fromLocal8Bit(QByteArray(data, maxSize))); | ||
60 | |||
61 | m_semaphore.release(); | ||
62 | return maxSize; | ||
63 | } | ||
64 | |||
65 | private: | ||
66 | QSemaphore m_semaphore; | ||
67 | Logger::LogLevel m_logLevel; | ||
68 | const char* m_file; | ||
69 | int m_line; | ||
70 | const char* m_function; | ||
71 | }; | ||
72 | |||
73 | |||
74 | // Forward declarations | ||
75 | static void cleanupLoggerPrivate(); | ||
76 | |||
77 | #if QT_VERSION >= 0x050000 | ||
78 | static void qtLoggerMessageHandler(QtMsgType, const QMessageLogContext& context, const QString& msg); | ||
79 | #else | ||
80 | static void qtLoggerMessageHandler(QtMsgType type, const char* msg); | ||
81 | #endif | ||
82 | |||
83 | /** | ||
84 | * \internal | ||
85 | * | ||
86 | * LoggerPrivate class implements the Singleton pattern in a thread-safe way. It uses a static pointer to itself | ||
87 | * protected by QReadWriteLock | ||
88 | * | ||
89 | * The appender list inside the LoggerPrivate class is also protected by QReadWriteLock so this class could be safely | ||
90 | * used in a multi-threaded application. | ||
91 | */ | ||
92 | class LoggerPrivate | ||
93 | { | ||
94 | public: | ||
95 | static LoggerPrivate* m_self; | ||
96 | static QReadWriteLock m_selfLock; | ||
97 | |||
98 | static LoggerPrivate* instance() | ||
99 | { | ||
100 | LoggerPrivate* result = 0; | ||
101 | { | ||
102 | QReadLocker locker(&m_selfLock); | ||
103 | result = m_self; | ||
104 | } | ||
105 | |||
106 | if (!result) | ||
107 | { | ||
108 | QWriteLocker locker(&m_selfLock); | ||
109 | m_self = new LoggerPrivate; | ||
110 | |||
111 | #if QT_VERSION >= 0x050000 | ||
112 | qInstallMessageHandler(qtLoggerMessageHandler); | ||
113 | #else | ||
114 | qInstallMsgHandler(qtLoggerMessageHandler); | ||
115 | #endif | ||
116 | qAddPostRoutine(cleanupLoggerPrivate); | ||
117 | result = m_self; | ||
118 | } | ||
119 | |||
120 | return result; | ||
121 | } | ||
122 | |||
123 | |||
124 | LoggerPrivate() | ||
125 | : m_logDevice(0) | ||
126 | {} | ||
127 | |||
128 | |||
129 | ~LoggerPrivate() | ||
130 | { | ||
131 | // Cleanup appenders | ||
132 | QReadLocker appendersLocker(&m_appendersLock); | ||
133 | foreach (AbstractAppender* appender, m_appenders) | ||
134 | delete appender; | ||
135 | |||
136 | // Cleanup device | ||
137 | QReadLocker deviceLocker(&m_logDeviceLock); | ||
138 | delete m_logDevice; | ||
139 | } | ||
140 | |||
141 | |||
142 | void registerAppender(AbstractAppender* appender) | ||
143 | { | ||
144 | QWriteLocker locker(&m_appendersLock); | ||
145 | |||
146 | if (!m_appenders.contains(appender)) | ||
147 | m_appenders.append(appender); | ||
148 | else | ||
149 | std::cerr << "Trying to register appender that was already registered" << std::endl; | ||
150 | } | ||
151 | |||
152 | |||
153 | LogDevice* logDevice() | ||
154 | { | ||
155 | LogDevice* result = 0; | ||
156 | { | ||
157 | QReadLocker locker(&m_logDeviceLock); | ||
158 | result = m_logDevice; | ||
159 | } | ||
160 | |||
161 | if (!result) | ||
162 | { | ||
163 | QWriteLocker locker(&m_logDeviceLock); | ||
164 | m_logDevice = new LogDevice; | ||
165 | result = m_logDevice; | ||
166 | } | ||
167 | |||
168 | return result; | ||
169 | } | ||
170 | |||
171 | |||
172 | void write(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line, const char* function, | ||
173 | const QString& message) | ||
174 | { | ||
175 | QReadLocker locker(&m_appendersLock); | ||
176 | |||
177 | if (!m_appenders.isEmpty()) | ||
178 | { | ||
179 | foreach (AbstractAppender* appender, m_appenders) | ||
180 | appender->write(timeStamp, logLevel, file, line, function, message); | ||
181 | } | ||
182 | else | ||
183 | { | ||
184 | // Fallback | ||
185 | QString result = QString(QLatin1String("[%1] <%2> %3")).arg(Logger::levelToString(logLevel), -7) | ||
186 | .arg(function).arg(message); | ||
187 | |||
188 | std::cerr << qPrintable(result) << std::endl; | ||
189 | } | ||
190 | |||
191 | if (logLevel == Logger::Fatal) | ||
192 | abort(); | ||
193 | } | ||
194 | |||
195 | |||
196 | void write(Logger::LogLevel logLevel, const char* file, int line, const char* function, const QString& message) | ||
197 | { | ||
198 | write(QDateTime::currentDateTime(), logLevel, file, line, function, message); | ||
199 | } | ||
200 | |||
201 | |||
202 | void write(Logger::LogLevel logLevel, const char* file, int line, const char* function, const char* message) | ||
203 | { | ||
204 | write(logLevel, file, line, function, QString(message)); | ||
205 | } | ||
206 | |||
207 | |||
208 | QDebug write(Logger::LogLevel logLevel, const char* file, int line, const char* function) | ||
209 | { | ||
210 | LogDevice* d = logDevice(); | ||
211 | d->lock(logLevel, file, line, function); | ||
212 | return QDebug(d); | ||
213 | } | ||
214 | |||
215 | |||
216 | void writeAssert(const char* file, int line, const char* function, const char* condition) | ||
217 | { | ||
218 | write(Logger::Fatal, file, line, function, QString("ASSERT: \"%1\"").arg(condition)); | ||
219 | } | ||
220 | |||
221 | private: | ||
222 | QList<AbstractAppender*> m_appenders; | ||
223 | QReadWriteLock m_appendersLock; | ||
224 | |||
225 | LogDevice* m_logDevice; | ||
226 | QReadWriteLock m_logDeviceLock; | ||
227 | }; | ||
228 | |||
229 | // Static fields initialization | ||
230 | LoggerPrivate* LoggerPrivate::m_self = 0; | ||
231 | QReadWriteLock LoggerPrivate::m_selfLock; | ||
232 | |||
233 | |||
234 | static void cleanupLoggerPrivate() | ||
235 | { | ||
236 | QWriteLocker locker(&LoggerPrivate::m_selfLock); | ||
237 | |||
238 | delete LoggerPrivate::m_self; | ||
239 | LoggerPrivate::m_self = 0; | ||
240 | } | ||
241 | |||
242 | |||
243 | #if QT_VERSION >= 0x050000 | ||
244 | static void qtLoggerMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg) | ||
245 | { | ||
246 | Logger::LogLevel level; | ||
247 | switch (type) | ||
248 | { | ||
249 | case QtDebugMsg: | ||
250 | level = Logger::Debug; | ||
251 | break; | ||
252 | case QtWarningMsg: | ||
253 | level = Logger::Warning; | ||
254 | break; | ||
255 | case QtCriticalMsg: | ||
256 | level = Logger::Error; | ||
257 | break; | ||
258 | case QtFatalMsg: | ||
259 | level = Logger::Fatal; | ||
260 | break; | ||
261 | } | ||
262 | |||
263 | Logger::write(level, context.file, context.line, context.function, msg); | ||
264 | } | ||
265 | |||
266 | #else | ||
267 | |||
268 | static void qtLoggerMessageHandler(QtMsgType type, const char* msg) | ||
269 | { | ||
270 | switch (type) | ||
271 | { | ||
272 | case QtDebugMsg: | ||
273 | LOG_DEBUG(msg); | ||
274 | break; | ||
275 | case QtWarningMsg: | ||
276 | LOG_WARNING(msg); | ||
277 | break; | ||
278 | case QtCriticalMsg: | ||
279 | LOG_ERROR(msg); | ||
280 | break; | ||
281 | case QtFatalMsg: | ||
282 | LOG_FATAL(msg); | ||
283 | break; | ||
284 | } | ||
285 | } | ||
286 | #endif | ||
287 | |||
288 | |||
289 | QString Logger::levelToString(Logger::LogLevel logLevel) | ||
290 | { | ||
291 | switch (logLevel) | ||
292 | { | ||
293 | case Trace: | ||
294 | return QLatin1String("Trace"); | ||
295 | case Debug: | ||
296 | return QLatin1String("Debug"); | ||
297 | case Info: | ||
298 | return QLatin1String("Info"); | ||
299 | case Warning: | ||
300 | return QLatin1String("Warning"); | ||
301 | case Error: | ||
302 | return QLatin1String("Error"); | ||
303 | case Fatal: | ||
304 | return QLatin1String("Fatal"); | ||
305 | } | ||
306 | |||
307 | return QString(); | ||
308 | } | ||
309 | |||
310 | |||
311 | Logger::LogLevel Logger::levelFromString(const QString& s) | ||
312 | { | ||
313 | QString str = s.trimmed().toLower(); | ||
314 | |||
315 | LogLevel result = Debug; | ||
316 | |||
317 | if (str == QLatin1String("trace")) | ||
318 | result = Trace; | ||
319 | else if (str == QLatin1String("debug")) | ||
320 | result = Debug; | ||
321 | else if (str == QLatin1String("info")) | ||
322 | result = Info; | ||
323 | else if (str == QLatin1String("warning")) | ||
324 | result = Warning; | ||
325 | else if (str == QLatin1String("error")) | ||
326 | result = Error; | ||
327 | else if (str == QLatin1String("fatal")) | ||
328 | result = Fatal; | ||
329 | |||
330 | return result; | ||
331 | } | ||
332 | |||
333 | |||
334 | void Logger::registerAppender(AbstractAppender* appender) | ||
335 | { | ||
336 | LoggerPrivate::instance()->registerAppender(appender); | ||
337 | } | ||
338 | |||
339 | |||
340 | void Logger::write(const QDateTime& timeStamp, LogLevel logLevel, const char* file, int line, const char* function, | ||
341 | const QString& message) | ||
342 | { | ||
343 | LoggerPrivate::instance()->write(timeStamp, logLevel, file, line, function, message); | ||
344 | } | ||
345 | |||
346 | |||
347 | void Logger::write(LogLevel logLevel, const char* file, int line, const char* function, const QString& message) | ||
348 | { | ||
349 | LoggerPrivate::instance()->write(logLevel, file, line, function, message); | ||
350 | } | ||
351 | |||
352 | |||
353 | void Logger::write(LogLevel logLevel, const char* file, int line, const char* function, const char* message, ...) | ||
354 | { | ||
355 | va_list va; | ||
356 | va_start(va, message); | ||
357 | LoggerPrivate::instance()->write(logLevel, file, line, function, QString().vsprintf(message,va)); | ||
358 | va_end(va); | ||
359 | } | ||
360 | |||
361 | QDebug Logger::write(LogLevel logLevel, const char* file, int line, const char* function) | ||
362 | { | ||
363 | return LoggerPrivate::instance()->write(logLevel, file, line, function); | ||
364 | } | ||
365 | |||
366 | |||
367 | void Logger::writeAssert(const char* file, int line, const char* function, const char* condition) | ||
368 | { | ||
369 | LoggerPrivate::instance()->writeAssert(file, line, function, condition); | ||
370 | } | ||