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  QTime t;
519  t.start();
520  while (!m_aborted && !logQueue.isEmpty() && t.elapsed() < 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  for (LoglevelMap::iterator it = loglevelMap.begin();
841  it != loglevelMap.end(); ++it)
842  {
843  LoglevelDef *item = (*it);
844  if ( item->name == level.toLower() )
845  return (LogLevel_t)item->value;
846  }
847 
848  return LOG_UNKNOWN;
849 }
850 
854 QString logLevelGetName(LogLevel_t level)
855 {
856  QMutexLocker locker(&loglevelMapMutex);
857  if (!verboseInitialized)
858  {
859  locker.unlock();
860  verboseInit();
861  locker.relock();
862  }
863  LoglevelMap::iterator it = loglevelMap.find((int)level);
864 
865  if ( it == loglevelMap.end() )
866  return QString("unknown");
867 
868  return (*it)->name;
869 }
870 
877 void verboseAdd(uint64_t mask, QString name, bool additive, QString helptext)
878 {
879  auto *item = new VerboseDef;
880 
881  item->mask = mask;
882  // VB_GENERAL -> general
883  name.remove(0, 3);
884  name = name.toLower();
885  item->name = name;
886  item->additive = additive;
887  item->helpText = std::move(helptext);
888 
889  verboseMap.insert(name, item);
890 }
891 
897 void loglevelAdd(int value, QString name, char shortname)
898 {
899  auto *item = new LoglevelDef;
900 
901  item->value = value;
902  // LOG_CRIT -> crit
903  name.remove(0, 4);
904  name = name.toLower();
905  item->name = name;
906  item->shortname = shortname;
907 
908  loglevelMap.insert(value, item);
909 }
910 
912 void verboseInit(void)
913 {
914  QMutexLocker locker(&verboseMapMutex);
915  QMutexLocker locker2(&loglevelMapMutex);
916  verboseMap.clear();
917  loglevelMap.clear();
918 
919  // This looks funky, so I'll put some explanation here. The verbosedefs.h
920  // file gets included as part of the mythlogging.h include, and at that
921  // time, the normal (without _IMPLEMENT_VERBOSE defined) code case will
922  // define the VerboseMask enum. At this point, we force it to allow us
923  // to include the file again, but with _IMPLEMENT_VERBOSE set so that the
924  // single definition of the VB_* values can be shared to define also the
925  // contents of verboseMap, via repeated calls to verboseAdd()
926 
927 #undef VERBOSEDEFS_H_
928 #define _IMPLEMENT_VERBOSE
929 #include "verbosedefs.h"
930 
931  verboseInitialized = true;
932 }
933 
934 
937 void verboseHelp(void)
938 {
939  QString m_verbose = userDefaultValueStr.trimmed();
940  m_verbose.replace(QRegExp(" "), ",");
941  m_verbose.remove(QRegExp("^,"));
942 
943  cerr << "Verbose debug levels.\n"
944  "Accepts any combination (separated by comma) of:\n\n";
945 
946  for (VerboseMap::Iterator vit = verboseMap.begin();
947  vit != verboseMap.end(); ++vit )
948  {
949  VerboseDef *item = vit.value();
950  QString name = QString(" %1").arg(item->name, -15, ' ');
951  if (item->helpText.isEmpty())
952  continue;
953  cerr << name.toLocal8Bit().constData() << " - " <<
954  item->helpText.toLocal8Bit().constData() << endl;
955  }
956 
957  cerr << endl <<
958  "The default for this program appears to be: '-v " <<
959  m_verbose.toLocal8Bit().constData() << "'\n\n"
960  "Most options are additive except for 'none' and 'all'.\n"
961  "These two are semi-exclusive and take precedence over any\n"
962  "other options. However, you may use something like\n"
963  "'-v none,jobqueue' to receive only JobQueue related messages\n"
964  "and override the default verbosity level.\n\n"
965  "Additive options may also be subtracted from 'all' by\n"
966  "prefixing them with 'no', so you may use '-v all,nodatabase'\n"
967  "to view all but database debug messages.\n\n";
968 
969  cerr << "The 'global' loglevel is specified with --loglevel, but can be\n"
970  << "overridden on a component by component basis by appending "
971  << "':level'\n"
972  << "to the component.\n"
973  << " For example: -v gui:debug,channel:notice,record\n\n";
974 
975  cerr << "Some debug levels may not apply to this program.\n" << endl;
976 }
977 
981 int verboseArgParse(const QString& arg)
982 {
983  QString option;
984 
985  if (!verboseInitialized)
986  verboseInit();
987 
988  QMutexLocker locker(&verboseMapMutex);
989 
991  verboseString = QString(verboseDefaultStr);
992 
993  if (arg.startsWith('-'))
994  {
995  cerr << "Invalid or missing argument to -v/--verbose option\n";
997  }
998 
999  QStringList verboseOpts = arg.split(QRegExp("[^\\w:]+",
1000  Qt::CaseInsensitive,
1001  QRegExp::RegExp2));
1002  for (QStringList::Iterator it = verboseOpts.begin();
1003  it != verboseOpts.end(); ++it )
1004  {
1005  option = (*it).toLower();
1006  bool reverseOption = false;
1007  QString optionLevel;
1008 
1009  if (option != "none" && option.startsWith("no"))
1010  {
1011  reverseOption = true;
1012  option = option.right(option.length() - 2);
1013  }
1014 
1015  if (option == "help")
1016  {
1017  verboseHelp();
1019  }
1020  if (option == "important")
1021  {
1022  cerr << "The \"important\" log mask is no longer valid.\n";
1023  }
1024  else if (option == "extra")
1025  {
1026  cerr << "The \"extra\" log mask is no longer valid. Please try "
1027  "--loglevel debug instead.\n";
1028  }
1029  else if (option == "default")
1030  {
1032  {
1035  }
1036  else
1037  {
1039  verboseString = QString(verboseDefaultStr);
1040  }
1041  }
1042  else
1043  {
1044  int idx = option.indexOf(':');
1045  if (idx != -1)
1046  {
1047  optionLevel = option.mid(idx + 1);
1048  option = option.left(idx);
1049  }
1050 
1051  VerboseDef *item = verboseMap.value(option);
1052 
1053  if (item)
1054  {
1055  if (reverseOption)
1056  {
1057  verboseMask &= ~(item->mask);
1058  verboseString = verboseString.remove(' ' + item->name);
1059  verboseString += " no" + item->name;
1060  }
1061  else
1062  {
1063  if (item->additive)
1064  {
1065  if (!(verboseMask & item->mask))
1066  {
1067  verboseMask |= item->mask;
1068  verboseString += ' ' + item->name;
1069  }
1070  }
1071  else
1072  {
1073  verboseMask = item->mask;
1074  verboseString = item->name;
1075  }
1076 
1077  if (!optionLevel.isEmpty())
1078  {
1079  LogLevel_t level = logLevelGet(optionLevel);
1080  if (level != LOG_UNKNOWN)
1081  componentLogLevel[item->mask] = level;
1082  }
1083  }
1084  }
1085  else
1086  {
1087  cerr << "Unknown argument for -v/--verbose: " <<
1088  option.toLocal8Bit().constData() << endl;;
1090  }
1091  }
1092  }
1093 
1094  if (!haveUserDefaultValues)
1095  {
1096  haveUserDefaultValues = true;
1099  }
1100 
1101  return GENERIC_EXIT_OK;
1102 }
1103 
1108 QString logStrerror(int errnum)
1109 {
1110  return QString("%1 (%2)").arg(strerror(errnum)).arg(errnum);
1111 }
1112 
1113 
1114 /*
1115  * vim:ts=4:sw=4:ai:et:si:sts=4
1116  */
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:897
QString logfile
QString m_path
Definition: logging.cpp:85
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
QString logLevelGetName(LogLevel_t level)
Map a log level enumerated value back to the name.
Definition: logging.cpp:854
def write(text, progress=True)
Definition: mythburn.py:279
char * m_function
Definition: logging.h:153
char m_message[LOGLINE_MAX+1]
Definition: logging.h:158
static QQueue< LoggingItem * > logQueue
Definition: logging.cpp:67
The logging thread that consumes the logging queue and dispatches each LoggingItem.
Definition: logging.h:171
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
LoggingItem()
Definition: logging.h:161
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:203
char * m_file
Definition: logging.h:152
QString name
Definition: verbosedefs.h:213
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:204
const char * rawThreadName() const
Definition: logging.h:136
#define TIMESTAMP_MAX
Definition: logging.cpp:92
static QMutex logThreadMutex
Definition: logging.cpp:71
QMap< uint64_t, LogLevel_t > ComponentLogLevelMap
Definition: verbosedefs.h:217
QString m_filename
Filename of debug logfile.
Definition: logging.h:198
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:199
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:62
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:66
QString userDefaultValueStr
Definition: logging.cpp:112
ComponentLogLevelMap componentLogLevel
Definition: logging.cpp:109
VerboseDef
Definition: verbosedefs.h:208
LoggingType m_type
Definition: logging.h:148
QWaitCondition * m_waitEmpty
Condition variable for waiting for the queue to be empty Protected by logQueueMutex.
Definition: logging.h:192
VerboseMap verboseMap
Definition: logging.cpp:98
pid_t m_pid
Cached pid value.
Definition: logging.h:205
qlonglong epoch
Definition: logging.h:74
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:877
static bool debugRegistration
Definition: logging.cpp:78
void verboseHelp(void)
Outputs the Verbose levels and their descriptions (for –verbose help)
Definition: logging.cpp:937
void logForwardMessage(const QList< QByteArray > &msg)
virtual int IncrRef(void)
Increments reference count.
qlonglong tid
Definition: logging.h:67
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
QMap< QString, VerboseDef * > VerboseMap
Definition: verbosedefs.h:209
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:42
#define PREFIX64
Definition: compat.h:393
QString logStrerror(int errnum)
Verbose helper function for ENO macro.
Definition: logging.cpp:1108
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
QByteArray toByteArray(void)
Definition: logging.cpp:166
static QRegExp logRegExp
Definition: logging.cpp:68
#define LOGLINE_MAX
Definition: logging.h:30
QString m_appname
Cached application name.
Definition: logging.h:201
qulonglong m_threadId
Definition: logging.h:145
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:188
LogLevel_t m_level
Definition: logging.h:149
char * m_threadName
Definition: logging.h:154
LoglevelMap loglevelMap
Definition: logging.cpp:101
char * m_table
Definition: logging.h:156
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:155
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:146
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:113
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:157
void setFacility(const int val)
Definition: logging.h:120
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:981
LoggerThread(QString filename, bool progress, bool quiet, QString table, int facility)
LoggerThread constructor.
Definition: logging.cpp:235
int m_line
Definition: logging.h:147
#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:151
int m_quiet
silence the console (console only)
Definition: logging.h:200
QMap< int, LoglevelDef * > LoglevelMap
Definition: verbosedefs.h:216
void verboseInit(void)
Initialize the logging levels and verbose levels.
Definition: logging.cpp:912
qlonglong m_tid
Definition: logging.h:144
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:46
~LoggerThread()
LoggerThread destructor. Triggers the deletion of all loggers.
Definition: logging.cpp:260
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
static LoggingItem * create(const char *, const char *, int, LogLevel_t, LoggingType)
Create a new LoggingItem.
Definition: logging.cpp:551
const uint64_t verboseDefaultInt
Definition: logging.cpp:104
QString verboseString
Definition: logging.cpp:108
bool m_aborted
Flag to abort the thread.
Definition: logging.h:196