MythTV  master
logging.cpp
Go to the documentation of this file.
1 #include <QAtomicInt>
2 #include <QMutex>
3 #include <QMutexLocker>
4 #include <QWaitCondition>
5 #include <QList>
6 #include <QQueue>
7 #include <QHash>
8 #include <QFileInfo>
9 #include <QStringList>
10 #include <QMap>
11 #include <QRegExp>
12 #include <QVariantMap>
13 #include <iostream>
14 
15 using namespace std;
16 
17 #include "mythlogging.h"
18 #include "logging.h"
19 #include "loggingserver.h"
20 #include "mythdb.h"
21 #include "mythdirs.h"
22 #include "mythcorecontext.h"
23 #include "mythsystemlegacy.h"
24 #include "mythsignalingtimer.h"
25 #include "dbutil.h"
26 #include "exitcodes.h"
27 #include "compat.h"
28 
29 #include <csignal>
30 #include <cstdarg>
31 #include <cstdio>
32 #include <cstdlib>
33 #include <cstring>
34 #include <fcntl.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <utility>
38 #if HAVE_GETTIMEOFDAY
39 #include <sys/time.h>
40 #endif
41 #define SYSLOG_NAMES
42 #ifndef _WIN32
43 #include <mythsyslog.h>
44 #endif
45 #include <unistd.h>
46 
47 // Various ways to get to thread's tid
48 #if defined(linux)
49 #include <sys/syscall.h>
50 #elif defined(__FreeBSD__)
51 extern "C" {
52 #include <sys/ucontext.h>
53 #include <sys/thr.h>
54 }
55 #elif CONFIG_DARWIN
56 #include <mach/mach.h>
57 #endif
58 
59 // QJson
60 #include "qjsonwrapper/Json.h"
61 
62 #ifdef Q_OS_ANDROID
63 #include <android/log.h>
64 #endif
65 
66 static QMutex logQueueMutex;
67 static QQueue<LoggingItem *> logQueue;
68 static QRegExp logRegExp = QRegExp("[%]{1,2}");
69 
70 static LoggerThread *logThread = nullptr;
71 static QMutex logThreadMutex;
72 static QHash<uint64_t, char *> logThreadHash;
73 
74 static QMutex logThreadTidMutex;
75 static QHash<uint64_t, int64_t> logThreadTidHash;
76 
77 static bool logThreadFinished = false;
78 static bool debugRegistration = false;
79 
82  int m_quiet;
84  bool m_dblog;
85  QString m_path;
86 };
87 
88 LogPropagateOpts logPropagateOpts {false, 0, 0, true, ""};
90 QStringList logPropagateArgList;
91 
92 LogLevel_t logLevel = LOG_INFO;
93 
94 bool verboseInitialized = false;
97 
100 
101 const uint64_t verboseDefaultInt = VB_GENERAL;
102 const char *verboseDefaultStr = " general";
103 
105 QString verboseString = QString(verboseDefaultStr);
107 
111 
112 void verboseAdd(uint64_t mask, QString name, bool additive, QString helptext);
113 void loglevelAdd(int value, QString name, char shortname);
114 void verboseInit(void);
115 void verboseHelp(void);
116 
117 void loggingGetTimeStamp(qlonglong *epoch, uint *usec)
118 {
119 #if HAVE_GETTIMEOFDAY
120  struct timeval tv {};
121  gettimeofday(&tv, nullptr);
122  *epoch = tv.tv_sec;
123  if (usec)
124  *usec = tv.tv_usec;
125 #else
126  /* Stupid system has no gettimeofday, use less precise QDateTime */
127  QDateTime date = MythDate::current();
128  *epoch = date.toTime_t();
129  if (usec)
130  {
131  QTime time = date.time();
132  *usec = time.msec() * 1000;
133  }
134 #endif
135 }
136 
137 LoggingItem::LoggingItem(const char *_file, const char *_function,
138  int _line, LogLevel_t _level, LoggingType _type) :
139  ReferenceCounter("LoggingItem", false),
140  m_threadId((uint64_t)(QThread::currentThreadId())),
141  m_line(_line), m_type(_type), m_level(_level),
142  m_file(strdup(_file)), m_function(strdup(_function))
143 {
145  setThreadTid();
146 }
147 
149 {
150  free(m_file);
151  free(m_function);
152  free(m_threadName);
153  free(m_appName);
154  free(m_table);
155  free(m_logFile);
156 }
157 
158 QByteArray LoggingItem::toByteArray(void)
159 {
160  QVariantMap variant = QJsonWrapper::qobject2qvariant(this);
161  QByteArray json = QJsonWrapper::toJson(variant);
162 
163  //cout << json.constData() << endl;
164 
165  return json;
166 }
167 
171 {
172  static constexpr char const *kSUnknown = "thread_unknown";
173 
174  if( m_threadName )
175  return m_threadName;
176 
177  QMutexLocker locker(&logThreadMutex);
178  return logThreadHash.value(m_threadId, (char *)kSUnknown);
179 }
180 
187 {
188  QMutexLocker locker(&logThreadTidMutex);
189  m_tid = logThreadTidHash.value(m_threadId, 0);
190  return m_tid;
191 }
192 
200 {
201  QMutexLocker locker(&logThreadTidMutex);
202 
203  m_tid = logThreadTidHash.value(m_threadId, -1);
204  if (m_tid == -1)
205  {
206  m_tid = 0;
207 
208 #if defined(Q_OS_ANDROID)
209  m_tid = (int64_t)gettid();
210 #elif defined(linux)
211  m_tid = syscall(SYS_gettid);
212 #elif defined(__FreeBSD__)
213  long lwpid;
214  int dummy = thr_self( &lwpid );
215  (void)dummy;
216  m_tid = (int64_t)lwpid;
217 #elif CONFIG_DARWIN
218  m_tid = (int64_t)mach_thread_self();
219 #endif
221  }
222 }
223 
225 QString LoggingItem::getTimestamp (void) const
226 {
227 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
228  QDateTime epoch = QDateTime::fromTime_t(m_epoch).toUTC();
229 #else
231 #endif
232  QString timestamp = epoch.toString("yyyy-MM-dd HH:mm:ss");
233  return timestamp;
234 }
235 
236 QString LoggingItem::getTimestampUs (void) const
237 {
238  QString timestamp = getTimestamp();
239  timestamp += QString(".%1").arg(m_usec,6,10,QChar('0'));
240  return timestamp;
241 }
242 
245 {
246  QMutexLocker locker(&loglevelMapMutex);
247  LoglevelMap::iterator it = loglevelMap.find(m_level);
248  if (it != loglevelMap.end())
249  return (*it)->shortname;
250  return '-';
251 }
252 
257  QString table, int facility) :
258  MThread("Logger"),
259  m_waitNotEmpty(new QWaitCondition()),
260  m_waitEmpty(new QWaitCondition()),
261  m_filename(std::move(filename)), m_progress(progress), m_quiet(quiet),
262  m_tablename(std::move(table)), m_facility(facility), m_pid(getpid())
263 {
264  char *debug = getenv("VERBOSE_THREADS");
265  if (debug != nullptr)
266  {
267  LOG(VB_GENERAL, LOG_NOTICE,
268  "Logging thread registration/deregistration enabled!");
269  debugRegistration = true;
270  }
271 
272  if (!logForwardStart())
273  {
274  LOG(VB_GENERAL, LOG_ERR,
275  "Failed to start LogServer thread");
276  }
277  moveToThread(qthread());
278 }
279 
282 {
283  stop();
284  wait();
285  logForwardStop();
286 
287  delete m_waitNotEmpty;
288  delete m_waitEmpty;
289 }
290 
296 {
297  RunProlog();
298 
299  logThreadFinished = false;
300 
301  LOG(VB_GENERAL, LOG_INFO, "Added logging to the console");
302 
303  bool dieNow = false;
304 
305  QMutexLocker qLock(&logQueueMutex);
306 
307  while (!m_aborted || !logQueue.isEmpty())
308  {
309  qLock.unlock();
310  qApp->processEvents(QEventLoop::AllEvents, 10);
311  qApp->sendPostedEvents(nullptr, QEvent::DeferredDelete);
312 
313  qLock.relock();
314  if (logQueue.isEmpty())
315  {
316  m_waitEmpty->wakeAll();
317  m_waitNotEmpty->wait(qLock.mutex(), 100);
318  continue;
319  }
320 
321  LoggingItem *item = logQueue.dequeue();
322  qLock.unlock();
323 
324  fillItem(item);
325  handleItem(item);
326  logConsole(item);
327  item->DecrRef();
328 
329  qLock.relock();
330  }
331 
332  qLock.unlock();
333 
334  // This must be before the timer stop below or we deadlock when the timer
335  // thread tries to deregister, and we wait for it.
336  logThreadFinished = true;
337 
338  RunEpilog();
339 
340  // cppcheck-suppress knownConditionTrueFalse
341  if (dieNow)
342  {
343  qApp->processEvents();
344  }
345 }
346 
353 {
354  if (item->m_type & kRegistering)
355  {
356  item->m_tid = item->getThreadTid();
357 
358  QMutexLocker locker(&logThreadMutex);
359  if (logThreadHash.contains(item->m_threadId))
360  {
361  char *threadName = logThreadHash.take(item->m_threadId);
362  free(threadName);
363  }
364  logThreadHash[item->m_threadId] = strdup(item->m_threadName);
365 
366  if (debugRegistration)
367  {
368  item->m_message = QString("Thread 0x%1 (%2) registered as \'%3\'")
369  .arg(QString::number(item->m_threadId,16),
370  QString::number(item->m_tid),
371  logThreadHash[item->m_threadId]);
372  }
373  }
374  else if (item->m_type & kDeregistering)
375  {
376  int64_t tid = 0;
377 
378  {
379  QMutexLocker locker(&logThreadTidMutex);
380  if( logThreadTidHash.contains(item->m_threadId) )
381  {
382  tid = logThreadTidHash[item->m_threadId];
383  logThreadTidHash.remove(item->m_threadId);
384  }
385  }
386 
387  QMutexLocker locker(&logThreadMutex);
388  if (logThreadHash.contains(item->m_threadId))
389  {
390  if (debugRegistration)
391  {
392  item->m_message = QString("Thread 0x%1 (%2) deregistered as \'%3\'")
393  .arg(QString::number(item->m_threadId,16),
394  QString::number(tid),
395  logThreadHash[item->m_threadId]);
396  }
397  char *threadName = logThreadHash.take(item->m_threadId);
398  free(threadName);
399  }
400  }
401 
402  if (!item->m_message.isEmpty())
403  {
411  QList<QByteArray> list;
412  list.append(QByteArray());
413  list.append(item->toByteArray());
414  logForwardMessage(list);
415  }
416 }
417 
421 {
422  if (m_quiet || (m_progress && item->m_level > LOG_ERR))
423  return false;
424 
425  if (!(item->m_type & kMessage))
426  return false;
427 
428  item->IncrRef();
429 
430 #ifndef Q_OS_ANDROID
431  std::string line;
432 
433  if (item->m_type & kStandardIO)
434  {
435  line = qPrintable(item->m_message);
436  }
437  else
438  {
439  QString timestamp = item->getTimestampUs();
440  char shortname = item->getLevelChar();
441 
442 #if CONFIG_DEBUGTYPE
443  if (item->tid())
444  {
445  line = qPrintable(QString("%1 %2 [%3/%4] %5 %6:%7:%8 %9\n")
446  .arg(timestamp, QString(shortname),
447  QString::number(item->pid()),
448  QString::number(item->tid()),
449  item->rawThreadName(),
450  item->m_file,
451  QString::number(item->m_line),
452  item->m_function,
453  item->m_message));
454  }
455  else
456  {
457  line = qPrintable(QString("%1 %2 [%3] %4 %5:%6:%7 %8\n")
458  .arg(timestamp, QString(shortname),
459  QString::number(item->pid()),
460  item->rawThreadName(),
461  item->m_file,
462  QString::number(item->m_line),
463  item->m_function,
464  item->m_message));
465  }
466 #else
467  line = qPrintable(QString("%1 %2 %3\n")
468  .arg(timestamp, QString(shortname),
469  item->m_message));
470 #endif
471  }
472 
473  (void)write(1, line.data(), line.size());
474 
475 #else // Q_OS_ANDROID
476 
477  android_LogPriority aprio;
478  switch (item->m_level)
479  {
480  case LOG_EMERG:
481  aprio = ANDROID_LOG_FATAL;
482  break;
483  case LOG_ALERT:
484  case LOG_CRIT:
485  case LOG_ERR:
486  aprio = ANDROID_LOG_ERROR;
487  break;
488  case LOG_WARNING:
489  aprio = ANDROID_LOG_WARN;
490  break;
491  case LOG_NOTICE:
492  case LOG_INFO:
493  aprio = ANDROID_LOG_INFO;
494  break;
495  case LOG_DEBUG:
496  aprio = ANDROID_LOG_DEBUG;
497  break;
498  case LOG_UNKNOWN:
499  default:
500  aprio = ANDROID_LOG_UNKNOWN;
501  break;
502  }
503 #if CONFIG_DEBUGTYPE
504  __android_log_print(aprio, "mfe", "%s:%d:%s %s", item->m_file,
505  item->m_line, item->m_function, qPrintable(item->m_message));
506 #else
507  __android_log_print(aprio, "mfe", "%s", qPrintable(item->m_message));
508 #endif
509 #endif
510 
511  item->DecrRef();
512 
513  return true;
514 }
515 
516 
520 {
521  logQueueMutex.lock();
522  flush(1000);
523  m_aborted = true;
524  logQueueMutex.unlock();
525  m_waitNotEmpty->wakeAll();
526 }
527 
531 bool LoggerThread::flush(int timeoutMS)
532 {
533  QElapsedTimer t;
534  t.start();
535  while (!m_aborted && !logQueue.isEmpty() && !t.hasExpired(timeoutMS))
536  {
537  m_waitNotEmpty->wakeAll();
538  int left = timeoutMS - t.elapsed();
539  if (left > 0)
540  m_waitEmpty->wait(&logQueueMutex, left);
541  }
542  return logQueue.isEmpty();
543 }
544 
546 {
547  if (!item)
548  return;
549 
550  item->setPid(m_pid);
551  item->setThreadName(item->getThreadName());
552  item->setAppName(m_appname);
553  item->setTable(m_tablename);
554  item->setLogFile(m_filename);
555  item->setFacility(m_facility);
556 }
557 
558 
566 LoggingItem *LoggingItem::create(const char *_file,
567  const char *_function,
568  int _line, LogLevel_t _level,
569  LoggingType _type)
570 {
571  auto *item = new LoggingItem(_file, _function, _line, _level, _type);
572 
573  return item;
574 }
575 
577 {
578  // Deserialize buffer
579  QVariant variant = QJsonWrapper::parseJson(buf);
580 
581  auto *item = new LoggingItem;
582  QJsonWrapper::qvariant2qobject(variant.toMap(), item);
583 
584  return item;
585 }
586 
587 
596 void LogPrintLine( uint64_t mask, LogLevel_t level, const char *file, int line,
597  const char *function, QString message)
598 {
599  int type = kMessage;
600  type |= (mask & VB_FLUSH) ? kFlush : 0;
601  type |= (mask & VB_STDIO) ? kStandardIO : 0;
602  LoggingItem *item = LoggingItem::create(file, function, line, level,
603  (LoggingType)type);
604  if (!item)
605  return;
606 
607  item->m_message = message;
608 
609  QMutexLocker qLock(&logQueueMutex);
610 
611 #if defined( _MSC_VER ) && defined( _DEBUG )
612  OutputDebugStringA( qPrintable(item->m_message) );
613  OutputDebugStringA( "\n" );
614 #endif
615 
616  logQueue.enqueue(item);
617 
619  {
620  while (!logQueue.isEmpty())
621  {
622  item = logQueue.dequeue();
623  qLock.unlock();
624  logThread->handleItem(item);
625  logThread->logConsole(item);
626  item->DecrRef();
627  qLock.relock();
628  }
629  }
630  else if (logThread && !logThreadFinished && (type & kFlush))
631  {
632  logThread->flush();
633  }
634 }
635 
636 
641 {
642  logPropagateArgList.clear();
643 
644  QString mask = verboseString.trimmed();
645  mask.replace(QRegExp(" "), ",");
646  mask.remove(QRegExp("^,"));
647  logPropagateArgs = " --verbose " + mask;
648  logPropagateArgList << "--verbose" << mask;
649 
651  {
652  logPropagateArgs += " --logpath " + logPropagateOpts.m_path;
653  logPropagateArgList << "--logpath" << logPropagateOpts.m_path;
654  }
655 
656  QString name = logLevelGetName(logLevel);
657  logPropagateArgs += " --loglevel " + name;
658  logPropagateArgList << "--loglevel" << name;
659 
660  for (int i = 0; i < logPropagateOpts.m_quiet; i++)
661  {
662  logPropagateArgs += " --quiet";
663  logPropagateArgList << "--quiet";
664  }
665 
667  {
668  logPropagateArgs += " --enable-dblog";
669  logPropagateArgList << "--enable-dblog";
670  }
671 
672 #if !defined(_WIN32) && !defined(Q_OS_ANDROID)
673  if (logPropagateOpts.m_facility >= 0)
674  {
675  const CODE *syslogname = nullptr;
676  for (syslogname = &facilitynames[0];
677  (syslogname->c_name &&
678  syslogname->c_val != logPropagateOpts.m_facility); syslogname++);
679 
680  logPropagateArgs += QString(" --syslog %1").arg(syslogname->c_name);
681  logPropagateArgList << "--syslog" << syslogname->c_name;
682  }
683 #if CONFIG_SYSTEMD_JOURNAL
684  else if (logPropagateOpts.m_facility == SYSTEMD_JOURNAL_FACILITY)
685  {
686  logPropagateArgs += " --systemd-journal";
687  logPropagateArgList << "--systemd-journal";
688  }
689 #endif
690 #endif
691 }
692 
696 {
697  return logPropagateOpts.m_quiet != 0;
698 }
699 
712 void logStart(const QString& logfile, bool progress, int quiet, int facility,
713  LogLevel_t level, bool dblog, bool propagate)
714 {
715  if (logThread && logThread->isRunning())
716  return;
717 
718  logLevel = level;
719  LOG(VB_GENERAL, LOG_NOTICE, QString("Setting Log Level to LOG_%1")
720  .arg(logLevelGetName(logLevel).toUpper()));
721 
722  logPropagateOpts.m_propagate = propagate;
724  logPropagateOpts.m_facility = facility;
725  logPropagateOpts.m_dblog = dblog;
726 
727  if (propagate)
728  {
729  QFileInfo finfo(logfile);
730  QString path = finfo.path();
731  logPropagateOpts.m_path = path;
732  }
733 
735 
736  QString table = dblog ? QString("logging") : QString("");
737 
738  if (!logThread)
739  logThread = new LoggerThread(logfile, progress, quiet, table, facility);
740 
741  logThread->start();
742 }
743 
745 void logStop(void)
746 {
747  if (logThread)
748  {
749  logThread->stop();
750  logThread->wait();
751  delete logThread;
752  logThread = nullptr;
753  }
754 }
755 
760 void loggingRegisterThread(const QString &name)
761 {
762  if (logThreadFinished)
763  return;
764 
765  QMutexLocker qLock(&logQueueMutex);
766 
767  LoggingItem *item = LoggingItem::create(__FILE__, __FUNCTION__,
768  __LINE__, LOG_DEBUG,
769  kRegistering);
770  if (item)
771  {
772  item->setThreadName((char *)name.toLocal8Bit().constData());
773  logQueue.enqueue(item);
774  }
775 }
776 
780 {
781  if (logThreadFinished)
782  return;
783 
784  QMutexLocker qLock(&logQueueMutex);
785 
786  LoggingItem *item = LoggingItem::create(__FILE__, __FUNCTION__, __LINE__,
787  LOG_DEBUG,
789  if (item)
790  logQueue.enqueue(item);
791 }
792 
793 
797 int syslogGetFacility(const QString& facility)
798 {
799 #ifdef _WIN32
800  LOG(VB_GENERAL, LOG_NOTICE,
801  "Windows does not support syslog, disabling" );
802  Q_UNUSED(facility);
803  return( -2 );
804 #elif defined(Q_OS_ANDROID)
805  LOG(VB_GENERAL, LOG_NOTICE,
806  "Android does not support syslog, disabling" );
807  Q_UNUSED(facility);
808  return( -2 );
809 #else
810  const CODE *name = nullptr;
811  int i = 0;
812  QByteArray ba = facility.toLocal8Bit();
813  char *string = (char *)ba.constData();
814 
815  for (i = 0, name = &facilitynames[0];
816  name->c_name && (strcmp(name->c_name, string) != 0); i++, name++);
817 
818  return( name->c_val );
819 #endif
820 }
821 
825 LogLevel_t logLevelGet(const QString& level)
826 {
827  QMutexLocker locker(&loglevelMapMutex);
828  if (!verboseInitialized)
829  {
830  locker.unlock();
831  verboseInit();
832  locker.relock();
833  }
834 
835  for (auto *item : qAsConst(loglevelMap))
836  {
837  if ( item->name == level.toLower() )
838  return (LogLevel_t)item->value;
839  }
840 
841  return LOG_UNKNOWN;
842 }
843 
847 QString logLevelGetName(LogLevel_t level)
848 {
849  QMutexLocker locker(&loglevelMapMutex);
850  if (!verboseInitialized)
851  {
852  locker.unlock();
853  verboseInit();
854  locker.relock();
855  }
856  LoglevelMap::iterator it = loglevelMap.find((int)level);
857 
858  if ( it == loglevelMap.end() )
859  return QString("unknown");
860 
861  return (*it)->name;
862 }
863 
870 void verboseAdd(uint64_t mask, QString name, bool additive, QString helptext)
871 {
872  auto *item = new VerboseDef;
873 
874  item->mask = mask;
875  // VB_GENERAL -> general
876  name.remove(0, 3);
877  name = name.toLower();
878  item->name = name;
879  item->additive = additive;
880  item->helpText = std::move(helptext);
881 
882  verboseMap.insert(name, item);
883 }
884 
890 void loglevelAdd(int value, QString name, char shortname)
891 {
892  auto *item = new LoglevelDef;
893 
894  item->value = value;
895  // LOG_CRIT -> crit
896  name.remove(0, 4);
897  name = name.toLower();
898  item->name = name;
899  item->shortname = shortname;
900 
901  loglevelMap.insert(value, item);
902 }
903 
905 void verboseInit(void)
906 {
907  QMutexLocker locker(&verboseMapMutex);
908  QMutexLocker locker2(&loglevelMapMutex);
909  verboseMap.clear();
910  loglevelMap.clear();
911 
912  // This looks funky, so I'll put some explanation here. The verbosedefs.h
913  // file gets included as part of the mythlogging.h include, and at that
914  // time, the normal (without MYTH_IMPLEMENT_VERBOSE defined) code case will
915  // define the VerboseMask enum. At this point, we force it to allow us to
916  // include the file again, but with MYTH_IMPLEMENT_VERBOSE set so that the
917  // single definition of the VB_* values can be shared to define also the
918  // contents of verboseMap, via repeated calls to verboseAdd()
919 
920 #undef VERBOSEDEFS_H_
921 #define MYTH_IMPLEMENT_VERBOSE
922 #include "verbosedefs.h"
923 
924  verboseInitialized = true;
925 }
926 
927 
930 void verboseHelp(void)
931 {
932  QString m_verbose = userDefaultValueStr.trimmed();
933  m_verbose.replace(QRegExp(" "), ",");
934  m_verbose.remove(QRegExp("^,"));
935 
936  cerr << "Verbose debug levels.\n"
937  "Accepts any combination (separated by comma) of:\n\n";
938 
939  for (VerboseMap::Iterator vit = verboseMap.begin();
940  vit != verboseMap.end(); ++vit )
941  {
942  VerboseDef *item = vit.value();
943  QString name = QString(" %1").arg(item->name, -15, ' ');
944  if (item->helpText.isEmpty())
945  continue;
946  cerr << name.toLocal8Bit().constData() << " - " <<
947  item->helpText.toLocal8Bit().constData() << endl;
948  }
949 
950  cerr << endl <<
951  "The default for this program appears to be: '-v " <<
952  m_verbose.toLocal8Bit().constData() << "'\n\n"
953  "Most options are additive except for 'none' and 'all'.\n"
954  "These two are semi-exclusive and take precedence over any\n"
955  "other options. However, you may use something like\n"
956  "'-v none,jobqueue' to receive only JobQueue related messages\n"
957  "and override the default verbosity level.\n\n"
958  "Additive options may also be subtracted from 'all' by\n"
959  "prefixing them with 'no', so you may use '-v all,nodatabase'\n"
960  "to view all but database debug messages.\n\n";
961 
962  cerr << "The 'global' loglevel is specified with --loglevel, but can be\n"
963  << "overridden on a component by component basis by appending "
964  << "':level'\n"
965  << "to the component.\n"
966  << " For example: -v gui:debug,channel:notice,record\n\n";
967 
968  cerr << "Some debug levels may not apply to this program.\n" << endl;
969 }
970 
974 int verboseArgParse(const QString& arg)
975 {
976  QString option;
977 
978  if (!verboseInitialized)
979  verboseInit();
980 
981  QMutexLocker locker(&verboseMapMutex);
982 
984  verboseString = QString(verboseDefaultStr);
985 
986  if (arg.startsWith('-'))
987  {
988  cerr << "Invalid or missing argument to -v/--verbose option\n";
990  }
991 
992  QStringList verboseOpts = arg.split(QRegExp("[^\\w:]+",
993  Qt::CaseInsensitive,
994  QRegExp::RegExp2));
995  for (const auto& opt : qAsConst(verboseOpts))
996  {
997  option = opt.toLower();
998  bool reverseOption = false;
999  QString optionLevel;
1000 
1001  if (option != "none" && option.startsWith("no"))
1002  {
1003  reverseOption = true;
1004  option = option.right(option.length() - 2);
1005  }
1006 
1007  if (option == "help")
1008  {
1009  verboseHelp();
1011  }
1012  if (option == "important")
1013  {
1014  cerr << "The \"important\" log mask is no longer valid.\n";
1015  }
1016  else if (option == "extra")
1017  {
1018  cerr << "The \"extra\" log mask is no longer valid. Please try "
1019  "--loglevel debug instead.\n";
1020  }
1021  else if (option == "default")
1022  {
1024  {
1027  }
1028  else
1029  {
1031  verboseString = QString(verboseDefaultStr);
1032  }
1033  }
1034  else
1035  {
1036  int idx = option.indexOf(':');
1037  if (idx != -1)
1038  {
1039  optionLevel = option.mid(idx + 1);
1040  option = option.left(idx);
1041  }
1042 
1043  VerboseDef *item = verboseMap.value(option);
1044 
1045  if (item)
1046  {
1047  if (reverseOption)
1048  {
1049  verboseMask &= ~(item->mask);
1050  verboseString = verboseString.remove(' ' + item->name);
1051  verboseString += " no" + item->name;
1052  }
1053  else
1054  {
1055  if (item->additive)
1056  {
1057  if (!(verboseMask & item->mask))
1058  {
1059  verboseMask |= item->mask;
1060  verboseString += ' ' + item->name;
1061  }
1062  }
1063  else
1064  {
1065  verboseMask = item->mask;
1066  verboseString = item->name;
1067  }
1068 
1069  if (!optionLevel.isEmpty())
1070  {
1071  LogLevel_t level = logLevelGet(optionLevel);
1072  if (level != LOG_UNKNOWN)
1073  componentLogLevel[item->mask] = level;
1074  }
1075  }
1076  }
1077  else
1078  {
1079  cerr << "Unknown argument for -v/--verbose: " <<
1080  option.toLocal8Bit().constData() << endl;;
1082  }
1083  }
1084  }
1085 
1086  if (!haveUserDefaultValues)
1087  {
1088  haveUserDefaultValues = true;
1091  }
1092 
1093  return GENERIC_EXIT_OK;
1094 }
1095 
1100 QString logStrerror(int errnum)
1101 {
1102  return QString("%1 (%2)").arg(strerror(errnum)).arg(errnum);
1103 }
1104 
1105 
1106 /*
1107  * vim:ts=4:sw=4:ai:et:si:sts=4
1108  */
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:215
void loglevelAdd(int value, QString name, char shortname)
Add a log level to the logLevelMap.
Definition: logging.cpp:890
QString logfile
QString m_path
Definition: logging.cpp:85
QString logLevelGetName(LogLevel_t level)
Map a log level enumerated value back to the name.
Definition: logging.cpp:847
def write(text, progress=True)
Definition: mythburn.py:308
char * m_function
Definition: logging.h:150
static QQueue< LoggingItem * > logQueue
Definition: logging.cpp:67
The logging thread that consumes the logging queue and dispatches each LoggingItem.
Definition: logging.h:168
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
LoggingItem()
Definition: logging.h:158
const char * verboseDefaultStr
Definition: logging.cpp:102
QString m_tablename
Cached table name for db logging.
Definition: logging.h:200
char * m_file
Definition: logging.h:149
char * getThreadName(void)
Get the name of the thread that produced the LoggingItem.
Definition: logging.cpp:170
#define GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:10
bool m_quiet
silence the console (console only)
Definition: logging.h:197
QMutex verboseMapMutex
Definition: logging.cpp:96
static QMutex logQueueMutex
Definition: logging.cpp:66
int m_facility
Cached syslog facility (or -1 to disable)
Definition: logging.h:201
const char * rawThreadName() const
Definition: logging.h:134
static QMutex logThreadMutex
Definition: logging.cpp:71
QString m_filename
Filename of debug logfile.
Definition: logging.h:195
bool verboseInitialized
Definition: logging.cpp:94
General purpose reference counter.
bool flush(int timeoutMS=200000)
Wait for the queue to be flushed (up to a timeout)
Definition: logging.cpp:531
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:311
bool m_progress
show only LOG_ERR and more important (console only)
Definition: logging.h:196
QString logPropagateArgs
Definition: logging.cpp:89
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.
Definition: logging.cpp:596
The logging items that are generated by LOG() and are sent to the console.
Definition: logging.h:61
int syslogGetFacility(const QString &facility)
Map a syslog facility name back to the enumerated value.
Definition: logging.cpp:797
void fillItem(LoggingItem *item)
Definition: logging.cpp:545
void loggingGetTimeStamp(qlonglong *epoch, uint *usec)
Definition: logging.cpp:117
uint64_t verboseMask
Definition: logging.cpp:104
void loggingRegisterThread(const QString &name)
Register the current thread with the given name.
Definition: logging.cpp:760
int pid
Definition: logging.h:65
QString userDefaultValueStr
Definition: logging.cpp:109
void logStart(const QString &logfile, bool progress, int quiet, int facility, LogLevel_t level, bool dblog, bool propagate)
Entry point to start logging for the application.
Definition: logging.cpp:712
ComponentLogLevelMap componentLogLevel
Definition: logging.cpp:106
LoggingType m_type
Definition: logging.h:145
QWaitCondition * m_waitEmpty
Condition variable for waiting for the queue to be empty Protected by logQueueMutex.
Definition: logging.h:189
QMap< uint64_t, LogLevel_t > ComponentLogLevelMap
Definition: verbosedefs.h:217
VerboseMap verboseMap
Definition: logging.cpp:95
pid_t m_pid
Cached pid value.
Definition: logging.h:202
static void handleItem(LoggingItem *item)
Handles each LoggingItem.
Definition: logging.cpp:352
void verboseAdd(uint64_t mask, QString name, bool additive, QString helptext)
Add a verbose level to the verboseMap.
Definition: logging.cpp:870
static bool debugRegistration
Definition: logging.cpp:78
QString getTimestamp(void) const
Convert numerical timestamp to a readable date and time.
Definition: logging.cpp:225
QMap< QString, VerboseDef * > VerboseMap
Definition: verbosedefs.h:209
void verboseHelp(void)
Outputs the Verbose levels and their descriptions (for –verbose help)
Definition: logging.cpp:930
void logForwardMessage(const QList< QByteArray > &msg)
virtual int IncrRef(void)
Increments reference count.
MBASE_PUBLIC QDateTime fromSecsSinceEpoch(uint seconds)
This function takes the number of seconds since the start of the epoch and returns a QDateTime with t...
Definition: mythdate.cpp:88
qlonglong tid
Definition: logging.h:66
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:85
QString getTimestampUs(void) const
Definition: logging.cpp:236
static LoggerThread * logThread
Definition: logging.cpp:70
bool logForwardStart(void)
void stop(void)
Stop the thread by setting the abort flag after waiting a second for the queue to be flushed.
Definition: logging.cpp:519
~LoggingItem() override
Definition: logging.cpp:148
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
bool logPropagateQuiet(void)
Check if we are propagating a "--quiet".
Definition: logging.cpp:695
static QHash< uint64_t, char * > logThreadHash
Definition: logging.cpp:72
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
bool isRunning(void) const
Definition: mthread.cpp:274
LoggingType
Definition: logging.h:41
char getLevelChar(void)
Get the message log level as a single character.
Definition: logging.cpp:244
QString logStrerror(int errnum)
Verbose helper function for ENO macro.
Definition: logging.cpp:1100
unsigned int uint
Definition: compat.h:140
void loggingDeregisterThread(void)
Deregister the current thread's name.
Definition: logging.cpp:779
QMap< int, LoglevelDef * > LoglevelMap
Definition: verbosedefs.h:216
QByteArray toByteArray(void)
Definition: logging.cpp:158
static QRegExp logRegExp
Definition: logging.cpp:68
QString m_appname
Cached application name.
Definition: logging.h:198
qulonglong m_threadId
Definition: logging.h:142
LogPropagateOpts logPropagateOpts
Definition: logging.cpp:88
bool logConsole(LoggingItem *item) const
Process a log message, writing to the console.
Definition: logging.cpp:420
QWaitCondition * m_waitNotEmpty
Condition variable for waiting for the queue to not be empty Protected by logQueueMutex.
Definition: logging.h:185
LogLevel_t m_level
Definition: logging.h:146
QString m_message
Definition: logging.h:155
char * m_threadName
Definition: logging.h:151
LoglevelMap loglevelMap
Definition: logging.cpp:98
char * m_table
Definition: logging.h:153
int64_t getThreadTid(void)
Get the thread ID of the thread that produced the LoggingItem.
Definition: logging.cpp:186
char * m_appName
Definition: logging.h:152
qlonglong epoch() const
Definition: logging.h:106
LogLevel_t logLevel
Definition: logging.cpp:92
VERBOSE_PREAMBLE Most debug(nodatabase, notimestamp, noextra)") VERBOSE_MAP(VB_GENERAL
uint64_t userDefaultValueInt
Definition: logging.cpp:108
uint m_usec
Definition: logging.h:143
void logStop(void)
Entry point for stopping logging for an application.
Definition: logging.cpp:745
static QMutex logThreadTidMutex
Definition: logging.cpp:74
QMutex loglevelMapMutex
Definition: logging.cpp:99
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:202
bool haveUserDefaultValues
Definition: logging.cpp:110
void setThreadTid(void)
Set the thread ID of the thread that produced the LoggingItem.
Definition: logging.cpp:199
void setPid(const int val)
Definition: logging.h:115
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
QThread * qthread(void)
Returns the thread, this will always return the same pointer no matter how often you restart the thre...
Definition: mthread.cpp:244
void logForwardStop(void)
char * m_logFile
Definition: logging.h:154
void setFacility(const int val)
Definition: logging.h:122
static QHash< uint64_t, int64_t > logThreadTidHash
Definition: logging.cpp:75
int verboseArgParse(const QString &arg)
Parse the –verbose commandline argument and set the verbose level.
Definition: logging.cpp:974
LoggerThread(QString filename, bool progress, bool quiet, QString table, int facility)
LoggerThread constructor.
Definition: logging.cpp:256
int m_line
Definition: logging.h:144
#define GENERIC_EXIT_INVALID_CMDLINE
Command line parse error.
Definition: exitcodes.h:15
LogLevel_t logLevelGet(const QString &level)
Map a log level name back to the enumerated value.
Definition: logging.cpp:825
static bool logThreadFinished
Definition: logging.cpp:77
qlonglong m_epoch
Definition: logging.h:148
void verboseInit(void)
Initialize the logging levels and verbose levels.
Definition: logging.cpp:905
qlonglong m_tid
Definition: logging.h:141
void logPropagateCalc(void)
Generate the logPropagateArgs global with the latest logging level, mask, etc to propagate to all of ...
Definition: logging.cpp:640
void run(void) override
Run the logging thread.
Definition: logging.cpp:295
QStringList logPropagateArgList
Definition: logging.cpp:90
Definition: logging.h:45
const uint64_t verboseDefaultInt
Definition: logging.cpp:101
QString verboseString
Definition: logging.cpp:105
static LoggingItem * create(const char *_file, const char *_function, int _line, LogLevel_t _level, LoggingType _type)
Create a new LoggingItem.
Definition: logging.cpp:566
~LoggerThread() override
LoggerThread destructor. Triggers the deletion of all loggers.
Definition: logging.cpp:281
bool m_aborted
Flag to abort the thread.
Definition: logging.h:193
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23