MythTV  master
loggingserver.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 <QSocketNotifier>
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 "mythcorecontext.h"
23 #include "mythsignalingtimer.h"
24 #include "dbutil.h"
25 #include "exitcodes.h"
26 #include "compat.h"
27 
28 #include <cstdlib>
29 #ifndef _WIN32
30 #include <mythsyslog.h>
31 #if CONFIG_SYSTEMD_JOURNAL
32 #define SD_JOURNAL_SUPPRESS_LOCATION 1
33 #include <systemd/sd-journal.h>
34 #endif
35 #endif
36 #include <cstdarg>
37 #include <cstring>
38 #include <sys/stat.h>
39 #include <sys/types.h>
40 #include <fcntl.h>
41 #include <cstdio>
42 #include <unistd.h>
43 #if HAVE_GETTIMEOFDAY
44 #include <sys/time.h>
45 #endif
46 #include <csignal>
47 
48 // Various ways to get to thread's tid
49 #if defined(linux)
50 #include <sys/syscall.h>
51 #elif defined(__FreeBSD__)
52 extern "C" {
53 #include <sys/ucontext.h>
54 #include <sys/thr.h>
55 }
56 #elif CONFIG_DARWIN
57 #include <mach/mach.h>
58 #endif
59 
60 static QMutex loggerMapMutex;
61 static QMap<QString, LoggerBase *> loggerMap;
62 
64 
65 using LoggerList = QList<LoggerBase *>;
66 
69  qlonglong m_itemEpoch;
70 };
71 using ClientMap = QMap<QString, LoggerListItem *>;
72 
73 using ClientList = QList<QString>;
74 using RevClientMap = QMap<LoggerBase *, ClientList *>;
75 
76 static QMutex logClientMapMutex;
78 static QAtomicInt logClientCount;
79 
80 static QMutex logRevClientMapMutex;
82 
83 static QMutex logMsgListMutex;
85 static QWaitCondition logMsgListNotEmpty;
86 
87 #define TIMESTAMP_MAX 30
88 #define MAX_STRING_LENGTH (LOGLINE_MAX+120)
89 
93 LoggerBase::LoggerBase(const char *string)
94 {
95  QMutexLocker locker(&loggerMapMutex);
96  if (string)
97  {
98  m_handle = strdup(string);
99  loggerMap.insert(QString(m_handle), this);
100  }
101  else
102  {
103  m_handle = nullptr;
104  loggerMap.insert(QString(""), this);
105  }
106 }
107 
108 
112 {
113  QMutexLocker locker(&loggerMapMutex);
114  loggerMap.remove(QString(m_handle));
115 
116  if (m_handle)
117  free(m_handle);
118 }
119 
120 
125 {
126  m_fd = open(filename, O_WRONLY|O_CREAT|O_APPEND, 0664);
127  m_opened = (m_fd != -1);
128  LOG(VB_GENERAL, LOG_INFO, QString("Added logging to %1")
129  .arg(filename));
130 }
131 
132 
135 {
136  if( m_opened )
137  {
138  LOG(VB_GENERAL, LOG_INFO, QString("Removed logging to %1")
139  .arg(m_handle));
140  close(m_fd);
141  m_fd = -1;
142  m_opened = false;
143  }
144 }
145 
146 FileLogger *FileLogger::create(const QString& filename, QMutex *mutex)
147 {
148  QByteArray ba = filename.toLocal8Bit();
149  const char *file = ba.constData();
150  FileLogger *logger =
151  dynamic_cast<FileLogger *>(loggerMap.value(filename, nullptr));
152 
153  if (logger)
154  return logger;
155 
156  // Need to add a new FileLogger
157  mutex->unlock();
158  // inserts into loggerMap
159  logger = new FileLogger(file);
160  mutex->lock();
161 
162  auto *clients = new ClientList;
163  logRevClientMap.insert(logger, clients);
164  return logger;
165 }
166 
170 {
171  close(m_fd);
172 
173  m_fd = open(m_handle, O_WRONLY|O_CREAT|O_APPEND, 0664);
174  m_opened = (m_fd != -1);
175  LOG(VB_GENERAL, LOG_INFO, QString("Rolled logging on %1") .arg(m_handle));
176 }
177 
181 {
182  char line[MAX_STRING_LENGTH];
183  char usPart[9];
184  char timestamp[TIMESTAMP_MAX];
185 
186  if (!m_opened)
187  return false;
188 
189  time_t epoch = item->epoch();
190  struct tm tm {};
191  localtime_r(&epoch, &tm);
192 
193  strftime(timestamp, TIMESTAMP_MAX-8, "%Y-%m-%d %H:%M:%S",
194  (const struct tm *)&tm);
195  snprintf( usPart, 9, ".%06d", (int)(item->usec()) );
196  strcat( timestamp, usPart );
197 
198  char shortname = '-';
199  {
200  QMutexLocker locker(&loglevelMapMutex);
201  LoglevelMap::iterator it = loglevelMap.find(item->level());
202  if (it != loglevelMap.end())
203  shortname = (*it)->shortname;
204  }
205 
206  if( item->tid() )
207  {
208  snprintf( line, MAX_STRING_LENGTH,
209  "%s %c [%d/%" PREFIX64 "d] %s %s:%d (%s) - %s\n",
210  timestamp, shortname, item->pid(), item->tid(),
211  item->rawThreadName(), item->rawFile(), item->line(),
212  item->rawFunction(), item->rawMessage() );
213  }
214  else
215  {
216  snprintf( line, MAX_STRING_LENGTH,
217  "%s %c [%d] %s %s:%d (%s) - %s\n",
218  timestamp, shortname, item->pid(), item->rawThreadName(),
219  item->rawFile(), item->line(), item->rawFunction(),
220  item->rawMessage() );
221  }
222 
223  int result = write(m_fd, line, strlen(line));
224 
225  if( result == -1 )
226  {
227  LOG(VB_GENERAL, LOG_ERR,
228  QString("Closed Log output on fd %1 due to errors").arg(m_fd));
229  m_opened = false;
230  close( m_fd );
231  return false;
232  }
233  return true;
234 }
235 
236 #ifndef _WIN32
237 SyslogLogger::SyslogLogger(bool open) :
240  LoggerBase(nullptr)
241 {
242  if (open)
243  {
244  openlog(nullptr, LOG_NDELAY, 0 );
245  m_opened = true;
246  }
247 
248  LOG(VB_GENERAL, LOG_INFO, "Added syslogging");
249 }
250 
253 {
254  LOG(VB_GENERAL, LOG_INFO, "Removing syslogging");
255  if (m_opened)
256  closelog();
257 }
258 
259 SyslogLogger *SyslogLogger::create(QMutex *mutex, bool open)
260 {
262  dynamic_cast<SyslogLogger *>(loggerMap.value("", nullptr));
263 
264  if (logger)
265  return logger;
266 
267  // Need to add a new FileLogger
268  mutex->unlock();
269  // inserts into loggerMap
270  logger = new SyslogLogger(open);
271  mutex->lock();
272 
273  auto *clients = new ClientList;
274  logRevClientMap.insert(logger, clients);
275  return logger;
276 }
277 
278 
282 {
283  if (!m_opened || item->facility() <= 0)
284  return false;
285 
286  char shortname = '-';
287 
288  {
289  QMutexLocker locker(&loglevelMapMutex);
290  LoglevelDef *lev = loglevelMap.value(item->level(), nullptr);
291  if (lev != nullptr)
292  shortname = lev->shortname;
293  }
294  syslog(item->level() | item->facility(), "%s[%d]: %c %s %s:%d (%s) %s",
295  item->rawAppName(), item->pid(), shortname, item->rawThreadName(),
296  item->rawFile(), item->line(), item->rawFunction(),
297  item->rawMessage());
298 
299  return true;
300 }
301 
302 #if CONFIG_SYSTEMD_JOURNAL
303 JournalLogger::JournalLogger() :
305  LoggerBase(nullptr)
306 {
307  LOG(VB_GENERAL, LOG_INFO, "Added journal logging");
308 }
309 
311 JournalLogger::~JournalLogger()
312 {
313  LOG(VB_GENERAL, LOG_INFO, "Removing journal logging");
314 }
315 
316 JournalLogger *JournalLogger::create(QMutex *mutex)
317 {
318  JournalLogger *logger =
319  dynamic_cast<JournalLogger *>(loggerMap.value("", nullptr));
320 
321  if (logger)
322  return logger;
323 
324  // Need to add a new FileLogger
325  mutex->unlock();
326  // inserts into loggerMap
327  logger = new JournalLogger();
328  mutex->lock();
329 
330  auto *clients = new ClientList;
331  logRevClientMap.insert(logger, clients);
332  return logger;
333 }
334 
335 
338 bool JournalLogger::logmsg(LoggingItem *item)
339 {
340  sd_journal_send(
341  "MESSAGE=%s", item->rawMessage(),
342  "PRIORITY=%d", item->level(),
343  "CODE_FILE=%s", item->rawFile(),
344  "CODE_LINE=%d", item->line(),
345  "CODE_FUNC=%s", item->rawFunction(),
346  "SYSLOG_IDENTIFIER=%s", item->rawAppName(),
347  "SYSLOG_PID=%d", item->pid(),
348  "MYTH_THREAD=%s", item->rawThreadName(),
349  NULL
350  );
351  return true;
352 }
353 #endif
354 #endif
355 
356 const int DatabaseLogger::kMinDisabledTime = 1000;
357 
360 DatabaseLogger::DatabaseLogger(const char *table) :
361  LoggerBase(table)
362 {
363  m_query = QString(
364  "INSERT INTO %1 "
365  " (host, application, pid, tid, thread, filename, "
366  " line, function, msgtime, level, message) "
367  "VALUES (:HOST, :APP, :PID, :TID, :THREAD, :FILENAME, "
368  " :LINE, :FUNCTION, :MSGTIME, :LEVEL, :MESSAGE)")
369  .arg(m_handle);
370 
371  LOG(VB_GENERAL, LOG_INFO, QString("Added database logging to table %1")
372  .arg(m_handle));
373 
374  m_thread = new DBLoggerThread(this);
375  m_thread->start();
376 }
377 
380 {
381  LOG(VB_GENERAL, LOG_INFO, "Removing database logging");
382 
384 }
385 
386 DatabaseLogger *DatabaseLogger::create(const QString& table, QMutex *mutex)
387 {
388  QByteArray ba = table.toLocal8Bit();
389  const char *tble = ba.constData();
391  dynamic_cast<DatabaseLogger *>(loggerMap.value(table, nullptr));
392 
393  if (logger)
394  return logger;
395 
396  // Need to add a new FileLogger
397  mutex->unlock();
398  // inserts into loggerMap
399  logger = new DatabaseLogger(tble);
400  mutex->lock();
401 
402  auto *clients = new ClientList;
403  logRevClientMap.insert(logger, clients);
404  return logger;
405 }
406 
409 {
410  if( m_thread )
411  {
412  m_thread->stop();
413  m_thread->wait();
414  delete m_thread;
415  m_thread = nullptr;
416  }
417 }
418 
422 {
423  if (!m_thread)
424  return false;
425 
426  if (!m_thread->isRunning())
427  {
428  m_disabledTime.start();
429  }
430 
431  if (!m_disabledTime.isValid() && m_thread->queueFull())
432  {
433  m_disabledTime.start();
434  LOG(VB_GENERAL, LOG_CRIT,
435  "Disabling DB Logging: too many messages queued");
436  return false;
437  }
438 
439  if (m_disabledTime.isValid() && m_disabledTime.hasExpired(kMinDisabledTime))
440  {
441  if (isDatabaseReady() && !m_thread->queueFull())
442  {
443  m_disabledTime.invalidate();
444  LOG(VB_GENERAL, LOG_CRIT, "Reenabling DB Logging");
445  }
446  }
447 
448  if (m_disabledTime.isValid())
449  return false;
450 
451  m_thread->enqueue(item);
452  return true;
453 }
454 
455 
460 {
461  char timestamp[TIMESTAMP_MAX];
462 
463  time_t epoch = item->epoch();
464  struct tm tm {};
465  localtime_r(&epoch, &tm);
466 
467  strftime(timestamp, TIMESTAMP_MAX-8, "%Y-%m-%d %H:%M:%S",
468  (const struct tm *)&tm);
469 
470  query.bindValue(":TID", item->tid());
471  query.bindValue(":THREAD", item->threadName());
472  query.bindValue(":FILENAME", item->file());
473  query.bindValue(":LINE", item->line());
474  query.bindValue(":FUNCTION", item->function());
475  query.bindValue(":MSGTIME", timestamp);
476  query.bindValue(":LEVEL", item->level());
477  query.bindValue(":MESSAGE", item->message());
478  query.bindValue(":APP", item->appName());
479  query.bindValue(":PID", item->pid());
480 
481  if (!query.exec())
482  {
483  // Suppress Driver not loaded errors that occur at startup.
484  // and suppress additional errors for one second after the
485  // previous error (to avoid spamming the log).
486  QSqlError err = query.lastError();
487  if ((err.type() != 1
488  || !err.nativeErrorCode().isEmpty()
489  ) &&
490  (!m_errorLoggingTime.isValid() ||
491  (m_errorLoggingTime.hasExpired(1000))))
492  {
493  MythDB::DBError("DBLogging", query);
494  m_errorLoggingTime.start();
495  }
496  return false;
497  }
498 
499  return true;
500 }
501 
505 {
506  query.prepare(m_query);
507  query.bindValue(":HOST", gCoreContext->GetHostName());
508 }
509 
513 {
514  bool ready = false;
515  MythDB *db = GetMythDB();
516 
517  if ((db) && db->HaveValidDatabase())
518  {
519  if ( !m_loggingTableExists )
521 
522  if ( m_loggingTableExists )
523  ready = true;
524  }
525 
526  return ready;
527 }
528 
532 bool DatabaseLogger::tableExists(const QString &table)
533 {
534  bool result = false;
535  MSqlQuery query(MSqlQuery::InitCon());
536  if (query.isConnected())
537  {
538  QString sql = "SELECT COLUMN_NAME "
539  " FROM INFORMATION_SCHEMA.COLUMNS "
540  " WHERE TABLE_SCHEMA = DATABASE() "
541  " AND TABLE_NAME = :TABLENAME "
542  " AND COLUMN_NAME = :COLUMNNAME;";
543  if (query.prepare(sql))
544  {
545  query.bindValue(":TABLENAME", table);
546  query.bindValue(":COLUMNNAME", "function");
547  if (query.exec() && query.next())
548  result = true;
549  }
550  }
551  return result;
552 }
553 
554 
558  MThread("DBLogger"), m_logger(logger),
559  m_queue(new QQueue<LoggingItem *>),
560  m_wait(new QWaitCondition())
561 {
562 }
563 
567 {
568  stop();
569  wait();
570 
571  QMutexLocker qLock(&m_queueMutex);
572  while (!m_queue->empty())
573  m_queue->dequeue()->DecrRef();
574  delete m_queue;
575  delete m_wait;
576  m_queue = nullptr;
577  m_wait = nullptr;
578 }
579 
582 {
583  RunProlog();
584 
585  // Wait a bit before we start logging to the DB.. If we wait too long,
586  // then short-running tasks (like mythpreviewgen) will not log to the db
587  // at all, and that's undesirable.
588  while (true)
589  {
591  break;
592 
593  QMutexLocker locker(&m_queueMutex);
594  m_wait->wait(locker.mutex(), 100);
595  }
596 
597  if (!m_aborted)
598  {
599  // We want the query to be out of scope before the RunEpilog() so
600  // shutdown occurs correctly as otherwise the connection appears still
601  // in use, and we get a qWarning on shutdown.
602  auto *query = new MSqlQuery(MSqlQuery::InitCon());
603  m_logger->prepare(*query);
604 
605  QMutexLocker qLock(&m_queueMutex);
606  while (!m_aborted || !m_queue->isEmpty())
607  {
608  if (m_queue->isEmpty())
609  {
610  m_wait->wait(qLock.mutex(), 100);
611  continue;
612  }
613 
614  LoggingItem *item = m_queue->dequeue();
615  if (!item)
616  continue;
617 
618  if (item->message()[0] != QChar('\0'))
619  {
620  qLock.unlock();
621  bool logged = m_logger->logqmsg(*query, item);
622  qLock.relock();
623 
624  if (!logged)
625  {
626  m_queue->prepend(item);
627  m_wait->wait(qLock.mutex(), 100);
628  delete query;
629  query = new MSqlQuery(MSqlQuery::InitCon());
630  m_logger->prepare(*query);
631  continue;
632  }
633  }
634 
635  item->DecrRef();
636  }
637 
638  delete query;
639  }
640 
641  RunEpilog();
642 }
643 
646 {
647  QMutexLocker qLock(&m_queueMutex);
648  m_aborted = true;
649  m_wait->wakeAll();
650 }
651 
653 {
654  QMutexLocker qLock(&m_queueMutex);
655  if (!m_aborted)
656  {
657  if (item)
658  {
659  item->IncrRef();
660  }
661  m_queue->enqueue(item);
662  }
663  return true;
664 }
665 
666 
667 #ifndef _WIN32
668 
671 void logSigHup(void)
672 {
673  if (!logForwardThread)
674  return;
675 
676  // This will be running in the thread that's used by SignalHandler
677  // Emit the signal which is connected to a slot that runs in the actual
678  // handling thread.
680 }
681 #endif
682 
683 
686  MThread("LogForward")
687 {
688  moveToThread(qthread());
689 }
690 
693 {
694  stop();
695  wait();
696 }
697 
702 {
703  RunProlog();
704 
705  connect(this, SIGNAL(incomingSigHup(void)), this, SLOT(handleSigHup(void)),
706  Qt::QueuedConnection);
707 
708  qRegisterMetaType<QList<QByteArray> >("QList<QByteArray>");
709 
710  while (!m_aborted)
711  {
712  qApp->processEvents(QEventLoop::AllEvents, 10);
713  qApp->sendPostedEvents(nullptr, QEvent::DeferredDelete);
714 
715  {
716  QMutexLocker lock(&logMsgListMutex);
717  if (logMsgList.isEmpty() &&
718  !logMsgListNotEmpty.wait(lock.mutex(), 90))
719  {
720  continue;
721  }
722 
723  int processed = 0;
724  while (!logMsgList.isEmpty())
725  {
726  processed++;
727  LogMessage *msg = logMsgList.takeFirst();
728  lock.unlock();
729  forwardMessage(msg);
730  delete msg;
731 
732  // Force a processEvents every 128 messages so a busy queue
733  // doesn't preclude timer notifications, etc.
734  if ((processed & 127) == 0)
735  {
736  qApp->processEvents(QEventLoop::AllEvents, 10);
737  qApp->sendPostedEvents(nullptr, QEvent::DeferredDelete);
738  }
739 
740  lock.relock();
741  }
742  }
743  }
744 
745  LoggerList loggers;
746 
747  {
748  QMutexLocker lock(&loggerMapMutex);
749  loggers = loggerMap.values();
750  }
751 
752  while (!loggers.isEmpty())
753  {
754  LoggerBase *logger = loggers.takeFirst();
755  delete logger;
756  }
757 
758  RunEpilog();
759 }
760 
761 
764 {
765 #ifndef _WIN32
766  LOG(VB_GENERAL, LOG_INFO, "SIGHUP received, rolling log files.");
767 
768  /* SIGHUP was sent. Close and reopen debug logfiles */
769  QMutexLocker locker(&loggerMapMutex);
770  QMap<QString, LoggerBase *>::iterator it;
771  for (it = loggerMap.begin(); it != loggerMap.end(); ++it)
772  {
773  it.value()->reopen();
774  }
775 #endif
776 }
777 
779 {
780 #ifdef DUMP_PACKET
781  QList<QByteArray>::const_iterator it = msg->begin();
782  int i = 0;
783  for (; it != msg->end(); ++it, i++)
784  {
785  QByteArray buf = *it;
786  cout << i << ":\t" << buf.size() << endl << "\t"
787  << buf.toHex().constData() << endl << "\t"
788  << buf.constData() << endl;
789  }
790 #endif
791 
792  // First section is the client id
793  QByteArray clientBa = msg->first();
794  QString clientId = QString(clientBa.toHex());
795 
796  QByteArray json = msg->at(1);
797 
798  if (json.size() == 0)
799  {
800  // cout << "invalid msg, no json data " << qPrintable(clientId) << endl;
801  return;
802  }
803 
804  QMutexLocker lock(&logClientMapMutex);
805  LoggerListItem *logItem = logClientMap.value(clientId, nullptr);
806 
807  // cout << "msg " << clientId.toLocal8Bit().constData() << endl;
808  if (logItem)
809  {
810  loggingGetTimeStamp(&logItem->m_itemEpoch, nullptr);
811  }
812  else
813  {
814  LoggingItem *item = LoggingItem::create(json);
815 
816  logClientCount.ref();
817  LOG(VB_FILE, LOG_DEBUG, QString("New Logging Client: ID: %1 (#%2)")
818  .arg(clientId).arg(logClientCount.fetchAndAddOrdered(0)));
819 
820  QMutexLocker lock2(&loggerMapMutex);
821  QMutexLocker lock3(&logRevClientMapMutex);
822 
823  // Need to find or create the loggers
824  auto *loggers = new LoggerList;
825 
826  // FileLogger from logFile
827  QString logfile = item->logFile();
828  if (!logfile.isEmpty())
829  {
830  LoggerBase *logger = FileLogger::create(logfile, lock2.mutex());
831 
832  ClientList *clients = logRevClientMap.value(logger);
833 
834  if (clients)
835  clients->insert(0, clientId);
836 
837  if (logger && loggers)
838  loggers->insert(0, logger);
839  }
840 
841 #ifndef _WIN32
842  // SyslogLogger from facility
843  int facility = item->facility();
844  if (facility > 0)
845  {
846  LoggerBase *logger = SyslogLogger::create(lock2.mutex());
847 
848  ClientList *clients = logRevClientMap.value(logger);
849 
850  if (clients)
851  clients->insert(0, clientId);
852 
853  if (logger && loggers)
854  loggers->insert(0, logger);
855  }
856 
857 #if CONFIG_SYSTEMD_JOURNAL
858  // Journal Logger
859  if (facility == SYSTEMD_JOURNAL_FACILITY)
860  {
861  LoggerBase *logger = JournalLogger::create(lock2.mutex());
862 
863  ClientList *clients = logRevClientMap.value(logger);
864 
865  if (clients)
866  clients->insert(0, clientId);
867 
868  if (logger && loggers)
869  loggers->insert(0, logger);
870  }
871 #endif
872 #endif
873 
874  // DatabaseLogger from table
875  QString table = item->table();
876  if (!table.isEmpty())
877  {
878  LoggerBase *logger = DatabaseLogger::create(table, lock2.mutex());
879 
880  ClientList *clients = logRevClientMap.value(logger);
881 
882  if (clients)
883  clients->insert(0, clientId);
884 
885  if (logger && loggers)
886  loggers->insert(0, logger);
887  }
888 
889  logItem = new LoggerListItem;
890  loggingGetTimeStamp(&logItem->m_itemEpoch, nullptr);
891  logItem->m_itemList = loggers;
892  logClientMap.insert(clientId, logItem);
893 
894  item->DecrRef();
895  }
896 
897  if (logItem && logItem->m_itemList && !logItem->m_itemList->isEmpty())
898  {
899  LoggingItem *item = LoggingItem::create(json);
900  if (!item)
901  return;
902  foreach (auto & it, *logItem->m_itemList)
903  it->logmsg(item);
904  item->DecrRef();
905  }
906 }
907 
910 {
911  m_aborted = true;
912 }
913 
914 bool logForwardStart(void)
915 {
918 
919  usleep(10000);
921 }
922 
923 void logForwardStop(void)
924 {
925  if (!logForwardThread)
926  return;
927 
929  delete logForwardThread;
930  logForwardThread = nullptr;
931 
932  QMutexLocker locker(&loggerMapMutex);
933  for (auto it = loggerMap.begin(); it != loggerMap.end(); ++it)
934  {
935  it.value()->stopDatabaseAccess();
936  }
937 }
938 
939 void logForwardMessage(const QList<QByteArray> &msg)
940 {
941  auto *message = new LogMessage(msg);
942  QMutexLocker lock(&logMsgListMutex);
943 
944  bool wasEmpty = logMsgList.isEmpty();
945  logMsgList.append(message);
946 
947  if (wasEmpty)
948  logMsgListNotEmpty.wakeAll();
949 }
950 
951 /*
952  * vim:ts=4:sw=4:ai:et:si:sts=4
953  */
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:215
QString logfile
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:783
bool m_loggingTableExists
The desired logging table exists.
def write(text, progress=True)
Definition: mythburn.py:279
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:864
bool m_aborted
Flag to abort the thread.
Syslog-based logger (not available in Windows)
Definition: loggingserver.h:64
QWaitCondition * m_wait
Wait condition used for waiting for the queue to not be full.
static QMutex loggerMapMutex
void run(void) override
Start the thread.
static QMutex logClientMapMutex
Base class for the various logging mechanisms.
Definition: loggingserver.h:27
bool queueFull(void)
Indicates when the queue is full.
DatabaseLogger(const char *table)
DatabaseLogger constructor.
const char * rawThreadName() const
Definition: logging.h:135
#define NULL
Definition: H264Parser.h:62
QString m_query
The database query to insert log messages.
int m_fd
contains the file descriptor for the logfile
Definition: loggingserver.h:60
static void forwardMessage(LogMessage *msg)
LogForwardThread * logForwardThread
static ClientMap logClientMap
int level
Definition: logging.h:71
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:311
const char * rawFunction() const
Definition: logging.h:134
static QMutex logMsgListMutex
DBLoggerThread(DatabaseLogger *logger)
DBLoggerThread constructor.
bool isConnected(void)
Only updated once during object creation.
Definition: mythdbcon.h:135
QElapsedTimer m_errorLoggingTime
Elapsed time since DB error logging was last done.
static RevClientMap logRevClientMap
~FileLogger() override
FileLogger deconstructor - close the logfile.
void reopen(void) override
Reopen the logfile after a SIGHUP.
Database logger - logs to the MythTV database.
Definition: loggingserver.h:98
static __inline struct tm * localtime_r(const time_t *timep, struct tm *result)
Definition: compat.h:286
The logging items that are generated by LOG() and are sent to the console.
Definition: logging.h:61
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
void loggingGetTimeStamp(qlonglong *epoch, uint *usec)
Definition: logging.cpp:120
LoggerBase(const char *string)
LoggerBase Constructor.
int pid
Definition: logging.h:65
static LogMessageList logMsgList
QMap< LoggerBase *, ClientList * > RevClientMap
void incomingSigHup(void)
QList< LogMessage * > LogMessageList
The logging thread that forwards received messages to the consuming loggers via ZeroMQ.
void stop(void)
Stop the thread by setting the abort flag.
~SyslogLogger() override
qlonglong epoch
Definition: logging.h:73
QString logFile
Definition: logging.h:79
FileLogger(const char *filename)
FileLogger constructor.
void logForwardMessage(const QList< QByteArray > &msg)
virtual int IncrRef(void)
Increments reference count.
uint usec
Definition: logging.h:68
qlonglong tid
Definition: logging.h:66
bool logForwardStart(void)
static FileLogger * create(const QString &filename, QMutex *mutex)
QSqlError lastError(void) const
Definition: mythdbcon.h:202
bool isDatabaseReady(void)
Check if the database is ready for use.
#define close
Definition: compat.h:16
static bool tableExists(const QString &table)
Checks whether table exists and is ready for writing.
bool logmsg(LoggingItem *item) override
Process a log message for the logger instance.
bool logmsg(LoggingItem *item) override
Process a log message, queuing it for logging to the database.
void prepare(MSqlQuery &query)
Prepare the database query for use, and bind constant values to it.
QList< LoggerBase * > LoggerList
static DatabaseLogger * create(const QString &table, QMutex *mutex)
bool m_opened
true when the logfile is opened
Definition: loggingserver.h:59
int line
Definition: logging.h:69
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
bool isRunning(void) const
Definition: mthread.cpp:274
static QWaitCondition logMsgListNotEmpty
void stop(void)
Tell the thread to stop by setting the m_aborted flag.
#define PREFIX64
Definition: compat.h:393
File-based logger - used for logfiles and console.
Definition: loggingserver.h:48
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
static const int kMinDisabledTime
Minimum time to disable DB logging (in ms)
bool logmsg(LoggingItem *item) override
Process a log message, writing to the logfile.
qlonglong m_itemEpoch
static void handleSigHup(void)
SIGHUP handler - reopen all open logfiles for logrollers.
static QMap< QString, LoggerBase * > loggerMap
int facility
Definition: logging.h:72
const char * rawFile() const
Definition: logging.h:133
QMap< QString, LoggerListItem * > ClientMap
void stopDatabaseAccess(void) override
Stop logging to the database and wait for the thread to stop.
const char * rawAppName() const
Definition: logging.h:136
bool enqueue(LoggingItem *item)
Enqueues a LoggingItem onto the queue for the thread to consume.
bool logqmsg(MSqlQuery &query, LoggingItem *item)
Actually insert a log message from the queue into the database.
QString function
Definition: logging.h:75
friend class DBLoggerThread
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:808
LoglevelMap loglevelMap
Definition: logging.cpp:101
void run(void) override
Run the log forwarding thread.
QList< QString > ClientList
~LogForwardThread() override
LogForwardThread destructor.
static QMutex logRevClientMapMutex
const char * rawMessage() const
Definition: logging.h:139
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
QString message
Definition: logging.h:80
LoggerList * m_itemList
char * m_handle
semi-opaque handle for identifying instance
Definition: loggingserver.h:44
DBLoggerThread * m_thread
The database queue handling thread.
DatabaseLogger * m_logger
The associated logger instance.
QMutex loglevelMapMutex
Definition: logging.cpp:102
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:202
QElapsedTimer m_disabledTime
Elapsed time since the DB logging was disabled.
volatile bool m_aborted
Used during shutdown to indicate that the thread should stop ASAP.
QList< QByteArray > LogMessage
QMutex m_queueMutex
Mutex for protecting the queue.
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
QQueue< LoggingItem * > * m_queue
Queue of LoggingItems to insert.
QThread * qthread(void)
Returns the thread, this will always return the same pointer no matter how often you restart the thre...
Definition: mthread.cpp:244
void logForwardStop(void)
static QAtomicInt logClientCount
char shortname
Definition: verbosedefs.h:214
QString appName
Definition: logging.h:77
static void logger(cdio_log_level_t level, const char message[])
Definition: cddecoder.cpp:37
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
static SyslogLogger * create(QMutex *mutex, bool open=true)
MythDB * GetMythDB(void)
Definition: mythdb.cpp:46
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
bool m_opened
true when syslog channel open.
Definition: loggingserver.h:77
~DBLoggerThread() override
DBLoggerThread deconstructor.
QString file
Definition: logging.h:74
QString threadName
Definition: logging.h:76
~LoggerBase() override
LoggerBase Deconstructor.
QString GetHostName(void)
#define MAX_STRING_LENGTH
~DatabaseLogger() override
DatabaseLogger deconstructor.
static LoggingItem * create(const char *_file, const char *_function, int _line, LogLevel_t _level, LoggingType _type)
Create a new LoggingItem.
Definition: logging.cpp:551
LogForwardThread()
LogForwardThread constructor.
#define TIMESTAMP_MAX
QString table
Definition: logging.h:78