Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added mythlogserver

Still needs more testing before this is complete.  And more plumbing.
  • Loading branch information...
commit 6f69e85d50d92b539273ff8163b20f536d34505f 1 parent 82fdb4f
@Beirdo Beirdo authored
View
16 mythtv/external/nzmqt/src/nzmqt.pro
@@ -4,7 +4,7 @@ QT += core
QT -= gui
-TARGET = mythnzmqt-$$LIBVERSION
+TARGET = mythnzmqt
target.path = $${LIBDIR}
#CONFIG += console
#CONFIG -= app_bundle
@@ -20,13 +20,13 @@ SOURCES += \
HEADERS += \
../include/nzmqt/nzmqt.hpp \
-# pubsub/PubSubServer.h \
-# pubsub/PubSubClient.h \
-# reqrep/ReqRepServer.h \
-# reqrep/ReqRepClient.h \
-# pushpull/PushPullWorker.h \
-# pushpull/PushPullVentilator.h \
-# pushpull/PushPullSink.h \
+ pubsub/PubSubServer.h \
+ pubsub/PubSubClient.h \
+ reqrep/ReqRepServer.h \
+ reqrep/ReqRepClient.h \
+ pushpull/PushPullWorker.h \
+ pushpull/PushPullVentilator.h \
+ pushpull/PushPullSink.h \
NzmqtApp.h \
common/Tools.h
View
3  mythtv/libs/libmythbase/libmythbase.pro
@@ -7,6 +7,7 @@ CONFIG += thread dll
target.path = $${LIBDIR}
INSTALLS = target
INCLUDEPATH += $$PREFIX/include
+INCLUDEPATH += $$PREFIX/include/qjson
QMAKE_CLEAN += $(TARGET) $(TARGETA) $(TARGETD) $(TARGET0) $(TARGET1) $(TARGET2)
@@ -124,4 +125,4 @@ QT += xml sql network
include ( ../libs-targetfix.pro )
-LIBS += $$EXTRA_LIBS $$LATE_LIBS -lzmq
+LIBS += $$EXTRA_LIBS $$LATE_LIBS -lzmq -lmythqjson
View
207 mythtv/libs/libmythbase/logging.cpp
@@ -10,6 +10,7 @@
#include <QStringList>
#include <QMap>
#include <QRegExp>
+#include <QVariantMap>
#include <iostream>
using namespace std;
@@ -54,19 +55,23 @@ extern "C" {
#endif
#include "nzmqt.hpp"
+#include "qjson/qobjecthelper.h"
+#include "qjson/serializer.h"
+#include "qjson/parser.h"
-QMutex logQueueMutex;
-QQueue<LoggingItem *> logQueue;
-QRegExp logRegExp = QRegExp("[%]{1,2}");
+static QMutex logQueueMutex;
+static QQueue<LoggingItem *> logQueue;
+static QRegExp logRegExp = QRegExp("[%]{1,2}");
-QMutex logThreadMutex;
-QHash<uint64_t, char *> logThreadHash;
+static LoggerThread *logThread = NULL;
+static QMutex logThreadMutex;
+static QHash<uint64_t, char *> logThreadHash;
-QMutex logThreadTidMutex;
-QHash<uint64_t, int64_t> logThreadTidHash;
+static QMutex logThreadTidMutex;
+static QHash<uint64_t, int64_t> logThreadTidHash;
-bool logThreadFinished = false;
-bool debugRegistration = false;
+static bool logThreadFinished = false;
+static bool debugRegistration = false;
typedef struct {
bool propagate;
@@ -84,21 +89,6 @@ QString logPropagateArgs;
LogLevel_t logLevel = (LogLevel_t)LOG_INFO;
-typedef struct {
- uint64_t mask;
- QString name;
- bool additive;
- QString helpText;
-} VerboseDef;
-typedef QMap<QString, VerboseDef *> VerboseMap;
-
-typedef struct {
- int value;
- QString name;
- char shortname;
-} LoglevelDef;
-typedef QMap<int, LoglevelDef *> LoglevelMap;
-
bool verboseInitialized = false;
VerboseMap verboseMap;
QMutex verboseMapMutex;
@@ -121,32 +111,31 @@ void loglevelAdd(int value, QString name, char shortname);
void verboseInit(void);
void verboseHelp(void);
+LoggingItem::LoggingItem()
+{
+}
LoggingItem::LoggingItem(const char *_file, const char *_function,
int _line, LogLevel_t _level, LoggingType _type) :
- threadId((uint64_t)(QThread::currentThreadId())),
- line(_line), type(_type), level(_level), file(_file),
- function(_function), threadName(NULL)
+ m_threadId((uint64_t)(QThread::currentThreadId())),
+ m_line(_line), m_type(_type), m_level(_level), m_file(_file),
+ m_function(_function), m_threadName(NULL)
{
- time_t epoch;
-
#if HAVE_GETTIMEOFDAY
struct timeval tv;
gettimeofday(&tv, NULL);
- epoch = tv.tv_sec;
- usec = tv.tv_usec;
+ m_epoch = tv.tv_sec;
+ m_usec = tv.tv_usec;
#else
/* Stupid system has no gettimeofday, use less precise QDateTime */
QDateTime date = QDateTime::currentDateTime();
QTime time = date.time();
- epoch = date.toTime_t();
- usec = time.msec() * 1000;
+ m_epoch = date.toTime_t();
+ m_usec = time.msec() * 1000;
#endif
- localtime_r(&epoch, &tm);
-
- message[0]='\0';
- message[LOGLINE_MAX]='\0';
+ m_message[0]='\0';
+ m_message[LOGLINE_MAX]='\0';
setThreadTid();
refcount.ref();
}
@@ -154,9 +143,13 @@ LoggingItem::LoggingItem(const char *_file, const char *_function,
QByteArray LoggingItem::toByteArray(void)
{
- QByteArray out("");
+ QVariantMap variant = QJson::QObjectHelper::qobject2qvariant(this);
+ QJson::Serializer serializer;
+ QByteArray json = serializer.serialize(variant);
+
+cout << json.constData() << endl;
- return out;
+ return json;
}
/// \brief Get the name of the thread that produced the LoggingItem
@@ -165,11 +158,12 @@ char *LoggingItem::getThreadName(void)
{
static const char *unknown = "thread_unknown";
- if( threadName )
- return threadName;
+ if( m_threadName )
+ return m_threadName;
QMutexLocker locker(&logThreadMutex);
- char *name = logThreadHash.value(threadId, (char *)unknown);
+ char *name = logThreadHash.value(m_threadId, (char *)unknown);
+ m_threadName = name;
return name;
}
@@ -181,8 +175,8 @@ char *LoggingItem::getThreadName(void)
int64_t LoggingItem::getThreadTid(void)
{
QMutexLocker locker(&logThreadTidMutex);
- int64_t tid = logThreadTidHash.value(threadId, 0);
- return tid;
+ m_tid = logThreadTidHash.value(m_threadId, 0);
+ return m_tid;
}
/// \brief Set the thread ID of the thread that produced the LoggingItem. This
@@ -195,33 +189,36 @@ void LoggingItem::setThreadTid(void)
{
QMutexLocker locker(&logThreadTidMutex);
- if( !logThreadTidHash.contains(threadId) )
+ m_tid = logThreadTidHash.value(m_threadId, -1);
+ if (m_tid == -1)
{
- int64_t tid = 0;
+ m_tid = 0;
#if defined(linux)
- tid = (int64_t)syscall(SYS_gettid);
+ m_tid = (int64_t)syscall(SYS_gettid);
#elif defined(__FreeBSD__)
long lwpid;
int dummy = thr_self( &lwpid );
(void)dummy;
- tid = (int64_t)lwpid;
+ m_tid = (int64_t)lwpid;
#elif CONFIG_DARWIN
- tid = (int64_t)mach_thread_self();
+ m_tid = (int64_t)mach_thread_self();
#endif
- logThreadTidHash[threadId] = tid;
+ logThreadTidHash[m_threadId] = m_tid;
}
}
/// \brief LoggerThread constructor. Enables debugging of thread registration
/// and deregistration if the VERBOSE_THREADS environment variable is
/// set.
-LoggerThread::LoggerThread(QString filename, bool progress, bool quiet) :
+LoggerThread::LoggerThread(QString filename, bool progress, bool quiet,
+ QString table, int facility) :
MThread("Logger"),
m_waitNotEmpty(new QWaitCondition()),
m_waitEmpty(new QWaitCondition()),
m_aborted(false), m_filename(filename), m_progress(progress),
- m_quiet(quiet)
+ m_quiet(quiet), m_appname(QCoreApplication::applicationName()),
+ m_tablename(table), m_facility(facility)
{
char *debug = getenv("VERBOSE_THREADS");
if (debug != NULL)
@@ -230,6 +227,8 @@ LoggerThread::LoggerThread(QString filename, bool progress, bool quiet) :
"Logging thread registration/deregistration enabled!");
debugRegistration = true;
}
+
+ moveToThread(qthread());
}
/// \brief LoggerThread destructor. Triggers the deletion of all loggers.
@@ -269,12 +268,14 @@ void LoggerThread::run(void)
kInitializing);
if (item)
{
+ fillItem(item);
m_zmqSocket->sendMessage(item->toByteArray());
item->deleteItem();
}
msleep(100); // wait up to 100ms for mythlogserver to respond
- if (m_initialWaiting)
+ if (m_initialWaiting &&
+ QCoreApplication::applicationName() != MYTH_APPNAME_MYTHLOGSERVER)
{
// Got no response from mythlogserver, let's assume it's dead and start
// it up
@@ -307,6 +308,7 @@ void LoggerThread::run(void)
LoggingItem *item = logQueue.dequeue();
qLock.unlock();
+ fillItem(item);
handleItem(item);
logConsole(item);
item->deleteItem();
@@ -350,54 +352,54 @@ void LoggerThread::messageReceived(const QList<QByteArray> &msg)
/// \param item The LoggingItem to be handled
void LoggerThread::handleItem(LoggingItem *item)
{
- if (item->type & kRegistering)
+ if (item->m_type & kRegistering)
{
- int64_t tid = item->getThreadTid();
+ item->m_tid = item->getThreadTid();
QMutexLocker locker(&logThreadMutex);
- logThreadHash[item->threadId] = strdup(item->threadName);
+ logThreadHash[item->m_threadId] = strdup(item->m_threadName);
if (debugRegistration)
{
- snprintf(item->message, LOGLINE_MAX,
+ snprintf(item->m_message, LOGLINE_MAX,
"Thread 0x%" PREFIX64 "X (%" PREFIX64
"d) registered as \'%s\'",
- (long long unsigned int)item->threadId,
- (long long int)tid,
- logThreadHash[item->threadId]);
+ (long long unsigned int)item->m_threadId,
+ (long long int)item->m_tid,
+ logThreadHash[item->m_threadId]);
}
}
- else if (item->type & kDeregistering)
+ else if (item->m_type & kDeregistering)
{
int64_t tid = 0;
{
QMutexLocker locker(&logThreadTidMutex);
- if( logThreadTidHash.contains(item->threadId) )
+ if( logThreadTidHash.contains(item->m_threadId) )
{
- tid = logThreadTidHash[item->threadId];
- logThreadTidHash.remove(item->threadId);
+ tid = logThreadTidHash[item->m_threadId];
+ logThreadTidHash.remove(item->m_threadId);
}
}
QMutexLocker locker(&logThreadMutex);
- if (logThreadHash.contains(item->threadId))
+ if (logThreadHash.contains(item->m_threadId))
{
if (debugRegistration)
{
- snprintf(item->message, LOGLINE_MAX,
+ snprintf(item->m_message, LOGLINE_MAX,
"Thread 0x%" PREFIX64 "X (%" PREFIX64
"d) deregistered as \'%s\'",
- (long long unsigned int)item->threadId,
+ (long long unsigned int)item->m_threadId,
(long long int)tid,
- logThreadHash[item->threadId]);
+ logThreadHash[item->m_threadId]);
}
- item->threadName = logThreadHash[item->threadId];
- logThreadHash.remove(item->threadId);
+ item->m_threadName = logThreadHash[item->m_threadId];
+ logThreadHash.remove(item->m_threadId);
}
}
- if (item->message[0] != '\0')
+ if (item->m_message[0] != '\0')
{
// Send it to mythlogserver
m_zmqSocket->sendMessage(item->toByteArray());
@@ -412,24 +414,31 @@ bool LoggerThread::logConsole(LoggingItem *item)
char usPart[9];
char timestamp[TIMESTAMP_MAX];
- if (m_quiet || (m_progress && item->level > LOG_ERR))
+ if (m_quiet || (m_progress && item->m_level > LOG_ERR))
+ return false;
+
+ if (!(item->m_type & kMessage))
return false;
item->refcount.ref();
- if (item->type & kStandardIO)
- snprintf( line, MAX_STRING_LENGTH, "%s", item->message );
+ if (item->m_type & kStandardIO)
+ snprintf( line, MAX_STRING_LENGTH, "%s", item->m_message );
else
{
- strftime( timestamp, TIMESTAMP_MAX-8, "%Y-%m-%d %H:%M:%S",
- (const struct tm *)&item->tm );
- snprintf( usPart, 9, ".%06d", (int)(item->usec) );
+ time_t epoch = item->epoch();
+ struct tm tm;
+ localtime_r(&epoch, &tm);
+
+ strftime( timestamp, TIMESTAMP_MAX-8, "%Y-%m-%d %H:%M:%S",
+ (const struct tm *)&tm );
+ snprintf( usPart, 9, ".%06d", (int)(item->m_usec) );
strcat( timestamp, usPart );
char shortname;
{
QMutexLocker locker(&loglevelMapMutex);
- LoglevelDef *lev = loglevelMap.value(item->level, NULL);
+ LoglevelDef *lev = loglevelMap.value(item->m_level, NULL);
if (!lev)
shortname = '-';
else
@@ -437,7 +446,7 @@ bool LoggerThread::logConsole(LoggingItem *item)
}
snprintf( line, MAX_STRING_LENGTH, "%s %c %s\n", timestamp,
- shortname, item->message );
+ shortname, item->m_message );
}
int result = write( 1, line, strlen(line) );
@@ -476,6 +485,17 @@ bool LoggerThread::flush(int timeoutMS)
return logQueue.isEmpty();
}
+void LoggerThread::fillItem(LoggingItem *item)
+{
+ if (!item)
+ return;
+
+ item->setAppName(m_appname);
+ item->setTable(m_tablename);
+ item->setLogFile(m_filename);
+ item->setFacility(m_facility);
+}
+
static QAtomicInt item_count;
static QAtomicInt malloc_count;
@@ -494,7 +514,8 @@ static QTime memory_time;
/// \return LoggingItem that was created
LoggingItem *LoggingItem::create(const char *_file,
const char *_function,
- int _line, LogLevel_t _level, int _type)
+ int _line, LogLevel_t _level,
+ LoggingType _type)
{
LoggingItem *item = new LoggingItem(_file, _function, _line, _level, _type);
@@ -524,14 +545,11 @@ LoggingItem *LoggingItem::create(const char *_file,
LoggingItem *LoggingItem::create(QByteArray &buf)
{
// Deserialize buffer
+ QJson::Parser parser;
+ QVariant variant = parser.parse(buf);
- const char *_file = "";
- const char *_function = "";
- int _line = 0;
- LogLevel_t _level = (LogLevel_t)LOG_DEBUG;
- LoggingType _type = kMessage;
-
- LoggingItem *item = new LoggingItem(_file, _function, _line, _level, _type);
+ LoggingItem *item = new LoggingItem;
+ QJson::QObjectHelper::qvariant2qobject(variant.toMap(), item);
malloc_count.ref();
@@ -562,8 +580,8 @@ void LoggingItem::deleteItem(void)
{
if (!refcount.deref())
{
- if (threadName)
- free(threadName);
+ if (m_threadName)
+ free(m_threadName);
item_count.deref();
this->deleteLater();
}
@@ -592,7 +610,8 @@ void LogPrintLine( uint64_t mask, LogLevel_t level, const char *file, int line,
int type = kMessage;
type |= (mask & VB_FLUSH) ? kFlush : 0;
type |= (mask & VB_STDIO) ? kStandardIO : 0;
- LoggingItem *item = LoggingItem::create(file, function, line, level, type);
+ LoggingItem *item = LoggingItem::create(file, function, line, level,
+ (LoggingType)type);
if (!item)
return;
@@ -606,7 +625,7 @@ void LogPrintLine( uint64_t mask, LogLevel_t level, const char *file, int line,
}
va_start(arguments, format);
- vsnprintf(item->message, LOGLINE_MAX, format, arguments);
+ vsnprintf(item->m_message, LOGLINE_MAX, format, arguments);
va_end(arguments);
if (formatcopy)
@@ -711,9 +730,11 @@ void logStart(QString logfile, int progress, int quiet, int facility,
logPropagateCalc();
+ QString table = dblog ? QString("logging") : QString("");
+
QMutexLocker qLock(&logQueueMutex);
if (!logThread)
- logThread = new LoggerThread(logfile, progress, quiet);
+ logThread = new LoggerThread(logfile, progress, quiet, table, facility);
logThread->start();
}
@@ -744,7 +765,7 @@ void loggingRegisterThread(const QString &name)
kRegistering);
if (item)
{
- item->threadName = strdup((char *)name.toLocal8Bit().constData());
+ item->setThreadName((char *)name.toLocal8Bit().constData());
logQueue.enqueue(item);
}
}
View
118 mythtv/libs/libmythbase/logging.h
@@ -37,16 +37,36 @@ typedef enum {
class LoggerThread;
+typedef struct tm tmType;
+
/// \brief The logging items that are generated by LOG() and are sent to the
/// console and to mythlogserver via ZeroMQ
class LoggingItem: public QObject
{
- Q_OBJECT;
+ Q_OBJECT
+
+ Q_PROPERTY(int pid READ pid WRITE setPid)
+ Q_PROPERTY(qlonglong tid READ tid WRITE setTid)
+ Q_PROPERTY(qulonglong threadId READ threadId WRITE setThreadId)
+ Q_PROPERTY(uint usec READ usec WRITE setUsec)
+ Q_PROPERTY(int line READ line WRITE setLine)
+ Q_PROPERTY(int type READ type WRITE setType)
+ Q_PROPERTY(int level READ level WRITE setLevel)
+ Q_PROPERTY(int facility READ facility WRITE setFacility)
+ Q_PROPERTY(qlonglong epoch READ epoch WRITE setEpoch)
+ Q_PROPERTY(QString file READ file WRITE setFile)
+ Q_PROPERTY(QString function READ function WRITE setFunction)
+ Q_PROPERTY(QString threadName READ threadName WRITE setThreadName)
+ Q_PROPERTY(QString appName READ appName WRITE setAppName)
+ Q_PROPERTY(QString table READ table WRITE setTable)
+ Q_PROPERTY(QString logFile READ logFile WRITE setLogFile)
+ Q_PROPERTY(QString message READ message WRITE setMessage)
+ Q_ENUMS(LoggingType)
+ Q_ENUMS(LogLevel_t)
friend class LoggerThread;
friend void LogPrintLine(uint64_t, LogLevel_t, const char *, int,
- const char *, int, const char *, ... );
- friend void loggingRegisterThread(const QString &);
+ const char *, int, const char *, ... );
public:
char *getThreadName(void);
@@ -58,22 +78,82 @@ class LoggingItem: public QObject
void deleteItem(void);
QByteArray toByteArray(void);
- protected:
QAtomicInt refcount;
- uint64_t threadId;
- uint32_t usec;
- int line;
- int type;
- LogLevel_t level;
- struct tm tm;
- const char *file;
- const char *function;
- char *threadName;
- char message[LOGLINE_MAX+1];
+
+ int pid() const { return m_pid; };
+ qlonglong tid() const { return m_tid; };
+ qulonglong threadId() const { return m_threadId; };
+ uint usec() const { return m_usec; };
+ int line() const { return m_line; };
+ int type() const { return (int)m_type; };
+ int level() const { return (int)m_level; };
+ int facility() const { return m_facility; };
+ qlonglong epoch() const { return m_epoch; };
+ QString file() const { return QString(m_file); };
+ QString function() const { return QString(m_function); };
+ QString threadName() const { return QString(m_threadName); };
+ QString appName() const { return QString(m_appName); };
+ QString table() const { return QString(m_table); };
+ QString logFile() const { return QString(m_logFile); };
+ QString message() const { return QString(m_message); };
+
+ void setPid(const int val) { m_pid = val; };
+ void setTid(const qlonglong val) { m_tid = val; };
+ void setThreadId(const qulonglong val) { m_threadId = val; };
+ void setUsec(const uint val) { m_usec = val; };
+ void setLine(const int val) { m_line = val; };
+ void setType(const int val) { m_type = (LoggingType)val; };
+ void setLevel(const int val) { m_level = (LogLevel_t)val; };
+ void setFacility(const int val) { m_facility = val; };
+ void setEpoch(const qlonglong val) { m_epoch = val; };
+ void setFile(const QString val)
+ { m_file = strdup(val.toLocal8Bit().constData()); };
+ void setFunction(const QString val)
+ { m_function = strdup(val.toLocal8Bit().constData()); };
+ void setThreadName(const QString val)
+ { m_threadName = strdup(val.toLocal8Bit().constData()); };
+ void setAppName(const QString val)
+ { m_appName = strdup(val.toLocal8Bit().constData()); };
+ void setTable(const QString val)
+ { m_table = strdup(val.toLocal8Bit().constData()); };
+ void setLogFile(const QString val)
+ { m_logFile = strdup(val.toLocal8Bit().constData()); };
+ void setMessage(const QString val)
+ {
+ strncpy(m_message, val.toLocal8Bit().constData(), LOGLINE_MAX);
+ m_message[LOGLINE_MAX] = '\0';
+ };
+
+ const char *rawFile() const { return m_file; };
+ const char *rawFunction() const { return m_function; };
+ const char *rawThreadName() const { return m_threadName; };
+ const char *rawAppName() const { return m_appName; };
+ const char *rawTable() const { return m_table; };
+ const char *rawLogFile() const { return m_logFile; };
+ const char *rawMessage() const { return m_message; };
+
+ protected:
+ int m_pid;
+ qlonglong m_tid;
+ qulonglong m_threadId;
+ uint m_usec;
+ int m_line;
+ LoggingType m_type;
+ LogLevel_t m_level;
+ int m_facility;
+ qlonglong m_epoch;
+ const char *m_file;
+ const char *m_function;
+ char *m_threadName;
+ const char *m_appName;
+ const char *m_table;
+ const char *m_logFile;
+ char m_message[LOGLINE_MAX+1];
private:
+ LoggingItem();
LoggingItem(const char *_file, const char *_function,
- int _line, LogLevel_t _level, int _type);
+ int _line, LogLevel_t _level, LoggingType _type);
};
/// \brief The logging thread that consumes the logging queue and dispatches
@@ -82,12 +162,14 @@ class LoggerThread : public QObject, public MThread
{
Q_OBJECT
public:
- LoggerThread(QString filename, bool progress, bool quiet);
+ LoggerThread(QString filename, bool progress, bool quiet, QString table,
+ int facility);
~LoggerThread();
void run(void);
void stop(void);
bool flush(int timeoutMS = 200000);
void handleItem(LoggingItem *item);
+ void fillItem(LoggingItem *item);
private:
bool logConsole(LoggingItem *item);
QWaitCondition *m_waitNotEmpty; ///< Condition variable for waiting
@@ -103,6 +185,10 @@ class LoggerThread : public QObject, public MThread
QString m_filename; ///< Filename of debug logfile
bool m_progress; ///< show only LOG_ERR and more important (console only)
int m_quiet; ///< silence the console (console only)
+ QString m_appname; ///< Cached application name
+ QString m_tablename; ///< Cached table name for db logging
+ int m_facility; ///< Cached syslog facility (or -1 to disable)
+
nzmqt::PollingZMQContext *m_zmqContext; ///< ZeroMQ context to use in this logger
nzmqt::ZMQSocket *m_zmqSocket; ///< ZeroMQ socket to talk to mythlogserver
View
886 mythtv/libs/libmythbase/loggingserver.cpp
@@ -25,7 +25,6 @@ using namespace std;
#include "compat.h"
#include <stdlib.h>
-#define SYSLOG_NAMES
#ifndef _WIN32
#include <syslog.h>
#endif
@@ -53,33 +52,11 @@ extern "C" {
#include <mach/mach.h>
#endif
-QMutex loggerListMutex;
-QList<LoggerBase *> loggerList;
+static QMutex loggerListMutex;
+static QList<LoggerBase *> loggerList;
-QMutex logQueueMutex;
-QQueue<LoggingItem *> logQueue;
-QRegExp logRegExp = QRegExp("[%]{1,2}");
-
-QMutex logThreadMutex;
-QHash<uint64_t, char *> logThreadHash;
-
-QMutex logThreadTidMutex;
-QHash<uint64_t, int64_t> logThreadTidHash;
-
-LogServerThread *logThread = NULL;
-bool logThreadFinished = false;
-bool debugRegistration = false;
-
-typedef struct {
- bool propagate;
- int quiet;
- int facility;
- bool dblog;
- QString path;
-} LogPropagateOpts;
-
-LogPropagateOpts logPropagateOpts;
-QString logPropagateArgs;
+static LogServerThread *logThread = NULL;
+static bool logThreadFinished = false;
#define TIMESTAMP_MAX 30
#define MAX_STRING_LENGTH (LOGLINE_MAX+120)
@@ -92,20 +69,13 @@ void logSighup( int signum, siginfo_t *info, void *secret );
/// \brief LoggerBase class constructor. Adds the new logger instance to the
/// loggerList.
/// \param string a C-string of the handle for this instance (NULL if unused)
-/// \param number an integer for the handle for this instance
-LoggerBase::LoggerBase(char *string, int number)
+LoggerBase::LoggerBase(char *string)
{
QMutexLocker locker(&loggerListMutex);
if (string)
- {
- m_handle.string = strdup(string);
- m_string = true;
- }
+ m_handle = strdup(string);
else
- {
- m_handle.number = number;
- m_string = false;
- }
+ m_handle = NULL;
loggerList.append(this);
}
@@ -116,15 +86,15 @@ LoggerBase::~LoggerBase()
QMutexLocker locker(&loggerListMutex);
loggerList.removeAll(this);
- if (m_string)
- free(m_handle.string);
+ if (m_handle)
+ free(m_handle);
}
/// \brief FileLogger constructor
/// \param filename Filename of the logfile.
FileLogger::FileLogger(char *filename) :
- LoggerBase(filename, 0), m_opened(false), m_fd(-1)
+ LoggerBase(filename), m_opened(false), m_fd(-1)
{
m_fd = open(filename, O_WRONLY|O_CREAT|O_APPEND, 0664);
m_opened = (m_fd != -1);
@@ -145,14 +115,11 @@ FileLogger::~FileLogger()
/// This allows for logrollers to be used.
void FileLogger::reopen(void)
{
- char *filename = m_handle.string;
-
close(m_fd);
- m_fd = open(filename, O_WRONLY|O_CREAT|O_APPEND, 0664);
+ m_fd = open(m_handle, O_WRONLY|O_CREAT|O_APPEND, 0664);
m_opened = (m_fd != -1);
- LOG(VB_GENERAL, LOG_INFO, QString("Rolled logging on %1")
- .arg(filename));
+ LOG(VB_GENERAL, LOG_INFO, QString("Rolled logging on %1") .arg(m_handle));
}
/// \brief Process a log message, writing to the logfile
@@ -168,36 +135,41 @@ bool FileLogger::logmsg(LoggingItem *item)
item->refcount.ref();
- strftime( timestamp, TIMESTAMP_MAX-8, "%Y-%m-%d %H:%M:%S",
- (const struct tm *)&item->tm );
- snprintf( usPart, 9, ".%06d", (int)(item->usec) );
+ time_t epoch = item->epoch();
+ struct tm tm;
+ localtime_r(&epoch, &tm);
+
+ strftime(timestamp, TIMESTAMP_MAX-8, "%Y-%m-%d %H:%M:%S",
+ (const struct tm *)&tm);
+ snprintf( usPart, 9, ".%06d", (int)(item->usec()) );
strcat( timestamp, usPart );
char shortname;
{
QMutexLocker locker(&loglevelMapMutex);
- LoglevelMap::iterator it = loglevelMap.find(item->level);
+ LoglevelMap::iterator it = loglevelMap.find(item->level());
if (it == loglevelMap.end())
shortname = '-';
else
shortname = (*it)->shortname;
}
- if( item->tid )
+ if( item->tid() )
snprintf( line, MAX_STRING_LENGTH,
- "%s %c [%d/%d] %s %s:%d (%s) - %s\n",
- timestamp, shortname, item->pid, item->tid, item->threadName,
- item->file, item->line, item->function, item->message );
+ "%s %c [%d/%" PREFIX64 "d] %s %s:%d (%s) - %s\n",
+ timestamp, shortname, item->pid(), item->tid(),
+ item->rawThreadName(), item->rawFile(), item->line(),
+ item->rawFunction(), item->rawMessage() );
else
snprintf( line, MAX_STRING_LENGTH,
"%s %c [%d] %s %s:%d (%s) - %s\n",
- timestamp, shortname, item->pid, item->threadName, item->file,
- item->line, item->function, item->message );
- }
+ timestamp, shortname, item->pid(), item->rawThreadName(),
+ item->rawFile(), item->line(), item->rawFunction(),
+ item->rawMessage() );
- int result = write( m_fd, line, strlen(line) );
+ int result = write(m_fd, line, strlen(line));
- deleteItem(item);
+ item->deleteItem();
if( result == -1 )
{
@@ -214,10 +186,8 @@ bool FileLogger::logmsg(LoggingItem *item)
#ifndef _WIN32
/// \brief SyslogLogger constructor
/// \param facility Syslog facility to use in logging
-SyslogLogger::SyslogLogger() : LoggerBase(NULL, 0), m_opened(false)
+SyslogLogger::SyslogLogger() : LoggerBase(NULL), m_opened(false)
{
- CODE *name;
-
openlog(NULL, LOG_NDELAY, 0 );
m_opened = true;
@@ -236,23 +206,24 @@ SyslogLogger::~SyslogLogger()
/// \param item LoggingItem containing the log message to process
bool SyslogLogger::logmsg(LoggingItem *item)
{
- if (!m_opened)
+ if (!m_opened || item->facility() <= 0)
return false;
char shortname;
{
QMutexLocker locker(&loglevelMapMutex);
- LoglevelDef *lev = loglevelMap.value(item->level, NULL);
+ LoglevelDef *lev = loglevelMap.value(item->level(), NULL);
if (!lev)
shortname = '-';
else
shortname = lev->shortname;
}
- syslog(item->level | item->facility, "%s[%d]: %c %s %s:%d (%s) %s",
- item->appName, item->pid, shortname, item->threadName, item->file,
- item->line, item->function, item->message);
+ syslog(item->level() | item->facility(), "%s[%d]: %c %s %s:%d (%s) %s",
+ item->rawAppName(), item->pid(), shortname, item->rawThreadName(),
+ item->rawFile(), item->line(), item->rawFunction(),
+ item->rawMessage());
return true;
}
@@ -262,7 +233,7 @@ const int DatabaseLogger::kMinDisabledTime = 1000;
/// \brief DatabaseLogger constructor
/// \param table C-string of the database table to log to
-DatabaseLogger::DatabaseLogger(char *table) : LoggerBase(table, 0),
+DatabaseLogger::DatabaseLogger(char *table) : LoggerBase(table),
m_opened(false),
m_loggingTableExists(false)
{
@@ -272,10 +243,10 @@ DatabaseLogger::DatabaseLogger(char *table) : LoggerBase(table, 0),
" line, function, msgtime, level, message) "
"VALUES (:HOST, :APP, :PID, :TID, :THREAD, :FILENAME, "
" :LINE, :FUNCTION, :MSGTIME, :LEVEL, :MESSAGE)")
- .arg(m_handle.string);
+ .arg(m_handle);
LOG(VB_GENERAL, LOG_INFO, QString("Added database logging to table %1")
- .arg(m_handle.string));
+ .arg(m_handle));
m_thread = new DBLoggerThread(this);
m_thread->start();
@@ -349,20 +320,22 @@ bool DatabaseLogger::logmsg(LoggingItem *item)
bool DatabaseLogger::logqmsg(MSqlQuery &query, LoggingItem *item)
{
char timestamp[TIMESTAMP_MAX];
- char *threadName = getThreadName(item);
- pid_t tid = getThreadTid(item);
- strftime( timestamp, TIMESTAMP_MAX-8, "%Y-%m-%d %H:%M:%S",
- (const struct tm *)&item->tm );
+ time_t epoch = item->epoch();
+ struct tm tm;
+ localtime_r(&epoch, &tm);
- query.bindValue(":TID", tid);
- query.bindValue(":THREAD", threadName);
- query.bindValue(":FILENAME", item->file);
- query.bindValue(":LINE", item->line);
- query.bindValue(":FUNCTION", item->function);
+ strftime(timestamp, TIMESTAMP_MAX-8, "%Y-%m-%d %H:%M:%S",
+ (const struct tm *)&tm);
+
+ query.bindValue(":TID", item->tid());
+ query.bindValue(":THREAD", item->threadName());
+ query.bindValue(":FILENAME", item->file());
+ query.bindValue(":LINE", item->line());
+ query.bindValue(":FUNCTION", item->function());
query.bindValue(":MSGTIME", timestamp);
- query.bindValue(":LEVEL", item->level);
- query.bindValue(":MESSAGE", item->message);
+ query.bindValue(":LEVEL", item->level());
+ query.bindValue(":MESSAGE", item->message());
if (!query.exec())
{
@@ -380,7 +353,7 @@ bool DatabaseLogger::logqmsg(MSqlQuery &query, LoggingItem *item)
return false;
}
- deleteItem(item);
+ item->deleteItem();
return true;
}
@@ -405,7 +378,7 @@ bool DatabaseLogger::isDatabaseReady(void)
if ((db) && db->HaveValidDatabase())
{
if ( !m_loggingTableExists )
- m_loggingTableExists = tableExists(m_handle.string);
+ m_loggingTableExists = tableExists(m_handle);
if ( m_loggingTableExists )
ready = true;
@@ -449,7 +422,7 @@ DBLoggerThread::DBLoggerThread(DatabaseLogger *logger) :
MThread("DBLogger"),
m_logger(logger),
m_queue(new QQueue<LoggingItem *>),
- m_wait(new QWaitCondition()), aborted(false)
+ m_wait(new QWaitCondition()), m_aborted(false)
{
}
@@ -462,7 +435,7 @@ DBLoggerThread::~DBLoggerThread()
QMutexLocker qLock(&m_queueMutex);
while (!m_queue->empty())
- deleteItem(m_queue->dequeue());
+ m_queue->dequeue()->deleteItem();
delete m_queue;
delete m_wait;
m_queue = NULL;
@@ -479,14 +452,14 @@ void DBLoggerThread::run(void)
// at all, and that's undesirable.
while (true)
{
- if ((aborted || (gCoreContext && m_logger->isDatabaseReady())))
+ if ((m_aborted || (gCoreContext && m_logger->isDatabaseReady())))
break;
QMutexLocker locker(&m_queueMutex);
m_wait->wait(locker.mutex(), 100);
}
- if (!aborted)
+ if (!m_aborted)
{
// We want the query to be out of scope before the RunEpilog() so
// shutdown occurs correctly as otherwise the connection appears still
@@ -495,7 +468,7 @@ void DBLoggerThread::run(void)
m_logger->prepare(*query);
QMutexLocker qLock(&m_queueMutex);
- while (!aborted || !m_queue->isEmpty())
+ while (!m_aborted || !m_queue->isEmpty())
{
if (m_queue->isEmpty())
{
@@ -509,7 +482,7 @@ void DBLoggerThread::run(void)
qLock.unlock();
- if (item->message[0] != '\0')
+ if (item->message()[0] != '\0')
{
if (!m_logger->logqmsg(*query, item))
{
@@ -524,7 +497,7 @@ void DBLoggerThread::run(void)
}
else
{
- deleteItem(item);
+ item->deleteItem();
}
qLock.relock();
@@ -538,94 +511,19 @@ void DBLoggerThread::run(void)
RunEpilog();
}
-/// \brief Tell the thread to stop by setting the aborted flag.
+/// \brief Tell the thread to stop by setting the m_aborted flag.
void DBLoggerThread::stop(void)
{
QMutexLocker qLock(&m_queueMutex);
- aborted = true;
+ m_aborted = true;
m_wait->wakeAll();
}
-/// \brief Get the name of the thread that produced the LoggingItem
-/// \param item the LoggingItem in question
-/// \return C-string of the thread name
-char *getThreadName( LoggingItem *item )
-{
- static const char *unknown = "thread_unknown";
- char *threadName;
-
- if( !item )
- return( (char *)unknown );
-
- if( !item->threadName )
- {
- QMutexLocker locker(&logThreadMutex);
- if( logThreadHash.contains(item->threadId) )
- threadName = logThreadHash[item->threadId];
- else
- threadName = (char *)unknown;
- }
- else
- {
- threadName = item->threadName;
- }
-
- return( threadName );
-}
-
-/// \brief Get the thread ID of the thread that produced the LoggingItem
-/// \param item the LoggingItem in question
-/// \return Thread ID of the producing thread, cast to a 64-bit signed integer
-/// \notes In different platforms, the actual value returned here will vary.
-/// The intention is to get a thread ID that will map well to what is
-/// shown in gdb.
-int64_t getThreadTid( LoggingItem *item )
-{
- pid_t tid = 0;
-
- if( !item )
- return( 0 );
-
- QMutexLocker locker(&logThreadTidMutex);
- if( logThreadTidHash.contains(item->threadId) )
- tid = logThreadTidHash[item->threadId];
-
- return( tid );
-}
-
-/// \brief Set the thread ID of the thread that produced the LoggingItem. This
-/// code is actually run in the thread in question as part of the call
-/// to LOG()
-/// \param item the LoggingItem in question
-/// \notes In different platforms, the actual value returned here will vary.
-/// The intention is to get a thread ID that will map well to what is
-/// shown in gdb.
-void setThreadTid( LoggingItem *item )
-{
- QMutexLocker locker(&logThreadTidMutex);
-
- if( ! logThreadTidHash.contains(item->threadId) )
- {
- int64_t tid = 0;
-
-#if defined(linux)
- tid = (int64_t)syscall(SYS_gettid);
-#elif defined(__FreeBSD__)
- long lwpid;
- int dummy = thr_self( &lwpid );
- (void)dummy;
- tid = (int64_t)lwpid;
-#elif CONFIG_DARWIN
- tid = (int64_t)mach_thread_self();
-#endif
- logThreadTidHash[item->threadId] = tid;
- }
-}
-
/// \brief LogServerThread constructor.
LogServerThread::LogServerThread() :
MThread("LogServer"), m_aborted(false)
{
+ moveToThread(qthread());
}
/// \brief LogServerThread destructor.
@@ -648,7 +546,7 @@ void LogServerThread::run(void)
m_zmqContext->start();
m_zmqInSock = m_zmqContext->createSocket(nzmqt::ZMQSocket::TYP_ROUTER);
- connect(m_zmqInSocket, SIGNAL(messageReceived(const QList<QByteArray>&)),
+ connect(m_zmqInSock, SIGNAL(messageReceived(const QList<QByteArray>&)),
this, SLOT(messageReceived(const QList<QByteArray>&)));
m_zmqInSock->bindTo("tcp://127.0.0.1:35327");
@@ -672,106 +570,27 @@ void LogServerThread::run(void)
/// \brief Handles messages received from logging clients
/// \param msg The message received (can be multi-part)
-void LoggerThread::messageReceived(const QList<QByteArray> &msg)
+void LogServerThread::messageReceived(const QList<QByteArray> &msg)
{
- m_zmqPubSock->sendMessage(msg);
-}
-
-
-
-/// \brief Handles each LoggingItem, generally by handing it off to the
-/// various running logger instances. There is a special case for
-/// thread registration and deregistration which are also included in
-/// the logging queue to keep the thread names in sync with the log
-/// messages.
-/// \param item The LoggingItem to be handled
-void LoggerThread::handleItem(LoggingItem *item)
-{
- if (item->type & kRegistering)
+ QList<QByteArray>::const_iterator it = msg.begin();
+ int i = 0;
+ for (; it != msg.end(); ++it, i++)
{
- int64_t tid = getThreadTid(item);
-
- QMutexLocker locker(&logThreadMutex);
- logThreadHash[item->threadId] = strdup(item->threadName);
-
- if (debugRegistration)
- {
- snprintf(item->message, LOGLINE_MAX,
- "Thread 0x%" PREFIX64 "X (%" PREFIX64
- "d) registered as \'%s\'",
- (long long unsigned int)item->threadId,
- (long long int)tid,
- logThreadHash[item->threadId]);
- }
- }
- else if (item->type & kDeregistering)
- {
- int64_t tid = 0;
-
- {
- QMutexLocker locker(&logThreadTidMutex);
- if( logThreadTidHash.contains(item->threadId) )
- {
- tid = logThreadTidHash[item->threadId];
- logThreadTidHash.remove(item->threadId);
- }
- }
-
- QMutexLocker locker(&logThreadMutex);
- if (logThreadHash.contains(item->threadId))
- {
- if (debugRegistration)
- {
- snprintf(item->message, LOGLINE_MAX,
- "Thread 0x%" PREFIX64 "X (%" PREFIX64
- "d) deregistered as \'%s\'",
- (long long unsigned int)item->threadId,
- (long long int)tid,
- logThreadHash[item->threadId]);
- }
- item->threadName = logThreadHash[item->threadId];
- logThreadHash.remove(item->threadId);
- }
+ QByteArray buf = *it;
+ cout << i << ":\t" << buf.toHex().constData() << endl << "\t" << buf.constData() << endl;
}
+ m_zmqPubSock->sendMessage(msg);
+}
- if (item->message[0] != '\0')
- {
- QMutexLocker locker(&loggerListMutex);
- QList<LoggerBase *>::iterator it;
- for (it = loggerList.begin(); it != loggerList.end(); ++it)
- (*it)->logmsg(item);
- }
-}
/// \brief Stop the thread by setting the abort flag after waiting a second for
/// the queue to be flushed.
-void LoggerThread::stop(void)
-{
- QMutexLocker qLock(&logQueueMutex);
- flush(1000);
- aborted = true;
- m_waitNotEmpty->wakeAll();
-}
-
-/// \brief Wait for the queue to be flushed (up to a timeout)
-/// \param timeoutMS The number of ms to wait for the queue to flush
-/// \return true if the queue is empty, false otherwise
-bool LoggerThread::flush(int timeoutMS)
+void LogServerThread::stop(void)
{
- QTime t;
- t.start();
- while (!aborted && logQueue.isEmpty() && t.elapsed() < timeoutMS)
- {
- m_waitNotEmpty->wakeAll();
- int left = timeoutMS - t.elapsed();
- if (left > 0)
- m_waitEmpty->wait(&logQueueMutex, left);
- }
- return logQueue.isEmpty();
+ m_aborted = true;
}
-static QList<LoggingItem*> item_recycler;
static QAtomicInt item_count;
static QAtomicInt malloc_count;
@@ -781,144 +600,6 @@ static int max_count = 0;
static QTime memory_time;
#endif
-/// \brief Create a new LoggingItem
-/// \param _file filename of the source file where the log message is from
-/// \param _function source function where the log message is from
-/// \param _line line number in the source where the log message is from
-/// \param _level logging level of the message (LogLevel_t)
-/// \param _type type of logging message
-/// \return LoggingItem that was created
-static LoggingItem *createItem(const char *_file, const char *_function,
- int _line, LogLevel_t _level, int _type)
-{
- LoggingItem *item = new LoggingItem(_file, _function, _line, _level, _type);
-
- malloc_count.ref();
-
-#if DEBUG_MEMORY
- int val = item_count.fetchAndAddRelaxed(1) + 1;
- if (val == 0)
- memory_time.start();
- max_count = (val > max_count) ? val : max_count;
- if (memory_time.elapsed() > 1000)
- {
- cout<<"current memory usage: "
- <<val<<" * "<<sizeof(LoggingItem)<<endl;
- cout<<"max memory usage: "
- <<max_count<<" * "<<sizeof(LoggingItem)<<endl;
- cout<<"malloc count: "<<(int)malloc_count<<endl;
- memory_time.start();
- }
-#else
- item_count.ref();
-#endif
-
- return item;
-}
-
-/// \brief Delete the LoggingItem once its reference count has run down
-/// \param item LoggingItem to delete.
-static void deleteItem(LoggingItem *item)
-{
- if (!item)
- return;
-
- if (!item->refcount.deref())
- {
- if (item->threadName)
- free(item->threadName);
- item_count.deref();
- delete item;
- }
-}
-
-/// \brief Fill in the time structure from the current time to make a timestamp
-/// for the log message. This is run as part of the LOG() call.
-/// \param tm pointer to the time structure to fill in
-/// \param usec pointer to a 32bit unsigned int to return the number of us
-void LogTimeStamp( struct tm *tm, uint32_t *usec )
-{
- if( !usec || !tm )
- return;
-
- time_t epoch;
-
-#if HAVE_GETTIMEOFDAY
- struct timeval tv;
- gettimeofday(&tv, NULL);
- epoch = tv.tv_sec;
- *usec = tv.tv_usec;
-#else
- /* Stupid system has no gettimeofday, use less precise QDateTime */
- QDateTime date = QDateTime::currentDateTime();
- QTime time = date.time();
- epoch = date.toTime_t();
- *usec = time.msec() * 1000;
-#endif
-
- localtime_r(&epoch, tm);
-}
-
-/// \brief Format and send a log message into the queue. This is called from
-/// the LOG() macro. The intention is minimal blocking of the caller.
-/// \param mask Verbosity mask of the message (VB_*)
-/// \param level Log level of this message (LOG_* - matching syslog levels)
-/// \param file Filename of source code logging the message
-/// \param line Line number within the source of log message source
-/// \param function Function name of the log message source
-/// \param fromQString true if this message originated from QString
-/// \param format printf format string (when not from QString), log message
-/// (when from QString)
-/// \param ... printf arguments (when not from QString)
-void LogPrintLine( uint64_t mask, LogLevel_t level, const char *file, int line,
- const char *function, int fromQString,
- const char *format, ... )
-{
- va_list arguments;
-
- QMutexLocker qLock(&logQueueMutex);
-
- int type = kMessage;
- type |= (mask & VB_FLUSH) ? kFlush : 0;
- type |= (mask & VB_STDIO) ? kStandardIO : 0;
- LoggingItem *item = createItem(file, function, line, level, type);
- if (!item)
- return;
-
- char *formatcopy = NULL;
- if( fromQString && strchr(format, '%') )
- {
- QString string(format);
- format = strdup(string.replace(logRegExp, "%%").toLocal8Bit()
- .constData());
- formatcopy = (char *)format;
- }
-
- va_start(arguments, format);
- vsnprintf(item->message, LOGLINE_MAX, format, arguments);
- va_end(arguments);
-
- if (formatcopy)
- free(formatcopy);
-
- logQueue.enqueue(item);
-
- if (logThread && logThreadFinished && !logThread->isRunning())
- {
- while (!logQueue.isEmpty())
- {
- item = logQueue.dequeue();
- qLock.unlock();
- logThread->handleItem(item);
- deleteItem(item);
- qLock.relock();
- }
- }
- else if (logThread && !logThreadFinished && (type & kFlush))
- {
- logThread->flush();
- }
-}
#ifndef _WIN32
/// \brief SIGHUP handler - reopen all open logfiles for logrollers
@@ -937,48 +618,6 @@ void logSighup( int signum, siginfo_t *info, void *secret )
}
#endif
-/// \brief Generate the logPropagateArgs global with the latest logging
-/// level, mask, etc to propagate to all of the mythtv programs
-/// spawned from this one.
-void logPropagateCalc(void)
-{
- QString mask = verboseString.trimmed();
- mask.replace(QRegExp(" "), ",");
- mask.remove(QRegExp("^,"));
- logPropagateArgs = " --verbose " + mask;
-
- if (logPropagateOpts.propagate)
- logPropagateArgs += " --logpath " + logPropagateOpts.path;
-
- QString name = logLevelGetName(logLevel);
- logPropagateArgs += " --loglevel " + name;
-
- for (int i = 0; i < logPropagateOpts.quiet; i++)
- logPropagateArgs += " --quiet";
-
- if (!logPropagateOpts.dblog)
- logPropagateArgs += " --nodblog";
-
-#ifndef _WIN32
- if (logPropagateOpts.facility >= 0)
- {
- CODE *syslogname;
-
- for (syslogname = &facilitynames[0];
- (syslogname->c_name &&
- syslogname->c_val != logPropagateOpts.facility); syslogname++);
-
- logPropagateArgs += QString(" --syslog %1").arg(syslogname->c_name);
- }
-#endif
-}
-
-/// \brief Check if we are propagating a "--quiet"
-/// \return true if --quiet is being propagated
-bool logPropagateQuiet(void)
-{
- return logPropagateOpts.quiet;
-}
/// \brief Entry point to start logging for the application. This will
/// start up all of the threads needed.
@@ -992,58 +631,13 @@ bool logPropagateQuiet(void)
/// \param dblog true if database logging is requested
/// \param propagate true if the logfile path needs to be propagated to child
/// processes.
-void logStart(QString logfile, int progress, int quiet, int facility,
- LogLevel_t level, bool dblog, bool propagate)
+void logServerStart(void)
{
- LoggerBase *logger;
-
- {
- QMutexLocker qLock(&logQueueMutex);
- if (!logThread)
- logThread = new LoggerThread();
- }
-
- if (logThread->isRunning())
+ if (logThread && logThread->isRunning())
return;
- logLevel = level;
- LOG(VB_GENERAL, LOG_NOTICE, QString("Setting Log Level to LOG_%1")
- .arg(logLevelGetName(logLevel).toUpper()));
-
- logPropagateOpts.propagate = propagate;
- logPropagateOpts.quiet = quiet;
- logPropagateOpts.facility = facility;
- logPropagateOpts.dblog = dblog;
-
- if (propagate)
- {
- QFileInfo finfo(logfile);
- QString path = finfo.path();
- logPropagateOpts.path = path;
- }
-
- logPropagateCalc();
-
- /* log to the console */
- logger = new FileLogger((char *)"-", progress, quiet);
-
- /* Debug logfile */
- if( !logfile.isEmpty() )
- logger = new FileLogger((char *)logfile.toLocal8Bit().constData(),
- false, false);
-
-#ifndef _WIN32
- /* Syslog */
- if( facility == -1 )
- LOG(VB_GENERAL, LOG_CRIT,
- "Syslogging facility unknown, disabling syslog output");
- else if( facility >= 0 )
- logger = new SyslogLogger(facility);
-#endif
-
- /* Database */
- if( dblog )
- logger = new DatabaseLogger((char *)"logging");
+ if (!logThread)
+ logThread = new LogServerThread();
#ifndef _WIN32
/* Setup SIGHUP */
@@ -1055,13 +649,11 @@ void logStart(QString logfile, int progress, int quiet, int facility,
sigaction( SIGHUP, &sa, NULL );
#endif
- (void)logger;
-
logThread->start();
}
/// \brief Entry point for stopping logging for an application
-void logStop(void)
+void logServerStop(void)
{
if (logThread)
{
@@ -1085,326 +677,16 @@ void logStop(void)
}
}
-/// \brief Register the current thread with the given name. This is triggered
-/// by the RunProlog() call in each thread.
-/// \param name the name of the thread being registered. This is used for
-/// indicating the thread each log message is coming from.
-void loggingRegisterThread(const QString &name)
+void FileLogger::messageReceived(const QList<QByteArray>&)
{
- if (logThreadFinished)
- return;
-
- QMutexLocker qLock(&logQueueMutex);
-
- LoggingItem *item = createItem(__FILE__, __FUNCTION__, __LINE__,
- (LogLevel_t)LOG_DEBUG, kRegistering);
- if (item)
- {
- item->threadName = strdup((char *)name.toLocal8Bit().constData());
- logQueue.enqueue(item);
- }
-}
-
-/// \brief Deregister the current thread's name. This is triggered by the
-/// RunEpilog() call in each thread.
-void loggingDeregisterThread(void)
-{
- if (logThreadFinished)
- return;
-
- QMutexLocker qLock(&logQueueMutex);
-
- LoggingItem *item = createItem(__FILE__, __FUNCTION__, __LINE__,
- (LogLevel_t)LOG_DEBUG, kDeregistering);
- if (item)
- logQueue.enqueue(item);
-}
-
-/// \brief Map a syslog facility name back to the enumerated value
-/// \param facility QString containing the facility name
-/// \return Syslog facility as enumerated type. Negative if not found.
-int syslogGetFacility(QString facility)
-{
-#ifdef _WIN32
- LOG(VB_GENERAL, LOG_NOTICE,
- "Windows does not support syslog, disabling" );
- return( -2 );
-#else
- CODE *name;
- int i;
- QByteArray ba = facility.toLocal8Bit();
- char *string = (char *)ba.constData();
-
- for (i = 0, name = &facilitynames[0];
- name->c_name && strcmp(name->c_name, string); i++, name++);
-
- return( name->c_val );
-#endif
-}
-
-/// \brief Map a log level name back to the enumerated value
-/// \param level QString containing the log level name
-/// \return Log level as enumerated type. LOG_UNKNOWN if not found.
-LogLevel_t logLevelGet(QString level)
-{
- QMutexLocker locker(&loglevelMapMutex);
- if (!verboseInitialized)
- {
- locker.unlock();
- verboseInit();
- locker.relock();
- }
-
- for (LoglevelMap::iterator it = loglevelMap.begin();
- it != loglevelMap.end(); ++it)
- {
- LoglevelDef *item = (*it);
- if ( item->name == level.toLower() )
- return (LogLevel_t)item->value;
- }
-
- return LOG_UNKNOWN;
-}
-
-/// \brief Map a log level enumerated value back to the name
-/// \param level Enumerated value of the log level
-/// \return Log level name. "unknown" if not found.
-QString logLevelGetName(LogLevel_t level)
-{
- QMutexLocker locker(&loglevelMapMutex);
- if (!verboseInitialized)
- {
- locker.unlock();
- verboseInit();
- locker.relock();
- }
- LoglevelMap::iterator it = loglevelMap.find((int)level);
-
- if ( it == loglevelMap.end() )
- return QString("unknown");
-
- return (*it)->name;
}
-/// \brief Add a verbose level to the verboseMap. Done at initialization.
-/// \param mask verbose mask (VB_*)
-/// \param name name of the verbosity level
-/// \param additive true if this is to be ORed with other masks. false if
-/// is will clear the other bits.
-/// \param helptext Descriptive text for --verbose help output
-void verboseAdd(uint64_t mask, QString name, bool additive, QString helptext)
+void SyslogLogger::messageReceived(const QList<QByteArray>&)
{
- VerboseDef *item = new VerboseDef;
-
- item->mask = mask;
- name.detach();
- // VB_GENERAL -> general
- name.remove(0, 3);
- name = name.toLower();
- item->name = name;
- item->additive = additive;
- helptext.detach();
- item->helpText = helptext;
-
- verboseMap.insert(name, item);
-}
-
-/// \brief Add a log level to the logLevelMap. Done at initialization.
-/// \param value log level enumerated value (LOG_*) - matches syslog
-/// levels
-/// \param name name of the log level
-/// \param shortname one-letter short name for output into logs
-void loglevelAdd(int value, QString name, char shortname)
-{
- LoglevelDef *item = new LoglevelDef;
-
- item->value = value;
- name.detach();
- // LOG_CRIT -> crit
- name.remove(0, 4);
- name = name.toLower();
- item->name = name;
- item->shortname = shortname;
-
- loglevelMap.insert(value, item);
-}
-
-/// \brief Initialize the logging levels and verbose levels.
-void verboseInit(void)
-{
- QMutexLocker locker(&verboseMapMutex);
- QMutexLocker locker2(&loglevelMapMutex);
- verboseMap.clear();
- loglevelMap.clear();
-
- // This looks funky, so I'll put some explanation here. The verbosedefs.h
- // file gets included as part of the mythlogging.h include, and at that
- // time, the normal (without _IMPLEMENT_VERBOSE defined) code case will
- // define the VerboseMask enum. At this point, we force it to allow us
- // to include the file again, but with _IMPLEMENT_VERBOSE set so that the
- // single definition of the VB_* values can be shared to define also the
- // contents of verboseMap, via repeated calls to verboseAdd()
-
-#undef VERBOSEDEFS_H_
-#define _IMPLEMENT_VERBOSE
-#include "verbosedefs.h"
-
- verboseInitialized = true;
-}
-
-
-/// \brief Outputs the Verbose levels and their descriptions
-/// (for --verbose help)
-void verboseHelp(void)
-{
- QString m_verbose = verboseString.trimmed();
- m_verbose.replace(QRegExp(" "), ",");
- m_verbose.remove(QRegExp("^,"));
-
- cerr << "Verbose debug levels.\n"
- "Accepts any combination (separated by comma) of:\n\n";
-
- for (VerboseMap::Iterator vit = verboseMap.begin();
- vit != verboseMap.end(); ++vit )
- {
- VerboseDef *item = vit.value();
- QString name = QString(" %1").arg(item->name, -15, ' ');
- if (item->helpText.isEmpty())
- continue;
- cerr << name.toLocal8Bit().constData() << " - " <<
- item->helpText.toLocal8Bit().constData() << endl;
- }
-
- cerr << endl <<
- "The default for this program appears to be: '-v " <<
- m_verbose.toLocal8Bit().constData() << "'\n\n"
- "Most options are additive except for 'none' and 'all'.\n"
- "These two are semi-exclusive and take precedence over any\n"
- "other options. However, you may use something like\n"
- "'-v none,jobqueue' to receive only JobQueue related messages\n"
- "and override the default verbosity level.\n\n"
- "Additive options may also be subtracted from 'all' by\n"
- "prefixing them with 'no', so you may use '-v all,nodatabase'\n"
- "to view all but database debug messages.\n\n"
- "Some debug levels may not apply to this program.\n\n";
-}
-
-/// \brief Parse the --verbose commandline argument and set the verbose level
-/// \param arg the commandline argument following "--verbose"
-/// \return an exit code. GENERIC_EXIT_OK if all is well.
-int verboseArgParse(QString arg)
-{
- QString option;
-
- if (!verboseInitialized)
- verboseInit();
-
- QMutexLocker locker(&verboseMapMutex);
-
- verboseMask = verboseDefaultInt;
- verboseString = QString(verboseDefaultStr);
-
- if (arg.startsWith('-'))
- {
- cerr << "Invalid or missing argument to -v/--verbose option\n";
- return GENERIC_EXIT_INVALID_CMDLINE;
- }
-
- QStringList verboseOpts = arg.split(QRegExp("\\W+"));
- for (QStringList::Iterator it = verboseOpts.begin();
- it != verboseOpts.end(); ++it )
- {
- option = (*it).toLower();
- bool reverseOption = false;
-
- if (option != "none" && option.left(2) == "no")
- {
- reverseOption = true;
- option = option.right(option.length() - 2);
- }
-
- if (option == "help")
- {
- verboseHelp();
- return GENERIC_EXIT_INVALID_CMDLINE;
- }
- else if (option == "important")
- {
- cerr << "The \"important\" log mask is no longer valid.\n";
- }
- else if (option == "extra")
- {
- cerr << "The \"extra\" log mask is no longer valid. Please try "
- "--loglevel debug instead.\n";
- }
- else if (option == "default")
- {
- if (haveUserDefaultValues)
- {
- verboseMask = userDefaultValueInt;
- verboseString = userDefaultValueStr;
- }
- else
- {
- verboseMask = verboseDefaultInt;
- verboseString = QString(verboseDefaultStr);
- }
- }
- else
- {
- VerboseDef *item = verboseMap.value(option);
-
- if (item)
- {
- if (reverseOption)
- {
- verboseMask &= ~(item->mask);
- verboseString = verboseString.remove(' ' + item->name);
- verboseString += " no" + item->name;
- }
- else
- {
- if (item->additive)
- {
- if (!(verboseMask & item->mask))
- {
- verboseMask |= item->mask;
- verboseString += ' ' + item->name;
- }
- }
- else
- {
- verboseMask = item->mask;
- verboseString = item->name;
- }
- }
- }
- else
- {
- cerr << "Unknown argument for -v/--verbose: " <<
- option.toLocal8Bit().constData() << endl;;
- return GENERIC_EXIT_INVALID_CMDLINE;
- }
- }
- }
-
- if (!haveUserDefaultValues)
- {
- haveUserDefaultValues = true;
- userDefaultValueInt = verboseMask;
- userDefaultValueStr = verboseString;
- }
-
- return GENERIC_EXIT_OK;
}
-/// \brief Verbose helper function for ENO macro.
-/// \param errnum system errno value
-/// \return QString containing the string version of the errno value, plus the
-/// errno value itself.
-QString logStrerror(int errnum)
+void DatabaseLogger::messageReceived(const QList<QByteArray>&)
{
- return QString("%1 (%2)").arg(strerror(errnum)).arg(errnum);
}
View
55 mythtv/libs/libmythbase/loggingserver.h
@@ -21,13 +21,16 @@ class QString;
class MSqlQuery;
class LoggingItem;
+MBASE_PUBLIC void logServerStart(void);
+MBASE_PUBLIC void logServerStop(void);
+
/// \brief Base class for the various logging mechanisms
class LoggerBase : public QObject {
Q_OBJECT
public:
/// \brief LoggerBase Constructor
- LoggerBase(char *string, int number);
+ LoggerBase(char *string);
/// \brief LoggerBase Deconstructor
virtual ~LoggerBase();
/// \brief Process a log message for the logger instance
@@ -40,8 +43,7 @@ class LoggerBase : public QObject {
/// \brief Deal with an incoming log message
virtual void messageReceived(const QList<QByteArray>&) = 0;
protected:
- LoggerHandle_t m_handle; ///< semi-opaque handle for identifying instance
- bool m_string; ///< true if m_handle.string valid, false otherwise
+ char *m_handle; ///< semi-opaque handle for identifying instance
};
/// \brief File-based logger - used for logfiles and console
@@ -61,7 +63,7 @@ class FileLogger : public LoggerBase {
/// \brief Syslog-based logger (not available in Windows)
class SyslogLogger : public LoggerBase {
public:
- SyslogLogger(int facility, char *application);
+ SyslogLogger();
~SyslogLogger();
bool logmsg(LoggingItem *item);
/// \brief Unused for this logger.
@@ -124,6 +126,9 @@ class LogServerThread : public QObject, public MThread
void messageReceived(const QList<QByteArray>&);
};
+class QWaitCondition;
+#define MAX_QUEUE_LEN 1000
+
/// \brief Thread that manages the queueing of logging inserts for the database.
/// The database logging gets throttled if it gets overwhelmed, and also
/// during startup. Having a second queue allows the rest of the
@@ -141,7 +146,7 @@ class DBLoggerThread : public MThread
bool enqueue(LoggingItem *item)
{
QMutexLocker qLock(&m_queueMutex);
- if (!aborted)
+ if (!m_aborted)
m_queue->enqueue(item);
return true;
}
@@ -160,45 +165,7 @@ class DBLoggerThread : public MThread
QWaitCondition *m_wait; ///< Wait condition used for waiting
/// for the queue to not be full.
/// Protected by m_queueMutex
- volatile bool aborted; ///< Used during shutdown to indicate
- /// that the thread should stop ASAP.
- /// Protected by m_queueMutex
-};
-
-/// \brief Thread that manages the queueing of logging inserts to mythlogserver,
-/// and dealing with any messages sent back.
-class ZeroMQLoggerThread : public MThread
-{
- public:
- ZeroMQLoggerThread(ZeroMQLogger *logger);
- ~ZeroMQLoggerThread();
- void run(void);
- void stop(void);
- /// \brief Enqueues a LoggingItem onto the queue for the thread to
- /// consume.
- bool enqueue(LoggingItem *item)
- {
- QMutexLocker qLock(&m_queueMutex);
- if (!aborted)
- m_queue->enqueue(item);
- return true;
- }
-
- /// \brief Indicates when the queue is full
- /// \return true when the queue is full
- bool queueFull(void)
- {
- QMutexLocker qLock(&m_queueMutex);
- return (m_queue->size() >= MAX_QUEUE_LEN);
- }
- private:
- ZeroMQLogger *m_logger; ///< The associated logger instance
- QMutex m_queueMutex; ///< Mutex for protecting the queue
- QQueue<LoggingItem *> *m_queue; ///< Queue of LoggingItems to insert
- QWaitCondition *m_wait; ///< Wait condition used for waiting
- /// for the queue to not be full.
- /// Protected by m_queueMutex
- volatile bool aborted; ///< Used during shutdown to indicate
+ volatile bool m_aborted; ///< Used during shutdown to indicate
/// that the thread should stop ASAP.
/// Protected by m_queueMutex
};
View
1  mythtv/libs/libmythbase/mythcorecontext.h
@@ -28,6 +28,7 @@
#define MYTH_APPNAME_MYTHMEDIASERVER "mythmediaserver"
#define MYTH_APPNAME_MYTHMETADATALOOKUP "mythmetadatalookup"
#define MYTH_APPNAME_MYTHUTIL "mythutil"
+#define MYTH_APPNAME_MYTHLOGSERVER "mythlogserver"
class MDBManager;
class MythCoreContextPrivate;
View
30 mythtv/libs/libmythbase/verbosedefs.h
@@ -1,6 +1,11 @@
#ifndef VERBOSEDEFS_H_
#define VERBOSEDEFS_H_
+#ifdef __cplusplus
+#include <QMap>
+#include <QString>
+#include <QMutex>
+#endif
#include <stdint.h>
/// This file gets included in two different ways:
@@ -180,4 +185,29 @@ LOGLEVEL_MAP(LOG_DEBUG, 7, 'D')
LOGLEVEL_MAP(LOG_UNKNOWN, 8, '-')
LOGLEVEL_POSTAMBLE
+#ifndef _IMPLEMENT_VERBOSE
+#ifdef __cplusplus
+typedef struct {
+ uint64_t mask;
+ QString name;
+ bool additive;
+ QString helpText;
+} VerboseDef;
+typedef QMap<QString, VerboseDef *> VerboseMap;
+
+typedef struct {
+ int value;
+ QString name;
+ char shortname;
+} LoglevelDef;
+typedef QMap<int, LoglevelDef *> LoglevelMap;
+
+extern VerboseMap verboseMap;
+extern QMutex verboseMapMutex;
+
+extern LoglevelMap loglevelMap;
+extern QMutex loglevelMapMutex;
+#endif
+#endif
+
#endif
View
3  mythtv/programs/mythbackend/main.cpp
@@ -49,7 +49,8 @@
static void qt_exit(int)
{
signal(SIGINT, SIG_DFL);
- QCoreApplication::exit(0);
+ signal(SIGTERM, SIG_DFL);
+ QCoreApplication::exit(GENERIC_EXIT_OK);
}
int main(int argc, char **argv)
View
1  mythtv/programs/mythlogserver/.gitignore
@@ -0,0 +1 @@
+mythlogserver
View
23 mythtv/programs/mythlogserver/commandlineparser.cpp
@@ -0,0 +1,23 @@
+#include "commandlineparser.h"
+#include "mythcorecontext.h"
+
+MythLogServerCommandLineParser::MythLogServerCommandLineParser() :
+ MythCommandLineParser(MYTH_APPNAME_MYTHLOGSERVER)
+{
+ LoadArguments();
+}
+
+void MythLogServerCommandLineParser::LoadArguments(void)
+{
+ addHelp();
+ addVersion();
+ addDaemon();
+ addLogging();
+}
+
+QString MythLogServerCommandLineParser::GetHelpHeader(void) const
+{
+ return
+ "This is the common logserver that accepts logging messages from\n"
+ "all the clients via ZeroMQ.";
+}
View
19 mythtv/programs/mythlogserver/commandlineparser.h
@@ -0,0 +1,19 @@
+// -*- Mode: c++ -*-
+
+#ifndef _MYTH_LOGSERVER_COMMAND_LINE_PARSER_H_
+#define _MYTH_LOGSERVER_COMMAND_LINE_PARSER_H_
+
+#include "mythcommandlineparser.h"
+
+class MythLogServerCommandLineParser : public MythCommandLineParser
+{
+ public:
+ MythLogServerCommandLineParser();
+ void LoadArguments(void);
+ protected:
+ QString GetHelpHeader(void) const;
+};
+
+#endif // _MYTH_LOGSERVER_COMMAND_LINE_PARSER_H_
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
View
116 mythtv/programs/mythlogserver/main.cpp
@@ -0,0 +1,116 @@
+// -*- Mode: c++ -*-
+
+// C++ headers
+#include <unistd.h>
+#include <iostream>
+using namespace std;
+
+// Qt headers
+#include <QCoreApplication>
+#include <QApplication>
+#include <QString>
+#include <QtCore>
+#include <QtGui>
+
+// MythTV headers
+#include "mythccextractorplayer.h"
+#include "commandlineparser.h"
+#include "mythcontext.h"
+#include "mythversion.h"
+#include "programinfo.h"
+#include "ringbuffer.h"
+#include "exitcodes.h"
+#include "loggingserver.h"
+
+namespace {
+ void cleanup()
+ {
+ delete gContext;
+ gContext = NULL;
+ }
+
+ class CleanupGuard
+ {
+ public:
+ typedef void (*CleanupFunc)();
+
+ public:
+ CleanupGuard(CleanupFunc cleanFunction) :
+ m_cleanFunction(cleanFunction) {}
+
+ ~CleanupGuard()
+ {
+ m_cleanFunction();
+ }
+
+ private:
+ CleanupFunc m_cleanFunction;
+ };
+}
+
+static void qt_exit(int)
+{
+ signal(SIGINT, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ QCoreApplication::exit(GENERIC_EXIT_OK);
+}
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+ QCoreApplication::setApplicationName(MYTH_APPNAME_MYTHLOGSERVER);
+
+ MythLogServerCommandLineParser cmdline;
+ if (!cmdline.Parse(argc, argv))
+ {
+ cmdline.PrintHelp();
+ return GENERIC_EXIT_INVALID_CMDLINE;
+ }
+
+ if (cmdline.toBool("showhelp"))
+ {
+ cmdline.PrintHelp();
+ return GENERIC_EXIT_OK;
+ }
+
+ if (cmdline.toBool("showversion"))
+ {
+ cmdline.PrintVersion();
+ return GENERIC_EXIT_OK;
+ }
+
+ //QString pidfile = cmdline.toString("pidfile");
+ int retval = cmdline.Daemonize();
+ if (retval != GENERIC_EXIT_OK)
+ return retval;
+
+ bool daemonize = cmdline.toBool("daemon");
+ QString mask("general");
+ if ((retval = cmdline.ConfigureLogging(mask, daemonize)) != GENERIC_EXIT_OK)
+ return retval;
+
+ if (daemonize)
+ // Don't listen to console input if daemonized
+ close(0);
+
+ CleanupGuard callCleanup(cleanup);
+ signal(SIGINT, qt_exit);
+ signal(SIGTERM, qt_exit);
+
+ gContext = new MythContext(MYTH_BINARY_VERSION);
+ if (!gContext->Init(false))
+ {
+ cerr << "Failed to init MythContext, exiting." << endl;
+ return GENERIC_EXIT_NO_MYTHCONTEXT;
+ }
+
+ logServerStart();
+
+ while (true)
+ usleep(100000);
+
+ return GENERIC_EXIT_OK;
+}
+
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
View
16 mythtv/programs/mythlogserver/mythlogserver.pro
@@ -0,0 +1,16 @@
+include (../../settings.pro)
+include ( ../programs-libs.pro )
+
+QT += sql network
+
+TEMPLATE = app
+CONFIG += thread
+target.path = $${PREFIX}/bin
+INSTALLS = target
+
+QMAKE_CLEAN += $(TARGET)
+
+# Input
+HEADERS += commandlineparser.h
+SOURCES += commandlineparser.cpp main.cpp
+
View
3  mythtv/programs/mythtranscode/replex/replex.pro
@@ -23,6 +23,9 @@ QMAKE_CFLAGS += -w
LIBS += -L../../../external/FFmpeg/libavutil
LIBS += -L../../../external/FFmpeg/libavcodec
LIBS += -L../../../external/FFmpeg/libavformat
+LIBS += -L../../../external/zeromq/src/.libs
+LIBS += -L../../../external/nzmqt/src
+LIBS += -L../../../external/qjson/lib
LIBS += -L../../../libs/libmythbase
LIBS += -lmythavformat
LIBS += -lmythavcodec
View
2  mythtv/programs/programs.pro
@@ -5,7 +5,7 @@ TEMPLATE = subdirs
# Directories
using_frontend {
SUBDIRS += mythavtest mythfrontend mythcommflag
- SUBDIRS += mythjobqueue mythlcdserver
+ SUBDIRS += mythjobqueue mythlcdserver mythlogserver
SUBDIRS += mythwelcome mythshutdown mythutil
SUBDIRS += mythpreviewgen mythmediaserver mythccextractor
!mingw: SUBDIRS += mythtranscode/replex
View
3  mythtv/settings.pro
@@ -84,6 +84,8 @@ contains(CONFIG_DARWIN, yes) {
}
INCLUDEPATH += $$unique(CONFIG_INCLUDEPATH)
+INCLUDEPATH += $$PREFIX/include
+INCLUDEPATH += $$PREFIX/include/qjson
# remove warn_{on|off} from CONFIG since we set it in our CFLAGS
CONFIG -= warn_on warn_off
@@ -156,5 +158,6 @@ EXTRA_LIBS += $$CONFIG_FIREWIRE_LIBS
EXTRA_LIBS += $$LOCAL_LIBDIR_OGL
EXTRA_LIBS += $$LOCAL_LIBDIR_X11
EXTRA_LIBS += $$CONFIG_OPENGL_LIBS
+EXTRA_LIBS += -lzmq -lmythnzmqt -lmythqjson
macx:using_firewire:using_backend:EXTRA_LIBS += -F$${CONFIG_MAC_AVC} -framework AVCVideoServices
Please sign in to comment.
Something went wrong with that request. Please try again.