MythTV  0.28pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
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 <stdlib.h>
31 #define SYSLOG_NAMES
32 #ifndef _WIN32
33 #include <syslog.h>
34 #endif
35 #include <stdarg.h>
36 #include <string.h>
37 #include <sys/stat.h>
38 #include <sys/types.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 #ifdef _MSC_VER
66  // This is needed to avoid creating a reparse point which git on windows doesn't like
67  #include "QJson/src/QObjectHelper"
68  #include "QJson/src/Serializer"
69  #include "QJson/src/Parser"
70 #else
71  #include "QJson/QObjectHelper"
72  #include "QJson/Serializer"
73  #include "QJson/Parser"
74 #endif
75 
76 static QMutex logQueueMutex;
77 static QQueue<LoggingItem *> logQueue;
78 static QRegExp logRegExp = QRegExp("[%]{1,2}");
79 
80 static LoggerThread *logThread = NULL;
81 static QMutex logThreadMutex;
82 static QHash<uint64_t, char *> logThreadHash;
83 
84 static QMutex logThreadTidMutex;
85 static QHash<uint64_t, int64_t> logThreadTidHash;
86 
87 static bool logThreadFinished = false;
88 static bool debugRegistration = false;
89 
90 typedef struct {
91  bool propagate;
92  int quiet;
93  int facility;
94  bool dblog;
95  QString path;
96  bool noserver;
98 
102 
103 #define TIMESTAMP_MAX 30
104 #define MAX_STRING_LENGTH (LOGLINE_MAX+120)
105 
106 LogLevel_t logLevel = (LogLevel_t)LOG_INFO;
107 
108 bool verboseInitialized = false;
111 
114 
115 const uint64_t verboseDefaultInt = VB_GENERAL;
116 const char *verboseDefaultStr = " general";
117 
119 QString verboseString = QString(verboseDefaultStr);
120 
124 
125 void verboseAdd(uint64_t mask, QString name, bool additive, QString helptext);
126 void loglevelAdd(int value, QString name, char shortname);
127 void verboseInit(void);
128 void verboseHelp(void);
129 
130 void loggingGetTimeStamp(qlonglong *epoch, uint *usec)
131 {
132 #if HAVE_GETTIMEOFDAY
133  struct timeval tv;
134  gettimeofday(&tv, NULL);
135  *epoch = tv.tv_sec;
136  if (usec)
137  *usec = tv.tv_usec;
138 #else
139  /* Stupid system has no gettimeofday, use less precise QDateTime */
140  QDateTime date = MythDate::current();
141  *epoch = date.toTime_t();
142  if (usec)
143  {
144  QTime time = date.time();
145  *usec = time.msec() * 1000;
146  }
147 #endif
148 }
149 
151  ReferenceCounter("LoggingItem", false),
152  m_pid(-1), m_tid(-1), m_threadId(-1), m_usec(0), m_line(0),
153  m_type(kMessage), m_level((LogLevel_t)LOG_INFO), m_facility(0), m_epoch(0),
154  m_file(NULL), m_function(NULL), m_threadName(NULL), m_appName(NULL),
155  m_table(NULL), m_logFile(NULL)
156 {
157  m_message[0]='\0';
158  m_message[LOGLINE_MAX]='\0';
159 }
160 
161 LoggingItem::LoggingItem(const char *_file, const char *_function,
162  int _line, LogLevel_t _level, LoggingType _type) :
163  ReferenceCounter("LoggingItem", false), m_pid(-1),
164  m_threadId((uint64_t)(QThread::currentThreadId())),
165  m_line(_line), m_type(_type), m_level(_level), m_facility(0),
166  m_file(strdup(_file)), m_function(strdup(_function)),
167  m_threadName(NULL), m_appName(NULL), m_table(NULL), m_logFile(NULL)
168 {
170 
171  m_message[0]='\0';
172  m_message[LOGLINE_MAX]='\0';
173  setThreadTid();
174 }
175 
177 {
178  if (m_file)
179  free((void *)m_file);
180 
181  if (m_function)
182  free((void *)m_function);
183 
184  if (m_threadName)
185  free(m_threadName);
186 
187  if (m_appName)
188  free((void *)m_appName);
189 
190  if (m_table)
191  free((void *)m_table);
192 
193  if (m_logFile)
194  free((void *)m_logFile);
195 }
196 
197 QByteArray LoggingItem::toByteArray(void)
198 {
199  QVariantMap variant = QJson::QObjectHelper::qobject2qvariant(this);
200  QJson::Serializer serializer;
201  QByteArray json = serializer.serialize(variant);
202 
203  //cout << json.constData() << endl;
204 
205  return json;
206 }
207 
211 {
212  static const char *unknown = "thread_unknown";
213 
214  if( m_threadName )
215  return m_threadName;
216 
217  QMutexLocker locker(&logThreadMutex);
218  return logThreadHash.value(m_threadId, (char *)unknown);
219 }
220 
227 {
228  QMutexLocker locker(&logThreadTidMutex);
229  m_tid = logThreadTidHash.value(m_threadId, 0);
230  return m_tid;
231 }
232 
240 {
241  QMutexLocker locker(&logThreadTidMutex);
242 
243  m_tid = logThreadTidHash.value(m_threadId, -1);
244  if (m_tid == -1)
245  {
246  m_tid = 0;
247 
248 #if defined(linux)
249  m_tid = (int64_t)syscall(SYS_gettid);
250 #elif defined(__FreeBSD__)
251  long lwpid;
252  int dummy = thr_self( &lwpid );
253  (void)dummy;
254  m_tid = (int64_t)lwpid;
255 #elif CONFIG_DARWIN
256  m_tid = (int64_t)mach_thread_self();
257 #endif
259  }
260 }
261 
266  QString table, int facility, bool noserver) :
267  MThread("Logger"),
268  m_waitNotEmpty(new QWaitCondition()),
269  m_waitEmpty(new QWaitCondition()),
270  m_aborted(false), m_initialWaiting(true),
271  m_filename(filename), m_progress(progress),
272  m_quiet(quiet), m_appname(QCoreApplication::applicationName()),
273  m_tablename(table), m_facility(facility), m_pid(getpid()), m_epoch(0),
274  m_zmqContext(NULL), m_zmqSocket(NULL), m_initialTimer(NULL),
275  m_heartbeatTimer(NULL), m_noserver(noserver)
276 {
277  char *debug = getenv("VERBOSE_THREADS");
278  if (debug != NULL)
279  {
280  LOG(VB_GENERAL, LOG_NOTICE,
281  "Logging thread registration/deregistration enabled!");
282  debugRegistration = true;
283  }
284  m_locallogs = (m_appname == MYTH_APPNAME_MYTHLOGSERVER);
285 
286 #ifdef NOLOGSERVER
287  if (!m_noserver && !logServerStart())
288  {
289  LOG(VB_GENERAL, LOG_ERR,
290  "Failed to start LogServer thread");
291  }
292 #endif
293  moveToThread(qthread());
294 }
295 
298 {
299  stop();
300  wait();
301 
302 #ifdef NOLOGSERVER
303  if (!m_noserver)
304  {
305  logServerStop();
306  }
307 #endif
308  delete m_waitNotEmpty;
309  delete m_waitEmpty;
310 }
311 
317 {
318  RunProlog();
319 
320  logThreadFinished = false;
321 
322  LOG(VB_GENERAL, LOG_INFO, "Added logging to the console");
323 
324  bool dieNow = false;
325 
326  if (!m_noserver)
327  {
328 #ifndef NOLOGSERVER
329  try
330  {
331  if (m_locallogs)
332  {
333  logServerWait();
335  }
336  else
337  {
338  m_zmqContext = nzmqt::createDefaultContext(NULL);
339  m_zmqContext->start();
340  }
341 
342  if (!m_zmqContext)
343  {
344  m_aborted = true;
345  dieNow = true;
346  }
347  else
348  {
349  qRegisterMetaType<QList<QByteArray> >("QList<QByteArray>");
350 
351  m_zmqSocket =
352  m_zmqContext->createSocket(nzmqt::ZMQSocket::TYP_DEALER, this);
353  connect(m_zmqSocket,
354  SIGNAL(messageReceived(const QList<QByteArray>&)),
355  SLOT(messageReceived(const QList<QByteArray>&)),
356  Qt::QueuedConnection);
357 
358  if (m_locallogs)
359  m_zmqSocket->connectTo("inproc://mylogs");
360  else
361  m_zmqSocket->connectTo("tcp://127.0.0.1:35327");
362  }
363  }
364  catch (nzmqt::ZMQException &e)
365  {
366  cerr << "Exception during logging socket setup: " << e.what() << endl;
367  m_aborted = true;
368  dieNow = true;
369  }
370 
371  if (!m_aborted)
372  {
373  if (!m_locallogs)
374  {
375  m_initialWaiting = true;
376  pingLogServer();
377 
378  // wait up to 150ms for mythlogserver to respond
380  SLOT(initialTimeout()));
381  m_initialTimer->start(150);
382  }
383  else
384  LOG(VB_GENERAL, LOG_INFO, "Added logging to mythlogserver locally");
385 
387 
389  m_heartbeatTimer->start(1000);
390  }
391  #else
392  logServerWait();
393  #endif
394  }
395 
396  QMutexLocker qLock(&logQueueMutex);
397 
398  while (!m_aborted || !logQueue.isEmpty())
399  {
400  qLock.unlock();
401  qApp->processEvents(QEventLoop::AllEvents, 10);
402  qApp->sendPostedEvents(NULL, QEvent::DeferredDelete);
403 
404  qLock.relock();
405  if (logQueue.isEmpty())
406  {
407  m_waitEmpty->wakeAll();
408  m_waitNotEmpty->wait(qLock.mutex(), 100);
409  continue;
410  }
411 
412  LoggingItem *item = logQueue.dequeue();
413  qLock.unlock();
414 
415  fillItem(item);
416  handleItem(item);
417  logConsole(item);
418  item->DecrRef();
419 
420  qLock.relock();
421  }
422 
423  qLock.unlock();
424 
425  // This must be before the timer stop below or we deadlock when the timer
426  // thread tries to deregister, and we wait for it.
427  logThreadFinished = true;
428 
429 #ifndef NOLOGSERVER
430  if (m_initialTimer)
431  {
432  m_initialTimer->stop();
433  delete m_initialTimer;
434  m_initialTimer = NULL;
435  }
436 
437  if (m_heartbeatTimer)
438  {
440  delete m_heartbeatTimer;
441  m_heartbeatTimer = NULL;
442  }
443 
444  if (m_zmqSocket)
445  {
446  m_zmqSocket->setLinger(0);
447  m_zmqSocket->close();
448  }
449 
450  if (!m_locallogs)
451  delete m_zmqContext;
452 #endif
453 
454  RunEpilog();
455 
456  if (dieNow)
457  {
458  qApp->processEvents();
459  }
460 }
461 
465 {
466 #ifndef NOLOGSERVER
467  if (m_initialTimer)
468  {
469  m_initialTimer->stop();
470  delete m_initialTimer;
471  m_initialTimer = NULL;
472  }
473 
474  if (m_initialWaiting)
475  {
476  // Got no response from mythlogserver, let's assume it's dead and
477  // start it up
478  launchLogServer();
479  }
480 
481  LOG(VB_GENERAL, LOG_INFO, "Added logging to mythlogserver at TCP:35327");
482 #endif
483 }
484 
488 {
489 #ifndef NOLOGSERVER
490  static bool launched = false;
491  qlonglong epoch;
492 
493  loggingGetTimeStamp(&epoch, NULL);
494  qlonglong age = (epoch - m_epoch) % 30;
495 
496  if (age == 5)
497  {
498  if (!launched)
499  {
500  launchLogServer();
501  launched = true;
502  }
503  }
504  else
505  {
506  launched = false;
507  }
508 #endif
509 }
510 
513 {
514 #ifndef NOLOGSERVER
515  // cout << "pong" << endl;
516  m_zmqSocket->sendMessage(QByteArray(""));
517 #endif
518 }
519 
522 {
523 #ifndef NOLOGSERVER
524  m_initialWaiting = false;
525  if (!m_locallogs)
526  {
527  LOG(VB_GENERAL, LOG_INFO, "Starting mythlogserver");
528 
531  kMSRunShell);
532  QStringList args;
533  args << "--daemon" << logPropagateArgs;
534 
535  MythSystemLegacy ms(GetAppBinDir() + "mythlogserver", args, mask);
536  ms.Run();
537  ms.Wait(0);
538  }
539 #endif
540 }
541 
551 void LoggerThread::messageReceived(const QList<QByteArray> &msg)
552 {
553  m_initialWaiting = false;
554  // cout << "ping" << endl;
556  pingLogServer();
557 }
558 
559 
567 {
568  if (item->m_type & kRegistering)
569  {
570  item->m_tid = item->getThreadTid();
571 
572  QMutexLocker locker(&logThreadMutex);
573  if (logThreadHash.contains(item->m_threadId))
574  {
575  char *threadName = logThreadHash.take(item->m_threadId);
576  free(threadName);
577  }
578  logThreadHash[item->m_threadId] = strdup(item->m_threadName);
579 
580  if (debugRegistration)
581  {
582  snprintf(item->m_message, LOGLINE_MAX,
583  "Thread 0x%" PREFIX64 "X (%" PREFIX64
584  "d) registered as \'%s\'",
585  (long long unsigned int)item->m_threadId,
586  (long long int)item->m_tid,
587  logThreadHash[item->m_threadId]);
588  }
589  }
590  else if (item->m_type & kDeregistering)
591  {
592  int64_t tid = 0;
593 
594  {
595  QMutexLocker locker(&logThreadTidMutex);
596  if( logThreadTidHash.contains(item->m_threadId) )
597  {
598  tid = logThreadTidHash[item->m_threadId];
599  logThreadTidHash.remove(item->m_threadId);
600  }
601  }
602 
603  QMutexLocker locker(&logThreadMutex);
604  if (logThreadHash.contains(item->m_threadId))
605  {
606  if (debugRegistration)
607  {
608  snprintf(item->m_message, LOGLINE_MAX,
609  "Thread 0x%" PREFIX64 "X (%" PREFIX64
610  "d) deregistered as \'%s\'",
611  (long long unsigned int)item->m_threadId,
612  (long long int)tid,
613  logThreadHash[item->m_threadId]);
614  }
615  char *threadName = logThreadHash.take(item->m_threadId);
616  free(threadName);
617  }
618  }
619 
620  if (m_noserver)
621  {
622  return;
623  }
624 
625  if (item->m_message[0] != '\0')
626  {
627 #ifndef NOLOGSERVER
628  // Send it to mythlogserver
630  m_zmqSocket->sendMessage(item->toByteArray());
631 #else
632  if (logServerThread)
633  {
634  QList<QByteArray> list;
635  list.append(QByteArray());
636  list.append(item->toByteArray());
638  }
639 #endif
640  }
641 }
642 
646 {
647  char line[MAX_STRING_LENGTH];
648  char usPart[9];
649  char timestamp[TIMESTAMP_MAX];
650 
651  if (m_quiet || (m_progress && item->m_level > LOG_ERR))
652  return false;
653 
654  if (!(item->m_type & kMessage))
655  return false;
656 
657  item->IncrRef();
658 
659  if (item->m_type & kStandardIO)
660  snprintf( line, MAX_STRING_LENGTH, "%s", item->m_message );
661  else
662  {
663  time_t epoch = item->epoch();
664  struct tm tm;
665  localtime_r(&epoch, &tm);
666 
667  strftime( timestamp, TIMESTAMP_MAX-8, "%Y-%m-%d %H:%M:%S",
668  (const struct tm *)&tm );
669  snprintf( usPart, 9, ".%06d", (int)(item->m_usec) );
670  strcat( timestamp, usPart );
671  char shortname;
672 
673  {
674  QMutexLocker locker(&loglevelMapMutex);
675  LoglevelDef *lev = loglevelMap.value(item->m_level, NULL);
676  if (!lev)
677  shortname = '-';
678  else
679  shortname = lev->shortname;
680  }
681 
682  snprintf( line, MAX_STRING_LENGTH, "%s %c %s\n", timestamp,
683  shortname, item->m_message );
684  }
685 
686  int result = write( 1, line, strlen(line) );
687  (void)result;
688 
689  item->DecrRef();
690 
691  return true;
692 }
693 
694 
698 {
699  logQueueMutex.lock();
700  flush(1000);
701  m_aborted = true;
702  logQueueMutex.unlock();
703  m_waitNotEmpty->wakeAll();
704 }
705 
709 bool LoggerThread::flush(int timeoutMS)
710 {
711  QTime t;
712  t.start();
713  while (!m_aborted && !logQueue.isEmpty() && t.elapsed() < timeoutMS)
714  {
715  m_waitNotEmpty->wakeAll();
716  int left = timeoutMS - t.elapsed();
717  if (left > 0)
718  m_waitEmpty->wait(&logQueueMutex, left);
719  }
720  return logQueue.isEmpty();
721 }
722 
724 {
725  if (!item)
726  return;
727 
728  item->setPid(m_pid);
729  item->setThreadName(item->getThreadName());
730  item->setAppName(m_appname);
731  item->setTable(m_tablename);
732  item->setLogFile(m_filename);
733  item->setFacility(m_facility);
734 }
735 
736 
744 LoggingItem *LoggingItem::create(const char *_file,
745  const char *_function,
746  int _line, LogLevel_t _level,
747  LoggingType _type)
748 {
749  LoggingItem *item = new LoggingItem(_file, _function, _line, _level, _type);
750 
751  return item;
752 }
753 
755 {
756  // Deserialize buffer
757  QJson::Parser parser;
758  QVariant variant = parser.parse(buf);
759 
760  LoggingItem *item = new LoggingItem;
761  QJson::QObjectHelper::qvariant2qobject(variant.toMap(), item);
762 
763  return item;
764 }
765 
766 
778 void LogPrintLine( uint64_t mask, LogLevel_t level, const char *file, int line,
779  const char *function, int fromQString,
780  const char *format, ... )
781 {
782  va_list arguments;
783 
784  int type = kMessage;
785  type |= (mask & VB_FLUSH) ? kFlush : 0;
786  type |= (mask & VB_STDIO) ? kStandardIO : 0;
787  LoggingItem *item = LoggingItem::create(file, function, line, level,
788  (LoggingType)type);
789  if (!item)
790  return;
791 
792  char *formatcopy = NULL;
793  if( fromQString && strchr(format, '%') )
794  {
795  QString string(format);
796  format = strdup(string.replace(logRegExp, "%%").toLocal8Bit()
797  .constData());
798  formatcopy = (char *)format;
799  }
800 
801  va_start(arguments, format);
802  vsnprintf(item->m_message, LOGLINE_MAX, format, arguments);
803  va_end(arguments);
804 
805  if (formatcopy)
806  free(formatcopy);
807 
808  QMutexLocker qLock(&logQueueMutex);
809 
810 #if defined( _MSC_VER ) && defined( _DEBUG )
811  OutputDebugStringA( item->m_message );
812  OutputDebugStringA( "\n" );
813 #endif
814 
815  logQueue.enqueue(item);
816 
818  {
819  while (!logQueue.isEmpty())
820  {
821  item = logQueue.dequeue();
822  qLock.unlock();
823  logThread->handleItem(item);
824  logThread->logConsole(item);
825  item->DecrRef();
826  qLock.relock();
827  }
828  }
829  else if (logThread && !logThreadFinished && (type & kFlush))
830  {
831  logThread->flush();
832  }
833 }
834 
835 
840 {
841  logPropagateArgList.clear();
842 
843  QString mask = verboseString.trimmed();
844  mask.replace(QRegExp(" "), ",");
845  mask.remove(QRegExp("^,"));
846  logPropagateArgs = " --verbose " + mask;
847  logPropagateArgList << "--verbose" << mask;
848 
850  {
851  logPropagateArgs += " --logpath " + logPropagateOpts.path;
852  logPropagateArgList << "--logpath" << logPropagateOpts.path;
853  }
854 
855  QString name = logLevelGetName(logLevel);
856  logPropagateArgs += " --loglevel " + name;
857  logPropagateArgList << "--loglevel" << name;
858 
859  for (int i = 0; i < logPropagateOpts.quiet; i++)
860  {
861  logPropagateArgs += " --quiet";
862  logPropagateArgList << "--quiet";
863  }
864 
866  {
867  logPropagateArgs += " --enable-dblog";
868  logPropagateArgList << "--enable-dblog";
869  }
870 
871 #ifndef _WIN32
872  if (logPropagateOpts.facility >= 0)
873  {
874  const CODE *syslogname;
875 
876  for (syslogname = &facilitynames[0];
877  (syslogname->c_name &&
878  syslogname->c_val != logPropagateOpts.facility); syslogname++);
879 
880  logPropagateArgs += QString(" --syslog %1").arg(syslogname->c_name);
881  logPropagateArgList << "--syslog" << syslogname->c_name;
882  }
883 #endif
884 
886  {
887  logPropagateArgs += " --nologserver";
888  logPropagateArgList << "--nologserver";
889  }
890 }
891 
895 {
896  return logPropagateOpts.quiet;
897 }
898 
902 {
903  return logPropagateOpts.noserver;
904 }
905 
918 void logStart(QString logfile, int progress, int quiet, int facility,
919  LogLevel_t level, bool dblog, bool propagate, bool noserver)
920 {
921  if (logThread && logThread->isRunning())
922  return;
923 
924  logLevel = level;
925  LOG(VB_GENERAL, LOG_NOTICE, QString("Setting Log Level to LOG_%1")
926  .arg(logLevelGetName(logLevel).toUpper()));
927 
928  logPropagateOpts.propagate = propagate;
930  logPropagateOpts.facility = facility;
931  logPropagateOpts.dblog = dblog;
932  logPropagateOpts.noserver = noserver;
933 
934  if (propagate)
935  {
936  QFileInfo finfo(logfile);
937  QString path = finfo.path();
938  logPropagateOpts.path = path;
939  }
940 
942 
943  QString table = dblog ? QString("logging") : QString("");
944 
945  if (!logThread)
946  logThread = new LoggerThread(logfile, progress, quiet, table, facility, noserver);
947 
948  logThread->start();
949 }
950 
952 void logStop(void)
953 {
954  if (logThread)
955  {
956  logThread->stop();
957  logThread->wait();
958  delete logThread;
959  logThread = NULL;
960  }
961 }
962 
967 void loggingRegisterThread(const QString &name)
968 {
969  if (logThreadFinished)
970  return;
971 
972  QMutexLocker qLock(&logQueueMutex);
973 
974  LoggingItem *item = LoggingItem::create(__FILE__, __FUNCTION__,
975  __LINE__, (LogLevel_t)LOG_DEBUG,
976  kRegistering);
977  if (item)
978  {
979  item->setThreadName((char *)name.toLocal8Bit().constData());
980  logQueue.enqueue(item);
981  }
982 }
983 
987 {
988  if (logThreadFinished)
989  return;
990 
991  QMutexLocker qLock(&logQueueMutex);
992 
993  LoggingItem *item = LoggingItem::create(__FILE__, __FUNCTION__, __LINE__,
994  (LogLevel_t)LOG_DEBUG,
996  if (item)
997  logQueue.enqueue(item);
998 }
999 
1000 
1004 int syslogGetFacility(QString facility)
1005 {
1006 #ifdef _WIN32
1007  LOG(VB_GENERAL, LOG_NOTICE,
1008  "Windows does not support syslog, disabling" );
1009  return( -2 );
1010 #else
1011  const CODE *name;
1012  int i;
1013  QByteArray ba = facility.toLocal8Bit();
1014  char *string = (char *)ba.constData();
1015 
1016  for (i = 0, name = &facilitynames[0];
1017  name->c_name && strcmp(name->c_name, string); i++, name++);
1018 
1019  return( name->c_val );
1020 #endif
1021 }
1022 
1026 LogLevel_t logLevelGet(QString level)
1027 {
1028  QMutexLocker locker(&loglevelMapMutex);
1029  if (!verboseInitialized)
1030  {
1031  locker.unlock();
1032  verboseInit();
1033  locker.relock();
1034  }
1035 
1036  for (LoglevelMap::iterator it = loglevelMap.begin();
1037  it != loglevelMap.end(); ++it)
1038  {
1039  LoglevelDef *item = (*it);
1040  if ( item->name == level.toLower() )
1041  return (LogLevel_t)item->value;
1042  }
1043 
1044  return LOG_UNKNOWN;
1045 }
1046 
1050 QString logLevelGetName(LogLevel_t level)
1051 {
1052  QMutexLocker locker(&loglevelMapMutex);
1053  if (!verboseInitialized)
1054  {
1055  locker.unlock();
1056  verboseInit();
1057  locker.relock();
1058  }
1059  LoglevelMap::iterator it = loglevelMap.find((int)level);
1060 
1061  if ( it == loglevelMap.end() )
1062  return QString("unknown");
1063 
1064  return (*it)->name;
1065 }
1066 
1073 void verboseAdd(uint64_t mask, QString name, bool additive, QString helptext)
1074 {
1075  VerboseDef *item = new VerboseDef;
1076 
1077  item->mask = mask;
1078  name.detach();
1079  // VB_GENERAL -> general
1080  name.remove(0, 3);
1081  name = name.toLower();
1082  item->name = name;
1083  item->additive = additive;
1084  helptext.detach();
1085  item->helpText = helptext;
1086 
1087  verboseMap.insert(name, item);
1088 }
1089 
1095 void loglevelAdd(int value, QString name, char shortname)
1096 {
1097  LoglevelDef *item = new LoglevelDef;
1098 
1099  item->value = value;
1100  name.detach();
1101  // LOG_CRIT -> crit
1102  name.remove(0, 4);
1103  name = name.toLower();
1104  item->name = name;
1105  item->shortname = shortname;
1106 
1107  loglevelMap.insert(value, item);
1108 }
1109 
1111 void verboseInit(void)
1112 {
1113  QMutexLocker locker(&verboseMapMutex);
1114  QMutexLocker locker2(&loglevelMapMutex);
1115  verboseMap.clear();
1116  loglevelMap.clear();
1117 
1118  // This looks funky, so I'll put some explanation here. The verbosedefs.h
1119  // file gets included as part of the mythlogging.h include, and at that
1120  // time, the normal (without _IMPLEMENT_VERBOSE defined) code case will
1121  // define the VerboseMask enum. At this point, we force it to allow us
1122  // to include the file again, but with _IMPLEMENT_VERBOSE set so that the
1123  // single definition of the VB_* values can be shared to define also the
1124  // contents of verboseMap, via repeated calls to verboseAdd()
1125 
1126 #undef VERBOSEDEFS_H_
1127 #define _IMPLEMENT_VERBOSE
1128 #include "verbosedefs.h"
1129 
1130  verboseInitialized = true;
1131 }
1132 
1133 
1136 void verboseHelp(void)
1137 {
1138  QString m_verbose = userDefaultValueStr.trimmed();
1139  m_verbose.replace(QRegExp(" "), ",");
1140  m_verbose.remove(QRegExp("^,"));
1141 
1142  cerr << "Verbose debug levels.\n"
1143  "Accepts any combination (separated by comma) of:\n\n";
1144 
1145  for (VerboseMap::Iterator vit = verboseMap.begin();
1146  vit != verboseMap.end(); ++vit )
1147  {
1148  VerboseDef *item = vit.value();
1149  QString name = QString(" %1").arg(item->name, -15, ' ');
1150  if (item->helpText.isEmpty())
1151  continue;
1152  cerr << name.toLocal8Bit().constData() << " - " <<
1153  item->helpText.toLocal8Bit().constData() << endl;
1154  }
1155 
1156  cerr << endl <<
1157  "The default for this program appears to be: '-v " <<
1158  m_verbose.toLocal8Bit().constData() << "'\n\n"
1159  "Most options are additive except for 'none' and 'all'.\n"
1160  "These two are semi-exclusive and take precedence over any\n"
1161  "other options. However, you may use something like\n"
1162  "'-v none,jobqueue' to receive only JobQueue related messages\n"
1163  "and override the default verbosity level.\n\n"
1164  "Additive options may also be subtracted from 'all' by\n"
1165  "prefixing them with 'no', so you may use '-v all,nodatabase'\n"
1166  "to view all but database debug messages.\n\n"
1167  "Some debug levels may not apply to this program.\n\n";
1168 }
1169 
1173 int verboseArgParse(QString arg)
1174 {
1175  QString option;
1176 
1177  if (!verboseInitialized)
1178  verboseInit();
1179 
1180  QMutexLocker locker(&verboseMapMutex);
1181 
1183  verboseString = QString(verboseDefaultStr);
1184 
1185  if (arg.startsWith('-'))
1186  {
1187  cerr << "Invalid or missing argument to -v/--verbose option\n";
1188  return GENERIC_EXIT_INVALID_CMDLINE;
1189  }
1190 
1191  QStringList verboseOpts = arg.split(QRegExp("\\W+"));
1192  for (QStringList::Iterator it = verboseOpts.begin();
1193  it != verboseOpts.end(); ++it )
1194  {
1195  option = (*it).toLower();
1196  bool reverseOption = false;
1197 
1198  if (option != "none" && option.startsWith("no"))
1199  {
1200  reverseOption = true;
1201  option = option.right(option.length() - 2);
1202  }
1203 
1204  if (option == "help")
1205  {
1206  verboseHelp();
1207  return GENERIC_EXIT_INVALID_CMDLINE;
1208  }
1209  else if (option == "important")
1210  {
1211  cerr << "The \"important\" log mask is no longer valid.\n";
1212  }
1213  else if (option == "extra")
1214  {
1215  cerr << "The \"extra\" log mask is no longer valid. Please try "
1216  "--loglevel debug instead.\n";
1217  }
1218  else if (option == "default")
1219  {
1221  {
1224  }
1225  else
1226  {
1228  verboseString = QString(verboseDefaultStr);
1229  }
1230  }
1231  else
1232  {
1233  VerboseDef *item = verboseMap.value(option);
1234 
1235  if (item)
1236  {
1237  if (reverseOption)
1238  {
1239  verboseMask &= ~(item->mask);
1240  verboseString = verboseString.remove(' ' + item->name);
1241  verboseString += " no" + item->name;
1242  }
1243  else
1244  {
1245  if (item->additive)
1246  {
1247  if (!(verboseMask & item->mask))
1248  {
1249  verboseMask |= item->mask;
1250  verboseString += ' ' + item->name;
1251  }
1252  }
1253  else
1254  {
1255  verboseMask = item->mask;
1256  verboseString = item->name;
1257  }
1258  }
1259  }
1260  else
1261  {
1262  cerr << "Unknown argument for -v/--verbose: " <<
1263  option.toLocal8Bit().constData() << endl;;
1264  return GENERIC_EXIT_INVALID_CMDLINE;
1265  }
1266  }
1267  }
1268 
1269  if (!haveUserDefaultValues)
1270  {
1271  haveUserDefaultValues = true;
1274  }
1275 
1276  return GENERIC_EXIT_OK;
1277 }
1278 
1283 QString logStrerror(int errnum)
1284 {
1285  return QString("%1 (%2)").arg(strerror(errnum)).arg(errnum);
1286 }
1287 
1288 
1289 /*
1290  * vim:ts=4:sw=4:ai:et:si:sts=4
1291  */