2 * @file Common/logger.cpp
3 * @brief iLogger interface implementation
6 * Copyright (c) 2011 Enar Vaikene
8 * This file is part of the eVaf C++ cross-platform application development framework.
10 * This file can be used under the terms of the GNU General Public License
11 * version 3.0 as published by the Free Software Foundation and appearing in
12 * the file LICENSE included in the packaging of this file. Please review the
13 * the following information to ensure the GNU General Public License version
14 * 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
16 * Alternatively, this file may be used in accordance with the Commercial License
17 * Agreement provided with the Software.
21 #include "iregistry.h"
38 //-------------------------------------------------------------------
40 void eVaf::Common::Internal::defFatalMsgHandler(QString
const & msg
, QString
const & source
, QString
const & where
)
44 fprintf(stderr
, "FATAL ERROR: %s (occurred in %s)\n", qPrintable(msg
), qPrintable(where
));
54 //-------------------------------------------------------------------
56 using namespace eVaf::Common
;
58 iLogger
* iLogger::instance()
60 static Internal::Logger singleton
;
65 //-------------------------------------------------------------------
67 using namespace eVaf::Common::Internal
;
69 LoggerSource::LoggerSource()
71 , severity(iLogger::Fatal
)
76 LoggerSource::LoggerSource(LoggerSource
const & o
)
78 , severity(o
.severity
)
80 , maxCount(o
.maxCount
)
83 void LoggerSource::init(QString
const & source
, QString
const & logDir
, QString
const & etcDir
)
87 fileName
= logDir
+ source
+ ".log";
91 //-------------------------------------------------------------------
93 /// Recursively renames backup files
94 void renameBackupFile(QDir
& dir
, QString
const & baseName
, int idx
)
96 QString f1
= QString("%1.%2").arg(baseName
).arg(idx
);
97 QString f2
= QString("%1.%2").arg(baseName
).arg(idx
+ 1);
100 renameBackupFile(dir
, baseName
, idx
+ 1);
105 void LoggerWorker::writeToLogFile(LoggerSource
const & src
, QString
const & msg
)
107 QFile
f(src
.fileName
);
108 QFile::OpenMode mode
;
110 mode
= QFile::Append
| QFile::Text
| QFile::Unbuffered
;
112 mode
= QFile::Append
| QFile::Text
;
118 // Write to the log file and then close the file
119 f
.write(msg
.toLocal8Bit());
122 // Check the file size
123 if (src
.maxSize
> 0 && f
.size() > src
.maxSize
) {
125 QDir
dir(QFileInfo(src
.fileName
).dir());
126 QString baseName
= QFileInfo(src
.fileName
).fileName();
128 // Delete the oldest backup file if the number of files has reached the maximum
129 if (src
.maxCount
> 0 && dir
.exists(QString("%1.%2").arg(baseName
).arg(src
.maxCount
- 1)))
130 dir
.remove(QString("%1.%2").arg(baseName
).arg(src
.maxCount
- 1));
132 // Shift backup files (.0 -> .1, .1 -> .2 etc)
133 renameBackupFile(dir
, baseName
, 0);
135 // Rename the current log file to the backup #0 file
136 dir
.rename(baseName
, baseName
+ ".0");
143 //-------------------------------------------------------------------
147 , mFatalMsgHandler(defFatalMsgHandler
)
148 , mConsoleSeverity(iLogger::Fatal
)
149 , mDefaultSource("evaf")
153 setObjectName(QString("%1-iLogger").arg(VER_MODULE_NAME_STR
));
155 qRegisterMetaType
<LoggerSource
>("LoggerSource");
157 write(Info
, QString("%1 created").arg(objectName()), 0, printf("%s:%s:%d", __FILE__
, __FUNCTION__
, __LINE__
));
162 // Disconnect any potential receivers from this object
163 disconnect(this, SIGNAL(loggerEvent(Common::iLogger::Severity
,QString
,QString
,QString
)), 0, 0);
165 // Destroy the worker thread
175 write(Info
, QString("%1 destroyed").arg(objectName()), 0, printf("%s:%s:%d", __FILE__
, __FUNCTION__
, __LINE__
));
180 // Register our interface
181 iRegistry::instance()->registerInterface("iLogger", this);
183 // Clear existing sources in case the application was restarted
186 // Destroy the previous worker thread
196 // Create the worker thread
197 mWorker
= new LoggerWorker
;
198 mThread
= new QThread
;
199 mWorker
->moveToThread(mThread
);
200 mThread
->start(QThread::IdlePriority
);
201 connect(this, SIGNAL(writeToLogFile(LoggerSource
,QString
)), mWorker
, SLOT(writeToLogFile(LoggerSource
,QString
)), Qt::QueuedConnection
);
203 write(Info
, QString("%1 initialized").arg(objectName()), 0, printf("%s:%s:%d", __FILE__
, __FUNCTION__
, __LINE__
));
208 void Logger::setDefaultSource(QString
const & source
)
210 mDefaultSource
= source
;
213 iLogger::Severity
Logger::severity(QString
const & source
)
215 return getSource(source
)->severity
;
218 void Logger::setSeverity(iLogger::Severity severity
, QString
const & source
)
220 getSource(source
)->severity
= severity
;
223 uint
Logger::maxSize(QString
const & source
)
225 return getSource(source
)->maxSize
;
228 void Logger::setMaxSize(uint maxSize
, QString
const & source
)
230 getSource(source
)->maxSize
= maxSize
;
233 uint
Logger::maxCount(QString
const & source
)
235 return getSource(source
)->maxCount
;
238 void Logger::setMaxCount(uint maxCount
, QString
const & source
)
240 getSource(source
)->maxCount
= maxCount
;
243 void Logger::setConsoleSeverity(iLogger::Severity severity
)
245 mConsoleSeverity
= severity
;
248 void Logger::write(Severity severity
, QString
const & msg
, QString
const & source
, QString
const & where
)
250 static char const * const severityText
[] =
260 // Make sure that we don't output messages with the None severity
261 if (severity
== iLogger::None
)
264 // Write to the log file
265 LoggerSource
* src
= getSource(source
);
266 if (severity
<= src
->severity
&& src
->severity
!= iLogger::None
) {
268 QTextStream
io(&buf
);
271 io
<< QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
274 io
<< " " << severityText
[severity
];
279 // Location in the source file
280 if (!where
.isEmpty())
281 io
<< " (occurred in " << where
<< ")";
286 // If the worker is initialized, use the worker thread to do the job
288 emit
writeToLogFile(*src
, buf
);
290 // Otherwise we have to do it ourselves
292 QFile
f(src
->fileName
);
293 QFile::OpenMode mode
;
295 mode
= QFile::Append
| QFile::Text
| QFile::Unbuffered
;
297 mode
= QFile::Append
| QFile::Text
;
301 f
.write(buf
.toLocal8Bit());
307 // Output to the console
308 if (source
.isEmpty() && severity
<= mConsoleSeverity
&& mConsoleSeverity
!= iLogger::None
) {
309 FILE * f
= (severity
< iLogger::Info
) ? stderr
: stdout
;
315 fprintf(f
, "\e[32m"); // Green
317 case iLogger::Warning
:
318 fprintf(f
, "\e[1m"); // Bold
321 fprintf(f
, "\e[31m"); // Red
324 fprintf(f
, "\e[31m\e[1m"); // Bold Red
327 fprintf(f
, "\e[34m"); // Blue
330 #elif defined Q_OS_WIN32
333 setColor(FOREGROUND_GREEN
);
335 case iLogger::Warning
:
336 setColor(FOREGROUND_INTENSITY
| FOREGROUND_GREEN
| FOREGROUND_BLUE
| FOREGROUND_RED
);
339 setColor(FOREGROUND_RED
| FOREGROUND_INTENSITY
);
342 setColor(FOREGROUND_RED
| FOREGROUND_GREEN
| FOREGROUND_INTENSITY
| BACKGROUND_RED
);
345 setColor(FOREGROUND_BLUE
);
350 // Output the message
351 fprintf(f
, "%s %s\n", severityText
[severity
], qPrintable(msg
));
354 if (!where
.isEmpty())
355 fprintf(f
, "\t(occurred in %s)\n\n", qPrintable(where
));
360 #elif defined Q_OS_WIN32
369 emit
loggerEvent(severity
, msg
, source
, where
);
371 // Handle fatal error messages
372 if (severity
== iLogger::Fatal
&& mFatalMsgHandler
)
373 mFatalMsgHandler(msg
, source
, where
);
376 QString
Logger::printf(char const * const fmt
, ...) const
387 _vsnprintf_s(str
, sizeof(str
), _TRUNCATE
, fmt
, ap
);
391 if (::vasprintf(&str
, fmt
, ap
)); // IF is needed to avoid the compiler warning
404 QString
Logger::printable(QByteArray
const & msg
) const
406 static char const * const ctrlChars
[] = {
407 "NUL", "SOH", "STX", "ETX", "EOT", "ENW", "ACK", "BEL",
408 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
409 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
410 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
415 for (int i
= 0; i
< sz
; ++i
) {
416 uchar ch
= uchar(msg
.at(i
));
418 rval
.append(QString("[%1]").arg(ctrlChars
[ch
]));
420 rval
.append(msg
.at(i
));
422 rval
.append("[DEL]");
424 rval
.append(QString("[\\x%1]").arg(ch
, 2, 16, QChar('0')));
430 FatalMsgHandler
Logger::installFatalMsgHandler(FatalMsgHandler newHandler
)
432 FatalMsgHandler oldHandler
= mFatalMsgHandler
;
433 mFatalMsgHandler
= newHandler
;
437 LoggerSource
* Logger::getSource(QString
const & source
)
439 QHash
<QString
, QExplicitlySharedDataPointer
<LoggerSource
> >::const_iterator it
= mSources
.constFind(source
);
440 if (it
!= mSources
.constEnd())
443 // Create the new source
444 QExplicitlySharedDataPointer
<LoggerSource
> src(new LoggerSource
);
445 mSources
.insert(source
, src
);
447 // Initialize the new source
448 src
->init(source
.isEmpty() ? mDefaultSource
: source
, iApp::instance()->logDir(), iApp::instance()->etcDir());
455 void Logger::setColor(short int c
)
457 HANDLE handle
= GetStdHandle(STD_OUTPUT_HANDLE
);
458 SetConsoleTextAttribute(handle
, c
);