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 "qjsonwrapper/Json.h"
61 #include <android/log.h>
109 void verboseAdd(uint64_t mask, QString name,
bool additive, QString helptext);
110 void loglevelAdd(
int value, QString name,
char shortname);
129 m_threadId((uint64_t)(QThread::currentThreadId())),
130 m_line(_line), m_type(_type), m_level(_level),
131 m_function(_function)
133 const char *slash = std::strrchr(_file,
'/');
134 m_file = (slash !=
nullptr) ? slash+1 : _file;
135 m_epoch = nowAsDuration<std::chrono::microseconds>();
141 QVariantMap variant = QJsonWrapper::qobject2qvariant(
this);
142 QByteArray json = QJsonWrapper::toJson(variant);
153 static constexpr
char const *kSUnknown =
"thread_unknown";
189 #if defined(Q_OS_ANDROID)
190 m_tid = (int64_t)gettid();
191 #elif defined(__linux__)
192 m_tid = syscall(SYS_gettid);
193 #elif defined(__FreeBSD__)
195 int dummy = thr_self( &lwpid );
197 m_tid = (int64_t)lwpid;
198 #elif defined(Q_OS_DARWIN)
199 m_tid = (int64_t)mach_thread_self();
208 QDateTime
epoch = QDateTime::fromMSecsSinceEpoch(
m_epoch.count()/1000);
209 QString timestamp =
epoch.toString(format);
216 timestamp += QString(
".%1").arg((
m_epoch % 1s).count(),6,10,QChar(
'0'));
226 return (*it)->shortname;
234 QString table,
int facility) :
236 m_waitNotEmpty(new QWaitCondition()),
237 m_waitEmpty(new QWaitCondition()),
239 m_tablename(
std::move(table)), m_facility(facility), m_pid(getpid())
241 if (qEnvironmentVariableIsSet(
"VERBOSE_THREADS"))
243 LOG(VB_GENERAL, LOG_NOTICE,
244 "Logging thread registration/deregistration enabled!");
250 LOG(VB_GENERAL, LOG_ERR,
251 "Failed to start LogServer thread");
277 LOG(VB_GENERAL, LOG_INFO,
"Added logging to the console");
286 qApp->processEvents(QEventLoop::AllEvents, 10);
287 qApp->sendPostedEvents(
nullptr, QEvent::DeferredDelete);
318 qApp->processEvents();
338 item->
m_message = QString(
"Thread 0x%1 (%2) registered as \'%3\'")
340 QString::number(item->
m_tid),
362 item->
m_message = QString(
"Thread 0x%1 (%2) deregistered as \'%3\'")
364 QString::number(tid),
380 QList<QByteArray> list;
381 list.append(QByteArray());
414 line = qPrintable(QString(
"%1 %2 [%3/%4] %5 %6:%7:%8 %9\n")
415 .arg(timestamp, QString(shortname),
416 QString::number(item->
pid()),
417 QString::number(item->
tid()),
420 QString::number(item->
m_line),
426 line = qPrintable(QString(
"%1 %2 [%3] %4 %5:%6:%7 %8\n")
427 .arg(timestamp, QString(shortname),
428 QString::number(item->
pid()),
431 QString::number(item->
m_line),
436 line = qPrintable(QString(
"%1 %2 %3\n")
437 .arg(timestamp, QString(shortname),
442 (void)
write(1, line.data(), line.size());
444 #else // Q_OS_ANDROID
446 android_LogPriority aprio;
450 aprio = ANDROID_LOG_FATAL;
455 aprio = ANDROID_LOG_ERROR;
458 aprio = ANDROID_LOG_WARN;
462 aprio = ANDROID_LOG_INFO;
465 aprio = ANDROID_LOG_DEBUG;
469 aprio = ANDROID_LOG_UNKNOWN;
473 __android_log_print(aprio,
"mfe",
"%s:%d:%s %s", qPrintable(item->
m_file),
477 __android_log_print(aprio,
"mfe",
"%s", qPrintable(item->
m_message));
508 int left = timeoutMS -
t.elapsed();
537 const char *_function,
538 int _line, LogLevel_t _level,
541 auto *item =
new LoggingItem(_file, _function, _line, _level, _type);
549 QVariant variant = QJsonWrapper::parseJson(buf);
552 QJsonWrapper::qvariant2qobject(variant.toMap(), item);
567 const char *
function, QString message)
581 #if defined( _MSC_VER ) && defined( _DEBUG )
582 OutputDebugStringA( qPrintable(item->
m_message) );
583 OutputDebugStringA(
"\n" );
640 #if !defined(_WIN32) && !defined(Q_OS_ANDROID)
643 const CODE *syslogname =
nullptr;
644 for (syslogname = &facilitynames[0];
645 (syslogname->c_name &&
651 #if CONFIG_SYSTEMD_JOURNAL
683 LogLevel_t level,
bool dblog,
bool propagate,
bool testHarness)
689 LOG(VB_GENERAL, LOG_NOTICE, QString(
"Setting Log Level to LOG_%1")
700 QString path = finfo.path();
708 QString table = dblog ? QString(
"logging") : QString(
"");
772 LOG(VB_GENERAL, LOG_NOTICE,
773 "Windows does not support syslog, disabling" );
776 #elif defined(Q_OS_ANDROID)
777 LOG(VB_GENERAL, LOG_NOTICE,
778 "Android does not support syslog, disabling" );
782 const CODE *name =
nullptr;
783 QByteArray ba = facility.toLocal8Bit();
784 char *
string = (
char *)ba.constData();
786 for (name = &facilitynames[0];
787 name->c_name && (strcmp(name->c_name,
string) != 0); name++);
789 return( name->c_val );
808 if ( item->name == level.toLower() )
809 return (LogLevel_t)item->value;
827 LoglevelMap::iterator it =
loglevelMap.find((
int)level);
841 void verboseAdd(uint64_t mask, QString name,
bool additive, QString helptext)
843 auto *item =
new VerboseDef;
848 name = name.toLower();
850 item->additive = additive;
851 item->helpText = std::move(helptext);
868 name = name.toLower();
870 item->shortname = shortname;
891 #undef VERBOSEDEFS_H_
892 #define MYTH_IMPLEMENT_VERBOSE
905 std::cerr <<
"Verbose debug levels.\n"
906 "Accepts any combination (separated by comma) of:\n\n";
908 for (VerboseMap::Iterator vit =
verboseMap.begin();
911 VerboseDef *item = vit.value();
912 QString name = QString(
" %1").arg(item->name, -15,
' ');
913 if (item->helpText.isEmpty())
915 std::cerr << name.toLocal8Bit().constData() <<
" - "
916 << item->helpText.toLocal8Bit().constData() << std::endl;
919 std::cerr << std::endl <<
920 "The default for this program appears to be: '-v " <<
921 m_verbose.toLocal8Bit().constData() <<
"'\n\n"
922 "Most options are additive except for 'none' and 'all'.\n"
923 "These two are semi-exclusive and take precedence over any\n"
924 "other options. However, you may use something like\n"
925 "'-v none,jobqueue' to receive only JobQueue related messages\n"
926 "and override the default verbosity level.\n\n"
927 "Additive options may also be subtracted from 'all' by\n"
928 "prefixing them with 'no', so you may use '-v all,nodatabase'\n"
929 "to view all but database debug messages.\n\n";
932 <<
"The 'global' loglevel is specified with --loglevel, but can be\n"
933 <<
"overridden on a component by component basis by appending "
935 <<
"to the component.\n"
936 <<
" For example: -v gui:debug,channel:notice,record\n\n";
938 std::cerr <<
"Some debug levels may not apply to this program.\n" << std::endl;
956 if (arg.startsWith(
'-'))
958 std::cerr <<
"Invalid or missing argument to -v/--verbose option\n";
962 static const QRegularExpression kSeparatorRE {
"[^\\w:]+" };
963 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
964 QStringList verboseOpts = arg.split(kSeparatorRE, QString::SkipEmptyParts);
966 QStringList verboseOpts = arg.split(kSeparatorRE, Qt::SkipEmptyParts);
968 for (
const auto& opt : qAsConst(verboseOpts))
970 option = opt.toLower();
971 bool reverseOption =
false;
974 if (option !=
"none" && option.startsWith(
"no"))
976 reverseOption =
true;
977 option = option.right(option.length() - 2);
980 if (option ==
"help")
985 if (option ==
"important")
987 std::cerr <<
"The \"important\" log mask is no longer valid.\n";
989 else if (option ==
"extra")
991 std::cerr <<
"The \"extra\" log mask is no longer valid. Please try "
992 "--loglevel debug instead.\n";
994 else if (option ==
"default")
1009 int idx = option.indexOf(
':');
1012 optionLevel = option.mid(idx + 1);
1013 option = option.left(idx);
1042 if (!optionLevel.isEmpty())
1045 if (level != LOG_UNKNOWN)
1052 std::cerr <<
"Unknown argument for -v/--verbose: " <<
1053 option.toLocal8Bit().constData() << std::endl;;
1075 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)
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.
QString m_tablename
Cached table name for db logging.
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)
void logStart(const QString &logfile, bool progress, int quiet, int facility, LogLevel_t level, bool dblog, bool propagate, bool testHarness)
Entry point to start logging for the application.
LogPropagateOpts logPropagateOpts
int syslogGetFacility(const QString &facility)
Map a syslog facility name back to the enumerated value.
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.
void logForwardMessage(const QList< QByteArray > &msg)
LoggerThread(QString filename, bool progress, bool quiet, QString table, int facility)
LoggerThread constructor.
#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 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
void setTable(const QString &val)
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.
std::chrono::microseconds m_epoch
static LoggerThread * logThread
int verboseArgParse(const QString &arg)
Parse the –verbose commandline argument and set the verbose level.
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.
QByteArray toByteArray(void)
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