MythTV  0.28pre
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 #define SYSLOG_NAMES
31 #ifndef _WIN32
32 #include <syslog.h>
33 #endif
34 #include <stdarg.h>
35 #include <string.h>
36 #include <sys/stat.h>
37 #include <sys/types.h>
38 #include <stdlib.h>
39 #include <fcntl.h>
40 #include <stdio.h>
41 #include <unistd.h>
42 #if HAVE_GETTIMEOFDAY
43 #include <sys/time.h>
44 #endif
45 #include <signal.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 #ifndef NOLOGSERVER
60 // nzmqt
61 #include "nzmqt.hpp"
62 #endif
63 
64 // QJson
65 #include "qjsonwrapper/Json.h"
66 
67 #ifdef Q_OS_ANDROID
68 #include <android/log.h>
69 #endif
70 
71 static QMutex logQueueMutex;
72 static QQueue<LoggingItem *> logQueue;
73 static QRegExp logRegExp = QRegExp("[%]{1,2}");
74 
75 static LoggerThread *logThread = NULL;
76 static QMutex logThreadMutex;
77 static QHash<uint64_t, char *> logThreadHash;
78 
79 static QMutex logThreadTidMutex;
80 static QHash<uint64_t, int64_t> logThreadTidHash;
81 
82 static bool logThreadFinished = false;
83 static bool debugRegistration = false;
84 
85 typedef struct {
86  bool propagate;
87  int quiet;
88  int facility;
89  bool dblog;
90  QString path;
91  bool noserver;
93 
96 QStringList logPropagateArgList;
97 
98 #define TIMESTAMP_MAX 30
99 #define MAX_STRING_LENGTH (LOGLINE_MAX+120)
100 
101 LogLevel_t logLevel = (LogLevel_t)LOG_INFO;
102 
103 bool verboseInitialized = false;
106 
109 
110 const uint64_t verboseDefaultInt = VB_GENERAL;
111 const char *verboseDefaultStr = " general";
112 
114 QString verboseString = QString(verboseDefaultStr);
116 
120 
121 void verboseAdd(uint64_t mask, QString name, bool additive, QString helptext);
122 void loglevelAdd(int value, QString name, char shortname);
123 void verboseInit(void);
124 void verboseHelp(void);
125 
126 void loggingGetTimeStamp(qlonglong *epoch, uint *usec)
127 {
128 #if HAVE_GETTIMEOFDAY
129  struct timeval tv;
130  gettimeofday(&tv, NULL);
131  *epoch = tv.tv_sec;
132  if (usec)
133  *usec = tv.tv_usec;
134 #else
135  /* Stupid system has no gettimeofday, use less precise QDateTime */
136  QDateTime date = MythDate::current();
137  *epoch = date.toTime_t();
138  if (usec)
139  {
140  QTime time = date.time();
141  *usec = time.msec() * 1000;
142  }
143 #endif
144 }
145 
147  ReferenceCounter("LoggingItem", false),
148  m_pid(-1), m_tid(-1), m_threadId(-1), m_usec(0), m_line(0),
149  m_type(kMessage), m_level((LogLevel_t)LOG_INFO), m_facility(0), m_epoch(0),
150  m_file(NULL), m_function(NULL), m_threadName(NULL), m_appName(NULL),
151  m_table(NULL), m_logFile(NULL)
152 {
153  m_message[0]='\0';
154  m_message[LOGLINE_MAX]='\0';
155 }
156 
157 LoggingItem::LoggingItem(const char *_file, const char *_function,
158  int _line, LogLevel_t _level, LoggingType _type) :
159  ReferenceCounter("LoggingItem", false), m_pid(-1),
160  m_threadId((uint64_t)(QThread::currentThreadId())),
161  m_line(_line), m_type(_type), m_level(_level), m_facility(0),
162  m_file(strdup(_file)), m_function(strdup(_function)),
163  m_threadName(NULL), m_appName(NULL), m_table(NULL), m_logFile(NULL)
164 {
166 
167  m_message[0]='\0';
168  m_message[LOGLINE_MAX]='\0';
169  setThreadTid();
170 }
171 
173 {
174  free(m_file);
175 
176  free(m_function);
177 
178  free(m_threadName);
179 
180  free(m_appName);
181 
182  free(m_table);
183 
184  free(m_logFile);
185 }
186 
187 QByteArray LoggingItem::toByteArray(void)
188 {
189  QVariantMap variant = QJsonWrapper::qobject2qvariant(this);
190  QByteArray json = QJsonWrapper::toJson(variant);
191 
192  //cout << json.constData() << endl;
193 
194  return json;
195 }
196 
200 {
201  static const char *unknown = "thread_unknown";
202 
203  if( m_threadName )
204  return m_threadName;
205 
206  QMutexLocker locker(&logThreadMutex);
207  return logThreadHash.value(m_threadId, (char *)unknown);
208 }
209 
216 {
217  QMutexLocker locker(&logThreadTidMutex);
218  m_tid = logThreadTidHash.value(m_threadId, 0);
219  return m_tid;
220 }
221 
229 {
230  QMutexLocker locker(&logThreadTidMutex);
231 
232  m_tid = logThreadTidHash.value(m_threadId, -1);
233  if (m_tid == -1)
234  {
235  m_tid = 0;
236 
237 #if defined(Q_OS_ANDROID)
238  m_tid = (int64_t)gettid();
239 #elif defined(linux)
240  m_tid = (int64_t)syscall(SYS_gettid);
241 #elif defined(__FreeBSD__)
242  long lwpid;
243  int dummy = thr_self( &lwpid );
244  (void)dummy;
245  m_tid = (int64_t)lwpid;
246 #elif CONFIG_DARWIN
247  m_tid = (int64_t)mach_thread_self();
248 #endif
250  }
251 }
252 
257  QString table, int facility, bool noserver) :
258  MThread("Logger"),
259  m_waitNotEmpty(new QWaitCondition()),
260  m_waitEmpty(new QWaitCondition()),
261  m_aborted(false), m_initialWaiting(true),
262  m_filename(filename), m_progress(progress),
263  m_quiet(quiet), m_appname(QCoreApplication::applicationName()),
264  m_tablename(table), m_facility(facility), m_pid(getpid()), m_epoch(0),
265  m_zmqContext(NULL), m_zmqSocket(NULL), m_initialTimer(NULL),
266  m_heartbeatTimer(NULL), m_noserver(noserver)
267 {
268  char *debug = getenv("VERBOSE_THREADS");
269  if (debug != NULL)
270  {
271  LOG(VB_GENERAL, LOG_NOTICE,
272  "Logging thread registration/deregistration enabled!");
273  debugRegistration = true;
274  }
275  m_locallogs = (m_appname == MYTH_APPNAME_MYTHLOGSERVER);
276 
277 #ifdef NOLOGSERVER
278  if (!m_noserver && !logServerStart())
279  {
280  LOG(VB_GENERAL, LOG_ERR,
281  "Failed to start LogServer thread");
282  }
283 #endif
284  moveToThread(qthread());
285 }
286 
289 {
290  stop();
291  wait();
292 
293 #ifdef NOLOGSERVER
294  if (!m_noserver)
295  {
296  logServerStop();
297  }
298 #endif
299  delete m_waitNotEmpty;
300  delete m_waitEmpty;
301 }
302 
308 {
309  RunProlog();
310 
311  logThreadFinished = false;
312 
313  LOG(VB_GENERAL, LOG_INFO, "Added logging to the console");
314 
315  bool dieNow = false;
316 
317  if (!m_noserver)
318  {
319 #ifndef NOLOGSERVER
320  try
321  {
322  if (m_locallogs)
323  {
324  logServerWait();
326  }
327  else
328  {
329  m_zmqContext = nzmqt::createDefaultContext(NULL);
330  m_zmqContext->start();
331  }
332 
333  if (!m_zmqContext)
334  {
335  m_aborted = true;
336  dieNow = true;
337  }
338  else
339  {
340  qRegisterMetaType<QList<QByteArray> >("QList<QByteArray>");
341 
342  m_zmqSocket =
343  m_zmqContext->createSocket(nzmqt::ZMQSocket::TYP_DEALER, this);
344  connect(m_zmqSocket,
345  SIGNAL(messageReceived(const QList<QByteArray>&)),
346  SLOT(messageReceived(const QList<QByteArray>&)),
347  Qt::QueuedConnection);
348 
349  if (m_locallogs)
350  m_zmqSocket->connectTo("inproc://mylogs");
351  else
352  m_zmqSocket->connectTo("tcp://127.0.0.1:35327");
353  }
354  }
355  catch (nzmqt::ZMQException &e)
356  {
357  cerr << "Exception during logging socket setup: " << e.what() << endl;
358  m_aborted = true;
359  dieNow = true;
360  }
361 
362  if (!m_aborted)
363  {
364  if (!m_locallogs)
365  {
366  m_initialWaiting = true;
367  pingLogServer();
368 
369  // wait up to 150ms for mythlogserver to respond
371  SLOT(initialTimeout()));
372  m_initialTimer->start(150);
373  }
374  else
375  LOG(VB_GENERAL, LOG_INFO, "Added logging to mythlogserver locally");
376 
378 
380  m_heartbeatTimer->start(1000);
381  }
382  #else
383  logServerWait();
384  #endif
385  }
386 
387  QMutexLocker qLock(&logQueueMutex);
388 
389  while (!m_aborted || !logQueue.isEmpty())
390  {
391  qLock.unlock();
392  qApp->processEvents(QEventLoop::AllEvents, 10);
393  qApp->sendPostedEvents(NULL, QEvent::DeferredDelete);
394 
395  qLock.relock();
396  if (logQueue.isEmpty())
397  {
398  m_waitEmpty->wakeAll();
399  m_waitNotEmpty->wait(qLock.mutex(), 100);
400  continue;
401  }
402 
403  LoggingItem *item = logQueue.dequeue();
404  qLock.unlock();
405 
406  fillItem(item);
407  handleItem(item);
408  logConsole(item);
409  item->DecrRef();
410 
411  qLock.relock();
412  }
413 
414  qLock.unlock();
415 
416  // This must be before the timer stop below or we deadlock when the timer
417  // thread tries to deregister, and we wait for it.
418  logThreadFinished = true;
419 
420 #ifndef NOLOGSERVER
421  if (m_initialTimer)
422  {
423  m_initialTimer->stop();
424  delete m_initialTimer;
425  m_initialTimer = NULL;
426  }
427 
428  if (m_heartbeatTimer)
429  {
431  delete m_heartbeatTimer;
432  m_heartbeatTimer = NULL;
433  }
434 
435  if (m_zmqSocket)
436  {
437  m_zmqSocket->setLinger(0);
438  m_zmqSocket->close();
439  }
440 
441  if (!m_locallogs)
442  delete m_zmqContext;
443 #endif
444 
445  RunEpilog();
446 
447  if (dieNow)
448  {
449  qApp->processEvents();
450  }
451 }
452 
456 {
457 #ifndef NOLOGSERVER
458  if (m_initialTimer)
459  {
460  m_initialTimer->stop();
461  delete m_initialTimer;
462  m_initialTimer = NULL;
463  }
464 
465  if (m_initialWaiting)
466  {
467  // Got no response from mythlogserver, let's assume it's dead and
468  // start it up
469  launchLogServer();
470  }
471 
472  LOG(VB_GENERAL, LOG_INFO, "Added logging to mythlogserver at TCP:35327");
473 #endif
474 }
475 
479 {
480 #ifndef NOLOGSERVER
481  static bool launched = false;
482  qlonglong epoch;
483 
484  loggingGetTimeStamp(&epoch, NULL);
485  qlonglong age = (epoch - m_epoch) % 30;
486 
487  if (age == 5)
488  {
489  if (!launched)
490  {
491  launchLogServer();
492  launched = true;
493  }
494  }
495  else
496  {
497  launched = false;
498  }
499 #endif
500 }
501 
504 {
505 #ifndef NOLOGSERVER
506  // cout << "pong" << endl;
507  m_zmqSocket->sendMessage(QByteArray(""));
508 #endif
509 }
510 
513 {
514 #ifndef NOLOGSERVER
515  m_initialWaiting = false;
516  if (!m_locallogs)
517  {
518  LOG(VB_GENERAL, LOG_INFO, "Starting mythlogserver");
519 
522  kMSRunShell);
523  QStringList args;
524  args << "--daemon" << logPropagateArgs;
525 
526  MythSystemLegacy ms(GetAppBinDir() + "mythlogserver", args, mask);
527  ms.Run();
528  ms.Wait(0);
529  }
530 #endif
531 }
532 
542 void LoggerThread::messageReceived(const QList<QByteArray> &msg)
543 {
544  m_initialWaiting = false;
545  // cout << "ping" << endl;
547  pingLogServer();
548 }
549 
550 
558 {
559  if (item->m_type & kRegistering)
560  {
561  item->m_tid = item->getThreadTid();
562 
563  QMutexLocker locker(&logThreadMutex);
564  if (logThreadHash.contains(item->m_threadId))
565  {
566  char *threadName = logThreadHash.take(item->m_threadId);
567  free(threadName);
568  }
569  logThreadHash[item->m_threadId] = strdup(item->m_threadName);
570 
571  if (debugRegistration)
572  {
573  snprintf(item->m_message, LOGLINE_MAX,
574  "Thread 0x%" PREFIX64 "X (%" PREFIX64
575  "d) registered as \'%s\'",
576  (long long unsigned int)item->m_threadId,
577  (long long int)item->m_tid,
578  logThreadHash[item->m_threadId]);
579  }
580  }
581  else if (item->m_type & kDeregistering)
582  {
583  int64_t tid = 0;
584 
585  {
586  QMutexLocker locker(&logThreadTidMutex);
587  if( logThreadTidHash.contains(item->m_threadId) )
588  {
589  tid = logThreadTidHash[item->m_threadId];
590  logThreadTidHash.remove(item->m_threadId);
591  }
592  }
593 
594  QMutexLocker locker(&logThreadMutex);
595  if (logThreadHash.contains(item->m_threadId))
596  {
597  if (debugRegistration)
598  {
599  snprintf(item->m_message, LOGLINE_MAX,
600  "Thread 0x%" PREFIX64 "X (%" PREFIX64
601  "d) deregistered as \'%s\'",
602  (long long unsigned int)item->m_threadId,
603  (long long int)tid,
604  logThreadHash[item->m_threadId]);
605  }
606  char *threadName = logThreadHash.take(item->m_threadId);
607  free(threadName);
608  }
609  }
610 
611  if (m_noserver)
612  {
613  return;
614  }
615 
616  if (item->m_message[0] != '\0')
617  {
618 #ifndef NOLOGSERVER
619  // Send it to mythlogserver
621  m_zmqSocket->sendMessage(item->toByteArray());
622 #else
623  if (logServerThread)
624  {
625  QList<QByteArray> list;
626  list.append(QByteArray());
627  list.append(item->toByteArray());
629  }
630 #endif
631  }
632 }
633 
637 {
638  char line[MAX_STRING_LENGTH];
639  char usPart[9];
640  char timestamp[TIMESTAMP_MAX];
641 
642  if (m_quiet || (m_progress && item->m_level > LOG_ERR))
643  return false;
644 
645  if (!(item->m_type & kMessage))
646  return false;
647 
648  item->IncrRef();
649 
650  if (item->m_type & kStandardIO)
651  snprintf( line, MAX_STRING_LENGTH, "%s", item->m_message );
652  else
653  {
654  time_t epoch = item->epoch();
655  struct tm tm;
656  localtime_r(&epoch, &tm);
657 
658  strftime( timestamp, TIMESTAMP_MAX-8, "%Y-%m-%d %H:%M:%S",
659  (const struct tm *)&tm );
660  snprintf( usPart, 9, ".%06d", (int)(item->m_usec) );
661  strcat( timestamp, usPart );
662  char shortname;
663 
664  {
665  QMutexLocker locker(&loglevelMapMutex);
666  LoglevelDef *lev = loglevelMap.value(item->m_level, NULL);
667  if (!lev)
668  shortname = '-';
669  else
670  shortname = lev->shortname;
671  }
672 
673  snprintf( line, MAX_STRING_LENGTH, "%s %c %s\n", timestamp,
674  shortname, item->m_message );
675  }
676 
677 #ifdef Q_OS_ANDROID
678  __android_log_print(ANDROID_LOG_INFO, "mfe", line);
679 #else
680  int result = write( 1, line, strlen(line) );
681  (void)result;
682 #endif
683 
684  item->DecrRef();
685 
686  return true;
687 }
688 
689 
693 {
694  logQueueMutex.lock();
695  flush(1000);
696  m_aborted = true;
697  logQueueMutex.unlock();
698  m_waitNotEmpty->wakeAll();
699 }
700 
704 bool LoggerThread::flush(int timeoutMS)
705 {
706  QTime t;
707  t.start();
708  while (!m_aborted && !logQueue.isEmpty() && t.elapsed() < timeoutMS)
709  {
710  m_waitNotEmpty->wakeAll();
711  int left = timeoutMS - t.elapsed();
712  if (left > 0)
713  m_waitEmpty->wait(&logQueueMutex, left);
714  }
715  return logQueue.isEmpty();
716 }
717 
719 {
720  if (!item)
721  return;
722 
723  item->setPid(m_pid);
724  item->setThreadName(item->getThreadName());
725  item->setAppName(m_appname);
726  item->setTable(m_tablename);
727  item->setLogFile(m_filename);
728  item->setFacility(m_facility);
729 }
730 
731 
739 LoggingItem *LoggingItem::create(const char *_file,
740  const char *_function,
741  int _line, LogLevel_t _level,
742  LoggingType _type)
743 {
744  LoggingItem *item = new LoggingItem(_file, _function, _line, _level, _type);
745 
746  return item;
747 }
748 
750 {
751  // Deserialize buffer
752  QVariant variant = QJsonWrapper::parseJson(buf);
753 
754  LoggingItem *item = new LoggingItem;
755  QJsonWrapper::qvariant2qobject(variant.toMap(), item);
756 
757  return item;
758 }
759 
760 
772 void LogPrintLine( uint64_t mask, LogLevel_t level, const char *file, int line,
773  const char *function, int fromQString,
774  const char *format, ... )
775 {
776  va_list arguments;
777 
778  int type = kMessage;
779  type |= (mask & VB_FLUSH) ? kFlush : 0;
780  type |= (mask & VB_STDIO) ? kStandardIO : 0;
781  LoggingItem *item = LoggingItem::create(file, function, line, level,
782  (LoggingType)type);
783  if (!item)
784  return;
785 
786  char *formatcopy = NULL;
787  if( fromQString && strchr(format, '%') )
788  {
789  QString string(format);
790  format = strdup(string.replace(logRegExp, "%%").toLocal8Bit()
791  .constData());
792  formatcopy = (char *)format;
793  }
794 
795  va_start(arguments, format);
796  vsnprintf(item->m_message, LOGLINE_MAX, format, arguments);
797  va_end(arguments);
798 
799  if (formatcopy)
800  free(formatcopy);
801 
802  QMutexLocker qLock(&logQueueMutex);
803 
804 #if defined( _MSC_VER ) && defined( _DEBUG )
805  OutputDebugStringA( item->m_message );
806  OutputDebugStringA( "\n" );
807 #endif
808 
809  logQueue.enqueue(item);
810 
811  if (logThread && logThreadFinished && !logThread->isRunning())
812  {
813  while (!logQueue.isEmpty())
814  {
815  item = logQueue.dequeue();
816  qLock.unlock();
817  logThread->handleItem(item);
818  logThread->logConsole(item);
819  item->DecrRef();
820  qLock.relock();
821  }
822  }
823  else if (logThread && !logThreadFinished && (type & kFlush))
824  {
825  logThread->flush();
826  }
827 }
828 
829 
834 {
835  logPropagateArgList.clear();
836 
837  QString mask = verboseString.trimmed();
838  mask.replace(QRegExp(" "), ",");
839  mask.remove(QRegExp("^,"));
840  logPropagateArgs = " --verbose " + mask;
841  logPropagateArgList << "--verbose" << mask;
842 
843  if (logPropagateOpts.propagate)
844  {
845  logPropagateArgs += " --logpath " + logPropagateOpts.path;
846  logPropagateArgList << "--logpath" << logPropagateOpts.path;
847  }
848 
849  QString name = logLevelGetName(logLevel);
850  logPropagateArgs += " --loglevel " + name;
851  logPropagateArgList << "--loglevel" << name;
852 
853  for (int i = 0; i < logPropagateOpts.quiet; i++)
854  {
855  logPropagateArgs += " --quiet";
856  logPropagateArgList << "--quiet";
857  }
858 
859  if (logPropagateOpts.dblog)
860  {
861  logPropagateArgs += " --enable-dblog";
862  logPropagateArgList << "--enable-dblog";
863  }
864 
865 #if !defined(_WIN32) && !defined(Q_OS_ANDROID)
866  if (logPropagateOpts.facility >= 0)
867  {
868  const CODE *syslogname;
869 
870  for (syslogname = &facilitynames[0];
871  (syslogname->c_name &&
872  syslogname->c_val != logPropagateOpts.facility); syslogname++);
873 
874  logPropagateArgs += QString(" --syslog %1").arg(syslogname->c_name);
875  logPropagateArgList << "--syslog" << syslogname->c_name;
876  }
877 #if CONFIG_SYSTEMD_JOURNAL
878  else if (logPropagateOpts.facility == SYSTEMD_JOURNAL_FACILITY)
879  {
880  logPropagateArgs += " --systemd-journal";
881  logPropagateArgList << "--systemd-journal";
882  }
883 #endif
884 #endif
885 
886  if (logPropagateOpts.noserver)
887  {
888  logPropagateArgs += " --disable-mythlogserver";
889  logPropagateArgList << "--disable-mythlogserver";
890  }
891 }
892 
896 {
897  return logPropagateOpts.quiet;
898 }
899 
903 {
904  return logPropagateOpts.noserver;
905 }
906 
919 void logStart(QString logfile, int progress, int quiet, int facility,
920  LogLevel_t level, bool dblog, bool propagate, bool noserver)
921 {
922  if (logThread && logThread->isRunning())
923  return;
924 
925  logLevel = level;
926  LOG(VB_GENERAL, LOG_NOTICE, QString("Setting Log Level to LOG_%1")
927  .arg(logLevelGetName(logLevel).toUpper()));
928 
929  logPropagateOpts.propagate = propagate;
930  logPropagateOpts.quiet = quiet;
931  logPropagateOpts.facility = facility;
932  logPropagateOpts.dblog = dblog;
933  logPropagateOpts.noserver = noserver;
934 
935  if (propagate)
936  {
937  QFileInfo finfo(logfile);
938  QString path = finfo.path();
939  logPropagateOpts.path = path;
940  }
941 
943 
944  QString table = dblog ? QString("logging") : QString("");
945 
946  if (!logThread)
947  logThread = new LoggerThread(logfile, progress, quiet, table, facility, noserver);
948 
949  logThread->start();
950 }
951 
953 void logStop(void)
954 {
955  if (logThread)
956  {
957  logThread->stop();
958  logThread->wait();
959  delete logThread;
960  logThread = NULL;
961  }
962 }
963 
968 void loggingRegisterThread(const QString &name)
969 {
970  if (logThreadFinished)
971  return;
972 
973  QMutexLocker qLock(&logQueueMutex);
974 
975  LoggingItem *item = LoggingItem::create(__FILE__, __FUNCTION__,
976  __LINE__, (LogLevel_t)LOG_DEBUG,
977  kRegistering);
978  if (item)
979  {
980  item->setThreadName((char *)name.toLocal8Bit().constData());
981  logQueue.enqueue(item);
982  }
983 }
984 
988 {
989  if (logThreadFinished)
990  return;
991 
992  QMutexLocker qLock(&logQueueMutex);
993 
994  LoggingItem *item = LoggingItem::create(__FILE__, __FUNCTION__, __LINE__,
995  (LogLevel_t)LOG_DEBUG,
997  if (item)
998  logQueue.enqueue(item);
999 }
1000 
1001 
1005 int syslogGetFacility(QString facility)
1006 {
1007 #ifdef _WIN32
1008  LOG(VB_GENERAL, LOG_NOTICE,
1009  "Windows does not support syslog, disabling" );
1010  return( -2 );
1011 #elif defined(Q_OS_ANDROID)
1012  LOG(VB_GENERAL, LOG_NOTICE,
1013  "Android does not support syslog, disabling" );
1014  return( -2 );
1015 #elif defined(Q_OS_ANDROID)
1016  LOG(VB_GENERAL, LOG_NOTICE,
1017  "Android does not support syslog, disabling" );
1018  return( -2 );
1019 #else
1020  const CODE *name;
1021  int i;
1022  QByteArray ba = facility.toLocal8Bit();
1023  char *string = (char *)ba.constData();
1024 
1025  for (i = 0, name = &facilitynames[0];
1026  name->c_name && strcmp(name->c_name, string); i++, name++);
1027 
1028  return( name->c_val );
1029 #endif
1030 }
1031 
1035 LogLevel_t logLevelGet(QString level)
1036 {
1037  QMutexLocker locker(&loglevelMapMutex);
1038  if (!verboseInitialized)
1039  {
1040  locker.unlock();
1041  verboseInit();
1042  locker.relock();
1043  }
1044 
1045  for (LoglevelMap::iterator it = loglevelMap.begin();
1046  it != loglevelMap.end(); ++it)
1047  {
1048  LoglevelDef *item = (*it);
1049  if ( item->name == level.toLower() )
1050  return (LogLevel_t)item->value;
1051  }
1052 
1053  return LOG_UNKNOWN;
1054 }
1055 
1059 QString logLevelGetName(LogLevel_t level)
1060 {
1061  QMutexLocker locker(&loglevelMapMutex);
1062  if (!verboseInitialized)
1063  {
1064  locker.unlock();
1065  verboseInit();
1066  locker.relock();
1067  }
1068  LoglevelMap::iterator it = loglevelMap.find((int)level);
1069 
1070  if ( it == loglevelMap.end() )
1071  return QString("unknown");
1072 
1073  return (*it)->name;
1074 }
1075 
1082 void verboseAdd(uint64_t mask, QString name, bool additive, QString helptext)
1083 {
1084  VerboseDef *item = new VerboseDef;
1085 
1086  item->mask = mask;
1087  name.detach();
1088  // VB_GENERAL -> general
1089  name.remove(0, 3);
1090  name = name.toLower();
1091  item->name = name;
1092  item->additive = additive;
1093  helptext.detach();
1094  item->helpText = helptext;
1095 
1096  verboseMap.insert(name, item);
1097 }
1098 
1104 void loglevelAdd(int value, QString name, char shortname)
1105 {
1106  LoglevelDef *item = new LoglevelDef;
1107 
1108  item->value = value;
1109  name.detach();
1110  // LOG_CRIT -> crit
1111  name.remove(0, 4);
1112  name = name.toLower();
1113  item->name = name;
1114  item->shortname = shortname;
1115 
1116  loglevelMap.insert(value, item);
1117 }
1118 
1120 void verboseInit(void)
1121 {
1122  QMutexLocker locker(&verboseMapMutex);
1123  QMutexLocker locker2(&loglevelMapMutex);
1124  verboseMap.clear();
1125  loglevelMap.clear();
1126 
1127  // This looks funky, so I'll put some explanation here. The verbosedefs.h
1128  // file gets included as part of the mythlogging.h include, and at that
1129  // time, the normal (without _IMPLEMENT_VERBOSE defined) code case will
1130  // define the VerboseMask enum. At this point, we force it to allow us
1131  // to include the file again, but with _IMPLEMENT_VERBOSE set so that the
1132  // single definition of the VB_* values can be shared to define also the
1133  // contents of verboseMap, via repeated calls to verboseAdd()
1134 
1135 #undef VERBOSEDEFS_H_
1136 #define _IMPLEMENT_VERBOSE
1137 #include "verbosedefs.h"
1138 
1139  verboseInitialized = true;
1140 }
1141 
1142 
1145 void verboseHelp(void)
1146 {
1147  QString m_verbose = userDefaultValueStr.trimmed();
1148  m_verbose.replace(QRegExp(" "), ",");
1149  m_verbose.remove(QRegExp("^,"));
1150 
1151  cerr << "Verbose debug levels.\n"
1152  "Accepts any combination (separated by comma) of:\n\n";
1153 
1154  for (VerboseMap::Iterator vit = verboseMap.begin();
1155  vit != verboseMap.end(); ++vit )
1156  {
1157  VerboseDef *item = vit.value();
1158  QString name = QString(" %1").arg(item->name, -15, ' ');
1159  if (item->helpText.isEmpty())
1160  continue;
1161  cerr << name.toLocal8Bit().constData() << " - " <<
1162  item->helpText.toLocal8Bit().constData() << endl;
1163  }
1164 
1165  cerr << endl <<
1166  "The default for this program appears to be: '-v " <<
1167  m_verbose.toLocal8Bit().constData() << "'\n\n"
1168  "Most options are additive except for 'none' and 'all'.\n"
1169  "These two are semi-exclusive and take precedence over any\n"
1170  "other options. However, you may use something like\n"
1171  "'-v none,jobqueue' to receive only JobQueue related messages\n"
1172  "and override the default verbosity level.\n\n"
1173  "Additive options may also be subtracted from 'all' by\n"
1174  "prefixing them with 'no', so you may use '-v all,nodatabase'\n"
1175  "to view all but database debug messages.\n\n";
1176 
1177  cerr << "The 'global' loglevel is specified with --loglevel, but can be\n"
1178  << "overridden on a component by component basis by appending "
1179  << "':level'\n"
1180  << "to the component.\n"
1181  << " For example: -v gui:debug,channel:notice,record\n\n";
1182 
1183  cerr << "Some debug levels may not apply to this program.\n" << endl;
1184 }
1185 
1189 int verboseArgParse(QString arg)
1190 {
1191  QString option;
1192  int idx;
1193 
1194  if (!verboseInitialized)
1195  verboseInit();
1196 
1197  QMutexLocker locker(&verboseMapMutex);
1198 
1200  verboseString = QString(verboseDefaultStr);
1201 
1202  if (arg.startsWith('-'))
1203  {
1204  cerr << "Invalid or missing argument to -v/--verbose option\n";
1205  return GENERIC_EXIT_INVALID_CMDLINE;
1206  }
1207 
1208  QStringList verboseOpts = arg.split(QRegExp("[^\\w:]+",
1209  Qt::CaseInsensitive,
1210  QRegExp::RegExp2));
1211  for (QStringList::Iterator it = verboseOpts.begin();
1212  it != verboseOpts.end(); ++it )
1213  {
1214  option = (*it).toLower();
1215  bool reverseOption = false;
1216  QString optionLevel;
1217 
1218  if (option != "none" && option.startsWith("no"))
1219  {
1220  reverseOption = true;
1221  option = option.right(option.length() - 2);
1222  }
1223 
1224  if (option == "help")
1225  {
1226  verboseHelp();
1227  return GENERIC_EXIT_INVALID_CMDLINE;
1228  }
1229  else if (option == "important")
1230  {
1231  cerr << "The \"important\" log mask is no longer valid.\n";
1232  }
1233  else if (option == "extra")
1234  {
1235  cerr << "The \"extra\" log mask is no longer valid. Please try "
1236  "--loglevel debug instead.\n";
1237  }
1238  else if (option == "default")
1239  {
1241  {
1244  }
1245  else
1246  {
1248  verboseString = QString(verboseDefaultStr);
1249  }
1250  }
1251  else
1252  {
1253  if ((idx = option.indexOf(':')) != -1)
1254  {
1255  optionLevel = option.mid(idx + 1);
1256  option = option.left(idx);
1257  }
1258 
1259  VerboseDef *item = verboseMap.value(option);
1260 
1261  if (item)
1262  {
1263  if (reverseOption)
1264  {
1265  verboseMask &= ~(item->mask);
1266  verboseString = verboseString.remove(' ' + item->name);
1267  verboseString += " no" + item->name;
1268  }
1269  else
1270  {
1271  if (item->additive)
1272  {
1273  if (!(verboseMask & item->mask))
1274  {
1275  verboseMask |= item->mask;
1276  verboseString += ' ' + item->name;
1277  }
1278  }
1279  else
1280  {
1281  verboseMask = item->mask;
1282  verboseString = item->name;
1283  }
1284 
1285  if (!optionLevel.isEmpty())
1286  {
1287  LogLevel_t level = logLevelGet(optionLevel);
1288  if (level != LOG_UNKNOWN)
1289  componentLogLevel[item->mask] = level;
1290  }
1291  }
1292  }
1293  else
1294  {
1295  cerr << "Unknown argument for -v/--verbose: " <<
1296  option.toLocal8Bit().constData() << endl;;
1297  return GENERIC_EXIT_INVALID_CMDLINE;
1298  }
1299  }
1300  }
1301 
1302  if (!haveUserDefaultValues)
1303  {
1304  haveUserDefaultValues = true;
1307  }
1308 
1309  return GENERIC_EXIT_OK;
1310 }
1311 
1316 QString logStrerror(int errnum)
1317 {
1318  return QString("%1 (%2)").arg(strerror(errnum)).arg(errnum);
1319 }
1320 
1321 
1322 /*
1323  * vim:ts=4:sw=4:ai:et:si:sts=4
1324  */
void RunEpilog(void)
Cleans up a thread&#39;s resources, call this if you reimplement run().
Definition: mthread.cpp:216
nzmqt::ZMQContext * getZMQContext(void)
void loglevelAdd(int value, QString name, char shortname)
Add a log level to the logLevelMap.
Definition: logging.cpp:1104
QString logfile
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:291
QString logLevelGetName(LogLevel_t level)
Map a log level enumerated value back to the name.
Definition: logging.cpp:1059
def write(text, progress=True)
Definition: mythburn.py:279
void Run(time_t timeout=0)
Runs a command inside the /bin/sh shell. Returns immediately.
int m_pid
Definition: logging.h:150
avoid disabling UI drawing
Definition: mythsystem.h:35
char * m_function
Definition: logging.h:163
char m_message[LOGLINE_MAX+1]
Definition: logging.h:168
static QQueue< LoggingItem * > logQueue
Definition: logging.cpp:72
The logging thread that consumes the logging queue and dispatches each LoggingItem to mythlogserver v...
Definition: logging.h:179
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
void logStart(QString logfile, int progress, int quiet, int facility, LogLevel_t level, bool dblog, bool propagate, bool noserver)
Entry point to start logging for the application.
Definition: logging.cpp:919
void receivedMessage(const QList< QByteArray > &)
Handles messages received from logging clients.
virtual void start(int msec)
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:83
const char * verboseDefaultStr
Definition: logging.cpp:111
QString m_tablename
Cached table name for db logging.
Definition: logging.h:209
char * m_file
Definition: logging.h:162
QString name
Definition: verbosedefs.h:211
char * getThreadName(void)
Get the name of the thread that produced the LoggingItem.
Definition: logging.cpp:199
QMutex verboseMapMutex
Definition: logging.cpp:105
static QMutex logQueueMutex
Definition: logging.cpp:71
int m_facility
Cached syslog facility (or -1 to disable)
Definition: logging.h:210
void checkHeartBeat(void)
Handles heartbeat checking once a second.
Definition: logging.cpp:478
static QMutex logThreadMutex
Definition: logging.cpp:76
QMap< uint64_t, LogLevel_t > ComponentLogLevelMap
Definition: verbosedefs.h:215
QString m_filename
Filename of debug logfile.
Definition: logging.h:205
bool verboseInitialized
Definition: logging.cpp:103
bool logPropagateNoServer(void)
Check if we are propagating a "--disable-mythlogserver".
Definition: logging.cpp:902
int syslogGetFacility(QString facility)
Map a syslog facility name back to the enumerated value.
Definition: logging.cpp:1005
General purpose reference counter.
bool flush(int timeoutMS=200000)
Wait for the queue to be flushed (up to a timeout)
Definition: logging.cpp:704
int dummy
Definition: programinfo.cpp:46
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:308
void logServerStop(void)
Entry point for stopping logging for an application.
bool m_progress
show only LOG_ERR and more important (console only)
Definition: logging.h:206
QString logPropagateArgs
Definition: logging.cpp:95
void messageReceived(const QList< QByteArray > &)
Handles messages received back from mythlogserver via ZeroMQ.
Definition: logging.cpp:542
bool m_locallogs
Are we logging locally (i.e.
Definition: logging.h:212
static __inline struct tm * localtime_r(const time_t *timep, struct tm *result)
Definition: compat.h:275
void pingLogServer(void)
Send a ping to the log server.
Definition: logging.cpp:503
volatile bool m_initialWaiting
Waiting for the initial response from mythlogserver.
Definition: logging.h:203
The logging items that are generated by LOG() and are sent to the console and to mythlogserver via Ze...
Definition: logging.h:72
const char * filename
Definition: ioapi.h:135
void fillItem(LoggingItem *item)
Definition: logging.cpp:718
unsigned int uint
Definition: compat.h:136
void loggingGetTimeStamp(qlonglong *epoch, uint *usec)
Definition: logging.cpp:126
uint64_t verboseMask
Definition: logging.cpp:113
bool logConsole(LoggingItem *item)
Process a log message, writing to the console.
Definition: logging.cpp:636
void loggingRegisterThread(const QString &name)
Register the current thread with the given name.
Definition: logging.cpp:968
bool logServerStart(void)
Entry point to start logging for the application.
QString userDefaultValueStr
Definition: logging.cpp:118
LogLevel_t logLevelGet(QString level)
Map a log level name back to the enumerated value.
Definition: logging.cpp:1035
ComponentLogLevelMap componentLogLevel
Definition: logging.cpp:115
qlonglong m_epoch
Time last heard from the server (seconds)
Definition: logging.h:214
MythSignalingTimer * m_initialTimer
Timer for the initial startup.
Definition: logging.h:220
VerboseDef
Definition: verbosedefs.h:206
LoggingType m_type
Definition: logging.h:158
voidpf void * buf
Definition: ioapi.h:136
__u32 type
Definition: videodev2.h:1038
QWaitCondition * m_waitEmpty
Condition variable for waiting for the queue to be empty Protected by logQueueMutex.
Definition: logging.h:198
LoggerThread(QString filename, bool progress, bool quiet, QString table, int facility, bool noserver)
LoggerThread constructor.
Definition: logging.cpp:256
VerboseMap verboseMap
Definition: logging.cpp:104
pid_t m_pid
Cached pid value.
Definition: logging.h:211
MythSystemMask
Definition: mythsystem.h:32
qlonglong epoch
Definition: logging.h:84
void handleItem(LoggingItem *item)
Handles each LoggingItem, generally by handing it off to mythlogserver via ZeroMQ.
Definition: logging.cpp:557
void initialTimeout(void)
Handles the initial startup timeout when waiting for the log server to show signs of life...
Definition: logging.cpp:455
void verboseAdd(uint64_t mask, QString name, bool additive, QString helptext)
Add a verbose level to the verboseMap.
Definition: logging.cpp:1082
bool m_noserver
Definition: logging.h:223
static bool debugRegistration
Definition: logging.cpp:83
void verboseHelp(void)
Outputs the Verbose levels and their descriptions (for –verbose help)
Definition: logging.cpp:1145
LogServerThread * logServerThread
void run(void)
Run the logging thread.
Definition: logging.cpp:307
virtual int IncrRef(void)
Increments reference count.
const char * name
Definition: ParseText.cpp:338
static LoggerThread * logThread
Definition: logging.cpp:75
void stop(void)
Stop the thread by setting the abort flag after waiting a second for the queue to be flushed...
Definition: logging.cpp:692
QString table() const
Definition: logging.h:119
QMap< QString, VerboseDef * > VerboseMap
Definition: verbosedefs.h:207
nzmqt::ZMQSocket * m_zmqSocket
ZeroMQ socket to talk to mythlogserver.
Definition: logging.h:217
void launchLogServer(void)
Launches the logging server daemon.
Definition: logging.cpp:512
QString GetAppBinDir(void)
Definition: mythdirs.cpp:209
run process through shell
Definition: mythsystem.h:41
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
friend void LogPrintLine(uint64_t, LogLevel_t, const char *, int, const char *, int, const char *,...)
Format and send a log message into the queue.
Definition: logging.cpp:772
unsigned char t
Definition: ParseText.cpp:339
bool logPropagateQuiet(void)
Check if we are propagating a "--quiet".
Definition: logging.cpp:895
static QHash< uint64_t, char * > logThreadHash
Definition: logging.cpp:77
QString path
Definition: logging.cpp:90
struct v4l2_mpeg_vbi_itv0_line line[35]
Definition: videodev2.h:1039
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
bool isRunning(void) const
Definition: mthread.cpp:271
LoggingType
Definition: logging.h:52
VERBOSE_PREAMBLE Most debug(nodatabase, notimestamp, noextra)") VERBOSE_MAP(VB_IMPORTANT
int facility() const
Definition: logging.h:113
QString logStrerror(int errnum)
Verbose helper function for ENO macro.
Definition: logging.cpp:1316
void loggingDeregisterThread(void)
Deregister the current thread&#39;s name.
Definition: logging.cpp:987
QByteArray toByteArray(void)
Definition: logging.cpp:187
static QRegExp logRegExp
Definition: logging.cpp:73
QString m_appname
Cached application name.
Definition: logging.h:208
int verboseArgParse(QString arg)
Parse the –verbose commandline argument and set the verbose level.
Definition: logging.cpp:1189
qulonglong m_threadId
Definition: logging.h:155
uint Wait(time_t timeout=0)
LogPropagateOpts logPropagateOpts
Definition: logging.cpp:94
QWaitCondition * m_waitNotEmpty
Condition variable for waiting for the queue to not be empty Protected by logQueueMutex.
Definition: logging.h:195
virtual void stop(void)
LogLevel_t m_level
Definition: logging.h:159
VERBOSE_PREAMBLE Most Errors or other very important messages true
Definition: verbosedefs.h:91
char * m_threadName
Definition: logging.h:164
LoglevelMap loglevelMap
Definition: logging.cpp:107
char * m_table
Definition: logging.h:166
int64_t getThreadTid(void)
Get the thread ID of the thread that produced the LoggingItem.
Definition: logging.cpp:215
char * m_appName
Definition: logging.h:165
LogLevel_t logLevel
Definition: logging.cpp:101
uint64_t userDefaultValueInt
Definition: logging.cpp:117
uint m_usec
Definition: logging.h:156
void logStop(void)
Entry point for stopping logging for an application.
Definition: logging.cpp:953
This class is essentially a workaround for a Qt 4.5.2 bug where it will get stuck in the Qt event loo...
static QMutex logThreadTidMutex
Definition: logging.cpp:79
int m_facility
Definition: logging.h:160
QMutex loglevelMapMutex
Definition: logging.cpp:108
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:203
bool haveUserDefaultValues
Definition: logging.cpp:119
void setThreadTid(void)
Set the thread ID of the thread that produced the LoggingItem.
Definition: logging.cpp:228
void setPid(const int val)
Definition: logging.h:123
QThread * qthread(void)
Returns the thread, this will always return the same pointer no matter how often you restart the thre...
Definition: mthread.cpp:241
char * m_logFile
Definition: logging.h:167
void setFacility(const int val)
Definition: logging.h:130
static QHash< uint64_t, int64_t > logThreadTidHash
Definition: logging.cpp:80
char shortname
Definition: verbosedefs.h:212
int m_line
Definition: logging.h:157
void logServerWait(void)
static bool logThreadFinished
Definition: logging.cpp:82
qlonglong m_epoch
Definition: logging.h:161
int m_quiet
silence the console (console only)
Definition: logging.h:207
QMap< int, LoglevelDef * > LoglevelMap
Definition: verbosedefs.h:214
void verboseInit(void)
Initialize the logging levels and verbose levels.
Definition: logging.cpp:1120
qlonglong m_tid
Definition: logging.h:154
void logPropagateCalc(void)
Generate the logPropagateArgs global with the latest logging level, mask, etc to propagate to all of ...
Definition: logging.cpp:833
QStringList logPropagateArgList
Definition: logging.cpp:96
char * string
Definition: videodev2.h:1044
__s32 value
Definition: videodev2.h:1042
nzmqt::ZMQContext * m_zmqContext
ZeroMQ context to use.
Definition: logging.h:216
Definition: logging.h:56
~LoggerThread()
LoggerThread destructor. Triggers the deletion of all loggers.
Definition: logging.cpp:288
static LoggingItem * create(const char *, const char *, int, LogLevel_t, LoggingType)
Create a new LoggingItem.
Definition: logging.cpp:739
avoid blocking LIRC & Joystick Menu
Definition: mythsystem.h:34
const uint64_t verboseDefaultInt
Definition: logging.cpp:110
QString verboseString
Definition: logging.cpp:114
bool m_aborted
Flag to abort the thread.
Definition: logging.h:201
MythSignalingTimer * m_heartbeatTimer
Timer for 1s heartbeats.
Definition: logging.h:221