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 <QCoreApplication>
9 #include <QFileInfo>
10 #include <QStringList>
11 #include <QMap>
12 #include <QRegExp>
13 #include <QVariantMap>
14 #include <iostream>
15 
16 using namespace std;
17 
18 #include "mythlogging.h"
19 #include "logging.h"
20 #include "loggingserver.h"
21 #include "mythdb.h"
22 #include "mythdirs.h"
23 #include "mythcorecontext.h"
24 #include "mythsystemlegacy.h"
25 #include "mythsignalingtimer.h"
26 #include "dbutil.h"
27 #include "exitcodes.h"
28 #include "compat.h"
29 
30 #include <csignal>
31 #include <cstdarg>
32 #include <cstdio>
33 #include <cstdlib>
34 #include <cstring>
35 #include <fcntl.h>
36 #include <sys/stat.h>
37 #include <sys/types.h>
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  m_pid(-1), m_tid(-1), m_threadId(-1), m_usec(0), m_line(0),
143  m_type(kMessage), m_level(LOG_INFO), m_facility(0), m_epoch(0),
144  m_file(nullptr), m_function(nullptr), m_threadName(nullptr), m_appName(nullptr),
145  m_table(nullptr), m_logFile(nullptr)
146 {
147  m_message[0]='\0';
148  m_message[LOGLINE_MAX]='\0';
149 }
150 
151 LoggingItem::LoggingItem(const char *_file, const char *_function,
152  int _line, LogLevel_t _level, LoggingType _type) :
153  ReferenceCounter("LoggingItem", false), m_pid(-1),
154  m_threadId((uint64_t)(QThread::currentThreadId())),
155  m_line(_line), m_type(_type), m_level(_level), m_facility(0),
156  m_file(strdup(_file)), m_function(strdup(_function)),
157  m_threadName(nullptr), m_appName(nullptr), m_table(nullptr), m_logFile(nullptr)
158 {
160 
161  m_message[0]='\0';
162  m_message[LOGLINE_MAX]='\0';
163  setThreadTid();
164 }
165 
167 {
168  free(m_file);
169 
170  free(m_function);
171 
172  free(m_threadName);
173 
174  free(m_appName);
175 
176  free(m_table);
177 
178  free(m_logFile);
179 }
180 
181 QByteArray LoggingItem::toByteArray(void)
182 {
183  QVariantMap variant = QJsonWrapper::qobject2qvariant(this);
184  QByteArray json = QJsonWrapper::toJson(variant);
185 
186  //cout << json.constData() << endl;
187 
188  return json;
189 }
190 
194 {
195  static const char *unknown = "thread_unknown";
196 
197  if( m_threadName )
198  return m_threadName;
199 
200  QMutexLocker locker(&logThreadMutex);
201  return logThreadHash.value(m_threadId, (char *)unknown);
202 }
203 
210 {
211  QMutexLocker locker(&logThreadTidMutex);
212  m_tid = logThreadTidHash.value(m_threadId, 0);
213  return m_tid;
214 }
215 
223 {
224  QMutexLocker locker(&logThreadTidMutex);
225 
226  m_tid = logThreadTidHash.value(m_threadId, -1);
227  if (m_tid == -1)
228  {
229  m_tid = 0;
230 
231 #if defined(Q_OS_ANDROID)
232  m_tid = (int64_t)gettid();
233 #elif defined(linux)
234  m_tid = syscall(SYS_gettid);
235 #elif defined(__FreeBSD__)
236  long lwpid;
237  int dummy = thr_self( &lwpid );
238  (void)dummy;
239  m_tid = (int64_t)lwpid;
240 #elif CONFIG_DARWIN
241  m_tid = (int64_t)mach_thread_self();
242 #endif
244  }
245 }
246 
250 LoggerThread::LoggerThread(QString filename, bool progress, bool quiet,
251  QString table, int facility) :
252  MThread("Logger"),
253  m_waitNotEmpty(new QWaitCondition()),
254  m_waitEmpty(new QWaitCondition()),
255  m_aborted(false), m_filename(filename), m_progress(progress),
256  m_quiet(quiet), m_appname(QCoreApplication::applicationName()),
257  m_tablename(table), m_facility(facility), m_pid(getpid())
258 {
259  char *debug = getenv("VERBOSE_THREADS");
260  if (debug != nullptr)
261  {
262  LOG(VB_GENERAL, LOG_NOTICE,
263  "Logging thread registration/deregistration enabled!");
264  debugRegistration = true;
265  }
266 
267  if (!logForwardStart())
268  {
269  LOG(VB_GENERAL, LOG_ERR,
270  "Failed to start LogServer thread");
271  }
272  moveToThread(qthread());
273 }
274 
277 {
278  stop();
279  wait();
280  logForwardStop();
281 
282  delete m_waitNotEmpty;
283  delete m_waitEmpty;
284 }
285 
291 {
292  RunProlog();
293 
294  logThreadFinished = false;
295 
296  LOG(VB_GENERAL, LOG_INFO, "Added logging to the console");
297 
298  bool dieNow = false;
299 
300  QMutexLocker qLock(&logQueueMutex);
301 
302  while (!m_aborted || !logQueue.isEmpty())
303  {
304  qLock.unlock();
305  qApp->processEvents(QEventLoop::AllEvents, 10);
306  qApp->sendPostedEvents(nullptr, QEvent::DeferredDelete);
307 
308  qLock.relock();
309  if (logQueue.isEmpty())
310  {
311  m_waitEmpty->wakeAll();
312  m_waitNotEmpty->wait(qLock.mutex(), 100);
313  continue;
314  }
315 
316  LoggingItem *item = logQueue.dequeue();
317  qLock.unlock();
318 
319  fillItem(item);
320  handleItem(item);
321  logConsole(item);
322  item->DecrRef();
323 
324  qLock.relock();
325  }
326 
327  qLock.unlock();
328 
329  // This must be before the timer stop below or we deadlock when the timer
330  // thread tries to deregister, and we wait for it.
331  logThreadFinished = true;
332 
333  RunEpilog();
334 
335  // cppcheck-suppress knownConditionTrueFalse
336  if (dieNow)
337  {
338  qApp->processEvents();
339  }
340 }
341 
348 {
349  if (item->m_type & kRegistering)
350  {
351  item->m_tid = item->getThreadTid();
352 
353  QMutexLocker locker(&logThreadMutex);
354  if (logThreadHash.contains(item->m_threadId))
355  {
356  char *threadName = logThreadHash.take(item->m_threadId);
357  free(threadName);
358  }
359  logThreadHash[item->m_threadId] = strdup(item->m_threadName);
360 
361  if (debugRegistration)
362  {
363  snprintf(item->m_message, LOGLINE_MAX,
364  "Thread 0x%" PREFIX64 "X (%" PREFIX64
365  "d) registered as \'%s\'",
366  item->m_threadId,
367  item->m_tid,
368  logThreadHash[item->m_threadId]);
369  }
370  }
371  else if (item->m_type & kDeregistering)
372  {
373  int64_t tid = 0;
374 
375  {
376  QMutexLocker locker(&logThreadTidMutex);
377  if( logThreadTidHash.contains(item->m_threadId) )
378  {
379  tid = logThreadTidHash[item->m_threadId];
380  logThreadTidHash.remove(item->m_threadId);
381  }
382  }
383 
384  QMutexLocker locker(&logThreadMutex);
385  if (logThreadHash.contains(item->m_threadId))
386  {
387  if (debugRegistration)
388  {
389  snprintf(item->m_message, LOGLINE_MAX,
390  "Thread 0x%" PREFIX64 "X (%" PREFIX64
391  "d) deregistered as \'%s\'",
392  item->m_threadId,
393  (long long int)tid,
394  logThreadHash[item->m_threadId]);
395  }
396  char *threadName = logThreadHash.take(item->m_threadId);
397  free(threadName);
398  }
399  }
400 
401  if (item->m_message[0] != '\0')
402  {
410  QList<QByteArray> list;
411  list.append(QByteArray());
412  list.append(item->toByteArray());
413  logForwardMessage(list);
414  }
415 }
416 
420 {
421  char line[MAX_STRING_LENGTH];
422 
423  if (m_quiet || (m_progress && item->m_level > LOG_ERR))
424  return false;
425 
426  if (!(item->m_type & kMessage))
427  return false;
428 
429  item->IncrRef();
430 
431 #ifndef Q_OS_ANDROID
432  if (item->m_type & kStandardIO)
433  snprintf( line, MAX_STRING_LENGTH, "%s", item->m_message );
434  else
435  {
436  char usPart[9];
437  char timestamp[TIMESTAMP_MAX];
438  time_t epoch = item->epoch();
439  struct tm tm;
440  localtime_r(&epoch, &tm);
441 
442  strftime( timestamp, TIMESTAMP_MAX-8, "%Y-%m-%d %H:%M:%S",
443  (const struct tm *)&tm );
444  snprintf( usPart, 9, ".%06d", (int)(item->m_usec) );
445  strcat( timestamp, usPart );
446  char shortname;
447 
448  {
449  QMutexLocker locker(&loglevelMapMutex);
450  LoglevelDef *lev = loglevelMap.value(item->m_level, nullptr);
451  if (!lev)
452  shortname = '-';
453  else
454  shortname = lev->shortname;
455  }
456 
457 #if CONFIG_DEBUGTYPE
458  snprintf( line, MAX_STRING_LENGTH, "%s %c %s:%d:%s %s\n", timestamp,
459  shortname, item->m_file, item->m_line, item->m_function, item->m_message );
460 #else
461  snprintf( line, MAX_STRING_LENGTH, "%s %c %s\n", timestamp,
462  shortname, item->m_message );
463 #endif
464  }
465 
466  int result = write( 1, line, strlen(line) );
467  (void)result;
468 
469 #else // Q_OS_ANDROID
470 
471  android_LogPriority aprio;
472  switch (item->m_level)
473  {
474  case LOG_EMERG:
475  aprio = ANDROID_LOG_FATAL;
476  case LOG_ALERT:
477  case LOG_CRIT:
478  case LOG_ERR:
479  aprio = ANDROID_LOG_ERROR;
480  break;
481  case LOG_WARNING:
482  aprio = ANDROID_LOG_WARN;
483  break;
484  case LOG_NOTICE:
485  case LOG_INFO:
486  aprio = ANDROID_LOG_INFO;
487  break;
488  case LOG_DEBUG:
489  aprio = ANDROID_LOG_DEBUG;
490  break;
491  case LOG_UNKNOWN:
492  default:
493  aprio = ANDROID_LOG_UNKNOWN;
494  break;
495  }
496 #if CONFIG_DEBUGTYPE
497  __android_log_print(aprio, "mfe", "%s:%d:%s %s", item->m_file,
498  item->m_line, item->m_function, item->m_message);
499 #else
500  __android_log_print(aprio, "mfe", "%s", item->m_message);
501 #endif
502 #endif
503 
504  item->DecrRef();
505 
506  return true;
507 }
508 
509 
513 {
514  logQueueMutex.lock();
515  flush(1000);
516  m_aborted = true;
517  logQueueMutex.unlock();
518  m_waitNotEmpty->wakeAll();
519 }
520 
524 bool LoggerThread::flush(int timeoutMS)
525 {
526  QTime t;
527  t.start();
528  while (!m_aborted && !logQueue.isEmpty() && t.elapsed() < timeoutMS)
529  {
530  m_waitNotEmpty->wakeAll();
531  int left = timeoutMS - t.elapsed();
532  if (left > 0)
533  m_waitEmpty->wait(&logQueueMutex, left);
534  }
535  return logQueue.isEmpty();
536 }
537 
539 {
540  if (!item)
541  return;
542 
543  item->setPid(m_pid);
544  item->setThreadName(item->getThreadName());
545  item->setAppName(m_appname);
546  item->setTable(m_tablename);
547  item->setLogFile(m_filename);
548  item->setFacility(m_facility);
549 }
550 
551 
559 LoggingItem *LoggingItem::create(const char *_file,
560  const char *_function,
561  int _line, LogLevel_t _level,
562  LoggingType _type)
563 {
564  LoggingItem *item = new LoggingItem(_file, _function, _line, _level, _type);
565 
566  return item;
567 }
568 
570 {
571  // Deserialize buffer
572  QVariant variant = QJsonWrapper::parseJson(buf);
573 
574  LoggingItem *item = new LoggingItem;
575  QJsonWrapper::qvariant2qobject(variant.toMap(), item);
576 
577  return item;
578 }
579 
580 
592 void LogPrintLine( uint64_t mask, LogLevel_t level, const char *file, int line,
593  const char *function, int fromQString,
594  const char *format, ... )
595 {
596  va_list arguments;
597 
598  int type = kMessage;
599  type |= (mask & VB_FLUSH) ? kFlush : 0;
600  type |= (mask & VB_STDIO) ? kStandardIO : 0;
601  LoggingItem *item = LoggingItem::create(file, function, line, level,
602  (LoggingType)type);
603  if (!item)
604  return;
605 
606  char *formatcopy = nullptr;
607  if( fromQString && strchr(format, '%') )
608  {
609  QString string(format);
610  format = strdup(string.replace(logRegExp, "%%").toLocal8Bit()
611  .constData());
612  formatcopy = (char *)format;
613  }
614 
615  va_start(arguments, format);
616  vsnprintf(item->m_message, LOGLINE_MAX, format, arguments);
617  va_end(arguments);
618 
619  if (formatcopy)
620  free(formatcopy);
621 
622  QMutexLocker qLock(&logQueueMutex);
623 
624 #if defined( _MSC_VER ) && defined( _DEBUG )
625  OutputDebugStringA( item->m_message );
626  OutputDebugStringA( "\n" );
627 #endif
628 
629  logQueue.enqueue(item);
630 
632  {
633  while (!logQueue.isEmpty())
634  {
635  item = logQueue.dequeue();
636  qLock.unlock();
637  logThread->handleItem(item);
638  logThread->logConsole(item);
639  item->DecrRef();
640  qLock.relock();
641  }
642  }
643  else if (logThread && !logThreadFinished && (type & kFlush))
644  {
645  logThread->flush();
646  }
647 }
648 
649 
654 {
655  logPropagateArgList.clear();
656 
657  QString mask = verboseString.trimmed();
658  mask.replace(QRegExp(" "), ",");
659  mask.remove(QRegExp("^,"));
660  logPropagateArgs = " --verbose " + mask;
661  logPropagateArgList << "--verbose" << mask;
662 
664  {
665  logPropagateArgs += " --logpath " + logPropagateOpts.path;
666  logPropagateArgList << "--logpath" << logPropagateOpts.path;
667  }
668 
669  QString name = logLevelGetName(logLevel);
670  logPropagateArgs += " --loglevel " + name;
671  logPropagateArgList << "--loglevel" << name;
672 
673  for (int i = 0; i < logPropagateOpts.quiet; i++)
674  {
675  logPropagateArgs += " --quiet";
676  logPropagateArgList << "--quiet";
677  }
678 
680  {
681  logPropagateArgs += " --enable-dblog";
682  logPropagateArgList << "--enable-dblog";
683  }
684 
685 #if !defined(_WIN32) && !defined(Q_OS_ANDROID)
686  if (logPropagateOpts.facility >= 0)
687  {
688  const CODE *syslogname;
689 
690  for (syslogname = &facilitynames[0];
691  (syslogname->c_name &&
692  syslogname->c_val != logPropagateOpts.facility); syslogname++);
693 
694  logPropagateArgs += QString(" --syslog %1").arg(syslogname->c_name);
695  logPropagateArgList << "--syslog" << syslogname->c_name;
696  }
697 #if CONFIG_SYSTEMD_JOURNAL
699  {
700  logPropagateArgs += " --systemd-journal";
701  logPropagateArgList << "--systemd-journal";
702  }
703 #endif
704 #endif
705 }
706 
710 {
711  return logPropagateOpts.quiet;
712 }
713 
726 void logStart(QString logfile, int progress, int quiet, int facility,
727  LogLevel_t level, bool dblog, bool propagate)
728 {
729  if (logThread && logThread->isRunning())
730  return;
731 
732  logLevel = level;
733  LOG(VB_GENERAL, LOG_NOTICE, QString("Setting Log Level to LOG_%1")
734  .arg(logLevelGetName(logLevel).toUpper()));
735 
736  logPropagateOpts.propagate = propagate;
738  logPropagateOpts.facility = facility;
739  logPropagateOpts.dblog = dblog;
740 
741  if (propagate)
742  {
743  QFileInfo finfo(logfile);
744  QString path = finfo.path();
745  logPropagateOpts.path = path;
746  }
747 
749 
750  QString table = dblog ? QString("logging") : QString("");
751 
752  if (!logThread)
753  logThread = new LoggerThread(logfile, progress, quiet, table, facility);
754 
755  logThread->start();
756 }
757 
759 void logStop(void)
760 {
761  if (logThread)
762  {
763  logThread->stop();
764  logThread->wait();
765  delete logThread;
766  logThread = nullptr;
767  }
768 }
769 
774 void loggingRegisterThread(const QString &name)
775 {
776  if (logThreadFinished)
777  return;
778 
779  QMutexLocker qLock(&logQueueMutex);
780 
781  LoggingItem *item = LoggingItem::create(__FILE__, __FUNCTION__,
782  __LINE__, LOG_DEBUG,
783  kRegistering);
784  if (item)
785  {
786  item->setThreadName((char *)name.toLocal8Bit().constData());
787  logQueue.enqueue(item);
788  }
789 }
790 
794 {
795  if (logThreadFinished)
796  return;
797 
798  QMutexLocker qLock(&logQueueMutex);
799 
800  LoggingItem *item = LoggingItem::create(__FILE__, __FUNCTION__, __LINE__,
801  LOG_DEBUG,
803  if (item)
804  logQueue.enqueue(item);
805 }
806 
807 
811 int syslogGetFacility(QString facility)
812 {
813 #ifdef _WIN32
814  LOG(VB_GENERAL, LOG_NOTICE,
815  "Windows does not support syslog, disabling" );
816  Q_UNUSED(facility);
817  return( -2 );
818 #elif defined(Q_OS_ANDROID)
819  LOG(VB_GENERAL, LOG_NOTICE,
820  "Android does not support syslog, disabling" );
821  Q_UNUSED(facility);
822  return( -2 );
823 #else
824  const CODE *name;
825  int i;
826  QByteArray ba = facility.toLocal8Bit();
827  char *string = (char *)ba.constData();
828 
829  for (i = 0, name = &facilitynames[0];
830  name->c_name && (strcmp(name->c_name, string) != 0); i++, name++);
831 
832  return( name->c_val );
833 #endif
834 }
835 
839 LogLevel_t logLevelGet(QString level)
840 {
841  QMutexLocker locker(&loglevelMapMutex);
842  if (!verboseInitialized)
843  {
844  locker.unlock();
845  verboseInit();
846  locker.relock();
847  }
848 
849  for (LoglevelMap::iterator it = loglevelMap.begin();
850  it != loglevelMap.end(); ++it)
851  {
852  LoglevelDef *item = (*it);
853  if ( item->name == level.toLower() )
854  return (LogLevel_t)item->value;
855  }
856 
857  return LOG_UNKNOWN;
858 }
859 
863 QString logLevelGetName(LogLevel_t level)
864 {
865  QMutexLocker locker(&loglevelMapMutex);
866  if (!verboseInitialized)
867  {
868  locker.unlock();
869  verboseInit();
870  locker.relock();
871  }
872  LoglevelMap::iterator it = loglevelMap.find((int)level);
873 
874  if ( it == loglevelMap.end() )
875  return QString("unknown");
876 
877  return (*it)->name;
878 }
879 
886 void verboseAdd(uint64_t mask, QString name, bool additive, QString helptext)
887 {
888  VerboseDef *item = new VerboseDef;
889 
890  item->mask = mask;
891  // VB_GENERAL -> general
892  name.remove(0, 3);
893  name = name.toLower();
894  item->name = name;
895  item->additive = additive;
896  item->helpText = helptext;
897 
898  verboseMap.insert(name, item);
899 }
900 
906 void loglevelAdd(int value, QString name, char shortname)
907 {
908  LoglevelDef *item = new LoglevelDef;
909 
910  item->value = value;
911  // LOG_CRIT -> crit
912  name.remove(0, 4);
913  name = name.toLower();
914  item->name = name;
915  item->shortname = shortname;
916 
917  loglevelMap.insert(value, item);
918 }
919 
921 void verboseInit(void)
922 {
923  QMutexLocker locker(&verboseMapMutex);
924  QMutexLocker locker2(&loglevelMapMutex);
925  verboseMap.clear();
926  loglevelMap.clear();
927 
928  // This looks funky, so I'll put some explanation here. The verbosedefs.h
929  // file gets included as part of the mythlogging.h include, and at that
930  // time, the normal (without _IMPLEMENT_VERBOSE defined) code case will
931  // define the VerboseMask enum. At this point, we force it to allow us
932  // to include the file again, but with _IMPLEMENT_VERBOSE set so that the
933  // single definition of the VB_* values can be shared to define also the
934  // contents of verboseMap, via repeated calls to verboseAdd()
935 
936 #undef VERBOSEDEFS_H_
937 #define _IMPLEMENT_VERBOSE
938 #include "verbosedefs.h"
939 
940  verboseInitialized = true;
941 }
942 
943 
946 void verboseHelp(void)
947 {
948  QString m_verbose = userDefaultValueStr.trimmed();
949  m_verbose.replace(QRegExp(" "), ",");
950  m_verbose.remove(QRegExp("^,"));
951 
952  cerr << "Verbose debug levels.\n"
953  "Accepts any combination (separated by comma) of:\n\n";
954 
955  for (VerboseMap::Iterator vit = verboseMap.begin();
956  vit != verboseMap.end(); ++vit )
957  {
958  VerboseDef *item = vit.value();
959  QString name = QString(" %1").arg(item->name, -15, ' ');
960  if (item->helpText.isEmpty())
961  continue;
962  cerr << name.toLocal8Bit().constData() << " - " <<
963  item->helpText.toLocal8Bit().constData() << endl;
964  }
965 
966  cerr << endl <<
967  "The default for this program appears to be: '-v " <<
968  m_verbose.toLocal8Bit().constData() << "'\n\n"
969  "Most options are additive except for 'none' and 'all'.\n"
970  "These two are semi-exclusive and take precedence over any\n"
971  "other options. However, you may use something like\n"
972  "'-v none,jobqueue' to receive only JobQueue related messages\n"
973  "and override the default verbosity level.\n\n"
974  "Additive options may also be subtracted from 'all' by\n"
975  "prefixing them with 'no', so you may use '-v all,nodatabase'\n"
976  "to view all but database debug messages.\n\n";
977 
978  cerr << "The 'global' loglevel is specified with --loglevel, but can be\n"
979  << "overridden on a component by component basis by appending "
980  << "':level'\n"
981  << "to the component.\n"
982  << " For example: -v gui:debug,channel:notice,record\n\n";
983 
984  cerr << "Some debug levels may not apply to this program.\n" << endl;
985 }
986 
990 int verboseArgParse(QString arg)
991 {
992  QString option;
993  int idx;
994 
995  if (!verboseInitialized)
996  verboseInit();
997 
998  QMutexLocker locker(&verboseMapMutex);
999 
1001  verboseString = QString(verboseDefaultStr);
1002 
1003  if (arg.startsWith('-'))
1004  {
1005  cerr << "Invalid or missing argument to -v/--verbose option\n";
1007  }
1008 
1009  QStringList verboseOpts = arg.split(QRegExp("[^\\w:]+",
1010  Qt::CaseInsensitive,
1011  QRegExp::RegExp2));
1012  for (QStringList::Iterator it = verboseOpts.begin();
1013  it != verboseOpts.end(); ++it )
1014  {
1015  option = (*it).toLower();
1016  bool reverseOption = false;
1017  QString optionLevel;
1018 
1019  if (option != "none" && option.startsWith("no"))
1020  {
1021  reverseOption = true;
1022  option = option.right(option.length() - 2);
1023  }
1024 
1025  if (option == "help")
1026  {
1027  verboseHelp();
1029  }
1030  else if (option == "important")
1031  {
1032  cerr << "The \"important\" log mask is no longer valid.\n";
1033  }
1034  else if (option == "extra")
1035  {
1036  cerr << "The \"extra\" log mask is no longer valid. Please try "
1037  "--loglevel debug instead.\n";
1038  }
1039  else if (option == "default")
1040  {
1042  {
1045  }
1046  else
1047  {
1049  verboseString = QString(verboseDefaultStr);
1050  }
1051  }
1052  else
1053  {
1054  if ((idx = option.indexOf(':')) != -1)
1055  {
1056  optionLevel = option.mid(idx + 1);
1057  option = option.left(idx);
1058  }
1059 
1060  VerboseDef *item = verboseMap.value(option);
1061 
1062  if (item)
1063  {
1064  if (reverseOption)
1065  {
1066  verboseMask &= ~(item->mask);
1067  verboseString = verboseString.remove(' ' + item->name);
1068  verboseString += " no" + item->name;
1069  }
1070  else
1071  {
1072  if (item->additive)
1073  {
1074  if (!(verboseMask & item->mask))
1075  {
1076  verboseMask |= item->mask;
1077  verboseString += ' ' + item->name;
1078  }
1079  }
1080  else
1081  {
1082  verboseMask = item->mask;
1083  verboseString = item->name;
1084  }
1085 
1086  if (!optionLevel.isEmpty())
1087  {
1088  LogLevel_t level = logLevelGet(optionLevel);
1089  if (level != LOG_UNKNOWN)
1090  componentLogLevel[item->mask] = level;
1091  }
1092  }
1093  }
1094  else
1095  {
1096  cerr << "Unknown argument for -v/--verbose: " <<
1097  option.toLocal8Bit().constData() << endl;;
1099  }
1100  }
1101  }
1102 
1103  if (!haveUserDefaultValues)
1104  {
1105  haveUserDefaultValues = true;
1108  }
1109 
1110  return GENERIC_EXIT_OK;
1111 }
1112 
1117 QString logStrerror(int errnum)
1118 {
1119  return QString("%1 (%2)").arg(strerror(errnum)).arg(errnum);
1120 }
1121 
1122 
1123 /*
1124  * vim:ts=4:sw=4:ai:et:si:sts=4
1125  */
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:216
void loglevelAdd(int value, QString name, char shortname)
Add a log level to the logLevelMap.
Definition: logging.cpp:906
QString logfile
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:295
QString logLevelGetName(LogLevel_t level)
Map a log level enumerated value back to the name.
Definition: logging.cpp:863
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:168
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:592
const char * verboseDefaultStr
Definition: logging.cpp:105
QString m_tablename
Cached table name for db logging.
Definition: logging.h:196
char * m_file
Definition: logging.h:151
QString name
Definition: verbosedefs.h:213
char * getThreadName(void)
Get the name of the thread that produced the LoggingItem.
Definition: logging.cpp:193
#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:197
#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:192
bool verboseInitialized
Definition: logging.cpp:97
int syslogGetFacility(QString facility)
Map a syslog facility name back to the enumerated value.
Definition: logging.cpp:811
General purpose reference counter.
bool flush(int timeoutMS=200000)
Wait for the queue to be flushed (up to a timeout)
Definition: logging.cpp:524
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:312
bool m_progress
show only LOG_ERR and more important (console only)
Definition: logging.h:193
QString logPropagateArgs
Definition: logging.cpp:89
#define SYSTEMD_JOURNAL_FACILITY
Definition: logging.h:26
static __inline struct tm * localtime_r(const time_t *timep, struct tm *result)
Definition: compat.h:279
The logging items that are generated by LOG() and are sent to the console.
Definition: logging.h:61
void fillItem(LoggingItem *item)
Definition: logging.cpp:538
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:419
void loggingRegisterThread(const QString &name)
Register the current thread with the given name.
Definition: logging.cpp:774
QString userDefaultValueStr
Definition: logging.cpp:112
LogLevel_t logLevelGet(QString level)
Map a log level name back to the enumerated value.
Definition: logging.cpp:839
ComponentLogLevelMap componentLogLevel
Definition: logging.cpp:109
void logStart(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:726
VerboseDef
Definition: verbosedefs.h:208
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:187
VerboseMap verboseMap
Definition: logging.cpp:98
pid_t m_pid
Cached pid value.
Definition: logging.h:198
qlonglong epoch
Definition: logging.h:73
void handleItem(LoggingItem *item)
Handles each LoggingItem.
Definition: logging.cpp:347
void verboseAdd(uint64_t mask, QString name, bool additive, QString helptext)
Add a verbose level to the verboseMap.
Definition: logging.cpp:886
static bool debugRegistration
Definition: logging.cpp:78
void verboseHelp(void)
Outputs the Verbose levels and their descriptions (for –verbose help)
Definition: logging.cpp:946
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:512
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:340
bool logPropagateQuiet(void)
Check if we are propagating a "--quiet".
Definition: logging.cpp:709
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:275
LoggingType
Definition: logging.h:41
#define PREFIX64
Definition: compat.h:386
QString logStrerror(int errnum)
Verbose helper function for ENO macro.
Definition: logging.cpp:1117
void loggingDeregisterThread(void)
Deregister the current thread's name.
Definition: logging.cpp:793
#define MAX_STRING_LENGTH
Definition: logging.cpp:93
const char * name
Definition: ParseText.cpp:339
QByteArray toByteArray(void)
Definition: logging.cpp:181
static QRegExp logRegExp
Definition: logging.cpp:68
#define LOGLINE_MAX
Definition: logging.h:29
QString m_appname
Cached application name.
Definition: logging.h:195
int verboseArgParse(QString arg)
Parse the –verbose commandline argument and set the verbose level.
Definition: logging.cpp:990
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:184
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:209
#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:759
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:203
bool haveUserDefaultValues
Definition: logging.cpp:113
void setThreadTid(void)
Set the thread ID of the thread that produced the LoggingItem.
Definition: logging.cpp:222
void setPid(const int val)
Definition: logging.h:112
QThread * qthread(void)
Returns the thread, this will always return the same pointer no matter how often you restart the thre...
Definition: mthread.cpp:245
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
LoggerThread(QString filename, bool progress, bool quiet, QString table, int facility)
LoggerThread constructor.
Definition: logging.cpp:250
int m_line
Definition: logging.h:146
#define GENERIC_EXIT_INVALID_CMDLINE
Command line parse error.
Definition: exitcodes.h:15
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:194
QMap< int, LoglevelDef * > LoglevelMap
Definition: verbosedefs.h:216
void verboseInit(void)
Initialize the logging levels and verbose levels.
Definition: logging.cpp:921
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:653
void run(void) override
Run the logging thread.
Definition: logging.cpp:290
QStringList logPropagateArgList
Definition: logging.cpp:90
Definition: logging.h:45
~LoggerThread()
LoggerThread destructor. Triggers the deletion of all loggers.
Definition: logging.cpp:276
static LoggingItem * create(const char *, const char *, int, LogLevel_t, LoggingType)
Create a new LoggingItem.
Definition: logging.cpp:559
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:190