Open MartinDelille opened 4 years ago
Hi @MartinDelille I can take a look to it, of course!
I ask you that because I have my own logging system based upon QDebug
which is quite powerful but I might change it to benefit QLogger
features. Of course I would need keeping the current feature.
My current logging system code is not yet publicly availlable but here is a short description:
There is three main concept to understand when using my logging system:
Each class inheriting MDLogger
can log on the default output:
MDINFO << "info";
MDDEBUG << "debug";
MDWARN << "warn";
MDERR << "error";
The mechanism use QDebug
and QLoggingCategory
under the hood.
In order to do so, the class must pass to MDLogger
constructor an identifier.
There is 4 categories (information, debug, warning and error) but only warning and error are displayed by default.
Enabling or disabling categories is possible by modifying a configuration file (~/Library/Preferences/QtProject/qtlogging.ini
on MacOS):
[Rules]
# Allow debug log for the FormTest window:
MyOrganisation.FormTest.MDWindow.debug=true
The message pattern display only the time and the message by default but you can enable other information by changing these settings to true
:
logTime
: display time (enabled by default)logColor
: enabled colored loglogFile
: display the file and line numberlogFunction
: display the function where the log was doneBut many of these are out of scope of operator <<
@MartinDelille I've check what I could do in QLogger, but I see some problems with your description. I'm using global functions instead of inheritance. The other thing is that I don't plan to support colored log.
Things easy achievable are:
I am also interested in this, you need to implement it like QMessageLogger, and you need to handle File, Line, and Function: FILE, LINE, FUNCTION, as well as the message. If you do not pass this info, you have no idea where the message came from.
I am working on this here https://github.com/Light-Wizzard/WeBookClient, you can see a working example of it.
I was thinking about a small wrapper:
static QString myModule = "";
static QString myLogFile = "";
#if defined(QLOGGER_LIBRARY)
# define QLOGGERSHARED_EXPORT Q_DECL_EXPORT
#else
# define QLOGGERSHARED_EXPORT Q_DECL_IMPORT
#endif
/******************************************************************************
** class QLoggerWrapper *
*******************************************************************************/
//class QLOGGERSHARED_EXPORT QLoggerWrapper
class QLoggerWrapper
{
//Q_DISABLE_COPY(QLoggerWrapper)
public:
QLoggerWrapper(LogLevel level, const char *file, int line, const char *function);
~QLoggerWrapper();
void write(const char* msg, ...)
#if defined(Q_CC_GNU) && !defined(__INSURE__)
# if defined(Q_CC_MINGW) && !defined(Q_CC_CLANG)
__attribute__ ((format (gnu_printf, 2, 3)))
# else
__attribute__ ((format (printf, 2, 3)))
# endif
#endif
;
private:
LogLevel myLogLevel;
const char *myFileName;
int myLine;
const char *myFunctionName;
QString myMessage;
}; // end class QLoggerWrapper
/******************************************************************************
** QLoggerWrapper Constructor *
*******************************************************************************/
QLoggerWrapper::QLoggerWrapper(LogLevel level, const char *file, int line, const char *function) : myLogLevel(level), myFileName(file), myLine(line), myFunctionName(function)
{
} // end CuteMessageLogger
/******************************************************************************
** ~CuteMessageLogger Deconstructor *
** manager = QLogger::QLoggerManager::getInstance();
** manager->addDestination(QLogger::myLogFile, QLogger::myModule, QLogger::LogLevel::Debug);
*******************************************************************************/
QLoggerWrapper::~QLoggerWrapper()
{
const auto manager = QLoggerManager::getInstance();
QMutexLocker(&manager->mutex);
const auto logWriter = manager->getLogWriter(myModule);
QString thisMessage = QString("%1 (%2:%3 =>%4)").arg(myMessage).arg(myFileName).arg(myLine).arg(myFunctionName);
if (logWriter && !logWriter->isStop() && logWriter->getLevel() <= myLogLevel)
{
manager->writeAndDequeueMessages(myModule);
logWriter->write(myModule, thisMessage, myLogLevel);
}
else if (!logWriter)
manager->queueMessage(myModule, { thisMessage, static_cast<int>(myLogLevel), QDateTime::currentDateTime().toString("dd-MM-yyyy hh:mm:ss.zzz") });
} // end ~QLoggerWrapper
/******************************************************************************
** write *
*******************************************************************************/
void QLoggerWrapper::write(const char *msg, ...)
{
va_list va;
va_start(va, msg);
myMessage = QString::vasprintf(msg, va);
va_end(va);
} // end write
# define Q_FUNC_INFO __PRETTY_FUNCTION__
//# define Q_ALIGNOF(type) __alignof__(type)
# define Q_TYPEOF(expr) __typeof__(expr)
# define Q_DECL_DEPRECATED __attribute__ ((__deprecated__))
//# define Q_DECL_ALIGN(n) __attribute__((__aligned__(n)))
# define Q_DECL_UNUSED __attribute__((__unused__))
# define Q_LIKELY(expr) __builtin_expect(!!(expr), true)
# define Q_UNLIKELY(expr) __builtin_expect(!!(expr), false)
# define Q_NORETURN __attribute__((__noreturn__))
//# define Q_REQUIRED_RESULT __attribute__ ((__warn_unused_result__))
# define Q_DECL_PURE_FUNCTION __attribute__((pure))
# define Q_DECL_CONST_FUNCTION __attribute__((const))
# define Q_DECL_COLD_FUNCTION __attribute__((cold))
# if !defined(QT_MOC_CPP)
# define Q_PACKED __attribute__ ((__packed__))
# ifndef __ARM_EABI__
# define QT_NO_ARM_EABI
# endif
# endif
# if Q_CC_GNU >= 403 && !defined(Q_CC_CLANG)
# define Q_ALLOC_SIZE(x) __attribute__((alloc_size(x)))
# endif
#define QLOG_TRACE QLogger::QLoggerWrapper(QLogger::LogLevel::Trace, __FILE__, __LINE__, Q_FUNC_INFO).write
#define QLOG_DEBUG QLogger::QLoggerWrapper(QLogger::LogLevel::Debug, __FILE__, __LINE__, Q_FUNC_INFO).write
#define QLOG_INFO QLogger::QLoggerWrapper(QLogger::LogLevel::Info, __FILE__, __LINE__, Q_FUNC_INFO).write
#define QLOG_WARNING QLogger::QLoggerWrapper(QLogger::LogLevel::Warning, __FILE__, __LINE__, Q_FUNC_INFO).write
#define QLOG_ERROR QLogger::QLoggerWrapper(QLogger::LogLevel::Error, __FILE__, __LINE__, Q_FUNC_INFO).write
#define QLOG_FATAL QLogger::QLoggerWrapper(QLogger::LogLevel::Fatal, __FILE__, __LINE__, Q_FUNC_INFO).write
What are your thoughts about how you want to implement it?
Flesh
Hi @Light-Wizzard that wrapper looks cool. I think we may need to sync about it because I was doing something similar in a branch.
The only thing I'd like is to support MSVC, so some compiler functions would need to be wrapped as well. But we can solve that.
Regarding the changes I'm doing, could you please check this branch: https://github.com/francescmm/QLogger/tree/multithread ?
Even that says multithread I've improved the log output as well. Now you will get the file and line (not the caller function yet, since I'm not sure at what level of verbosity would be better).
AppVeyor needs MSVS support for sure.
One idea about how to use it is in a custom function for qInstallMessageHandler.
Flesh
I have updated my project, it is not connected to yours yet, I need to know more details.
I added a Wiki, to better communicate with https://github.com/Light-Wizzard/WeBookClient/wiki/3.Logging, with the Client and Server App being a Real-World Test Case.
Here is one change I would recommend:
#include <QObject>
#include <QMetaEnum>
namespace QLogger
{
class QLogger
{
Q_GADGET
public:
/**
* @brief The LogLevel enum class defines the level of the log message.
*/
enum LogLevel
{
Trace = 0, //! \c Trace \brief Trace level is used for internal code tracing.
Debug, //! \c Debug \brief Debug level is used for writing custom debug output.
Info, //! \c Info \brief Info level is used for informational messages.
Warning, //! \c Warning \brief Warning level is used to report warnings and recoverable errors in your application.
Error, //! \c Error \brief Error level is used for writing critical error messages and reporting system errors.
Critical, //! \c Critical \brief Critical level is used for writing critical error messages and reporting system errors more sever than Error.
Fatal //! \c Fatal \brief Fatal level is used for writing fatal error messages shortly before exiting.
};
Q_ENUM(LogLevel)
static QString levelToText(const QLogger::LogLevel &value)
{
auto metaEnum = QMetaEnum::fromType<QLogger::QLogger::LogLevel>();
return QString(metaEnum.valueToKey(value));
}
}; // end class QLogger
} // end namespace QLogger
You can see how I integrated this change into your code, which I have in a folder called src/QLogger. I have a few questions that puzzle me, maybe an example of how to use it will help, I am stuck at that part in the conversion.
modules, you use them as String List for threads, if you look at how I am trying to use it:
static QString myModule = "WeBookClient";
static QString myLogPath = "logs";
#define QLOG_TRACE QLogger::QLoggerWrapper(myModule, myLogPath, QLogger::QLogger::LogLevel::Trace, __FILE__, __LINE__, Q_FUNC_INFO).write
#define QLOG_DEBUG QLogger::QLoggerWrapper(myModule, myLogPath, QLogger::QLogger::LogLevel::Debug, __FILE__, __LINE__, Q_FUNC_INFO).write
#define QLOG_INFO QLogger::QLoggerWrapper(myModule, myLogPath, QLogger::QLogger::LogLevel::Info, __FILE__, __LINE__, Q_FUNC_INFO).write
#define QLOG_WARNING QLogger::QLoggerWrapper(myModule, myLogPath, QLogger::QLogger::LogLevel::Warning, __FILE__, __LINE__, Q_FUNC_INFO).write
#define QLOG_ERROR QLogger::QLoggerWrapper(myModule, myLogPath, QLogger::QLogger::LogLevel::Error, __FILE__, __LINE__, Q_FUNC_INFO).write
#define QLOG_FATAL QLogger::QLoggerWrapper(myModule, myLogPath, QLogger::QLogger::LogLevel::Fatal, __FILE__, __LINE__, Q_FUNC_INFO).write
The Static would be in a module using it, I do not like to use a Static, this was just a fast example.
Thanks, Flesh
In case you did not know it, your website has a reCAPTCHA key error: https://www.francescmm.com/about-me-software-engineer/ so your contact page does not work for me anyway, I am on Arch Linux Manjaro, and I tried both Chrome and FireFox.
My full name, replacing spaces with dots at Gmail will get to me.
Thanks, Jeffrey Scott Flesher
Hi @Light-Wizzard,
First of all, thanks for such a work! I think that you have several great ideas in there. It has been a looong week but I think I can find some time during the weekend to check more in detail what you'd like to achieve and see if (or how actually) this can be adapted.
Thanks for noticing me the problem in the site, I've already fixed it :)
I have it working, a start, currently the log does not write till exit, I will fix that, but the main issue is the way I wrap it, I need to rethink that, and I need to write a flush function to fix the issue with not writing till exit.
https://github.com/Light-Wizzard/WeBookClient
As you see, the Common file is used to get and set QSettings, it takes arguments from the command line, and could also get them from the Environment variables, all before the INI file is read, and the wrappers job is for redirection, but it needs to be merged with the writer now, and add a flush function.
Issues with this line crashing
const auto level = qvariant_cast<QLoggerLevel::LogLevel>(values.at(2).toInt());
// removing .toInt() works for me
const auto level = qvariant_cast<QLoggerLevel::LogLevel>(values.at(2));
LOGLEVEL_CLASS was to test the change in LogLevel, this needs to work across all the modules.
This needs to be optimized, and integrated right, I hacked it in with a wrapper to test the concept, and it seems to work fine.
I changed this to use qInstallMessageHandler in main.cpp, so it just uses normal qDebug, let me know what you think about this idea.
As you know, it still hangs on exit, I now understand why you put an exit(0) above the wait().
Update:
qInstallMessageHandler works fine for GUI windows, but not the command line, so you will see in the Server that I use QLog_Debug.
Flesh
I really enjoy the
<<
operator used by std::stream and QDebug. Do you think your logger could be compatible with it?