overte-HifiExperiments/libraries/shared/src/LogHandler.h
Dale Glass e01f6e3049 Finish journald logging implementation
This adds command-line arguments, disables it by default on interface,
adds target logging, and can deal with variable numbers of fields.

It also adds some documentation.
2022-06-28 00:34:43 +02:00

216 lines
6.7 KiB
C++

//
// LogHandler.cpp
// libraries/shared/src
//
// Created by Stephen Birarda on 2014-10-28.
// Migrated from Logging.cpp created on 6/11/13
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_LogHandler_h
#define hifi_LogHandler_h
#include <QObject>
#include <QString>
#include <QRegExp>
#include <QRecursiveMutex>
#include <vector>
#include <memory>
const int VERBOSE_LOG_INTERVAL_SECONDS = 5;
enum LogMsgType {
LogInfo = QtInfoMsg,
LogDebug = QtDebugMsg,
LogWarning = QtWarningMsg,
LogCritical = QtCriticalMsg,
LogFatal = QtFatalMsg,
LogSuppressed = 100
};
///
/**
* @brief Handles custom message handling and sending of stats/logs to Logstash instance
*
*/
class LogHandler : public QObject {
Q_OBJECT
public:
/**
* @brief Returns the one instance of the LogHandler object
*
* @return LogHandler&
*/
static LogHandler& getInstance();
/**
* @brief Parse logging options
*
* This parses the logging settings in the environment variable, or from the commandline
*
* @param options Option list
* @return true Option list was parsed successfully
* @return false There was an error
*/
bool parseOptions(QString options);
/**
* @brief Set the name of the component that's producing log output
*
* For instance, "assignment-client", "audio-mixer", etc.
* Called once before logging begins
*
* @param targetName the desired target name to output in logs
*/
void setTargetName(const QString& targetName);
/**
* @brief Set whether to output the process ID
*
* @note This has no effect when logging with journald, the PID is always logged
*
* @param shouldOutputProcessID Whether to output the PID
*/
void setShouldOutputProcessID(bool shouldOutputProcessID);
/**
* @brief Set whether to output the thread ID
*
* @param shouldOutputThreadID
*/
void setShouldOutputThreadID(bool shouldOutputThreadID);
/**
* @brief Set whether to display timestamps with milliseconds
*
* @param shouldDisplayMilliseconds
*/
void setShouldDisplayMilliseconds(bool shouldDisplayMilliseconds);
/**
* @brief Set whether to use Journald, if it's available
*
* @param shouldUseJournald Whether to use journald
*/
void setShouldUseJournald(bool shouldUseJournald);
/**
* @brief Whether Journald is available on this version/system.
*
* Support is available depending on compile options and only on Linux.
*
* @return true Journald is available
* @return false Journald is not available
*/
bool isJournaldAvailable() const;
/**
* @brief Process a log message
*
* This writes it to a file, logs it to the console, or sends it to journald.
*
* @param type Log message type
* @param context Context of the log message (source file, line, function)
* @param message Log message
* @return QString The log message's text with added severity and timestamp
*/
QString printMessage(LogMsgType type, const QMessageLogContext& context, const QString &message);
/**
* @brief A qtMessageHandler that can be hooked up to a target that links to Qt
*
* Prints various process, message type, and time information
*
* @param type Log message type
* @param context Context of the log message (source file, line, function)
* @param message Log message
*/
static void verboseMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message);
int newRepeatedMessageID();
void printRepeatedMessage(int messageID, LogMsgType type, const QMessageLogContext& context, const QString &message);
void setupRepeatedMessageFlusher();
/**
* @brief Break when a message that contains the specified string is logged
*
* This is a function intended to be invoked from inside a debugger. It should be of help when it's hard to put a breakpoint
* on the generating line because it comes from inlined code, or the interesting text is generated at runtime.
*
* Example usage:
*
* @code {.cpp}
* LogHandler::breakOnMessage("No instance available for");
* @endcode
*
* Then the debugger should be triggered as soon as a message containing that string is logged. Backtracking
* through the call stack should lead back to the source.
*
* @note Support for creating a breakpoint in software is compiler and OS specific. If there's no support for
* creating a breakpoint on the current compiler/OS, then an abort will be triggered instead.
*
* @param str Text to match
*/
static void breakOnMessage(const char *str);
private:
LogHandler();
~LogHandler() = default;
void flushRepeatedMessages();
QString _targetName;
bool _shouldOutputProcessID { false };
bool _shouldOutputThreadID { false };
bool _shouldDisplayMilliseconds { false };
bool _useColor { false };
bool _keepRepeats { false };
bool _useJournald { false };
QString _previousMessage;
int _repeatCount { 0 };
int _currentMessageID { 0 };
struct RepeatedMessageRecord {
int repeatCount;
QString repeatString;
};
std::vector<RepeatedMessageRecord> _repeatedMessageRecords;
QStringList _breakMessages;
static QRecursiveMutex _mutex;
};
#define HIFI_FCDEBUG(category, message) \
do { \
if (category.isDebugEnabled()) { \
static int repeatedMessageID_ = LogHandler::getInstance().newRepeatedMessageID(); \
QString logString_; \
QDebug debugStringReceiver_(&logString_); \
debugStringReceiver_ << message; \
LogHandler::getInstance().printRepeatedMessage(repeatedMessageID_, LogDebug, QMessageLogContext(__FILE__, \
__LINE__, __func__, category().categoryName()), logString_); \
} \
} while (false)
#define HIFI_FDEBUG(message) HIFI_FCDEBUG((*QLoggingCategory::defaultCategory()), message)
#define HIFI_FCDEBUG_ID(category, messageID, message) \
do { \
if (category.isDebugEnabled()) { \
QString logString_; \
QDebug debugStringReceiver_(&logString_); \
debugStringReceiver_ << message; \
LogHandler::getInstance().printRepeatedMessage(messageID, LogDebug, QMessageLogContext(__FILE__, \
__LINE__, __func__, category().categoryName()), logString_); \
} \
} while (false)
#define HIFI_FDEBUG_ID(messageID, message) HIFI_FCDEBUG_ID((*QLoggingCategory::defaultCategory()), messageID, message)
#endif // hifi_LogHandler_h