Example #1
0
/// \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;

    int type = kMessage;
    type |= (mask & VB_FLUSH) ? kFlush : 0;
    type |= (mask & VB_STDIO) ? kStandardIO : 0;
    LoggingItem *item = LoggingItem::create(file, function, line, level,
                                            (LoggingType)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->m_message, LOGLINE_MAX, format, arguments);
    va_end(arguments);

    if (formatcopy)
        free(formatcopy);

    QMutexLocker qLock(&logQueueMutex);

#if defined( _MSC_VER ) && defined( _DEBUG )
        OutputDebugStringA( item->m_message );
        OutputDebugStringA( "\n" );
#endif

    logQueue.enqueue(item);

    if (logThread && logThreadFinished && !logThread->isRunning())
    {
        while (!logQueue.isEmpty())
        {
            item = logQueue.dequeue();
            qLock.unlock();
            logThread->handleItem(item);
            logThread->logConsole(item);
            item->DecrRef();
            qLock.relock();
        }
    }
    else if (logThread && !logThreadFinished && (type & kFlush))
    {
        logThread->flush();
    }
}
Example #2
0
/// \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)
{
    if (logThreadFinished)
        return;

    QMutexLocker qLock(&logQueueMutex);

    LoggingItem *item = LoggingItem::create(__FILE__, __FUNCTION__,
                                            __LINE__, (LogLevel_t)LOG_DEBUG,
                                            kRegistering);
    if (item)
    {
        item->setThreadName((char *)name.toLocal8Bit().constData());
        logQueue.enqueue(item);
    }
}
void DatabaseLogger::receivedMessage(const QList<QByteArray> &msg)
{
    // Filter on the clientId
    QByteArray clientBa = msg.first();
    QString clientId = QString(clientBa.toHex());

    {
        QMutexLocker locker(&logRevClientMapMutex);

        ClientList *clients = logRevClientMap.value(this, NULL);
        if (!clients || !clients->contains(clientId))
            return;
    }

    QByteArray json     = msg.at(1);
    LoggingItem *item = LoggingItem::create(json);
    if (!logmsg(item))
        item->DecrRef();
}
Example #4
0
/// \brief Run the logging thread.  This thread reads from the logging queue,
///        and handles distributing the LoggingItems to each logger instance.
///        The thread will not exit until the logging queue is emptied
///        completely, ensuring that all logging is flushed.
void LoggerThread::run(void)
{
    RunProlog();

    logThreadFinished = false;

    LOG(VB_GENERAL, LOG_INFO, "Added logging to the console");

    bool dieNow = false;

    if (!m_noserver)
    {
#ifndef NOLOGSERVER
        try
        {
            if (m_locallogs)
            {
                logServerWait();
                m_zmqContext = logServerThread->getZMQContext();
            }
            else
            {
                m_zmqContext = nzmqt::createDefaultContext(NULL);
                m_zmqContext->start();
            }

            if (!m_zmqContext)
            {
                m_aborted = true;
                dieNow = true;
            }
            else
            {
                qRegisterMetaType<QList<QByteArray> >("QList<QByteArray>");

                m_zmqSocket =
                    m_zmqContext->createSocket(nzmqt::ZMQSocket::TYP_DEALER, this);
                connect(m_zmqSocket,
                        SIGNAL(messageReceived(const QList<QByteArray>&)),
                        SLOT(messageReceived(const QList<QByteArray>&)),
                        Qt::QueuedConnection);

                if (m_locallogs)
                    m_zmqSocket->connectTo("inproc://mylogs");
                else
                    m_zmqSocket->connectTo("tcp://127.0.0.1:35327");
            }
        }
        catch (nzmqt::ZMQException &e)
        {
            cerr << "Exception during logging socket setup: " << e.what() << endl;
            m_aborted = true;
            dieNow = true;
        }

        if (!m_aborted)
        {
            if (!m_locallogs)
            {
                m_initialWaiting = true;
                pingLogServer();

                // wait up to 150ms for mythlogserver to respond
                m_initialTimer = new MythSignalingTimer(this,
                                                        SLOT(initialTimeout()));
                m_initialTimer->start(150);
            }
            else
                LOG(VB_GENERAL, LOG_INFO, "Added logging to mythlogserver locally");

            loggingGetTimeStamp(&m_epoch, NULL);

            m_heartbeatTimer = new MythSignalingTimer(this, SLOT(checkHeartBeat()));
            m_heartbeatTimer->start(1000);
        }
    #else
        logServerWait();
    #endif
    }

    QMutexLocker qLock(&logQueueMutex);

    while (!m_aborted || !logQueue.isEmpty())
    {
        qLock.unlock();
        qApp->processEvents(QEventLoop::AllEvents, 10);
        qApp->sendPostedEvents(NULL, QEvent::DeferredDelete);

        qLock.relock();
        if (logQueue.isEmpty())
        {
            m_waitEmpty->wakeAll();
            m_waitNotEmpty->wait(qLock.mutex(), 100);
            continue;
        }

        LoggingItem *item = logQueue.dequeue();
        qLock.unlock();

        fillItem(item);
        handleItem(item);
        logConsole(item);
        item->DecrRef();

        qLock.relock();
    }

    qLock.unlock();

    // This must be before the timer stop below or we deadlock when the timer
    // thread tries to deregister, and we wait for it.
    logThreadFinished = true;

#ifndef NOLOGSERVER
    if (m_initialTimer)
    {
        m_initialTimer->stop();
        delete m_initialTimer;
        m_initialTimer = NULL;
    }

    if (m_heartbeatTimer)
    {
        m_heartbeatTimer->stop();
        delete m_heartbeatTimer;
        m_heartbeatTimer = NULL;
    }

    if (m_zmqSocket)
    {
        m_zmqSocket->setLinger(0);
        m_zmqSocket->close();
    }

    if (!m_locallogs)
        delete m_zmqContext;
#endif

    RunEpilog();

    if (dieNow)
    {
        qApp->processEvents();
    }
}
Example #5
0
/// \brief Start the thread.
void DBLoggerThread::run(void)
{
    RunProlog();

    // Wait a bit before we start logging to the DB..  If we wait too long,
    // then short-running tasks (like mythpreviewgen) will not log to the db
    // at all, and that's undesirable.
    while (true)
    {
        if ((m_aborted || (gCoreContext && m_logger->isDatabaseReady())))
            break;

        QMutexLocker locker(&m_queueMutex);
        m_wait->wait(locker.mutex(), 100);
    }

    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
        // in use, and we get a qWarning on shutdown.
        MSqlQuery *query = new MSqlQuery(MSqlQuery::InitCon());
        m_logger->prepare(*query);

        QMutexLocker qLock(&m_queueMutex);
        while (!m_aborted || !m_queue->isEmpty())
        {
            if (m_queue->isEmpty())
            {
                m_wait->wait(qLock.mutex(), 100);
                continue;
            }

            LoggingItem *item = m_queue->dequeue();
            if (!item)
                continue;

            if (item->message()[0] != '\0')
            {
                qLock.unlock();
                bool logged = m_logger->logqmsg(*query, item);
                qLock.relock();

                if (!logged)
                {
                    m_queue->prepend(item);
                    m_wait->wait(qLock.mutex(), 100);
                    delete query;
                    query = new MSqlQuery(MSqlQuery::InitCon());
                    m_logger->prepare(*query);
                    continue;
                }
            }

            item->DecrRef();
        }

        delete query;
    }

    RunEpilog();
}
Example #6
0
void LogForwardThread::forwardMessage(LogMessage *msg)
{
#ifdef DUMP_PACKET
    QList<QByteArray>::const_iterator it = msg->begin();
    int i = 0;
    for (; it != msg->end(); ++it, i++)
    {
        QByteArray buf = *it;
        cout << i << ":\t" << buf.size() << endl << "\t"
             << buf.toHex().constData() << endl << "\t"
             << buf.constData() << endl;
    }
#endif

    msgsSinceHeartbeat.ref();

    // First section is the client id
    QByteArray clientBa = msg->first();
    QString clientId = QString(clientBa.toHex());

    QByteArray json     = msg->at(1);

    if (json.size() == 0)
    {
        // This is either a ping response or a first gasp
        logClientMapMutex.lock();
        LoggerListItem *logItem = logClientMap.value(clientId, NULL);
        logClientMapMutex.unlock();
        if (!logItem)
        {
            // Send an initial ping so the client knows we are in the house
            emit pingClient(clientId);
        }
        else
        {
            // cout << "pong " << clientId.toLocal8Bit().constData() << endl;
            loggingGetTimeStamp(&logItem->epoch, NULL);
        }
        return;
    }

    QMutexLocker lock(&logClientMapMutex);
    LoggerListItem *logItem = logClientMap.value(clientId, NULL);

    // cout << "msg  " << clientId.toLocal8Bit().constData() << endl;
    if (logItem)
    {
        loggingGetTimeStamp(&logItem->epoch, NULL);
    }
    else
    {
        LoggingItem *item = LoggingItem::create(json);

        logClientCount.ref();
        LOG(VB_FILE, LOG_DEBUG, QString("New Logging Client: ID: %1 (#%2)")
            .arg(clientId).arg(logClientCount.fetchAndAddOrdered(0)));

#ifndef NOLOGSERVER
        // TODO FIXME This is not thread-safe!
        if (logClientCount.fetchAndAddOrdered(0) > 1 && m_shutdownTimer &&
            m_shutdownTimer->isActive())
        {
            LOG(VB_GENERAL, LOG_INFO, "Aborting shutdown timer");
            m_shutdownTimer->stop();
        }
#endif

        QMutexLocker lock2(&loggerMapMutex);
        QMutexLocker lock3(&logRevClientMapMutex);

        // Need to find or create the loggers
        LoggerList *loggers = new LoggerList;
        LoggerBase *logger;

        // FileLogger from logFile
        QString logfile = item->logFile();
        logfile.detach();
        if (!logfile.isEmpty())
        {
            logger = FileLogger::create(logfile, lock2.mutex());

            ClientList *clients = logRevClientMap.value(logger);

            if (clients)
                clients->insert(0, clientId);

            if (logger && loggers)
                loggers->insert(0, logger);
        }

#ifndef _WIN32
        // SyslogLogger from facility
        int facility = item->facility();
#if CONFIG_SYSTEMD_JOURNAL
        if ((facility > 0) || (facility == SYSTEMD_JOURNAL_FACILITY))
        {
            logger = SyslogLogger::create(lock2.mutex(), facility > 0);
#else
        if (facility > 0)
        {
            logger = SyslogLogger::create(lock2.mutex());
#endif

            ClientList *clients = logRevClientMap.value(logger);

            if (clients)
                clients->insert(0, clientId);

            if (logger && loggers)
                loggers->insert(0, logger);
        }
#endif

        // DatabaseLogger from table
        QString table = item->table();
        if (!table.isEmpty())
        {
            logger = DatabaseLogger::create(table, lock2.mutex());

            ClientList *clients = logRevClientMap.value(logger);

            if (clients)
                clients->insert(0, clientId);

            if (logger && loggers)
                loggers->insert(0, logger);
        }

        logItem = new LoggerListItem;
        loggingGetTimeStamp(&logItem->epoch, NULL);
        logItem->list = loggers;
        logClientMap.insert(clientId, logItem);

        item->DecrRef();
    }

#ifndef NOLOGSERVER
    m_zmqPubSock->sendMessage(*msg);
#else
    if (logItem && logItem->list && !logItem->list->isEmpty())
    {
        LoggerList::iterator it = logItem->list->begin();
        LoggingItem *item = LoggingItem::create(json);
        if (!item)
            return;
        for (; it != logItem->list->end(); ++it)
        {
            (*it)->logmsg(item);
        }
        item->DecrRef();
    }
#endif
}

/// \brief Stop the thread by setting the abort flag
void LogForwardThread::stop(void)
{
    m_aborted = true;
}