2 * @file Common/logger.cpp
3 * @brief iLogger interface implementation
6 * Copyright (c) 2011-2012 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"
41 //-------------------------------------------------------------------
43 void eVaf::Common::Internal::defFatalMsgHandler(QString
const & msg
, QString
const & source
, QString
const & where
)
47 fprintf(stderr
, "FATAL ERROR: %s (occurred in %s)\n", qPrintable(msg
), qPrintable(where
));
57 //-------------------------------------------------------------------
59 using namespace eVaf::Common
;
61 iLogger
* iLogger::instance()
63 static Internal::Logger singleton
;
68 //-------------------------------------------------------------------
70 using namespace eVaf::Common::Internal
;
72 LoggerSource::LoggerSource()
74 , severity(iLogger::Fatal
)
79 LoggerSource::LoggerSource(LoggerSource
const & o
)
82 , severity(o
.severity
)
83 , fileName(o
.fileName
)
85 , maxCount(o
.maxCount
)
88 void LoggerSource::init(QString
const & source
)
91 fileName
= iApp::instance()->logDir() + source
+ ".log";
93 // Set default settings
94 severity
= iLogger::Fatal
;
98 // Read settings from the 'logger.ini' file
99 QString confFileName
= iApp::instance()->etcDir() + "logger.ini";
100 if (QFile::exists(confFileName
)) {
101 IniFile
ini(confFileName
, QIODevice::ReadOnly
);
103 // Default values for all sources
104 maxSize
= 1024 * ini
.getValue(".default/log_size", maxSize
/ 1024).toInt();
105 maxCount
= ini
.getValue(".default/log_count", maxCount
).toInt();
107 // Default values for this source
108 maxSize
= 1024 * ini
.getValue(source
.toLatin1() + "/log_size", maxSize
/ 1024).toInt();
109 maxCount
= ini
.getValue(source
.toLatin1() + "/log_count", maxCount
).toInt();
114 //-------------------------------------------------------------------
116 /// Recursively renames backup files
117 void renameBackupFile(QDir
& dir
, QString
const & baseName
, int idx
)
119 QString f1
= QString("%1.%2").arg(baseName
).arg(idx
);
120 QString f2
= QString("%1.%2").arg(baseName
).arg(idx
+ 1);
123 renameBackupFile(dir
, baseName
, idx
+ 1);
128 void LoggerWorker::writeToLogFile(LoggerSource
const & src
, QString
const & msg
)
130 //::printf("writeToLogFile(\'%s\', \'%s\') fileName = \'%s\'\n", qPrintable(src.name), qPrintable(msg), qPrintable(src.fileName));
131 if (src
.fileName
.isEmpty())
133 QFile
f(src
.fileName
);
134 QFile::OpenMode mode
;
136 mode
= QFile::Append
| QFile::Text
| QFile::Unbuffered
;
138 mode
= QFile::Append
| QFile::Text
;
144 // Write to the log file and then close the file
145 f
.write(msg
.toLocal8Bit());
148 // Check the file size
149 if (src
.maxSize
> 0 && f
.size() > src
.maxSize
) {
151 QDir
dir(QFileInfo(src
.fileName
).dir());
152 QString baseName
= QFileInfo(src
.fileName
).fileName();
154 // Delete the oldest backup file if the number of files has reached the maximum
155 if (src
.maxCount
> 0 && dir
.exists(QString("%1.%2").arg(baseName
).arg(src
.maxCount
- 1)))
156 dir
.remove(QString("%1.%2").arg(baseName
).arg(src
.maxCount
- 1));
158 // Shift backup files (.0 -> .1, .1 -> .2 etc)
159 renameBackupFile(dir
, baseName
, 0);
161 // Rename the current log file to the backup #0 file
162 dir
.rename(baseName
, baseName
+ ".0");
169 //-------------------------------------------------------------------
174 , mFatalMsgHandler(defFatalMsgHandler
)
175 , mConsoleSeverity(iLogger::Fatal
)
179 setObjectName(QString("%1-iLogger").arg(VER_MODULE_NAME_STR
));
181 qRegisterMetaType
<LoggerSource
>("LoggerSource");
183 // Create the default source
184 mDefaultSource
= new LoggerSource
;
185 mDefaultSource
->name
= "common";
187 write(Info
, QString("%1 created").arg(objectName()), 0, printf("%s:%s:%d", __FILE__
, __FUNCTION__
, __LINE__
));
192 // Disconnect any potential receivers from this object
193 disconnect(this, SIGNAL(loggerEvent(Common::iLogger::Severity
,QString
,QString
,QString
)), 0, 0);
195 // Destroy the worker thread
205 write(Info
, QString("%1 destroyed").arg(objectName()), 0, printf("%s:%s:%d", __FILE__
, __FUNCTION__
, __LINE__
));
210 // Register our interface
211 iRegistry::instance()->registerInterface("iLogger", this);
213 // Clear existing sources in case the application was restarted
216 // Set the default source name to the name of the application
217 setDefaultSource(iApp::instance()->name());
219 // Read configuration parameters from the application's INI file
220 QVariant v
= iConfig::instance()->getValue(QString("%1/general/log_level").arg(iApp::instance()->name()), severity());
222 setSeverity(iLogger::Severity(qBound(int(iLogger::None
), v
.toInt(), int(iLogger::Debug
))));
223 v
= iConfig::instance()->getValue(QString("%1/general/log_size").arg(iApp::instance()->name()), maxSize());
225 setMaxSize(v
.toUInt());
226 v
= iConfig::instance()->getValue(QString("%1/general/log_cnt").arg(iApp::instance()->name()), maxCount());
228 setMaxCount(v
.toUInt());
230 // Destroy the previous worker thread
240 // Create the worker thread
241 mWorker
= new LoggerWorker
;
242 mThread
= new QThread
;
243 mWorker
->moveToThread(mThread
);
244 mThread
->start(QThread::IdlePriority
);
245 connect(this, SIGNAL(writeToLogFile(LoggerSource
,QString
)), mWorker
, SLOT(writeToLogFile(LoggerSource
,QString
)), Qt::QueuedConnection
);
249 write(Info
, QString("%1 initialized").arg(objectName()), 0, printf("%s:%s:%d", __FILE__
, __FUNCTION__
, __LINE__
));
254 QString
Logger::defaultSource() const
256 return mDefaultSource
->name
;
259 void Logger::setDefaultSource(QString
const & source
)
261 LoggerSource
* src
= getSource();
262 if (src
&& src
->name
!= source
)
263 getSource(QString())->init(source
);
266 iLogger::Severity
Logger::severity(QString
const & source
)
268 return getSource(source
)->severity
;
271 void Logger::setSeverity(iLogger::Severity severity
, QString
const & source
)
273 getSource(source
)->severity
= severity
;
276 uint
Logger::maxSize(QString
const & source
)
278 return getSource(source
)->maxSize
;
281 void Logger::setMaxSize(uint maxSize
, QString
const & source
)
283 getSource(source
)->maxSize
= maxSize
* 1024;
286 uint
Logger::maxCount(QString
const & source
)
288 return getSource(source
)->maxCount
;
291 void Logger::setMaxCount(uint maxCount
, QString
const & source
)
293 getSource(source
)->maxCount
= maxCount
;
296 void Logger::setConsoleSeverity(iLogger::Severity severity
)
298 mConsoleSeverity
= severity
;
301 void Logger::write(Severity severity
, QString
const & msg
, QString
const & source
, QString
const & where
)
303 static char const * const severityText
[] =
313 // Make sure that we don't output messages with the None severity
314 if (severity
== iLogger::None
)
317 // Write to the log file
319 LoggerSource
* src
= getSource(source
);
320 if (severity
<= src
->severity
&& src
->severity
!= iLogger::None
) {
322 QTextStream
io(&buf
);
325 io
<< QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
328 io
<< " " << severityText
[severity
];
333 // Location in the source file
334 if (!where
.isEmpty())
335 io
<< " (occurred in " << where
<< ")";
340 emit
writeToLogFile(*src
, buf
);
344 // Output to the console
345 if (source
.isEmpty() && severity
<= mConsoleSeverity
&& mConsoleSeverity
!= iLogger::None
) {
346 FILE * f
= (severity
< iLogger::Info
) ? stderr
: stdout
;
352 fprintf(f
, "\e[32m"); // Green
354 case iLogger::Warning
:
355 fprintf(f
, "\e[1m"); // Bold
358 fprintf(f
, "\e[31m"); // Red
361 fprintf(f
, "\e[31m\e[1m"); // Bold Red
364 fprintf(f
, "\e[34m"); // Blue
367 #elif defined Q_OS_WIN32
370 setColor(FOREGROUND_GREEN
);
372 case iLogger::Warning
:
373 setColor(FOREGROUND_INTENSITY
| FOREGROUND_GREEN
| FOREGROUND_BLUE
| FOREGROUND_RED
);
376 setColor(FOREGROUND_RED
| FOREGROUND_INTENSITY
);
379 setColor(FOREGROUND_RED
| FOREGROUND_GREEN
| FOREGROUND_INTENSITY
| BACKGROUND_RED
);
382 setColor(FOREGROUND_BLUE
);
387 // Output the message
388 fprintf(f
, "%s %s\n", severityText
[severity
], qPrintable(msg
));
391 if (!where
.isEmpty())
392 fprintf(f
, "\t(occurred in %s)\n\n", qPrintable(where
));
397 #elif defined Q_OS_WIN32
406 emit
loggerEvent(severity
, msg
, source
, where
);
408 // Handle fatal error messages
409 if (severity
== iLogger::Fatal
&& mFatalMsgHandler
)
410 mFatalMsgHandler(msg
, source
, where
);
413 QString
Logger::printf(char const * const fmt
, ...) const
425 vsnprintf(str
, sizeof(str
), fmt
, ap
);
427 _vsnprintf_s(str
, sizeof(str
), _TRUNCATE
, fmt
, ap
);
432 if (::vasprintf(&str
, fmt
, ap
)) {}; // IF is needed to avoid the compiler warning
445 QString
Logger::printable(QByteArray
const & msg
) const
447 static char const * const ctrlChars
[] = {
448 "NUL", "SOH", "STX", "ETX", "EOT", "ENW", "ACK", "BEL",
449 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
450 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
451 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
456 for (int i
= 0; i
< sz
; ++i
) {
457 uchar ch
= uchar(msg
.at(i
));
459 rval
.append(QString("[%1]").arg(ctrlChars
[ch
]));
461 rval
.append(msg
.at(i
));
463 rval
.append("[DEL]");
465 rval
.append(QString("[\\x%1]").arg(ch
, 2, 16, QChar('0')));
471 FatalMsgHandler
Logger::installFatalMsgHandler(FatalMsgHandler newHandler
)
473 FatalMsgHandler oldHandler
= mFatalMsgHandler
;
474 mFatalMsgHandler
= newHandler
;
478 LoggerSource
* Logger::getSource(QString
const & source
)
480 if (source
.isEmpty() || source
== mDefaultSource
->name
)
481 return mDefaultSource
.data();
483 QHash
<QString
, QExplicitlySharedDataPointer
<LoggerSource
> >::const_iterator it
= mSources
.constFind(source
);
484 if (it
!= mSources
.constEnd())
487 // Create the new source
488 QExplicitlySharedDataPointer
<LoggerSource
> src(new LoggerSource
);
489 mSources
.insert(source
, src
);
491 // Initialize the new source
499 void Logger::setColor(short int c
)
501 HANDLE handle
= GetStdHandle(STD_OUTPUT_HANDLE
);
502 SetConsoleTextAttribute(handle
, c
);