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 #define TIMESTAMP_MAX 30
93 #define MAX_STRING_LENGTH (LOGLINE_MAX+120)
94 
95 LogLevel_t logLevel = LOG_INFO;
96 
97 bool verboseInitialized = false;
100 
103 
104 const uint64_t verboseDefaultInt = VB_GENERAL;
105 const char *verboseDefaultStr = " general";
106 
108 QString verboseString = QString(verboseDefaultStr);
110 
114 
115 void verboseAdd(uint64_t mask, QString name, bool additive, QString helptext);
116 void loglevelAdd(int value, QString name, char shortname);
117 void verboseInit(void);
118 void verboseHelp(void);
119 
120 void loggingGetTimeStamp(qlonglong *epoch, uint *usec)
121 {
122 #if HAVE_GETTIMEOFDAY
123  struct timeval tv {};
124  gettimeofday(&tv, nullptr);
125  *epoch = tv.tv_sec;
126  if (usec)
127  *usec = tv.tv_usec;
128 #else
129  /* Stupid system has no gettimeofday, use less precise QDateTime */
130  QDateTime date = MythDate::current();
131  *epoch = date.toTime_t();
132  if (usec)
133  {
134  QTime time = date.time();
135  *usec = time.msec() * 1000;
136  }
137 #endif
138 }
139 
140 LoggingItem::LoggingItem(const char *_file, const char *_function,
141  int _line, LogLevel_t _level, LoggingType _type) :
142  ReferenceCounter("LoggingItem", false),
143  m_threadId((uint64_t)(QThread::currentThreadId())),
144  m_line(_line), m_type(_type), m_level(_level),
145  m_file(strdup(_file)), m_function(strdup(_function))
146 {
148  setThreadTid();
149 }
150 
152 {
153  free(m_file);
154 
155  free(m_function);
156 
157  free(m_threadName);
158 
159  free(m_appName);
160 
161  free(m_table);
162 
163  free(m_logFile);
164 }
165 
166 QByteArray LoggingItem::toByteArray(void)
167 {
168  QVariantMap variant = QJsonWrapper::qobject2qvariant(this);
169  QByteArray json = QJsonWrapper::toJson(variant);
170 
171  //cout << json.constData() << endl;
172 
173  return json;
174 }
175 
179 {
180  static constexpr char kSUnknown[] = "thread_unknown";
181 
182  if( m_threadName )
183  return m_threadName;
184 
185  QMutexLocker locker(&logThreadMutex);
186  return logThreadHash.value(m_threadId, (char *)kSUnknown);
187 }
188 
195 {
196  QMutexLocker locker(&logThreadTidMutex);
197  m_tid = logThreadTidHash.value(m_threadId, 0);
198  return m_tid;
199 }
200 
208 {
209  QMutexLocker locker(&logThreadTidMutex);
210 
211  m_tid = logThreadTidHash.value(m_threadId, -1);
212  if (m_tid == -1)
213  {
214  m_tid = 0;
215 
216 #if defined(Q_OS_ANDROID)
217  m_tid = (int64_t)gettid();
218 #elif defined(linux)
219  m_tid = syscall(SYS_gettid);
220 #elif defined(__FreeBSD__)
221  long lwpid;
222  int dummy = thr_self( &lwpid );
223  (void)dummy;
224  m_tid = (int64_t)lwpid;
225 #elif CONFIG_DARWIN
226  m_tid = (int64_t)mach_thread_self();
227 #endif
229  }
230 }
231 
236  QString table, int facility) :
237  MThread("Logger"),
238  m_waitNotEmpty(new QWaitCondition()),
239  m_waitEmpty(new QWaitCondition()),
240  m_filename(std::move(filename)), m_progress(progress), m_quiet(quiet),
241  m_tablename(std::move(table)), m_facility(facility), m_pid(getpid())
242 {
243  char *debug = getenv("VERBOSE_THREADS");
244  if (debug != nullptr)
245  {
246  LOG(VB_GENERAL, LOG_NOTICE,
247  "Logging thread registration/deregistration enabled!");
248  debugRegistration = true;
249  }
250 
251  if (!logForwardStart())
252  {
253  LOG(VB_GENERAL, LOG_ERR,
254  "Failed to start LogServer thread");
255  }
256  moveToThread(qthread());
257 }
258 
261 {
262  stop();
263  wait();
264  logForwardStop();
265 
266  delete m_waitNotEmpty;
267  delete m_waitEmpty;
268 }
269 
275 {
276  RunProlog();
277 
278  logThreadFinished = false;
279 
280  LOG(VB_GENERAL, LOG_INFO, "Added logging to the console");
281 
282  bool dieNow = false;
283 
284  QMutexLocker qLock(&logQueueMutex);
285 
286  while (!m_aborted || !logQueue.isEmpty())
287  {
288  qLock.unlock();
289  qApp->processEvents(QEventLoop::AllEvents, 10);
290  qApp->sendPostedEvents(nullptr, QEvent::DeferredDelete);
291 
292  qLock.relock();
293  if (logQueue.isEmpty())
294  {
295  m_waitEmpty->wakeAll();
296  m_waitNotEmpty->wait(qLock.mutex(), 100);
297  continue;
298  }
299 
300  LoggingItem *item = logQueue.dequeue();
301  qLock.unlock();
302 
303  fillItem(item);
304  handleItem(item);
305  logConsole(item);
306  item->DecrRef();
307 
308  qLock.relock();
309  }
310 
311  qLock.unlock();
312 
313  // This must be before the timer stop below or we deadlock when the timer
314  // thread tries to deregister, and we wait for it.
315  logThreadFinished = true;
316 
317  RunEpilog();
318 
319  // cppcheck-suppress knownConditionTrueFalse
320  if (dieNow)
321  {
322  qApp->processEvents();
323  }
324 }
325 
332 {
333  if (item->m_type & kRegistering)
334  {
335  item->m_tid = item->getThreadTid();
336 
337  QMutexLocker locker(&logThreadMutex);
338  if (logThreadHash.contains(item->m_threadId))
339  {
340  char *threadName = logThreadHash.take(item->m_threadId);
341  free(threadName);
342  }
343  logThreadHash[item->m_threadId] = strdup(item->m_threadName);
344 
345  if (debugRegistration)
346  {
347  snprintf(item->m_message, LOGLINE_MAX,
348  "Thread 0x%" PREFIX64 "X (%" PREFIX64
349  "d) registered as \'%s\'",
350  item->m_threadId,
351  item->m_tid,
352  logThreadHash[item->m_threadId]);
353  }
354  }
355  else if (item->m_type & kDeregistering)
356  {
357  int64_t tid = 0;
358 
359  {
360  QMutexLocker locker(&logThreadTidMutex);
361  if( logThreadTidHash.contains(item->m_threadId) )
362  {
363  tid = logThreadTidHash[item->m_threadId];
364  logThreadTidHash.remove(item->m_threadId);
365  }
366  }
367 
368  QMutexLocker locker(&logThreadMutex);
369  if (logThreadHash.contains(item->m_threadId))
370  {
371  if (debugRegistration)
372  {
373  snprintf(item->m_message, LOGLINE_MAX,
374  "Thread 0x%" PREFIX64 "X (%" PREFIX64
375  "d) deregistered as \'%s\'",
376  item->m_threadId,
377  (long long int)tid,
378  logThreadHash[item->m_threadId]);
379  }
380  char *threadName = logThreadHash.take(item->m_threadId);
381  free(threadName);
382  }
383  }
384 
385  if (item->m_message[0] != '\0')
386  {
394  QList<QByteArray> list;
395  list.append(QByteArray());
396  list.append(item->toByteArray());
397  logForwardMessage(list);
398  }
399 }
400 
404 {
405  if (m_quiet || (m_progress && item->m_level > LOG_ERR))
406  return false;
407 
408  if (!(item->m_type & kMessage))
409  return false;
410 
411  item->IncrRef();
412 
413 #ifndef Q_OS_ANDROID
414  char line[MAX_STRING_LENGTH];
415 
416  if (item->m_type & kStandardIO)
417  {
418  snprintf(line, MAX_STRING_LENGTH, "%s", item->m_message);
419  }
420  else
421  {
422  char usPart[9];
423  char timestamp[TIMESTAMP_MAX];
424  time_t epoch = item->epoch();
425  struct tm tm {};
426  localtime_r(&epoch, &tm);
427  strftime(timestamp, TIMESTAMP_MAX-8, "%Y-%m-%d %H:%M:%S",
428  static_cast<const struct tm *>(&tm));
429  snprintf(usPart, 9, ".%06d", static_cast<int>(item->m_usec));
430  strcat(timestamp, usPart);
431  char shortname = '-';
432 
433  {
434  QMutexLocker locker(&loglevelMapMutex);
435  LoglevelDef *lev = loglevelMap.value(item->m_level, nullptr);
436  if (lev != nullptr)
437  shortname = lev->shortname;
438  }
439 
440 #if CONFIG_DEBUGTYPE
441  if(item->tid())
442  {
443  snprintf(line, MAX_STRING_LENGTH, "%s %c [%d/%" PREFIX64 "d] %s %s:%d:%s %s\n", timestamp,
444  shortname, item->pid(), item->tid(), item->rawThreadName(),
445  item->m_file, item->m_line, item->m_function, item->m_message);
446  }
447  else
448  {
449  snprintf(line, MAX_STRING_LENGTH, "%s %c [%d] %s %s:%d:%s %s\n", timestamp,
450  shortname, item->pid(), item->rawThreadName(),
451  item->m_file, item->m_line, item->m_function, item->m_message);
452  }
453 #else
454  snprintf(line, MAX_STRING_LENGTH, "%s %c %s\n", timestamp,
455  shortname, item->m_message);
456 #endif
457  }
458 
459  (void)write(1, line, strlen(line));
460 
461 #else // Q_OS_ANDROID
462 
463  android_LogPriority aprio;
464  switch (item->m_level)
465  {
466  case LOG_EMERG:
467  aprio = ANDROID_LOG_FATAL;
468  case LOG_ALERT:
469  case LOG_CRIT:
470  case LOG_ERR:
471  aprio = ANDROID_LOG_ERROR;
472  break;
473  case LOG_WARNING:
474  aprio = ANDROID_LOG_WARN;
475  break;
476  case LOG_NOTICE:
477  case LOG_INFO:
478  aprio = ANDROID_LOG_INFO;
479  break;
480  case LOG_DEBUG:
481  aprio = ANDROID_LOG_DEBUG;
482  break;
483  case LOG_UNKNOWN:
484  default:
485  aprio = ANDROID_LOG_UNKNOWN;
486  break;
487  }
488 #if CONFIG_DEBUGTYPE
489  __android_log_print(aprio, "mfe", "%s:%d:%s %s", item->m_file,
490  item->m_line, item->m_function, item->m_message);
491 #else
492  __android_log_print(aprio, "mfe", "%s", item->m_message);
493 #endif
494 #endif
495 
496  item->DecrRef();
497 
498  return true;
499 }
500 
501 
505 {
506  logQueueMutex.lock();
507  flush(1000);
508  m_aborted = true;
509  logQueueMutex.unlock();
510  m_waitNotEmpty->wakeAll();
511 }
512 
516 bool LoggerThread::flush(int timeoutMS)
517 {
518  QElapsedTimer t;
519  t.start();
520  while (!m_aborted && !logQueue.isEmpty() && !t.hasExpired(timeoutMS))
521  {
522  m_waitNotEmpty->wakeAll();
523  int left = timeoutMS - t.elapsed();
524  if (left > 0)
525  m_waitEmpty->wait(&logQueueMutex, left);
526  }
527  return logQueue.isEmpty();
528 }
529 
531 {
532  if (!item)
533  return;
534 
535  item->setPid(m_pid);
536  item->setThreadName(item->getThreadName());
537  item->setAppName(m_appname);
538  item->setTable(m_tablename);
539  item->setLogFile(m_filename);
540  item->setFacility(m_facility);
541 }
542 
543 
551 LoggingItem *LoggingItem::create(const char *_file,
552  const char *_function,
553  int _line, LogLevel_t _level,
554  LoggingType _type)
555 {
556  auto *item = new LoggingItem(_file, _function, _line, _level, _type);
557 
558  return item;
559 }
560 
562 {
563  // Deserialize buffer
564  QVariant variant = QJsonWrapper::parseJson(buf);
565 
566  auto *item = new LoggingItem;
567  QJsonWrapper::qvariant2qobject(variant.toMap(), item);
568 
569  return item;
570 }
571 
572 
584 void LogPrintLine( uint64_t mask, LogLevel_t level, const char *file, int line,
585  const char *function, int fromQString,
586  const char *format, ... )
587 {
588  va_list arguments;
589 
590  int type = kMessage;
591  type |= (mask & VB_FLUSH) ? kFlush : 0;
592  type |= (mask & VB_STDIO) ? kStandardIO : 0;
593  LoggingItem *item = LoggingItem::create(file, function, line, level,
594  (LoggingType)type);
595  if (!item)
596  return;
597 
598  char *formatcopy = nullptr;
599  if( fromQString && strchr(format, '%') )
600  {
601  QString string(format);
602  format = strdup(string.replace(logRegExp, "%%").toLocal8Bit()
603  .constData());
604  formatcopy = (char *)format;
605  }
606 
607  va_start(arguments, format);
608  vsnprintf(item->m_message, LOGLINE_MAX, format, arguments);
609  va_end(arguments);
610 
611  if (formatcopy)
612  free(formatcopy);
613 
614  QMutexLocker qLock(&logQueueMutex);
615 
616 #if defined( _MSC_VER ) && defined( _DEBUG )
617  OutputDebugStringA( item->m_message );
618  OutputDebugStringA( "\n" );
619 #endif
620 
621  logQueue.enqueue(item);
622 
624  {
625  while (!logQueue.isEmpty())
626  {
627  item = logQueue.dequeue();
628  qLock.unlock();
629  logThread->handleItem(item);
630  logThread->logConsole(item);
631  item->DecrRef();
632  qLock.relock();
633  }
634  }
635  else if (logThread && !logThreadFinished && (type & kFlush))
636  {
637  logThread->flush();
638  }
639 }
640 
641 
646 {
647  logPropagateArgList.clear();
648 
649  QString mask = verboseString.trimmed();
650  mask.replace(QRegExp(" "), ",");
651  mask.remove(QRegExp("^,"));
652  logPropagateArgs = " --verbose " + mask;
653  logPropagateArgList << "--verbose" << mask;
654 
656  {
657  logPropagateArgs += " --logpath " + logPropagateOpts.m_path;
658  logPropagateArgList << "--logpath" << logPropagateOpts.m_path;
659  }
660 
661  QString name = logLevelGetName(logLevel);
662  logPropagateArgs += " --loglevel " + name;
663  logPropagateArgList << "--loglevel" << name;
664 
665  for (int i = 0; i < logPropagateOpts.m_quiet; i++)
666  {
667  logPropagateArgs += " --quiet";
668  logPropagateArgList << "--quiet";
669  }
670 
672  {
673  logPropagateArgs += " --enable-dblog";
674  logPropagateArgList << "--enable-dblog";
675  }
676 
677 #if !defined(_WIN32) && !defined(Q_OS_ANDROID)
678  if (logPropagateOpts.m_facility >= 0)
679  {
680  const CODE *syslogname = nullptr;
681  for (syslogname = &facilitynames[0];
682  (syslogname->c_name &&
683  syslogname->c_val != logPropagateOpts.m_facility); syslogname++);
684 
685  logPropagateArgs += QString(" --syslog %1").arg(syslogname->c_name);
686  logPropagateArgList << "--syslog" << syslogname->c_name;
687  }
688 #if CONFIG_SYSTEMD_JOURNAL
689  else if (logPropagateOpts.m_facility == SYSTEMD_JOURNAL_FACILITY)
690  {
691  logPropagateArgs += " --systemd-journal";
692  logPropagateArgList << "--systemd-journal";
693  }
694 #endif
695 #endif
696 }
697 
701 {
702  return logPropagateOpts.m_quiet;
703 }
704 
717 void logStart(const QString& logfile, int progress, int quiet, int facility,
718  LogLevel_t level, bool dblog, bool propagate)
719 {
720  if (logThread && logThread->isRunning())
721  return;
722 
723  logLevel = level;
724  LOG(VB_GENERAL, LOG_NOTICE, QString("Setting Log Level to LOG_%1")
725  .arg(logLevelGetName(logLevel).toUpper()));
726 
727  logPropagateOpts.m_propagate = propagate;
729  logPropagateOpts.m_facility = facility;
730  logPropagateOpts.m_dblog = dblog;
731 
732  if (propagate)
733  {
734  QFileInfo finfo(logfile);
735  QString path = finfo.path();
736  logPropagateOpts.m_path = path;
737  }
738 
740 
741  QString table = dblog ? QString("logging") : QString("");
742 
743  if (!logThread)
744  logThread = new LoggerThread(logfile, progress, quiet, table, facility);
745 
746  logThread->start();
747 }
748 
750 void logStop(void)
751 {
752  if (logThread)
753  {
754  logThread->stop();
755  logThread->wait();
756  delete logThread;
757  logThread = nullptr;
758  }
759 }
760 
765 void loggingRegisterThread(const QString &name)
766 {
767  if (logThreadFinished)
768  return;
769 
770  QMutexLocker qLock(&logQueueMutex);
771 
772  LoggingItem *item = LoggingItem::create(__FILE__, __FUNCTION__,
773  __LINE__, LOG_DEBUG,
774  kRegistering);
775  if (item)
776  {
777  item->setThreadName((char *)name.toLocal8Bit().constData());
778  logQueue.enqueue(item);
779  }
780 }
781 
785 {
786  if (logThreadFinished)
787  return;
788 
789  QMutexLocker qLock(&logQueueMutex);
790 
791  LoggingItem *item = LoggingItem::create(__FILE__, __FUNCTION__, __LINE__,
792  LOG_DEBUG,
794  if (item)
795  logQueue.enqueue(item);
796 }
797 
798 
802 int syslogGetFacility(const QString& facility)
803 {
804 #ifdef _WIN32
805  LOG(VB_GENERAL, LOG_NOTICE,
806  "Windows does not support syslog, disabling" );
807  Q_UNUSED(facility);
808  return( -2 );
809 #elif defined(Q_OS_ANDROID)
810  LOG(VB_GENERAL, LOG_NOTICE,
811  "Android does not support syslog, disabling" );
812  Q_UNUSED(facility);
813  return( -2 );
814 #else
815  const CODE *name = nullptr;
816  int i = 0;
817  QByteArray ba = facility.toLocal8Bit();
818  char *string = (char *)ba.constData();
819 
820  for (i = 0, name = &facilitynames[0];
821  name->c_name && (strcmp(name->c_name, string) != 0); i++, name++);
822 
823  return( name->c_val );
824 #endif
825 }
826 
830 LogLevel_t logLevelGet(const QString& level)
831 {
832  QMutexLocker locker(&loglevelMapMutex);
833  if (!verboseInitialized)
834  {
835  locker.unlock();
836  verboseInit();
837  locker.relock();
838  }
839 
840  foreach (auto item, loglevelMap)
841  {
842  if ( item->name == level.toLower() )
843  return (LogLevel_t)item->value;
844  }
845 
846  return LOG_UNKNOWN;
847 }
848 
852 QString logLevelGetName(LogLevel_t level)
853 {
854  QMutexLocker locker(&loglevelMapMutex);
855  if (!verboseInitialized)
856  {
857  locker.unlock();
858  verboseInit();
859  locker.relock();
860  }
861  LoglevelMap::iterator it = loglevelMap.find((int)level);
862 
863  if ( it == loglevelMap.end() )
864  return QString("unknown");
865 
866  return (*it)->name;
867 }
868 
875 void verboseAdd(uint64_t mask, QString name, bool additive, QString helptext)
876 {
877  auto *item = new VerboseDef;
878 
879  item->mask = mask;
880  // VB_GENERAL -> general
881  name.remove(0, 3);
882  name = name.toLower();
883  item->name = name;
884  item->additive = additive;
885  item->helpText = std::move(helptext);
886 
887  verboseMap.insert(name, item);
888 }
889 
895 void loglevelAdd(int value, QString name, char shortname)
896 {
897  auto *item = new LoglevelDef;
898 
899  item->value = value;
900  // LOG_CRIT -> crit
901  name.remove(0, 4);
902  name = name.toLower();
903  item->name = name;
904  item->shortname = shortname;
905 
906  loglevelMap.insert(value, item);
907 }
908 
910 void verboseInit(void)
911 {
912  QMutexLocker locker(&verboseMapMutex);
913  QMutexLocker locker2(&loglevelMapMutex);
914  verboseMap.clear();
915  loglevelMap.clear();
916 
917  // This looks funky, so I'll put some explanation here. The verbosedefs.h
918  // file gets included as part of the mythlogging.h include, and at that
919  // time, the normal (without _IMPLEMENT_VERBOSE defined) code case will
920  // define the VerboseMask enum. At this point, we force it to allow us
921  // to include the file again, but with _IMPLEMENT_VERBOSE set so that the
922  // single definition of the VB_* values can be shared to define also the
923  // contents of verboseMap, via repeated calls to verboseAdd()
924 
925 #undef VERBOSEDEFS_H_
926 #define _IMPLEMENT_VERBOSE
927 #include "verbosedefs.h"
928 
929  verboseInitialized = true;
930 }
931 
932 
935 void verboseHelp(void)
936 {
937  QString m_verbose = userDefaultValueStr.trimmed();
938  m_verbose.replace(QRegExp(" "), ",");
939  m_verbose.remove(QRegExp("^,"));
940 
941  cerr << "Verbose debug levels.\n"
942  "Accepts any combination (separated by comma) of:\n\n";
943 
944  for (VerboseMap::Iterator vit = verboseMap.begin();
945  vit != verboseMap.end(); ++vit )
946  {
947  VerboseDef *item = vit.value();
948  QString name = QString(" %1").arg(item->name, -15, ' ');
949  if (item->helpText.isEmpty())
950  continue;
951  cerr << name.toLocal8Bit().constData() << " - " <<
952  item->helpText.toLocal8Bit().constData() << endl;
953  }
954 
955  cerr << endl <<
956  "The default for this program appears to be: '-v " <<
957  m_verbose.toLocal8Bit().constData() << "'\n\n"
958  "Most options are additive except for 'none' and 'all'.\n"
959  "These two are semi-exclusive and take precedence over any\n"
960  "other options. However, you may use something like\n"
961  "'-v none,jobqueue' to receive only JobQueue related messages\n"
962  "and override the default verbosity level.\n\n"
963  "Additive options may also be subtracted from 'all' by\n"
964  "prefixing them with 'no', so you may use '-v all,nodatabase'\n"
965  "to view all but database debug messages.\n\n";
966 
967  cerr << "The 'global' loglevel is specified with --loglevel, but can be\n"
968  << "overridden on a component by component basis by appending "
969  << "':level'\n"
970  << "to the component.\n"
971  << " For example: -v gui:debug,channel:notice,record\n\n";
972 
973  cerr << "Some debug levels may not apply to this program.\n" << endl;
974 }
975 
979 int verboseArgParse(const QString& arg)
980 {
981  QString option;
982 
983  if (!verboseInitialized)
984  verboseInit();
985 
986  QMutexLocker locker(&verboseMapMutex);
987 
989  verboseString = QString(verboseDefaultStr);
990 
991  if (arg.startsWith('-'))
992  {
993  cerr << "Invalid or missing argument to -v/--verbose option\n";
995  }
996 
997  QStringList verboseOpts = arg.split(QRegExp("[^\\w:]+",
998  Qt::CaseInsensitive,
999  QRegExp::RegExp2));
1000  foreach (auto & opt, verboseOpts)
1001  {
1002  option = opt.toLower();
1003  bool reverseOption = false;
1004  QString optionLevel;
1005 
1006  if (option != "none" && option.startsWith("no"))
1007  {
1008  reverseOption = true;
1009  option = option.right(option.length() - 2);
1010  }
1011 
1012  if (option == "help")
1013  {
1014  verboseHelp();
1016  }
1017  if (option == "important")
1018  {
1019  cerr << "The \"important\" log mask is no longer valid.\n";
1020  }
1021  else if (option == "extra")
1022  {
1023  cerr << "The \"extra\" log mask is no longer valid. Please try "
1024  "--loglevel debug instead.\n";
1025  }
1026  else if (option == "default")
1027  {
1029  {
1032  }
1033  else
1034  {
1036  verboseString = QString(verboseDefaultStr);
1037  }
1038  }
1039  else
1040  {
1041  int idx = option.indexOf(':');
1042  if (idx != -1)
1043  {
1044  optionLevel = option.mid(idx + 1);
1045  option = option.left(idx);
1046  }
1047 
1048  VerboseDef *item = verboseMap.value(option);
1049 
1050  if (item)
1051  {
1052  if (reverseOption)
1053  {
1054  verboseMask &= ~(item->mask);
1055  verboseString = verboseString.remove(' ' + item->name);
1056  verboseString += " no" + item->name;
1057  }
1058  else
1059  {
1060  if (item->additive)
1061  {
1062  if (!(verboseMask & item->mask))
1063  {
1064  verboseMask |= item->mask;
1065  verboseString += ' ' + item->name;
1066  }
1067  }
1068  else
1069  {
1070  verboseMask = item->mask;
1071  verboseString = item->name;
1072  }
1073 
1074  if (!optionLevel.isEmpty())
1075  {
1076  LogLevel_t level = logLevelGet(optionLevel);
1077  if (level != LOG_UNKNOWN)
1078  componentLogLevel[item->mask] = level;
1079  }
1080  }
1081  }
1082  else
1083  {
1084  cerr << "Unknown argument for -v/--verbose: " <<
1085  option.toLocal8Bit().constData() << endl;;
1087  }
1088  }
1089  }
1090 
1091  if (!haveUserDefaultValues)
1092  {
1093  haveUserDefaultValues = true;
1096  }
1097 
1098  return GENERIC_EXIT_OK;
1099 }
1100 
1105 QString logStrerror(int errnum)
1106 {
1107  return QString("%1 (%2)").arg(strerror(errnum)).arg(errnum);
1108 }
1109 
1110 
1111 /*
1112  * vim:ts=4:sw=4:ai:et:si:sts=4
1113  */
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:895
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:852
def write(text, progress=True)
Definition: mythburn.py:279
char * m_function
Definition: logging.h:152
char m_message[LOGLINE_MAX+1]
Definition: logging.h:157
static QQueue< LoggingItem * > logQueue
Definition: logging.cpp:67
The logging thread that consumes the logging queue and dispatches each LoggingItem.
Definition: logging.h:170
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
LoggingItem()
Definition: logging.h:160
void LogPrintLine(uint64_t mask, LogLevel_t level, const char *file, int line, const char *function, int fromQString, const char *format,...)
Format and send a log message into the queue.
Definition: logging.cpp:584
const char * verboseDefaultStr
Definition: logging.cpp:105
QString m_tablename
Cached table name for db logging.
Definition: logging.h:202
char * m_file
Definition: logging.h:151
char * getThreadName(void)
Get the name of the thread that produced the LoggingItem.
Definition: logging.cpp:178
#define GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:10
QMutex verboseMapMutex
Definition: logging.cpp:99
static QMutex logQueueMutex
Definition: logging.cpp:66
int m_facility
Cached syslog facility (or -1 to disable)
Definition: logging.h:203
const char * rawThreadName() const
Definition: logging.h:135
#define TIMESTAMP_MAX
Definition: logging.cpp:92
static QMutex logThreadMutex
Definition: logging.cpp:71
QString m_filename
Filename of debug logfile.
Definition: logging.h:197
bool verboseInitialized
Definition: logging.cpp:97
General purpose reference counter.
bool flush(int timeoutMS=200000)
Wait for the queue to be flushed (up to a timeout)
Definition: logging.cpp:516
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:198
QString logPropagateArgs
Definition: logging.cpp:89
static __inline struct tm * localtime_r(const time_t *timep, struct tm *result)
Definition: compat.h:286
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:802
void fillItem(LoggingItem *item)
Definition: logging.cpp:530
void loggingGetTimeStamp(qlonglong *epoch, uint *usec)
Definition: logging.cpp:120
uint64_t verboseMask
Definition: logging.cpp:107
bool logConsole(LoggingItem *item)
Process a log message, writing to the console.
Definition: logging.cpp:403
void loggingRegisterThread(const QString &name)
Register the current thread with the given name.
Definition: logging.cpp:765
int pid
Definition: logging.h:65
QString userDefaultValueStr
Definition: logging.cpp:112
ComponentLogLevelMap componentLogLevel
Definition: logging.cpp:109
LoggingType m_type
Definition: logging.h:147
QWaitCondition * m_waitEmpty
Condition variable for waiting for the queue to be empty Protected by logQueueMutex.
Definition: logging.h:191
QMap< uint64_t, LogLevel_t > ComponentLogLevelMap
Definition: verbosedefs.h:217
VerboseMap verboseMap
Definition: logging.cpp:98
pid_t m_pid
Cached pid value.
Definition: logging.h:204
qlonglong epoch
Definition: logging.h:73
static void handleItem(LoggingItem *item)
Handles each LoggingItem.
Definition: logging.cpp:331
void verboseAdd(uint64_t mask, QString name, bool additive, QString helptext)
Add a verbose level to the verboseMap.
Definition: logging.cpp:875
static bool debugRegistration
Definition: logging.cpp:78
QMap< QString, VerboseDef * > VerboseMap
Definition: verbosedefs.h:209
void verboseHelp(void)
Outputs the Verbose levels and their descriptions (for –verbose help)
Definition: logging.cpp:935
void logForwardMessage(const QList< QByteArray > &msg)
virtual int IncrRef(void)
Increments reference count.
qlonglong tid
Definition: logging.h:66
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:85
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:504
~LoggingItem() override
Definition: logging.cpp:151
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:700
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
#define PREFIX64
Definition: compat.h:393
QString logStrerror(int errnum)
Verbose helper function for ENO macro.
Definition: logging.cpp:1105
unsigned int uint
Definition: compat.h:140
void loggingDeregisterThread(void)
Deregister the current thread's name.
Definition: logging.cpp:784
#define MAX_STRING_LENGTH
Definition: logging.cpp:93
QMap< int, LoglevelDef * > LoglevelMap
Definition: verbosedefs.h:216
QByteArray toByteArray(void)
Definition: logging.cpp:166
static QRegExp logRegExp
Definition: logging.cpp:68
#define LOGLINE_MAX
Definition: logging.h:29
QString m_appname
Cached application name.
Definition: logging.h:200
qulonglong m_threadId
Definition: logging.h:144
LogPropagateOpts logPropagateOpts
Definition: logging.cpp:88
QWaitCondition * m_waitNotEmpty
Condition variable for waiting for the queue to not be empty Protected by logQueueMutex.
Definition: logging.h:187
LogLevel_t m_level
Definition: logging.h:148
char * m_threadName
Definition: logging.h:153
LoglevelMap loglevelMap
Definition: logging.cpp:101
char * m_table
Definition: logging.h:155
int64_t getThreadTid(void)
Get the thread ID of the thread that produced the LoggingItem.
Definition: logging.cpp:194
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
char * m_appName
Definition: logging.h:154
LogLevel_t logLevel
Definition: logging.cpp:95
VERBOSE_PREAMBLE Most debug(nodatabase, notimestamp, noextra)") VERBOSE_MAP(VB_GENERAL
uint64_t userDefaultValueInt
Definition: logging.cpp:111
uint m_usec
Definition: logging.h:145
void logStop(void)
Entry point for stopping logging for an application.
Definition: logging.cpp:750
static QMutex logThreadTidMutex
Definition: logging.cpp:74
QMutex loglevelMapMutex
Definition: logging.cpp:102
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:202
bool haveUserDefaultValues
Definition: logging.cpp:113
void setThreadTid(void)
Set the thread ID of the thread that produced the LoggingItem.
Definition: logging.cpp:207
void setPid(const int val)
Definition: logging.h:112
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:156
void setFacility(const int val)
Definition: logging.h:119
static QHash< uint64_t, int64_t > logThreadTidHash
Definition: logging.cpp:75
char shortname
Definition: verbosedefs.h:214
int verboseArgParse(const QString &arg)
Parse the –verbose commandline argument and set the verbose level.
Definition: logging.cpp:979
LoggerThread(QString filename, bool progress, bool quiet, QString table, int facility)
LoggerThread constructor.
Definition: logging.cpp:235
int m_line
Definition: logging.h:146
#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:830
static bool logThreadFinished
Definition: logging.cpp:77
qlonglong m_epoch
Definition: logging.h:150
int m_quiet
silence the console (console only)
Definition: logging.h:199
void verboseInit(void)
Initialize the logging levels and verbose levels.
Definition: logging.cpp:910
qlonglong m_tid
Definition: logging.h:143
void logPropagateCalc(void)
Generate the logPropagateArgs global with the latest logging level, mask, etc to propagate to all of ...
Definition: logging.cpp:645
void run(void) override
Run the logging thread.
Definition: logging.cpp:274
QStringList logPropagateArgList
Definition: logging.cpp:90
Definition: logging.h:45
void logStart(const QString &logfile, int progress, int quiet, int facility, LogLevel_t level, bool dblog, bool propagate)
Entry point to start logging for the application.
Definition: logging.cpp:717
const uint64_t verboseDefaultInt
Definition: logging.cpp:104
QString verboseString
Definition: logging.cpp:108
static LoggingItem * create(const char *_file, const char *_function, int _line, LogLevel_t _level, LoggingType _type)
Create a new LoggingItem.
Definition: logging.cpp:551
~LoggerThread() override
LoggerThread destructor. Triggers the deletion of all loggers.
Definition: logging.cpp:260
bool m_aborted
Flag to abort the thread.
Definition: logging.h:195