übernahme Code Shortcut
This commit is contained in:
147
CuteLogger/src/AbstractAppender.cpp
Normal file
147
CuteLogger/src/AbstractAppender.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License version 2.1
|
||||
as published by the Free Software Foundation and appearing in the file
|
||||
LICENSE.LGPL included in the packaging of this file.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
*/
|
||||
// Local
|
||||
#include "AbstractAppender.h"
|
||||
|
||||
// Qt
|
||||
#include <QMutexLocker>
|
||||
|
||||
|
||||
/**
|
||||
* \class AbstractAppender
|
||||
*
|
||||
* \brief The AbstractAppender class provides an abstract base class for writing a log entries.
|
||||
*
|
||||
* The AbstractAppender class is the base interface class for all log appenders that could be used with Logger.
|
||||
*
|
||||
* AbstractAppender provides a common implementation for the thread safe, mutex-protected logging of application
|
||||
* messages, such as ConsoleAppender, FileAppender or something else. AbstractAppender is abstract and can not be
|
||||
* instantiated, but you can use any of its subclasses or create a custom log appender at your choice.
|
||||
*
|
||||
* Appenders are the logical devices that is aimed to be attached to Logger object by calling
|
||||
* Logger::registerAppender(). On each log record call from the application Logger object sequentially calls write()
|
||||
* function on all the appenders registered in it.
|
||||
*
|
||||
* You can subclass AbstractAppender to implement a logging target of any kind you like. It may be the external logging
|
||||
* subsystem (for example, syslog in *nix), XML file, SQL database entries, D-Bus messages or anything else you can
|
||||
* imagine.
|
||||
*
|
||||
* For the simple non-structured plain text logging (for example, to a plain text file or to the console output) you may
|
||||
* like to subclass the AbstractStringAppender instead of AbstractAppender, which will give you a more convinient way to
|
||||
* control the format of the log output.
|
||||
*
|
||||
* \sa AbstractStringAppender
|
||||
* \sa Logger::registerAppender()
|
||||
*/
|
||||
|
||||
|
||||
//! Constructs a AbstractAppender object.
|
||||
AbstractAppender::AbstractAppender()
|
||||
: m_detailsLevel(Logger::Debug)
|
||||
{}
|
||||
|
||||
|
||||
//! Destructs the AbstractAppender object.
|
||||
AbstractAppender::~AbstractAppender()
|
||||
{}
|
||||
|
||||
|
||||
//! Returns the current details level of appender.
|
||||
/**
|
||||
* Log records with a log level lower than a current detailsLevel() will be silently ignored by appender and would not
|
||||
* be sent to its append() function.
|
||||
*
|
||||
* It provides additional logging flexibility, allowing you to set the different severity levels for different types
|
||||
* of logs.
|
||||
*
|
||||
* \note This function is thread safe.
|
||||
*
|
||||
* \sa setDetailsLevel()
|
||||
* \sa Logger::LogLevel
|
||||
*/
|
||||
Logger::LogLevel AbstractAppender::detailsLevel() const
|
||||
{
|
||||
QMutexLocker locker(&m_detailsLevelMutex);
|
||||
return m_detailsLevel;
|
||||
}
|
||||
|
||||
|
||||
//! Sets the current details level of appender.
|
||||
/**
|
||||
* Default details level is Logger::Debug
|
||||
*
|
||||
* \note This function is thread safe.
|
||||
*
|
||||
* \sa detailsLevel()
|
||||
* \sa Logger::LogLevel
|
||||
*/
|
||||
void AbstractAppender::setDetailsLevel(Logger::LogLevel level)
|
||||
{
|
||||
QMutexLocker locker(&m_detailsLevelMutex);
|
||||
m_detailsLevel = level;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//! Sets the current details level of appender
|
||||
/**
|
||||
* This function is provided for convenience, it behaves like an above function.
|
||||
*
|
||||
* \sa detailsLevel()
|
||||
* \sa Logger::LogLevel
|
||||
*/
|
||||
void AbstractAppender::setDetailsLevel(const QString& level)
|
||||
{
|
||||
setDetailsLevel(Logger::levelFromString(level));
|
||||
}
|
||||
|
||||
|
||||
//! Tries to write the log record to this logger
|
||||
/**
|
||||
* This is the function called by Logger object to write a log message to the appender.
|
||||
*
|
||||
* \note This function is thread safe.
|
||||
*
|
||||
* \sa Logger::write()
|
||||
* \sa detailsLevel()
|
||||
*/
|
||||
void AbstractAppender::write(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
|
||||
const char* function, const QString& category, const QString& message)
|
||||
{
|
||||
if (logLevel >= detailsLevel())
|
||||
{
|
||||
QMutexLocker locker(&m_writeMutex);
|
||||
append(timeStamp, logLevel, file, line, function, category, message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \fn virtual void AbstractAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file,
|
||||
* int line, const char* function, const QString& message)
|
||||
*
|
||||
* \brief Writes the log record to the logger instance
|
||||
*
|
||||
* This function is called every time when user tries to write a message to this AbstractAppender instance using
|
||||
* the write() function. Write function works as proxy and transfers only the messages with log level more or equal
|
||||
* to the current logLevel().
|
||||
*
|
||||
* Overload this function when you are implementing a custom appender.
|
||||
*
|
||||
* \note This function is not needed to be thread safe because it is never called directly by Logger object. The
|
||||
* write() function works as a proxy and protects this function from concurrent access.
|
||||
*
|
||||
* \sa Logger::write()
|
||||
*/
|
||||
|
||||
460
CuteLogger/src/AbstractStringAppender.cpp
Normal file
460
CuteLogger/src/AbstractStringAppender.cpp
Normal file
@@ -0,0 +1,460 @@
|
||||
/*
|
||||
Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com) Nikolay Matyunin (matyunin.n at gmail dot com)
|
||||
|
||||
Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License version 2.1
|
||||
as published by the Free Software Foundation and appearing in the file
|
||||
LICENSE.LGPL included in the packaging of this file.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
*/
|
||||
// Local
|
||||
#include "AbstractStringAppender.h"
|
||||
|
||||
// Qt
|
||||
#include <QReadLocker>
|
||||
#include <QWriteLocker>
|
||||
#include <QDateTime>
|
||||
#include <QRegularExpression>
|
||||
#include <QCoreApplication>
|
||||
#include <QThread>
|
||||
|
||||
|
||||
/**
|
||||
* \class AbstractStringAppender
|
||||
*
|
||||
* \brief The AbstractStringAppender class provides a convinient base for appenders working with plain text formatted
|
||||
* logs.
|
||||
*
|
||||
* AbstractSringAppender is the simple extension of the AbstractAppender class providing the convinient way to create
|
||||
* custom log appenders working with a plain text formatted log targets.
|
||||
*
|
||||
* It have the formattedString() protected function that formats the logging arguments according to a format set with
|
||||
* setFormat().
|
||||
*
|
||||
* This class can not be directly instantiated because it contains pure virtual function inherited from AbstractAppender
|
||||
* class.
|
||||
*
|
||||
* For more detailed description of customizing the log output format see the documentation on the setFormat() function.
|
||||
*/
|
||||
|
||||
|
||||
const char formattingMarker = '%';
|
||||
|
||||
|
||||
//! Constructs a new string appender object
|
||||
AbstractStringAppender::AbstractStringAppender()
|
||||
: m_format(QLatin1String("%{time}{yyyy-MM-ddTHH:mm:ss.zzz} [%{type:-7}] <%{function}> %{message}\n"))
|
||||
{}
|
||||
|
||||
|
||||
//! Returns the current log format string.
|
||||
/**
|
||||
* The default format is set to "%{time}{yyyy-MM-ddTHH:mm:ss.zzz} [%{type:-7}] <%{function}> %{message}\n". You can set a different log record
|
||||
* format using the setFormat() function.
|
||||
*
|
||||
* \sa setFormat(const QString&)
|
||||
*/
|
||||
QString AbstractStringAppender::format() const
|
||||
{
|
||||
QReadLocker locker(&m_formatLock);
|
||||
return m_format;
|
||||
}
|
||||
|
||||
|
||||
//! Sets the logging format for writing strings to the log target with this appender.
|
||||
/**
|
||||
* The string format seems to be very common to those developers who have used a standart sprintf function.
|
||||
*
|
||||
* Log output format is a simple QString with the special markers (starting with % sign) which will be replaced with
|
||||
* it's internal meaning when writing a log record.
|
||||
*
|
||||
* Controlling marker begins with the percent sign (%) which is followed by the command inside {} brackets
|
||||
* (the command describes, what will be put to log record instead of marker).
|
||||
* Optional field width argument may be specified right after the command (through the colon symbol before the closing bracket)
|
||||
* Some commands requires an additional formatting argument (in the second {} brackets).
|
||||
*
|
||||
* Field width argument works almost identically to the \c QString::arg() \c fieldWidth argument (and uses it
|
||||
* internally). For example, \c "%{type:-7}" will be replaced with the left padded debug level of the message
|
||||
* (\c "Debug ") or something. For the more detailed description of it you may consider to look to the Qt
|
||||
* Reference Documentation.
|
||||
*
|
||||
* Supported marker commands are:
|
||||
* \arg \c %{time} - timestamp. You may specify your custom timestamp format using the second {} brackets after the marker,
|
||||
* timestamp format here will be similiar to those used in QDateTime::toString() function. For example,
|
||||
* "%{time}{dd-MM-yyyy, HH:mm}" may be replaced with "17-12-2010, 20:17" depending on current date and time.
|
||||
* The default format used here is "HH:mm:ss.zzz".
|
||||
* \arg \c %{type} - Log level. Possible log levels are shown in the Logger::LogLevel enumerator.
|
||||
* \arg \c %{Type} - Uppercased log level.
|
||||
* \arg \c %{typeOne} - One letter log level.
|
||||
* \arg \c %{TypeOne} - One uppercase letter log level.
|
||||
* \arg \c %{File} - Full source file name (with path) of the file that requested log recording. Uses the \c __FILE__
|
||||
* preprocessor macro.
|
||||
* \arg \c %{file} - Short file name (with stripped path).
|
||||
* \arg \c %{line} - Line number in the source file. Uses the \c __LINE__ preprocessor macro.
|
||||
* \arg \c %{Function} - Name of function that called on of the LOG_* macros. Uses the \c Q_FUNC_INFO macro provided with
|
||||
* Qt.
|
||||
* \arg \c %{function} - Similiar to the %{Function}, but the function name is stripped using stripFunctionName
|
||||
* \arg \c %{message} - The log message sent by the caller.
|
||||
* \arg \c %{category} - The log category.
|
||||
* \arg \c %{appname} - Application name (returned by QCoreApplication::applicationName() function).
|
||||
* \arg \c %{pid} - Application pid (returned by QCoreApplication::applicationPid() function).
|
||||
* \arg \c %{threadid} - ID of current thread.
|
||||
* \arg \c %% - Convinient marker that is replaced with the single \c % mark.
|
||||
*
|
||||
* \note Format doesn't add \c '\\n' to the end of the format line. Please consider adding it manually.
|
||||
*
|
||||
* \sa format()
|
||||
* \sa stripFunctionName()
|
||||
* \sa Logger::LogLevel
|
||||
*/
|
||||
void AbstractStringAppender::setFormat(const QString& format)
|
||||
{
|
||||
QWriteLocker locker(&m_formatLock);
|
||||
m_format = format;
|
||||
}
|
||||
|
||||
|
||||
//! Strips the long function signature (as added by Q_FUNC_INFO macro)
|
||||
/**
|
||||
* The string processing drops the returning type, arguments and template parameters of function. It is definitely
|
||||
* useful for enchancing the log output readability.
|
||||
* \return stripped function name
|
||||
*/
|
||||
QString AbstractStringAppender::stripFunctionName(const char* name)
|
||||
{
|
||||
return QString::fromLatin1(qCleanupFuncinfo(name));
|
||||
}
|
||||
|
||||
|
||||
// The function was backported from Qt5 sources (qlogging.h)
|
||||
QByteArray AbstractStringAppender::qCleanupFuncinfo(const char* name)
|
||||
{
|
||||
QByteArray info(name);
|
||||
|
||||
// Strip the function info down to the base function name
|
||||
// note that this throws away the template definitions,
|
||||
// the parameter types (overloads) and any const/volatile qualifiers.
|
||||
if (info.isEmpty())
|
||||
return info;
|
||||
|
||||
int pos;
|
||||
|
||||
// skip trailing [with XXX] for templates (gcc)
|
||||
pos = info.size() - 1;
|
||||
if (info.endsWith(']')) {
|
||||
while (--pos) {
|
||||
if (info.at(pos) == '[')
|
||||
info.truncate(pos);
|
||||
}
|
||||
}
|
||||
|
||||
bool hasLambda = false;
|
||||
QRegularExpression lambdaRegex("::<lambda\\(.*?\\)>");
|
||||
QRegularExpressionMatch match = lambdaRegex.match(QString::fromLatin1(info));
|
||||
int lambdaIndex = match.capturedStart();
|
||||
if (lambdaIndex != -1)
|
||||
{
|
||||
hasLambda = true;
|
||||
info.remove(lambdaIndex, match.capturedLength());
|
||||
}
|
||||
|
||||
// operator names with '(', ')', '<', '>' in it
|
||||
static const char operator_call[] = "operator()";
|
||||
static const char operator_lessThan[] = "operator<";
|
||||
static const char operator_greaterThan[] = "operator>";
|
||||
static const char operator_lessThanEqual[] = "operator<=";
|
||||
static const char operator_greaterThanEqual[] = "operator>=";
|
||||
|
||||
// canonize operator names
|
||||
info.replace("operator ", "operator");
|
||||
|
||||
// remove argument list
|
||||
forever {
|
||||
int parencount = 0;
|
||||
pos = info.lastIndexOf(')');
|
||||
if (pos == -1) {
|
||||
// Don't know how to parse this function name
|
||||
return info;
|
||||
}
|
||||
|
||||
// find the beginning of the argument list
|
||||
--pos;
|
||||
++parencount;
|
||||
while (pos && parencount) {
|
||||
if (info.at(pos) == ')')
|
||||
++parencount;
|
||||
else if (info.at(pos) == '(')
|
||||
--parencount;
|
||||
--pos;
|
||||
}
|
||||
if (parencount != 0)
|
||||
return info;
|
||||
|
||||
info.truncate(++pos);
|
||||
|
||||
if (info.at(pos - 1) == ')') {
|
||||
if (info.indexOf(operator_call) == pos - (int)strlen(operator_call))
|
||||
break;
|
||||
|
||||
// this function returns a pointer to a function
|
||||
// and we matched the arguments of the return type's parameter list
|
||||
// try again
|
||||
info.remove(0, info.indexOf('('));
|
||||
info.chop(1);
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasLambda)
|
||||
info.append("::lambda");
|
||||
|
||||
// find the beginning of the function name
|
||||
int parencount = 0;
|
||||
int templatecount = 0;
|
||||
--pos;
|
||||
|
||||
// make sure special characters in operator names are kept
|
||||
if (pos > -1) {
|
||||
switch (info.at(pos)) {
|
||||
case ')':
|
||||
if (info.indexOf(operator_call) == pos - (int)strlen(operator_call) + 1)
|
||||
pos -= 2;
|
||||
break;
|
||||
case '<':
|
||||
if (info.indexOf(operator_lessThan) == pos - (int)strlen(operator_lessThan) + 1)
|
||||
--pos;
|
||||
break;
|
||||
case '>':
|
||||
if (info.indexOf(operator_greaterThan) == pos - (int)strlen(operator_greaterThan) + 1)
|
||||
--pos;
|
||||
break;
|
||||
case '=': {
|
||||
int operatorLength = (int)strlen(operator_lessThanEqual);
|
||||
if (info.indexOf(operator_lessThanEqual) == pos - operatorLength + 1)
|
||||
pos -= 2;
|
||||
else if (info.indexOf(operator_greaterThanEqual) == pos - operatorLength + 1)
|
||||
pos -= 2;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (pos > -1) {
|
||||
if (parencount < 0 || templatecount < 0)
|
||||
return info;
|
||||
|
||||
char c = info.at(pos);
|
||||
if (c == ')')
|
||||
++parencount;
|
||||
else if (c == '(')
|
||||
--parencount;
|
||||
else if (c == '>')
|
||||
++templatecount;
|
||||
else if (c == '<')
|
||||
--templatecount;
|
||||
else if (c == ' ' && templatecount == 0 && parencount == 0)
|
||||
break;
|
||||
|
||||
--pos;
|
||||
}
|
||||
info = info.mid(pos + 1);
|
||||
|
||||
// remove trailing '*', '&' that are part of the return argument
|
||||
while ((info.at(0) == '*')
|
||||
|| (info.at(0) == '&'))
|
||||
info = info.mid(1);
|
||||
|
||||
// we have the full function name now.
|
||||
// clean up the templates
|
||||
while ((pos = info.lastIndexOf('>')) != -1) {
|
||||
if (!info.contains('<'))
|
||||
break;
|
||||
|
||||
// find the matching close
|
||||
int end = pos;
|
||||
templatecount = 1;
|
||||
--pos;
|
||||
while (pos && templatecount) {
|
||||
char c = info.at(pos);
|
||||
if (c == '>')
|
||||
++templatecount;
|
||||
else if (c == '<')
|
||||
--templatecount;
|
||||
--pos;
|
||||
}
|
||||
++pos;
|
||||
info.remove(pos, end - pos + 1);
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
//! Returns the string to record to the logging target, formatted according to the format().
|
||||
/**
|
||||
* \sa format()
|
||||
* \sa setFormat(const QString&)
|
||||
*/
|
||||
QString AbstractStringAppender::formattedString(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file,
|
||||
int line, const char* function, const QString& category, const QString& message) const
|
||||
{
|
||||
QString f = format();
|
||||
const int size = f.size();
|
||||
|
||||
QString result;
|
||||
|
||||
int i = 0;
|
||||
while (i < f.size())
|
||||
{
|
||||
QChar c = f.at(i);
|
||||
|
||||
// We will silently ignore the broken % marker at the end of string
|
||||
if (c != QLatin1Char(formattingMarker) || (i + 2) >= size)
|
||||
{
|
||||
result.append(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
i += 2;
|
||||
QChar currentChar = f.at(i);
|
||||
QString command;
|
||||
int fieldWidth = 0;
|
||||
|
||||
if (currentChar.isLetter())
|
||||
{
|
||||
command.append(currentChar);
|
||||
int j = 1;
|
||||
while ((i + j) < size && f.at(i + j).isLetter())
|
||||
{
|
||||
command.append(f.at(i+j));
|
||||
j++;
|
||||
}
|
||||
|
||||
i+=j;
|
||||
currentChar = f.at(i);
|
||||
|
||||
// Check for the padding instruction
|
||||
if (currentChar == QLatin1Char(':'))
|
||||
{
|
||||
currentChar = f.at(++i);
|
||||
if (currentChar.isDigit() || currentChar.category() == QChar::Punctuation_Dash)
|
||||
{
|
||||
int j = 1;
|
||||
while ((i + j) < size && f.at(i + j).isDigit())
|
||||
j++;
|
||||
fieldWidth = f.mid(i, j).toInt();
|
||||
|
||||
i += j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Log record chunk to insert instead of formatting instruction
|
||||
QString chunk;
|
||||
|
||||
// Time stamp
|
||||
if (command == QLatin1String("time"))
|
||||
{
|
||||
if (f.at(i + 1) == QLatin1Char('{'))
|
||||
{
|
||||
int j = 1;
|
||||
while ((i + 2 + j) < size && f.at(i + 2 + j) != QLatin1Char('}'))
|
||||
j++;
|
||||
|
||||
if ((i + 2 + j) < size)
|
||||
{
|
||||
chunk = timeStamp.toString(f.mid(i + 2, j));
|
||||
|
||||
i += j;
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (chunk.isNull())
|
||||
chunk = timeStamp.toString(QLatin1String("HH:mm:ss.zzz"));
|
||||
}
|
||||
|
||||
// Log level
|
||||
else if (command == QLatin1String("type"))
|
||||
chunk = Logger::levelToString(logLevel);
|
||||
|
||||
// Uppercased log level
|
||||
else if (command == QLatin1String("Type"))
|
||||
chunk = Logger::levelToString(logLevel).toUpper();
|
||||
|
||||
// One letter log level
|
||||
else if (command == QLatin1String("typeOne"))
|
||||
chunk = Logger::levelToString(logLevel).left(1).toLower();
|
||||
|
||||
// One uppercase letter log level
|
||||
else if (command == QLatin1String("TypeOne"))
|
||||
chunk = Logger::levelToString(logLevel).left(1).toUpper();
|
||||
|
||||
// Filename
|
||||
else if (command == QLatin1String("File"))
|
||||
chunk = QLatin1String(file);
|
||||
|
||||
// Filename without a path
|
||||
else if (command == QLatin1String("file"))
|
||||
chunk = QString(QLatin1String(file)).section(QRegularExpression("[/\\\\]"), -1);
|
||||
|
||||
// Source line number
|
||||
else if (command == QLatin1String("line"))
|
||||
chunk = QString::number(line);
|
||||
|
||||
// Function name, as returned by Q_FUNC_INFO
|
||||
else if (command == QLatin1String("Function"))
|
||||
chunk = QString::fromLatin1(function);
|
||||
|
||||
// Stripped function name
|
||||
else if (command == QLatin1String("function"))
|
||||
chunk = stripFunctionName(function);
|
||||
|
||||
// Log message
|
||||
else if (command == QLatin1String("message"))
|
||||
chunk = message;
|
||||
|
||||
else if (command == QLatin1String("category"))
|
||||
chunk = category;
|
||||
|
||||
// Application pid
|
||||
else if (command == QLatin1String("pid"))
|
||||
chunk = QString::number(QCoreApplication::applicationPid());
|
||||
|
||||
// Appplication name
|
||||
else if (command == QLatin1String("appname"))
|
||||
chunk = QCoreApplication::applicationName();
|
||||
|
||||
// Thread ID (duplicates Qt5 threadid debbuging way)
|
||||
else if (command == QLatin1String("threadid"))
|
||||
chunk = QLatin1String("0x") + QString::number(qlonglong(QThread::currentThread()->currentThread()), 16);
|
||||
|
||||
// We simply replace the double formatting marker (%) with one
|
||||
else if (command == QString(formattingMarker))
|
||||
chunk = QLatin1Char(formattingMarker);
|
||||
|
||||
// Do not process any unknown commands
|
||||
else
|
||||
{
|
||||
chunk = QString(formattingMarker);
|
||||
chunk.append(command);
|
||||
}
|
||||
|
||||
result.append(QString(QLatin1String("%1")).arg(chunk, fieldWidth));
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
47
CuteLogger/src/AndroidAppender.cpp
Normal file
47
CuteLogger/src/AndroidAppender.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
// Local
|
||||
#include "AndroidAppender.h"
|
||||
|
||||
// Android
|
||||
#include <android/log.h>
|
||||
|
||||
|
||||
AndroidAppender::AndroidAppender()
|
||||
{
|
||||
setFormat(QLatin1String("<%{function}> %{message}\n"));
|
||||
}
|
||||
|
||||
|
||||
int AndroidAppender::androidLogPriority(Logger::LogLevel logLevel)
|
||||
{
|
||||
switch (logLevel)
|
||||
{
|
||||
case Logger::Trace:
|
||||
return ANDROID_LOG_VERBOSE;
|
||||
case Logger::Debug:
|
||||
return ANDROID_LOG_DEBUG;
|
||||
case Logger::Info:
|
||||
return ANDROID_LOG_INFO;
|
||||
case Logger::Warning:
|
||||
return ANDROID_LOG_WARN;
|
||||
case Logger::Error:
|
||||
return ANDROID_LOG_ERROR;
|
||||
case Logger::Fatal:
|
||||
return ANDROID_LOG_FATAL;
|
||||
}
|
||||
|
||||
// Just in case
|
||||
return ANDROID_LOG_DEFAULT;
|
||||
}
|
||||
|
||||
|
||||
void AndroidAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
|
||||
const char* function, const QString& category, const QString& message)
|
||||
{
|
||||
QString msg = formattedString(timeStamp, logLevel, file, line, function, category, message);
|
||||
QString cat = category;
|
||||
if (cat.isEmpty())
|
||||
cat = QLatin1String("Logger");
|
||||
|
||||
__android_log_write(androidLogPriority(logLevel), qPrintable(cat), qPrintable(msg));
|
||||
}
|
||||
|
||||
64
CuteLogger/src/ConsoleAppender.cpp
Normal file
64
CuteLogger/src/ConsoleAppender.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License version 2.1
|
||||
as published by the Free Software Foundation and appearing in the file
|
||||
LICENSE.LGPL included in the packaging of this file.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
*/
|
||||
// Local
|
||||
#include "ConsoleAppender.h"
|
||||
|
||||
// STL
|
||||
#include <iostream>
|
||||
|
||||
|
||||
/**
|
||||
* \class ConsoleAppender
|
||||
*
|
||||
* \brief ConsoleAppender is the simple appender that writes the log records to the std::cerr output stream.
|
||||
*
|
||||
* ConsoleAppender uses "[%{type:-7}] <%{function}> %{message}\n" as a default output format. It is similar to the
|
||||
* AbstractStringAppender but doesn't show a timestamp.
|
||||
*
|
||||
* You can modify ConsoleAppender output format without modifying your code by using \c QT_MESSAGE_PATTERN environment
|
||||
* variable. If you need your application to ignore this environment variable you can call
|
||||
* ConsoleAppender::ignoreEnvironmentPattern(true)
|
||||
*/
|
||||
|
||||
|
||||
ConsoleAppender::ConsoleAppender()
|
||||
: AbstractStringAppender()
|
||||
, m_ignoreEnvPattern(false)
|
||||
{
|
||||
setFormat("[%{type:-7}] <%{function}> %{message}\n");
|
||||
}
|
||||
|
||||
|
||||
QString ConsoleAppender::format() const
|
||||
{
|
||||
const QString envPattern = QString::fromLocal8Bit(qgetenv("QT_MESSAGE_PATTERN"));
|
||||
return (m_ignoreEnvPattern || envPattern.isEmpty()) ? AbstractStringAppender::format() : (envPattern + "\n");
|
||||
}
|
||||
|
||||
|
||||
void ConsoleAppender::ignoreEnvironmentPattern(bool ignore)
|
||||
{
|
||||
m_ignoreEnvPattern = ignore;
|
||||
}
|
||||
|
||||
|
||||
//! Writes the log record to the std::cerr stream.
|
||||
/**
|
||||
* \sa AbstractStringAppender::format()
|
||||
*/
|
||||
void ConsoleAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
|
||||
const char* function, const QString& category, const QString& message)
|
||||
{
|
||||
std::cerr << qPrintable(formattedString(timeStamp, logLevel, file, line, function, category, message));
|
||||
}
|
||||
149
CuteLogger/src/FileAppender.cpp
Normal file
149
CuteLogger/src/FileAppender.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License version 2.1
|
||||
as published by the Free Software Foundation and appearing in the file
|
||||
LICENSE.LGPL included in the packaging of this file.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
*/
|
||||
// Local
|
||||
#include "FileAppender.h"
|
||||
|
||||
// STL
|
||||
#include <iostream>
|
||||
|
||||
/**
|
||||
* \class FileAppender
|
||||
*
|
||||
* \brief Simple appender that writes the log records to the plain text file.
|
||||
*/
|
||||
|
||||
|
||||
//! Constructs the new file appender assigned to file with the given name.
|
||||
FileAppender::FileAppender(const QString& fileName)
|
||||
: m_flushOnWrite(false)
|
||||
{
|
||||
setFileName(fileName);
|
||||
}
|
||||
|
||||
|
||||
FileAppender::~FileAppender()
|
||||
{
|
||||
closeFile();
|
||||
}
|
||||
|
||||
|
||||
//! Returns the name set by setFileName() or to the FileAppender constructor.
|
||||
/**
|
||||
* \sa setFileName()
|
||||
*/
|
||||
QString FileAppender::fileName() const
|
||||
{
|
||||
QMutexLocker locker(&m_logFileMutex);
|
||||
return m_logFile.fileName();
|
||||
}
|
||||
|
||||
|
||||
//! Sets the name of the file. The name can have no path, a relative path, or an absolute path.
|
||||
/**
|
||||
* \sa fileName()
|
||||
*/
|
||||
void FileAppender::setFileName(const QString& s)
|
||||
{
|
||||
if (s.isEmpty())
|
||||
std::cerr << "<FileAppender::FileAppender> File name is empty. The appender will do nothing" << std::endl;
|
||||
|
||||
QMutexLocker locker(&m_logFileMutex);
|
||||
if (m_logFile.isOpen())
|
||||
m_logFile.close();
|
||||
|
||||
m_logFile.setFileName(s);
|
||||
}
|
||||
|
||||
|
||||
bool FileAppender::flushOnWrite() const
|
||||
{
|
||||
return m_flushOnWrite;
|
||||
}
|
||||
|
||||
|
||||
//! Allows FileAppender to flush file immediately after writing a log record.
|
||||
/**
|
||||
* Default value is false. This could result in substantial app slowdown when writing massive amount of log records
|
||||
* with FileAppender on a rather slow file system due to FileAppender blocking until the data would be phisically
|
||||
* written.
|
||||
*
|
||||
* Leaving this as is may result in some log data not being written if the application crashes.
|
||||
*/
|
||||
void FileAppender::setFlushOnWrite(bool flush)
|
||||
{
|
||||
m_flushOnWrite = flush;
|
||||
}
|
||||
|
||||
|
||||
//! Force-flush any remaining buffers to file system. Returns true if successful, otherwise returns false.
|
||||
bool FileAppender::flush()
|
||||
{
|
||||
QMutexLocker locker(&m_logFileMutex);
|
||||
if (m_logFile.isOpen())
|
||||
return m_logFile.flush();
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool FileAppender::reopenFile()
|
||||
{
|
||||
closeFile();
|
||||
return openFile();
|
||||
}
|
||||
|
||||
|
||||
bool FileAppender::openFile()
|
||||
{
|
||||
if (m_logFile.fileName().isEmpty())
|
||||
return false;
|
||||
|
||||
bool isOpen = m_logFile.isOpen();
|
||||
if (!isOpen)
|
||||
{
|
||||
isOpen = m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
|
||||
if (isOpen)
|
||||
m_logStream.setDevice(&m_logFile);
|
||||
else
|
||||
std::cerr << "<FileAppender::append> Cannot open the log file " << qPrintable(m_logFile.fileName()) << std::endl;
|
||||
}
|
||||
return isOpen;
|
||||
}
|
||||
|
||||
|
||||
//! Write the log record to the file.
|
||||
/**
|
||||
* \sa fileName()
|
||||
* \sa AbstractStringAppender::format()
|
||||
*/
|
||||
void FileAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
|
||||
const char* function, const QString& category, const QString& message)
|
||||
{
|
||||
QMutexLocker locker(&m_logFileMutex);
|
||||
|
||||
if (openFile())
|
||||
{
|
||||
m_logStream << formattedString(timeStamp, logLevel, file, line, function, category, message);
|
||||
m_logStream.flush();
|
||||
if (m_flushOnWrite)
|
||||
m_logFile.flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FileAppender::closeFile()
|
||||
{
|
||||
QMutexLocker locker(&m_logFileMutex);
|
||||
m_logFile.close();
|
||||
}
|
||||
1108
CuteLogger/src/Logger.cpp
Normal file
1108
CuteLogger/src/Logger.cpp
Normal file
File diff suppressed because it is too large
Load Diff
43
CuteLogger/src/OutputDebugAppender.cpp
Normal file
43
CuteLogger/src/OutputDebugAppender.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Copyright (c) 2010 Karl-Heinz Reichel (khreichel at googlemail dot com)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License version 2.1
|
||||
as published by the Free Software Foundation and appearing in the file
|
||||
LICENSE.LGPL included in the packaging of this file.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
*/
|
||||
// Local
|
||||
#include "OutputDebugAppender.h"
|
||||
|
||||
// STL
|
||||
#include <windows.h>
|
||||
|
||||
|
||||
/**
|
||||
* \class OutputDebugAppender
|
||||
*
|
||||
* \brief Appender that writes the log records to the Microsoft Debug Log
|
||||
*/
|
||||
|
||||
|
||||
//! Writes the log record to the windows debug log.
|
||||
/**
|
||||
* \sa AbstractStringAppender::format()
|
||||
*/
|
||||
void OutputDebugAppender::append(const QDateTime& timeStamp,
|
||||
Logger::LogLevel logLevel,
|
||||
const char* file,
|
||||
int line,
|
||||
const char* function,
|
||||
const QString& category,
|
||||
const QString& message)
|
||||
{
|
||||
QString s = formattedString(timeStamp, logLevel, file, line, function, category, message);
|
||||
OutputDebugStringW((LPCWSTR) s.utf16());
|
||||
}
|
||||
|
||||
253
CuteLogger/src/RollingFileAppender.cpp
Normal file
253
CuteLogger/src/RollingFileAppender.cpp
Normal file
@@ -0,0 +1,253 @@
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
|
||||
#include "RollingFileAppender.h"
|
||||
|
||||
|
||||
RollingFileAppender::RollingFileAppender(const QString& fileName)
|
||||
: FileAppender(fileName)
|
||||
, m_frequency(DailyRollover)
|
||||
, m_logFilesLimit(0)
|
||||
{}
|
||||
|
||||
|
||||
void RollingFileAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
|
||||
const char* function, const QString& category, const QString& message)
|
||||
{
|
||||
if (!m_rollOverTime.isNull() && QDateTime::currentDateTime() > m_rollOverTime)
|
||||
rollOver();
|
||||
|
||||
FileAppender::append(timeStamp, logLevel, file, line, function, category, message);
|
||||
}
|
||||
|
||||
|
||||
RollingFileAppender::DatePattern RollingFileAppender::datePattern() const
|
||||
{
|
||||
QMutexLocker locker(&m_rollingMutex);
|
||||
return m_frequency;
|
||||
}
|
||||
|
||||
|
||||
QString RollingFileAppender::datePatternString() const
|
||||
{
|
||||
QMutexLocker locker(&m_rollingMutex);
|
||||
return m_datePatternString;
|
||||
}
|
||||
|
||||
|
||||
void RollingFileAppender::setDatePattern(DatePattern datePattern)
|
||||
{
|
||||
switch (datePattern)
|
||||
{
|
||||
case MinutelyRollover:
|
||||
setDatePatternString(QLatin1String("'.'yyyy-MM-dd-hh-mm"));
|
||||
break;
|
||||
case HourlyRollover:
|
||||
setDatePatternString(QLatin1String("'.'yyyy-MM-dd-hh"));
|
||||
break;
|
||||
case HalfDailyRollover:
|
||||
setDatePatternString(QLatin1String("'.'yyyy-MM-dd-a"));
|
||||
break;
|
||||
case DailyRollover:
|
||||
setDatePatternString(QLatin1String("'.'yyyy-MM-dd"));
|
||||
break;
|
||||
case WeeklyRollover:
|
||||
setDatePatternString(QLatin1String("'.'yyyy-ww"));
|
||||
break;
|
||||
case MonthlyRollover:
|
||||
setDatePatternString(QLatin1String("'.'yyyy-MM"));
|
||||
break;
|
||||
default:
|
||||
Q_ASSERT_X(false, "DailyRollingFileAppender::setDatePattern()", "Invalid datePattern constant");
|
||||
setDatePattern(DailyRollover);
|
||||
};
|
||||
|
||||
QMutexLocker locker(&m_rollingMutex);
|
||||
m_frequency = datePattern;
|
||||
|
||||
computeRollOverTime();
|
||||
}
|
||||
|
||||
|
||||
void RollingFileAppender::setDatePattern(const QString& datePattern)
|
||||
{
|
||||
setDatePatternString(datePattern);
|
||||
computeFrequency();
|
||||
|
||||
computeRollOverTime();
|
||||
}
|
||||
|
||||
|
||||
void RollingFileAppender::setDatePatternString(const QString& datePatternString)
|
||||
{
|
||||
QMutexLocker locker(&m_rollingMutex);
|
||||
m_datePatternString = datePatternString;
|
||||
}
|
||||
|
||||
|
||||
void RollingFileAppender::computeFrequency()
|
||||
{
|
||||
QMutexLocker locker(&m_rollingMutex);
|
||||
|
||||
const QDateTime startTime(QDate(1999, 1, 1), QTime(0, 0));
|
||||
const QString startString = startTime.toString(m_datePatternString);
|
||||
|
||||
if (startString != startTime.addSecs(60).toString(m_datePatternString))
|
||||
m_frequency = MinutelyRollover;
|
||||
else if (startString != startTime.addSecs(60 * 60).toString(m_datePatternString))
|
||||
m_frequency = HourlyRollover;
|
||||
else if (startString != startTime.addSecs(60 * 60 * 12).toString(m_datePatternString))
|
||||
m_frequency = HalfDailyRollover;
|
||||
else if (startString != startTime.addDays(1).toString(m_datePatternString))
|
||||
m_frequency = DailyRollover;
|
||||
else if (startString != startTime.addDays(7).toString(m_datePatternString))
|
||||
m_frequency = WeeklyRollover;
|
||||
else if (startString != startTime.addMonths(1).toString(m_datePatternString))
|
||||
m_frequency = MonthlyRollover;
|
||||
else
|
||||
{
|
||||
Q_ASSERT_X(false, "DailyRollingFileAppender::computeFrequency", "The pattern '%1' does not specify a frequency");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RollingFileAppender::removeOldFiles()
|
||||
{
|
||||
if (m_logFilesLimit <= 1)
|
||||
return;
|
||||
|
||||
QFileInfo fileInfo(fileName());
|
||||
QDir logDirectory(fileInfo.absoluteDir());
|
||||
logDirectory.setFilter(QDir::Files);
|
||||
logDirectory.setNameFilters(QStringList() << fileInfo.fileName() + "*");
|
||||
QFileInfoList logFiles = logDirectory.entryInfoList();
|
||||
|
||||
QMap<QDateTime, QString> fileDates;
|
||||
for (int i = 0; i < logFiles.length(); ++i)
|
||||
{
|
||||
QString name = logFiles[i].fileName();
|
||||
QString suffix = name.mid(name.indexOf(fileInfo.fileName()) + fileInfo.fileName().length());
|
||||
QDateTime fileDateTime = QDateTime::fromString(suffix, datePatternString());
|
||||
|
||||
if (fileDateTime.isValid())
|
||||
fileDates.insert(fileDateTime, logFiles[i].absoluteFilePath());
|
||||
}
|
||||
|
||||
QList<QString> fileDateNames = fileDates.values();
|
||||
for (int i = 0; i < fileDateNames.length() - m_logFilesLimit + 1; ++i)
|
||||
QFile::remove(fileDateNames[i]);
|
||||
}
|
||||
|
||||
|
||||
void RollingFileAppender::computeRollOverTime()
|
||||
{
|
||||
Q_ASSERT_X(!m_datePatternString.isEmpty(), "DailyRollingFileAppender::computeRollOverTime()", "No active date pattern");
|
||||
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
QDate nowDate = now.date();
|
||||
QTime nowTime = now.time();
|
||||
QDateTime start;
|
||||
|
||||
switch (m_frequency)
|
||||
{
|
||||
case MinutelyRollover:
|
||||
{
|
||||
start = QDateTime(nowDate, QTime(nowTime.hour(), nowTime.minute(), 0, 0));
|
||||
m_rollOverTime = start.addSecs(60);
|
||||
}
|
||||
break;
|
||||
case HourlyRollover:
|
||||
{
|
||||
start = QDateTime(nowDate, QTime(nowTime.hour(), 0, 0, 0));
|
||||
m_rollOverTime = start.addSecs(60*60);
|
||||
}
|
||||
break;
|
||||
case HalfDailyRollover:
|
||||
{
|
||||
int hour = nowTime.hour();
|
||||
if (hour >= 12)
|
||||
hour = 12;
|
||||
else
|
||||
hour = 0;
|
||||
start = QDateTime(nowDate, QTime(hour, 0, 0, 0));
|
||||
m_rollOverTime = start.addSecs(60*60*12);
|
||||
}
|
||||
break;
|
||||
case DailyRollover:
|
||||
{
|
||||
start = QDateTime(nowDate, QTime(0, 0, 0, 0));
|
||||
m_rollOverTime = start.addDays(1);
|
||||
}
|
||||
break;
|
||||
case WeeklyRollover:
|
||||
{
|
||||
// Qt numbers the week days 1..7. The week starts on Monday.
|
||||
// Change it to being numbered 0..6, starting with Sunday.
|
||||
int day = nowDate.dayOfWeek();
|
||||
if (day == Qt::Sunday)
|
||||
day = 0;
|
||||
start = QDateTime(nowDate, QTime(0, 0, 0, 0)).addDays(-1 * day);
|
||||
m_rollOverTime = start.addDays(7);
|
||||
}
|
||||
break;
|
||||
case MonthlyRollover:
|
||||
{
|
||||
start = QDateTime(QDate(nowDate.year(), nowDate.month(), 1), QTime(0, 0, 0, 0));
|
||||
m_rollOverTime = start.addMonths(1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Q_ASSERT_X(false, "DailyRollingFileAppender::computeInterval()", "Invalid datePattern constant");
|
||||
#if QT_VERSION >= 0x050800
|
||||
m_rollOverTime = QDateTime::fromSecsSinceEpoch(0);
|
||||
#else
|
||||
m_rollOverTime = QDateTime::fromTime_t(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
m_rollOverSuffix = start.toString(m_datePatternString);
|
||||
Q_ASSERT_X(now.toString(m_datePatternString) == m_rollOverSuffix,
|
||||
"DailyRollingFileAppender::computeRollOverTime()", "File name changes within interval");
|
||||
Q_ASSERT_X(m_rollOverSuffix != m_rollOverTime.toString(m_datePatternString),
|
||||
"DailyRollingFileAppender::computeRollOverTime()", "File name does not change with rollover");
|
||||
}
|
||||
|
||||
|
||||
void RollingFileAppender::rollOver()
|
||||
{
|
||||
Q_ASSERT_X(!m_datePatternString.isEmpty(), "DailyRollingFileAppender::rollOver()", "No active date pattern");
|
||||
|
||||
QString rollOverSuffix = m_rollOverSuffix;
|
||||
computeRollOverTime();
|
||||
if (rollOverSuffix == m_rollOverSuffix)
|
||||
return;
|
||||
|
||||
closeFile();
|
||||
|
||||
QString targetFileName = fileName() + rollOverSuffix;
|
||||
QFile f(targetFileName);
|
||||
if (f.exists() && !f.remove())
|
||||
return;
|
||||
f.setFileName(fileName());
|
||||
if (!f.rename(targetFileName))
|
||||
return;
|
||||
|
||||
openFile();
|
||||
removeOldFiles();
|
||||
}
|
||||
|
||||
|
||||
void RollingFileAppender::setLogFilesLimit(int limit)
|
||||
{
|
||||
QMutexLocker locker(&m_rollingMutex);
|
||||
m_logFilesLimit = limit;
|
||||
}
|
||||
|
||||
|
||||
int RollingFileAppender::logFilesLimit() const
|
||||
{
|
||||
QMutexLocker locker(&m_rollingMutex);
|
||||
return m_logFilesLimit;
|
||||
}
|
||||
Reference in New Issue
Block a user