Go to the documentation of this file.
4 #include <QMutexLocker>
5 #include <QWaitCondition>
9 #include <QCoreApplication>
11 #include <QStringList>
13 #include <QSocketNotifier>
28 #if CONFIG_SYSTEMD_JOURNAL
29 #define SD_JOURNAL_SUPPRESS_LOCATION 1 // NOLINT(cppcoreguidelines-macro-usage)
30 #include <systemd/sd-journal.h>
36 #include <sys/types.h>
46 #if defined(__linux__)
47 #include <sys/syscall.h>
48 #elif defined(__FreeBSD__)
50 #include <sys/ucontext.h>
53 #elif defined(Q_OS_DARWIN)
54 #include <mach/mach.h>
111 LOG(VB_GENERAL, LOG_INFO, QString(
"Added logging to %1")
121 LOG(VB_GENERAL, LOG_INFO, QString(
"Removed logging to %1")
131 QByteArray ba =
filename.toLocal8Bit();
132 const char *
file = ba.constData();
156 m_fd = open(qPrintable(
m_handle), O_WRONLY|O_CREAT|O_APPEND, 0664);
158 LOG(VB_GENERAL, LOG_INFO, QString(
"Rolled logging on %1") .arg(
m_handle));
174 line = qPrintable(QString(
"%1 %2 [%3/%4] %5 %6:%7 (%8) - %9\n")
175 .arg(timestamp, shortname,
176 QString::number(item->
pid()),
177 QString::number(item->
tid()),
179 QString::number(item->
line()),
185 line = qPrintable(QString(
"%1 %2 [%3] %5 %6:%7 (%8) - %9\n")
186 .arg(timestamp, shortname,
187 QString::number(item->
pid()),
189 QString::number(item->
line()),
194 int result =
write(
m_fd, line.data(), line.size());
198 LOG(VB_GENERAL, LOG_ERR,
199 QString(
"Closed Log output on fd %1 due to errors").arg(
m_fd));
208 SyslogLogger::SyslogLogger(
bool open) :
215 openlog(
nullptr, LOG_NDELAY, 0 );
219 LOG(VB_GENERAL, LOG_INFO,
"Added syslogging");
223 SyslogLogger::~SyslogLogger()
225 LOG(VB_GENERAL, LOG_INFO,
"Removing syslogging");
230 SyslogLogger *SyslogLogger::create(QMutex *mutex,
bool open)
232 auto *
logger = qobject_cast<SyslogLogger *>(
loggerMap.value(
"",
nullptr));
239 logger =
new SyslogLogger(open);
252 if (!m_opened || item->
facility() <= 0)
256 syslog(item->
level() | item->
facility(),
"%s[%d]: %c %s %s:%d (%s) %s",
257 qPrintable(item->
appName()), item->
pid(), shortname,
264 #if CONFIG_SYSTEMD_JOURNAL
265 JournalLogger::JournalLogger() :
269 LOG(VB_GENERAL, LOG_INFO,
"Added journal logging");
273 JournalLogger::~JournalLogger()
275 LOG(VB_GENERAL, LOG_INFO,
"Removing journal logging");
278 JournalLogger *JournalLogger::create(QMutex *mutex)
280 auto *
logger = qobject_cast<JournalLogger *>(
loggerMap.value(
"",
nullptr));
287 logger =
new JournalLogger();
301 "MESSAGE=%s", qUtf8Printable(item->
message()),
302 "PRIORITY=%d", item->
level(),
303 "CODE_FILE=%s", qUtf8Printable(item->
file()),
304 "CODE_LINE=%d", item->
line(),
305 "CODE_FUNC=%s", qUtf8Printable(item->
function()),
306 "SYSLOG_IDENTIFIER=%s", qUtf8Printable(item->
appName()),
307 "SYSLOG_PID=%d", item->
pid(),
308 "MYTH_THREAD=%s", qUtf8Printable(item->
threadName()),
323 " (`host`, `application`, `pid`, `tid`, `thread`, `filename`, "
324 " `line`, `function`, `msgtime`, `level`, `message`) "
325 "VALUES (:HOST, :APP, :PID, :TID, :THREAD, :FILENAME, "
326 " :LINE, :FUNCTION, :MSGTIME, :LEVEL, :MESSAGE)")
329 LOG(VB_GENERAL, LOG_INFO, QString(
"Added database logging to table %1")
339 LOG(VB_GENERAL, LOG_INFO,
"Removing database logging");
346 QByteArray ba = table.toLocal8Bit();
347 const char *tble = ba.constData();
349 qobject_cast<DatabaseLogger *>(
loggerMap.value(table,
nullptr));
392 LOG(VB_GENERAL, LOG_CRIT,
393 "Disabling DB Logging: too many messages queued");
402 LOG(VB_GENERAL, LOG_CRIT,
"Reenabling DB Logging");
439 || !err.nativeErrorCode().isEmpty()
468 if ((db) && db->HaveValidDatabase())
489 QString sql =
"SELECT COLUMN_NAME "
490 " FROM INFORMATION_SCHEMA.COLUMNS "
491 " WHERE TABLE_SCHEMA = DATABASE() "
492 " AND TABLE_NAME = :TABLENAME "
493 " AND COLUMN_NAME = :COLUMNNAME;";
497 query.
bindValue(
":COLUMNNAME",
"function");
511 m_wait(new QWaitCondition())
545 m_wait->wait(locker.mutex(), 100);
561 m_wait->wait(qLock.mutex(), 100);
569 if (!item->
message().isEmpty())
578 m_wait->wait(qLock.mutex(), 100);
658 Qt::QueuedConnection);
660 qRegisterMetaType<QList<QByteArray> >(
"QList<QByteArray>");
664 qApp->processEvents(QEventLoop::AllEvents, 10);
665 qApp->sendPostedEvents(
nullptr, QEvent::DeferredDelete);
686 if ((processed & 127) == 0)
688 qApp->processEvents(QEventLoop::AllEvents, 10);
689 qApp->sendPostedEvents(
nullptr, QEvent::DeferredDelete);
704 while (!loggers.isEmpty())
718 LOG(VB_GENERAL, LOG_INFO,
"SIGHUP received, rolling log files.");
722 QMap<QString, LoggerBase *>::iterator it;
725 it.value()->reopen();
733 QList<QByteArray>::const_iterator it = msg->begin();
735 for (; it != msg->end(); ++it, i++)
737 QByteArray buf = *it;
738 cout << i <<
":\t" << buf.size() << endl <<
"\t"
739 << buf.toHex().constData() << endl <<
"\t"
740 << buf.constData() << endl;
745 QByteArray clientBa = msg->first();
746 QString clientId = QString(clientBa.toHex());
748 QByteArray json = msg->at(1);
750 if (json.size() == 0)
762 logItem->
m_itemEpoch = nowAsDuration<std::chrono::seconds>();
769 LOG(VB_FILE, LOG_DEBUG, QString(
"New Logging Client: ID: %1 (#%2)")
787 clients->insert(0, clientId);
790 loggers->insert(0,
logger);
803 clients->insert(0, clientId);
806 loggers->insert(0,
logger);
809 #if CONFIG_SYSTEMD_JOURNAL
811 if (facility == SYSTEMD_JOURNAL_FACILITY)
818 clients->insert(0, clientId);
821 loggers->insert(0,
logger);
827 QString table = item->
table();
828 if (!table.isEmpty())
835 clients->insert(0, clientId);
838 loggers->insert(0,
logger);
842 logItem->
m_itemEpoch = nowAsDuration<std::chrono::seconds>();
854 for (
auto *it : qAsConst(*logItem->
m_itemList))
887 it.value()->stopDatabaseAccess();
static QAtomicInt logClientCount
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
bool logqmsg(MSqlQuery &query, LoggingItem *item)
Actually insert a log message from the queue into the database.
QSqlQuery wrapper that fetches a DB connection from the connection pool.
QElapsedTimer m_errorLoggingTime
Elapsed time since DB error logging was last done.
bool m_opened
true when the logfile is opened
void logForwardStop(void)
QString getTimestamp(const char *format="yyyy-MM-dd HH:mm:ss") const
Convert numerical timestamp to a readable date and time.
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
static QMap< QString, LoggerBase * > loggerMap
static QMutex logClientMapMutex
~DBLoggerThread() override
DBLoggerThread deconstructor.
QList< LogMessage * > LogMessageList
Base class for the various logging mechanisms.
static DatabaseLogger * create(const QString &table, QMutex *mutex)
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
DBLoggerThread * m_thread
The database queue handling thread.
bool logmsg(LoggingItem *item) override
Process a log message, writing to the logfile.
LogForwardThread * logForwardThread
def write(text, progress=True)
static bool tableExists(const QString &table)
Checks whether table exists and is ready for writing.
bool logmsg(LoggingItem *item) override
Process a log message, queuing it for logging to the database.
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
void logForwardMessage(const QList< QByteArray > &msg)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
bool logForwardStart(void)
~LoggerBase() override
LoggerBase Deconstructor.
DBLoggerThread(DatabaseLogger *logger)
DBLoggerThread constructor.
bool m_aborted
Flag to abort the thread.
void stopDatabaseAccess(void) override
Stop logging to the database and wait for the thread to stop.
static constexpr std::chrono::milliseconds kMinDisabledTime
Minimum time to disable DB logging.
The logging items that are generated by LOG() and are sent to the console.
QHash< LoggerBase *, ClientList * > RevClientMap
bool isDatabaseReady(void)
Check if the database is ready for use.
char getLevelChar(void)
Get the message log level as a single character.
bool m_loggingTableExists
The desired logging table exists.
QString m_query
The database query to insert log messages.
The logging thread that forwards received messages to the consuming loggers via ZeroMQ.
~LogForwardThread() override
LogForwardThread destructor.
void stop(void)
Tell the thread to stop by setting the m_aborted flag.
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
static RevClientMap logRevClientMap
static void DBError(const QString &where, const MSqlQuery &query)
static void handleSigHup(void)
SIGHUP handler - reopen all open logfiles for logrollers.
static LogMessageList logMsgList
void run(void) override
Run the log forwarding thread.
QString m_handle
semi-opaque handle for identifying instance
QThread * qthread(void)
Returns the thread, this will always return the same pointer no matter how often you restart the thre...
DatabaseLogger(const char *table)
DatabaseLogger constructor.
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
friend class DBLoggerThread
LoggerBase(const char *string)
LoggerBase Constructor.
bool isConnected(void) const
Only updated once during object creation.
~DatabaseLogger() override
DatabaseLogger deconstructor.
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
int m_fd
contains the file descriptor for the logfile
LogForwardThread()
LogForwardThread constructor.
static QMutex logRevClientMapMutex
QSqlError lastError(void) const
volatile bool m_aborted
Used during shutdown to indicate that the thread should stop ASAP.
QMap< QString, LoggerListItem * > ClientMap
static ClientMap logClientMap
Database logger - logs to the MythTV database.
static QWaitCondition logMsgListNotEmpty
QElapsedTimer m_disabledTime
Elapsed time since the DB logging was disabled.
File-based logger - used for logfiles and console.
void stop(void)
Stop the thread by setting the abort flag.
void prepare(MSqlQuery &query)
Prepare the database query for use, and bind constant values to it.
static LoggingItem * create(const char *_file, const char *_function, int _line, LogLevel_t _level, LoggingType _type)
Create a new LoggingItem.
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
static QMutex logMsgListMutex
~FileLogger() override
FileLogger deconstructor - close the logfile.
QString getTimestampUs(const char *format="yyyy-MM-dd HH:mm:ss") const
bool enqueue(LoggingItem *item)
Enqueues a LoggingItem onto the queue for the thread to consume.
static void logger(cdio_log_level_t level, const char *message)
This is a wrapper around QThread that does several additional things.
bool isRunning(void) const
DatabaseLogger * m_logger
The associated logger instance.
QMutex m_queueMutex
Mutex for protecting the queue.
QList< QString > ClientList
QString GetHostName(void)
FileLogger(const char *filename)
FileLogger constructor.
void incomingSigHup(void)
void reopen(void) override
Reopen the logfile after a SIGHUP.
static FileLogger * create(const QString &filename, QMutex *mutex)
bool queueFull(void)
Indicates when the queue is full.
static QMutex loggerMapMutex
virtual int IncrRef(void)
Increments reference count.
QList< QByteArray > LogMessage
std::chrono::seconds m_itemEpoch
QQueue< LoggingItem * > * m_queue
Queue of LoggingItems to insert.
static void forwardMessage(LogMessage *msg)
void run(void) override
Start the thread.
QList< LoggerBase * > LoggerList
QWaitCondition * m_wait
Wait condition used for waiting for the queue to not be full.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.