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