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 
80 typedef struct {
81  bool propagate;
82  int quiet;
83  int facility;
84  bool dblog;
85  QString path;
87 
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 
141  ReferenceCounter("LoggingItem", false)
142 {
143  m_message[0]='\0';
144  m_message[LOGLINE_MAX]='\0';
145 }
146 
147 LoggingItem::LoggingItem(const char *_file, const char *_function,
148  int _line, LogLevel_t _level, LoggingType _type) :
149  ReferenceCounter("LoggingItem", false),
150  m_threadId((uint64_t)(QThread::currentThreadId())),
151  m_line(_line), m_type(_type), m_level(_level),
152  m_file(strdup(_file)), m_function(strdup(_function))
153 {
155 
156  m_message[0]='\0';
157  m_message[LOGLINE_MAX]='\0';
158  setThreadTid();
159 }
160 
162 {
163  free(m_file);
164 
165  free(m_function);
166 
167  free(m_threadName);
168 
169  free(m_appName);
170 
171  free(m_table);
172 
173  free(m_logFile);
174 }
175 
176 QByteArray LoggingItem::toByteArray(void)
177 {
178  QVariantMap variant = QJsonWrapper::qobject2qvariant(this);
179  QByteArray json = QJsonWrapper::toJson(variant);
180 
181  //cout << json.constData() << endl;
182 
183  return json;
184 }
185 
189 {
190  static const char *unknown = "thread_unknown";
191 
192  if( m_threadName )
193  return m_threadName;
194 
195  QMutexLocker locker(&logThreadMutex);
196  return logThreadHash.value(m_threadId, (char *)unknown);
197 }
198 
205 {
206  QMutexLocker locker(&logThreadTidMutex);
207  m_tid = logThreadTidHash.value(m_threadId, 0);
208  return m_tid;
209 }
210 
218 {
219  QMutexLocker locker(&logThreadTidMutex);
220 
221  m_tid = logThreadTidHash.value(m_threadId, -1);
222  if (m_tid == -1)
223  {
224  m_tid = 0;
225 
226 #if defined(Q_OS_ANDROID)
227  m_tid = (int64_t)gettid();
228 #elif defined(linux)
229  m_tid = syscall(SYS_gettid);
230 #elif defined(__FreeBSD__)
231  long lwpid;
232  int dummy = thr_self( &lwpid );
233  (void)dummy;
234  m_tid = (int64_t)lwpid;
235 #elif CONFIG_DARWIN
236  m_tid = (int64_t)mach_thread_self();
237 #endif
239  }
240 }
241 
245 LoggerThread::LoggerThread(QString filename, bool progress, bool quiet,
246  QString table, int facility) :
247  MThread("Logger"),
248  m_waitNotEmpty(new QWaitCondition()),
249  m_waitEmpty(new QWaitCondition()),
250  m_filename(std::move(filename)), m_progress(progress), m_quiet(quiet),
251  m_tablename(std::move(table)), m_facility(facility), m_pid(getpid())
252 {
253  char *debug = getenv("VERBOSE_THREADS");
254  if (debug != nullptr)
255  {
256  LOG(VB_GENERAL, LOG_NOTICE,
257  "Logging thread registration/deregistration enabled!");
258  debugRegistration = true;
259  }
260 
261  if (!logForwardStart())
262  {
263  LOG(VB_GENERAL, LOG_ERR,
264  "Failed to start LogServer thread");
265  }
266  moveToThread(qthread());
267 }
268 
271 {
272  stop();
273  wait();
274  logForwardStop();
275 
276  delete m_waitNotEmpty;
277  delete m_waitEmpty;
278 }
279 
285 {
286  RunProlog();
287 
288  logThreadFinished = false;
289 
290  LOG(VB_GENERAL, LOG_INFO, "Added logging to the console");
291 
292  bool dieNow = false;
293 
294  QMutexLocker qLock(&logQueueMutex);
295 
296  while (!m_aborted || !logQueue.isEmpty())
297  {
298  qLock.unlock();
299  qApp->processEvents(QEventLoop::AllEvents, 10);
300  qApp->sendPostedEvents(nullptr, QEvent::DeferredDelete);
301 
302  qLock.relock();
303  if (logQueue.isEmpty())
304  {
305  m_waitEmpty->wakeAll();
306  m_waitNotEmpty->wait(qLock.mutex(), 100);
307  continue;
308  }
309 
310  LoggingItem *item = logQueue.dequeue();
311  qLock.unlock();
312 
313  fillItem(item);
314  handleItem(item);
315  logConsole(item);
316  item->DecrRef();
317 
318  qLock.relock();
319  }
320 
321  qLock.unlock();
322 
323  // This must be before the timer stop below or we deadlock when the timer
324  // thread tries to deregister, and we wait for it.
325  logThreadFinished = true;
326 
327  RunEpilog();
328 
329  // cppcheck-suppress knownConditionTrueFalse
330  if (dieNow)
331  {
332  qApp->processEvents();
333  }
334 }
335 
342 {
343  if (item->m_type & kRegistering)
344  {
345  item->m_tid = item->getThreadTid();
346 
347  QMutexLocker locker(&logThreadMutex);
348  if (logThreadHash.contains(item->m_threadId))
349  {
350  char *threadName = logThreadHash.take(item->m_threadId);
351  free(threadName);
352  }
353  logThreadHash[item->m_threadId] = strdup(item->m_threadName);
354 
355  if (debugRegistration)
356  {
357  snprintf(item->m_message, LOGLINE_MAX,
358  "Thread 0x%" PREFIX64 "X (%" PREFIX64
359  "d) registered as \'%s\'",
360  item->m_threadId,
361  item->m_tid,
362  logThreadHash[item->m_threadId]);
363  }
364  }
365  else if (item->m_type & kDeregistering)
366  {
367  int64_t tid = 0;
368 
369  {
370  QMutexLocker locker(&logThreadTidMutex);
371  if( logThreadTidHash.contains(item->m_threadId) )
372  {
373  tid = logThreadTidHash[item->m_threadId];
374  logThreadTidHash.remove(item->m_threadId);
375  }
376  }
377 
378  QMutexLocker locker(&logThreadMutex);
379  if (logThreadHash.contains(item->m_threadId))
380  {
381  if (debugRegistration)
382  {
383  snprintf(item->m_message, LOGLINE_MAX,
384  "Thread 0x%" PREFIX64 "X (%" PREFIX64
385  "d) deregistered as \'%s\'",
386  item->m_threadId,
387  (long long int)tid,
388  logThreadHash[item->m_threadId]);
389  }
390  char *threadName = logThreadHash.take(item->m_threadId);
391  free(threadName);
392  }
393  }
394 
395  if (item->m_message[0] != '\0')
396  {
404  QList<QByteArray> list;
405  list.append(QByteArray());
406  list.append(item->toByteArray());
407  logForwardMessage(list);
408  }
409 }
410 
414 {
415  if (m_quiet || (m_progress && item->m_level > LOG_ERR))
416  return false;
417 
418  if (!(item->m_type & kMessage))
419  return false;
420 
421  item->IncrRef();
422 
423 #ifndef Q_OS_ANDROID
424  char line[MAX_STRING_LENGTH];
425 
426  if (item->m_type & kStandardIO)
427  snprintf( line, MAX_STRING_LENGTH, "%s", item->m_message );
428  else
429  {
430  char usPart[9];
431  char timestamp[TIMESTAMP_MAX];
432  time_t epoch = item->epoch();
433  struct tm tm;
434  localtime_r(&epoch, &tm);
435 
436  strftime( timestamp, TIMESTAMP_MAX-8, "%Y-%m-%d %H:%M:%S",
437  (const struct tm *)&tm );
438  snprintf( usPart, 9, ".%06d", (int)(item->m_usec) );
439  strcat( timestamp, usPart );
440  char shortname;
441 
442  {
443  QMutexLocker locker(&loglevelMapMutex);
444  LoglevelDef *lev = loglevelMap.value(item->m_level, nullptr);
445  if (!lev)
446  shortname = '-';
447  else
448  shortname = lev->shortname;
449  }
450 
451 #if CONFIG_DEBUGTYPE
452  snprintf( line, MAX_STRING_LENGTH, "%s %c %s:%d:%s %s\n", timestamp,
453  shortname, item->m_file, item->m_line, item->m_function, item->m_message );
454 #else
455  snprintf( line, MAX_STRING_LENGTH, "%s %c %s\n", timestamp,
456  shortname, item->m_message );
457 #endif
458  }
459 
460  int result = write( 1, line, strlen(line) );
461  (void)result;
462 
463 #else // Q_OS_ANDROID
464 
465  android_LogPriority aprio;
466  switch (item->m_level)
467  {
468  case LOG_EMERG:
469  aprio = ANDROID_LOG_FATAL;
470  case LOG_ALERT:
471  case LOG_CRIT:
472  case LOG_ERR:
473  aprio = ANDROID_LOG_ERROR;
474  break;
475  case LOG_WARNING:
476  aprio = ANDROID_LOG_WARN;
477  break;
478  case LOG_NOTICE:
479  case LOG_INFO:
480  aprio = ANDROID_LOG_INFO;
481  break;
482  case LOG_DEBUG:
483  aprio = ANDROID_LOG_DEBUG;
484  break;
485  case LOG_UNKNOWN:
486  default:
487  aprio = ANDROID_LOG_UNKNOWN;
488  break;
489  }
490 #if CONFIG_DEBUGTYPE
491  __android_log_print(aprio, "mfe", "%s:%d:%s %s", item->m_file,
492  item->m_line, item->m_function, item->m_message);
493 #else
494  __android_log_print(aprio, "mfe", "%s", item->m_message);
495 #endif
496 #endif
497 
498  item->DecrRef();
499 
500  return true;
501 }
502 
503 
507 {
508  logQueueMutex.lock();
509  flush(1000);
510  m_aborted = true;
511  logQueueMutex.unlock();
512  m_waitNotEmpty->wakeAll();
513 }
514 
518 bool LoggerThread::flush(int timeoutMS)
519 {
520  QTime t;
521  t.start();
522  while (!m_aborted && !logQueue.isEmpty() && t.elapsed() < timeoutMS)
523  {
524  m_waitNotEmpty->wakeAll();
525  int left = timeoutMS - t.elapsed();
526  if (left > 0)
527  m_waitEmpty->wait(&logQueueMutex, left);
528  }
529  return logQueue.isEmpty();
530 }
531 
533 {
534  if (!item)
535  return;
536 
537  item->setPid(m_pid);
538  item->setThreadName(item->getThreadName());
539  item->setAppName(m_appname);
540  item->setTable(m_tablename);
541  item->setLogFile(m_filename);
542  item->setFacility(m_facility);
543 }
544 
545 
553 LoggingItem *LoggingItem::create(const char *_file,
554  const char *_function,
555  int _line, LogLevel_t _level,
556  LoggingType _type)
557 {
558  LoggingItem *item = new LoggingItem(_file, _function, _line, _level, _type);
559 
560  return item;
561 }
562 
564 {
565  // Deserialize buffer
566  QVariant variant = QJsonWrapper::parseJson(buf);
567 
568  LoggingItem *item = new LoggingItem;
569  QJsonWrapper::qvariant2qobject(variant.toMap(), item);
570 
571  return item;
572 }
573 
574 
586 void LogPrintLine( uint64_t mask, LogLevel_t level, const char *file, int line,
587  const char *function, int fromQString,
588  const char *format, ... )
589 {
590  va_list arguments;
591 
592  int type = kMessage;
593  type |= (mask & VB_FLUSH) ? kFlush : 0;
594  type |= (mask & VB_STDIO) ? kStandardIO : 0;
595  LoggingItem *item = LoggingItem::create(file, function, line, level,
596  (LoggingType)type);
597  if (!item)
598  return;
599 
600  char *formatcopy = nullptr;
601  if( fromQString && strchr(format, '%') )
602  {
603  QString string(format);
604  format = strdup(string.replace(logRegExp, "%%").toLocal8Bit()
605  .constData());
606  formatcopy = (char *)format;
607  }
608 
609  va_start(arguments, format);
610  vsnprintf(item->m_message, LOGLINE_MAX, format, arguments);
611  va_end(arguments);
612 
613  if (formatcopy)
614  free(formatcopy);
615 
616  QMutexLocker qLock(&logQueueMutex);
617 
618 #if defined( _MSC_VER ) && defined( _DEBUG )
619  OutputDebugStringA( item->m_message );
620  OutputDebugStringA( "\n" );
621 #endif
622 
623  logQueue.enqueue(item);
624 
626  {
627  while (!logQueue.isEmpty())
628  {
629  item = logQueue.dequeue();
630  qLock.unlock();
631  logThread->handleItem(item);
632  logThread->logConsole(item);
633  item->DecrRef();
634  qLock.relock();
635  }
636  }
637  else if (logThread && !logThreadFinished && (type & kFlush))
638  {
639  logThread->flush();
640  }
641 }
642 
643 
648 {
649  logPropagateArgList.clear();
650 
651  QString mask = verboseString.trimmed();
652  mask.replace(QRegExp(" "), ",");
653  mask.remove(QRegExp("^,"));
654  logPropagateArgs = " --verbose " + mask;
655  logPropagateArgList << "--verbose" << mask;
656 
658  {
659  logPropagateArgs += " --logpath " + logPropagateOpts.path;
660  logPropagateArgList << "--logpath" << logPropagateOpts.path;
661  }
662 
663  QString name = logLevelGetName(logLevel);
664  logPropagateArgs += " --loglevel " + name;
665  logPropagateArgList << "--loglevel" << name;
666 
667  for (int i = 0; i < logPropagateOpts.quiet; i++)
668  {
669  logPropagateArgs += " --quiet";
670  logPropagateArgList << "--quiet";
671  }
672 
674  {
675  logPropagateArgs += " --enable-dblog";
676  logPropagateArgList << "--enable-dblog";
677  }
678 
679 #if !defined(_WIN32) && !defined(Q_OS_ANDROID)
680  if (logPropagateOpts.facility >= 0)
681  {
682  const CODE *syslogname;
683 
684  for (syslogname = &facilitynames[0];
685  (syslogname->c_name &&
686  syslogname->c_val != logPropagateOpts.facility); syslogname++);
687 
688  logPropagateArgs += QString(" --syslog %1").arg(syslogname->c_name);
689  logPropagateArgList << "--syslog" << syslogname->c_name;
690  }
691 #if CONFIG_SYSTEMD_JOURNAL
692  else if (logPropagateOpts.facility == SYSTEMD_JOURNAL_FACILITY)
693  {
694  logPropagateArgs += " --systemd-journal";
695  logPropagateArgList << "--systemd-journal";
696  }
697 #endif
698 #endif
699 }
700 
704 {
705  return logPropagateOpts.quiet;
706 }
707 
720 void logStart(const QString& logfile, int progress, int quiet, int facility,
721  LogLevel_t level, bool dblog, bool propagate)
722 {
723  if (logThread && logThread->isRunning())
724  return;
725 
726  logLevel = level;
727  LOG(VB_GENERAL, LOG_NOTICE, QString("Setting Log Level to LOG_%1")
728  .arg(logLevelGetName(logLevel).toUpper()));
729 
730  logPropagateOpts.propagate = propagate;
732  logPropagateOpts.facility = facility;
733  logPropagateOpts.dblog = dblog;
734 
735  if (propagate)
736  {
737  QFileInfo finfo(logfile);
738  QString path = finfo.path();
739  logPropagateOpts.path = path;
740  }
741 
743 
744  QString table = dblog ? QString("logging") : QString("");
745 
746  if (!logThread)
747  logThread = new LoggerThread(logfile, progress, quiet, table, facility);
748 
749  logThread->start();
750 }
751 
753 void logStop(void)
754 {
755  if (logThread)
756  {
757  logThread->stop();
758  logThread->wait();
759  delete logThread;
760  logThread = nullptr;
761  }
762 }
763 
768 void loggingRegisterThread(const QString &name)
769 {
770  if (logThreadFinished)
771  return;
772 
773  QMutexLocker qLock(&logQueueMutex);
774 
775  LoggingItem *item = LoggingItem::create(__FILE__, __FUNCTION__,
776  __LINE__, LOG_DEBUG,
777  kRegistering);
778  if (item)
779  {
780  item->setThreadName((char *)name.toLocal8Bit().constData());
781  logQueue.enqueue(item);
782  }
783 }
784 
788 {
789  if (logThreadFinished)
790  return;
791 
792  QMutexLocker qLock(&logQueueMutex);
793 
794  LoggingItem *item = LoggingItem::create(__FILE__, __FUNCTION__, __LINE__,
795  LOG_DEBUG,
797  if (item)
798  logQueue.enqueue(item);
799 }
800 
801 
805 int syslogGetFacility(const QString& facility)
806 {
807 #ifdef _WIN32
808  LOG(VB_GENERAL, LOG_NOTICE,
809  "Windows does not support syslog, disabling" );
810  Q_UNUSED(facility);
811  return( -2 );
812 #elif defined(Q_OS_ANDROID)
813  LOG(VB_GENERAL, LOG_NOTICE,
814  "Android does not support syslog, disabling" );
815  Q_UNUSED(facility);
816  return( -2 );
817 #else
818  const CODE *name;
819  int i;
820  QByteArray ba = facility.toLocal8Bit();
821  char *string = (char *)ba.constData();
822 
823  for (i = 0, name = &facilitynames[0];
824  name->c_name && (strcmp(name->c_name, string) != 0); i++, name++);
825 
826  return( name->c_val );
827 #endif
828 }
829 
833 LogLevel_t logLevelGet(const QString& level)
834 {
835  QMutexLocker locker(&loglevelMapMutex);
836  if (!verboseInitialized)
837  {
838  locker.unlock();
839  verboseInit();
840  locker.relock();
841  }
842 
843  for (LoglevelMap::iterator it = loglevelMap.begin();
844  it != loglevelMap.end(); ++it)
845  {
846  LoglevelDef *item = (*it);
847  if ( item->name == level.toLower() )
848  return (LogLevel_t)item->value;
849  }
850 
851  return LOG_UNKNOWN;
852 }
853 
857 QString logLevelGetName(LogLevel_t level)
858 {
859  QMutexLocker locker(&loglevelMapMutex);
860  if (!verboseInitialized)
861  {
862  locker.unlock();
863  verboseInit();
864  locker.relock();
865  }
866  LoglevelMap::iterator it = loglevelMap.find((int)level);
867 
868  if ( it == loglevelMap.end() )
869  return QString("unknown");
870 
871  return (*it)->name;
872 }
873 
880 void verboseAdd(uint64_t mask, QString name, bool additive, QString helptext)
881 {
882  VerboseDef *item = new VerboseDef;
883 
884  item->mask = mask;
885  // VB_GENERAL -> general
886  name.remove(0, 3);
887  name = name.toLower();
888  item->name = name;
889  item->additive = additive;
890  item->helpText = std::move(helptext);
891 
892  verboseMap.insert(name, item);
893 }
894 
900 void loglevelAdd(int value, QString name, char shortname)
901 {
902  LoglevelDef *item = new LoglevelDef;
903 
904  item->value = value;
905  // LOG_CRIT -> crit
906  name.remove(0, 4);
907  name = name.toLower();
908  item->name = name;
909  item->shortname = shortname;
910 
911  loglevelMap.insert(value, item);
912 }
913 
915 void verboseInit(void)
916 {
917  QMutexLocker locker(&verboseMapMutex);
918  QMutexLocker locker2(&loglevelMapMutex);
919  verboseMap.clear();
920  loglevelMap.clear();
921 
922  // This looks funky, so I'll put some explanation here. The verbosedefs.h
923  // file gets included as part of the mythlogging.h include, and at that
924  // time, the normal (without _IMPLEMENT_VERBOSE defined) code case will
925  // define the VerboseMask enum. At this point, we force it to allow us
926  // to include the file again, but with _IMPLEMENT_VERBOSE set so that the
927  // single definition of the VB_* values can be shared to define also the
928  // contents of verboseMap, via repeated calls to verboseAdd()
929 
930 #undef VERBOSEDEFS_H_
931 #define _IMPLEMENT_VERBOSE
932 #include "verbosedefs.h"
933 
934  verboseInitialized = true;
935 }
936 
937 
940 void verboseHelp(void)
941 {
942  QString m_verbose = userDefaultValueStr.trimmed();
943  m_verbose.replace(QRegExp(" "), ",");
944  m_verbose.remove(QRegExp("^,"));
945 
946  cerr << "Verbose debug levels.\n"
947  "Accepts any combination (separated by comma) of:\n\n";
948 
949  for (VerboseMap::Iterator vit = verboseMap.begin();
950  vit != verboseMap.end(); ++vit )
951  {
952  VerboseDef *item = vit.value();
953  QString name = QString(" %1").arg(item->name, -15, ' ');
954  if (item->helpText.isEmpty())
955  continue;
956  cerr << name.toLocal8Bit().constData() << " - " <<
957  item->helpText.toLocal8Bit().constData() << endl;
958  }
959 
960  cerr << endl <<
961  "The default for this program appears to be: '-v " <<
962  m_verbose.toLocal8Bit().constData() << "'\n\n"
963  "Most options are additive except for 'none' and 'all'.\n"
964  "These two are semi-exclusive and take precedence over any\n"
965  "other options. However, you may use something like\n"
966  "'-v none,jobqueue' to receive only JobQueue related messages\n"
967  "and override the default verbosity level.\n\n"
968  "Additive options may also be subtracted from 'all' by\n"
969  "prefixing them with 'no', so you may use '-v all,nodatabase'\n"
970  "to view all but database debug messages.\n\n";
971 
972  cerr << "The 'global' loglevel is specified with --loglevel, but can be\n"
973  << "overridden on a component by component basis by appending "
974  << "':level'\n"
975  << "to the component.\n"
976  << " For example: -v gui:debug,channel:notice,record\n\n";
977 
978  cerr << "Some debug levels may not apply to this program.\n" << endl;
979 }
980 
984 int verboseArgParse(const QString& arg)
985 {
986  QString option;
987  int idx;
988 
989  if (!verboseInitialized)
990  verboseInit();
991 
992  QMutexLocker locker(&verboseMapMutex);
993 
995  verboseString = QString(verboseDefaultStr);
996 
997  if (arg.startsWith('-'))
998  {
999  cerr << "Invalid or missing argument to -v/--verbose option\n";
1001  }
1002 
1003  QStringList verboseOpts = arg.split(QRegExp("[^\\w:]+",
1004  Qt::CaseInsensitive,
1005  QRegExp::RegExp2));
1006  for (QStringList::Iterator it = verboseOpts.begin();
1007  it != verboseOpts.end(); ++it )
1008  {
1009  option = (*it).toLower();
1010  bool reverseOption = false;
1011  QString optionLevel;
1012 
1013  if (option != "none" && option.startsWith("no"))
1014  {
1015  reverseOption = true;
1016  option = option.right(option.length() - 2);
1017  }
1018 
1019  if (option == "help")
1020  {
1021  verboseHelp();
1023  }
1024  if (option == "important")
1025  {
1026  cerr << "The \"important\" log mask is no longer valid.\n";
1027  }
1028  else if (option == "extra")
1029  {
1030  cerr << "The \"extra\" log mask is no longer valid. Please try "
1031  "--loglevel debug instead.\n";
1032  }
1033  else if (option == "default")
1034  {
1036  {
1039  }
1040  else
1041  {
1043  verboseString = QString(verboseDefaultStr);
1044  }
1045  }
1046  else
1047  {
1048  if ((idx = option.indexOf(':')) != -1)
1049  {
1050  optionLevel = option.mid(idx + 1);
1051  option = option.left(idx);
1052  }
1053 
1054  VerboseDef *item = verboseMap.value(option);
1055 
1056  if (item)
1057  {
1058  if (reverseOption)
1059  {
1060  verboseMask &= ~(item->mask);
1061  verboseString = verboseString.remove(' ' + item->name);
1062  verboseString += " no" + item->name;
1063  }
1064  else
1065  {
1066  if (item->additive)
1067  {
1068  if (!(verboseMask & item->mask))
1069  {
1070  verboseMask |= item->mask;
1071  verboseString += ' ' + item->name;
1072  }
1073  }
1074  else
1075  {
1076  verboseMask = item->mask;
1077  verboseString = item->name;
1078  }
1079 
1080  if (!optionLevel.isEmpty())
1081  {
1082  LogLevel_t level = logLevelGet(optionLevel);
1083  if (level != LOG_UNKNOWN)
1084  componentLogLevel[item->mask] = level;
1085  }
1086  }
1087  }
1088  else
1089  {
1090  cerr << "Unknown argument for -v/--verbose: " <<
1091  option.toLocal8Bit().constData() << endl;;
1093  }
1094  }
1095  }
1096 
1097  if (!haveUserDefaultValues)
1098  {
1099  haveUserDefaultValues = true;
1102  }
1103 
1104  return GENERIC_EXIT_OK;
1105 }
1106 
1111 QString logStrerror(int errnum)
1112 {
1113  return QString("%1 (%2)").arg(strerror(errnum)).arg(errnum);
1114 }
1115 
1116 
1117 /*
1118  * vim:ts=4:sw=4:ai:et:si:sts=4
1119  */
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:900
QString logfile
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:857
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:170
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
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:586
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:152
QString name
Definition: verbosedefs.h:213
char * getThreadName(void)
Get the name of the thread that produced the LoggingItem.
Definition: logging.cpp:188
#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
#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: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:518
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:62
int syslogGetFacility(const QString &facility)
Map a syslog facility name back to the enumerated value.
Definition: logging.cpp:805
void fillItem(LoggingItem *item)
Definition: logging.cpp:532
unsigned int uint
Definition: compat.h:140
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:413
void loggingRegisterThread(const QString &name)
Register the current thread with the given name.
Definition: logging.cpp:768
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:191
VerboseMap verboseMap
Definition: logging.cpp:98
pid_t m_pid
Cached pid value.
Definition: logging.h:204
qlonglong epoch
Definition: logging.h:74
void handleItem(LoggingItem *item)
Handles each LoggingItem.
Definition: logging.cpp:341
void verboseAdd(uint64_t mask, QString name, bool additive, QString helptext)
Add a verbose level to the verboseMap.
Definition: logging.cpp:880
static bool debugRegistration
Definition: logging.cpp:78
void verboseHelp(void)
Outputs the Verbose levels and their descriptions (for –verbose help)
Definition: logging.cpp:940
void logForwardMessage(const QList< QByteArray > &msg)
virtual int IncrRef(void)
Increments reference count.
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:506
QMap< QString, VerboseDef * > VerboseMap
Definition: verbosedefs.h:209
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
unsigned char t
Definition: ParseText.cpp:329
bool logPropagateQuiet(void)
Check if we are propagating a "--quiet".
Definition: logging.cpp:703
static QHash< uint64_t, char * > logThreadHash
Definition: logging.cpp:72
QString path
Definition: logging.cpp:85
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:1111
void loggingDeregisterThread(void)
Deregister the current thread's name.
Definition: logging.cpp:787
#define MAX_STRING_LENGTH
Definition: logging.cpp:93
const char * name
Definition: ParseText.cpp:328
QByteArray toByteArray(void)
Definition: logging.cpp:176
static QRegExp logRegExp
Definition: logging.cpp:68
#define LOGLINE_MAX
Definition: logging.h:30
QString m_appname
Cached application name.
Definition: logging.h:200
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:187
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:204
#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:753
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:217
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:984
LoggerThread(QString filename, bool progress, bool quiet, QString table, int facility)
LoggerThread constructor.
Definition: logging.cpp:245
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:833
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:199
QMap< int, LoglevelDef * > LoglevelMap
Definition: verbosedefs.h:216
void verboseInit(void)
Initialize the logging levels and verbose levels.
Definition: logging.cpp:915
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:647
void run(void) override
Run the logging thread.
Definition: logging.cpp:284
QStringList logPropagateArgList
Definition: logging.cpp:90
Definition: logging.h:46
~LoggerThread()
LoggerThread destructor. Triggers the deletion of all loggers.
Definition: logging.cpp:270
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:720
static LoggingItem * create(const char *, const char *, int, LogLevel_t, LoggingType)
Create a new LoggingItem.
Definition: logging.cpp:553
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:195