übernahme Code Shortcut

This commit is contained in:
georg0480
2026-01-31 15:28:10 +01:00
parent 6f4d6b9301
commit ef46c21291
1787 changed files with 1126465 additions and 0 deletions

View 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()
*/

View 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;
}

View 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));
}

View 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));
}

View 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

File diff suppressed because it is too large Load Diff

View 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());
}

View 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;
}