Go to the documentation of this file.
4 #include <QMutexLocker>
5 #include <QWaitCondition>
10 #include <QStringList>
12 #include <QRegularExpression>
13 #include <QVariantMap>
34 #include <sys/types.h>
46 #if defined(__linux__)
47 #include <sys/syscall.h>
48 #elif defined(__FreeBSD__)
50 #include <sys/ucontext.h>
53 #elif defined(Q_OS_DARWIN)
54 #include <mach/mach.h>
58 #include <android/log.h>
105 void verboseAdd(uint64_t mask, QString name,
bool additive, QString helptext);
106 void loglevelAdd(
int value, QString name,
char shortname);
125 m_threadId((uint64_t)(QThread::currentThreadId())),
126 m_line(_line), m_type(_type), m_level(_level),
127 m_function(_function)
129 const char *slash = std::strrchr(_file,
'/');
130 m_file = (slash !=
nullptr) ? slash+1 : _file;
131 m_epoch = nowAsDuration<std::chrono::microseconds>();
139 static constexpr
char const *kSUnknown =
"thread_unknown";
175 #if defined(Q_OS_ANDROID)
176 m_tid = (int64_t)gettid();
177 #elif defined(__linux__)
178 m_tid = syscall(SYS_gettid);
179 #elif defined(__FreeBSD__)
181 [[maybe_unused]]
int dummy = thr_self( &lwpid );
182 m_tid = (int64_t)lwpid;
183 #elif defined(Q_OS_DARWIN)
184 m_tid = (int64_t)mach_thread_self();
193 QDateTime
epoch = QDateTime::fromMSecsSinceEpoch(
m_epoch.count()/1000);
194 QString timestamp =
epoch.toString(format);
201 timestamp += QString(
".%1").arg((
m_epoch % 1s).count(),6,10,QChar(
'0'));
211 return (*it)->shortname;
221 m_waitNotEmpty(new QWaitCondition()),
222 m_waitEmpty(new QWaitCondition()),
224 m_facility(facility), m_pid(getpid())
226 if (qEnvironmentVariableIsSet(
"VERBOSE_THREADS"))
228 LOG(VB_GENERAL, LOG_NOTICE,
229 "Logging thread registration/deregistration enabled!");
235 LOG(VB_GENERAL, LOG_ERR,
236 "Failed to start LogServer thread");
262 LOG(VB_GENERAL, LOG_INFO,
"Added logging to the console");
271 qApp->processEvents(QEventLoop::AllEvents, 10);
272 qApp->sendPostedEvents(
nullptr, QEvent::DeferredDelete);
303 qApp->processEvents();
323 item->
m_message = QString(
"Thread 0x%1 (%2) registered as \'%3\'")
325 QString::number(item->
m_tid),
347 item->
m_message = QString(
"Thread 0x%1 (%2) deregistered as \'%3\'")
349 QString::number(tid),
389 line = qPrintable(QString(
"%1 %2 [%3/%4] %5 %6:%7:%8 %9\n")
390 .arg(timestamp, QString(shortname),
391 QString::number(item->
pid()),
392 QString::number(item->
tid()),
395 QString::number(item->
m_line),
401 line = qPrintable(QString(
"%1 %2 [%3] %4 %5:%6:%7 %8\n")
402 .arg(timestamp, QString(shortname),
403 QString::number(item->
pid()),
406 QString::number(item->
m_line),
411 line = qPrintable(QString(
"%1 %2 %3\n")
412 .arg(timestamp, QString(shortname),
417 (void)
write(1, line.data(), line.size());
419 #else // Q_OS_ANDROID
421 android_LogPriority aprio;
425 aprio = ANDROID_LOG_FATAL;
430 aprio = ANDROID_LOG_ERROR;
433 aprio = ANDROID_LOG_WARN;
437 aprio = ANDROID_LOG_INFO;
440 aprio = ANDROID_LOG_DEBUG;
444 aprio = ANDROID_LOG_UNKNOWN;
448 __android_log_print(aprio,
"mfe",
"%s:%d:%s %s", qPrintable(item->
m_file),
452 __android_log_print(aprio,
"mfe",
"%s", qPrintable(item->
m_message));
483 int left = timeoutMS -
t.elapsed();
511 const char *_function,
512 int _line, LogLevel_t _level,
515 auto *item =
new LoggingItem(_file, _function, _line, _level, _type);
529 const char *
function, QString message)
543 #if defined( _MSC_VER ) && defined( _DEBUG )
544 OutputDebugStringA( qPrintable(item->
m_message) );
545 OutputDebugStringA(
"\n" );
596 #if !defined(_WIN32) && !defined(Q_OS_ANDROID)
599 const CODE *syslogname =
nullptr;
600 for (syslogname = &facilitynames[0];
601 (syslogname->c_name &&
607 #if CONFIG_SYSTEMD_JOURNAL
638 LogLevel_t level,
bool propagate,
bool testHarness)
644 LOG(VB_GENERAL, LOG_NOTICE, QString(
"Setting Log Level to LOG_%1")
654 QString path = finfo.path();
724 LOG(VB_GENERAL, LOG_NOTICE,
725 "Windows does not support syslog, disabling" );
727 #elif defined(Q_OS_ANDROID)
728 LOG(VB_GENERAL, LOG_NOTICE,
729 "Android does not support syslog, disabling" );
732 const CODE *name =
nullptr;
733 QByteArray ba = facility.toLocal8Bit();
734 char *
string = (
char *)ba.constData();
736 for (name = &facilitynames[0];
737 name->c_name && (strcmp(name->c_name,
string) != 0); name++);
739 return( name->c_val );
758 if ( item->name == level.toLower() )
759 return (LogLevel_t)item->value;
777 LoglevelMap::iterator it =
loglevelMap.find((
int)level);
791 void verboseAdd(uint64_t mask, QString name,
bool additive, QString helptext)
793 auto *item =
new VerboseDef;
798 name = name.toLower();
800 item->additive = additive;
801 item->helpText = std::move(helptext);
818 name = name.toLower();
820 item->shortname = shortname;
841 #undef VERBOSEDEFS_H_
842 #define MYTH_IMPLEMENT_VERBOSE
855 std::cerr <<
"Verbose debug levels.\n"
856 "Accepts any combination (separated by comma) of:\n\n";
858 for (VerboseMap::Iterator vit =
verboseMap.begin();
861 VerboseDef *item = vit.value();
862 QString name = QString(
" %1").arg(item->name, -15,
' ');
863 if (item->helpText.isEmpty())
865 std::cerr << name.toLocal8Bit().constData() <<
" - "
866 << item->helpText.toLocal8Bit().constData() << std::endl;
869 std::cerr << std::endl <<
870 "The default for this program appears to be: '-v " <<
871 m_verbose.toLocal8Bit().constData() <<
"'\n\n"
872 "Most options are additive except for 'none' and 'all'.\n"
873 "These two are semi-exclusive and take precedence over any\n"
874 "other options. However, you may use something like\n"
875 "'-v none,jobqueue' to receive only JobQueue related messages\n"
876 "and override the default verbosity level.\n\n"
877 "Additive options may also be subtracted from 'all' by\n"
878 "prefixing them with 'no', so you may use '-v all,nodatabase'\n"
879 "to view all but database debug messages.\n\n";
882 <<
"The 'global' loglevel is specified with --loglevel, but can be\n"
883 <<
"overridden on a component by component basis by appending "
885 <<
"to the component.\n"
886 <<
" For example: -v gui:debug,channel:notice,record\n\n";
888 std::cerr <<
"Some debug levels may not apply to this program.\n" << std::endl;
906 if (arg.startsWith(
'-'))
908 std::cerr <<
"Invalid or missing argument to -v/--verbose option\n";
912 static const QRegularExpression kSeparatorRE {
"[^\\w:]+" };
913 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
914 QStringList verboseOpts = arg.split(kSeparatorRE, QString::SkipEmptyParts);
916 QStringList verboseOpts = arg.split(kSeparatorRE, Qt::SkipEmptyParts);
918 for (
const auto& opt : qAsConst(verboseOpts))
920 option = opt.toLower();
921 bool reverseOption =
false;
924 if (option !=
"none" && option.startsWith(
"no"))
926 reverseOption =
true;
927 option = option.right(option.length() - 2);
930 if (option ==
"help")
935 if (option ==
"important")
937 std::cerr <<
"The \"important\" log mask is no longer valid.\n";
939 else if (option ==
"extra")
941 std::cerr <<
"The \"extra\" log mask is no longer valid. Please try "
942 "--loglevel debug instead.\n";
944 else if (option ==
"default")
959 int idx = option.indexOf(
':');
962 optionLevel = option.mid(idx + 1);
963 option = option.left(idx);
992 if (!optionLevel.isEmpty())
995 if (level != LOG_UNKNOWN)
1002 std::cerr <<
"Unknown argument for -v/--verbose: " <<
1003 option.toLocal8Bit().constData() << std::endl;;
1025 return QString(
"%1 (%2)").arg(strerror(errnum)).arg(errnum);
void loggingDeregisterThread(void)
Deregister the current thread's name.
QMap< int, LoglevelDef * > LoglevelMap
static bool debugRegistration
void logForwardStop(void)
void logStart(const QString &logfile, bool progress, int quiet, int facility, LogLevel_t level, bool propagate, bool testHarness)
Entry point to start logging for the application.
QString getTimestamp(const char *format="yyyy-MM-dd HH:mm:ss") const
Convert numerical timestamp to a readable date and time.
static QHash< uint64_t, QString > logThreadHash
QString logStrerror(int errnum)
Verbose helper function for ENO macro.
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
static QQueue< LoggingItem * > logQueue
void logPropagateCalc(void)
Generate the logPropagateArgs global with the latest logging level, mask, etc to propagate to all of ...
ComponentLogLevelMap componentLogLevel
QWaitCondition * m_waitNotEmpty
Condition variable for waiting for the queue to not be empty Protected by logQueueMutex.
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
QString m_appname
Cached application name.
void verboseHelp(void)
Outputs the Verbose levels and their descriptions (for –verbose help)
QString getThreadName(void)
Get the name of the thread that produced the LoggingItem.
void verboseInit(void)
Initialize the logging levels and verbose levels.
def write(text, progress=True)
LogPropagateOpts logPropagateOpts
The logging thread that consumes the logging queue and dispatches each LoggingItem.
QWaitCondition * m_waitEmpty
Condition variable for waiting for the queue to be empty Protected by logQueueMutex.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
bool logForwardStart(void)
@ GENERIC_EXIT_OK
Exited with no error.
static bool logThreadFinished
void logForwardMessage(LoggingItem *item)
void setThreadName(const QString &val)
The logging items that are generated by LOG() and are sent to the console.
bool m_progress
show only LOG_ERR and more important (console only)
void fillItem(LoggingItem *item)
char getLevelChar(void)
Get the message log level as a single character.
void LogPrintLine(uint64_t mask, LogLevel_t level, const char *file, int line, const char *function, QString message)
Format and send a log message into the queue.
void verboseAdd(uint64_t mask, QString name, bool additive, QString helptext)
Add a verbose level to the verboseMap.
QString logLevelGetName(LogLevel_t level)
Map a log level enumerated value back to the name.
void loggingRegisterThread(const QString &name)
Register the current thread with the given name.
bool m_aborted
Flag to abort the thread.
LogLevel_t logLevelGet(const QString &level)
Map a log level name back to the enumerated value.
static QMutex logThreadMutex
QThread * qthread(void)
Returns the thread, this will always return the same pointer no matter how often you restart the thre...
const QString verboseDefaultStr
void setThreadTid(void)
Set the thread ID of the thread that produced the LoggingItem.
QString m_filename
Filename of debug logfile.
void run(void) override
Run the logging thread.
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
uint64_t userDefaultValueInt
bool logConsole(LoggingItem *item) const
Process a log message, writing to the console.
bool flush(int timeoutMS=200000)
Wait for the queue to be flushed (up to a timeout)
void setFacility(const int val)
void stop(void)
Stop the thread by setting the abort flag after waiting a second for the queue to be flushed.
QString userDefaultValueStr
bool m_quiet
silence the console (console only)
int m_facility
Cached syslog facility (or -1 to disable)
void logStop(void)
Entry point for stopping logging for an application.
LoggerThread(QString filename, bool progress, bool quiet, int facility)
LoggerThread constructor.
std::chrono::microseconds m_epoch
static LoggerThread * logThread
int verboseArgParse(const QString &arg)
Parse the –verbose commandline argument and set the verbose level.
int syslogGetFacility([[maybe_unused]] const QString &facility)
Map a syslog facility name back to the enumerated value.
bool haveUserDefaultValues
const uint64_t verboseDefaultInt
static QMutex logQueueMutex
static LoggingItem * create(const char *_file, const char *_function, int _line, LogLevel_t _level, LoggingType _type)
Create a new LoggingItem.
static QHash< uint64_t, int64_t > logThreadTidHash
QString getTimestampUs(const char *format="yyyy-MM-dd HH:mm:ss") const
QStringList logPropagateArgList
This is a wrapper around QThread that does several additional things.
QMap< uint64_t, LogLevel_t > ComponentLogLevelMap
bool isRunning(void) const
void loglevelAdd(int value, QString name, char shortname)
Add a log level to the logLevelMap.
void setAppName(const QString &val)
bool logPropagateQuiet(void)
Check if we are propagating a "--quiet".
pid_t m_pid
Cached pid value.
int64_t getThreadTid(void)
Get the thread ID of the thread that produced the LoggingItem.
static QMutex logThreadTidMutex
static void handleItem(LoggingItem *item)
Handles each LoggingItem.
virtual int IncrRef(void)
Increments reference count.
@ GENERIC_EXIT_INVALID_CMDLINE
Command line parse error.
~LoggerThread() override
LoggerThread destructor. Triggers the deletion of all loggers.
void setPid(const int val)
void setLogFile(const QString &val)
void resetLogging(void)
Intended for use only by the test harness.
General purpose reference counter.
QMap< QString, VerboseDef * > VerboseMap