00001 #include <QAtomicInt>
00002 #include <QMutex>
00003 #include <QMutexLocker>
00004 #include <QWaitCondition>
00005 #include <QList>
00006 #include <QQueue>
00007 #include <QHash>
00008 #include <QCoreApplication>
00009 #include <QFileInfo>
00010 #include <QStringList>
00011 #include <QMap>
00012 #include <QRegExp>
00013 #include <iostream>
00014
00015 using namespace std;
00016
00017 #include "mythlogging.h"
00018 #include "logging.h"
00019 #include "mythconfig.h"
00020 #include "mythdb.h"
00021 #include "mythcorecontext.h"
00022 #include "dbutil.h"
00023 #include "exitcodes.h"
00024 #include "compat.h"
00025
00026 #include <stdlib.h>
00027 #define SYSLOG_NAMES
00028 #ifndef _WIN32
00029 #include <syslog.h>
00030 #endif
00031 #include <stdarg.h>
00032 #include <string.h>
00033 #include <sys/stat.h>
00034 #include <sys/types.h>
00035 #include <fcntl.h>
00036 #include <stdio.h>
00037 #include <unistd.h>
00038 #if HAVE_GETTIMEOFDAY
00039 #include <sys/time.h>
00040 #endif
00041 #include <signal.h>
00042
00043
00044 #if defined(linux)
00045 #include <sys/syscall.h>
00046 #elif defined(__FreeBSD__)
00047 extern "C" {
00048 #include <sys/ucontext.h>
00049 #include <sys/thr.h>
00050 }
00051 #elif CONFIG_DARWIN
00052 #include <mach/mach.h>
00053 #endif
00054
00055 QMutex loggerListMutex;
00056 QList<LoggerBase *> loggerList;
00057
00058 QMutex logQueueMutex;
00059 QQueue<LoggingItem *> logQueue;
00060 QRegExp logRegExp = QRegExp("[%]{1,2}");
00061
00062 QMutex logThreadMutex;
00063 QHash<uint64_t, char *> logThreadHash;
00064
00065 QMutex logThreadTidMutex;
00066 QHash<uint64_t, int64_t> logThreadTidHash;
00067
00068 LoggerThread *logThread = NULL;
00069 bool logThreadFinished = false;
00070 bool debugRegistration = false;
00071
00072 typedef struct {
00073 bool propagate;
00074 int quiet;
00075 int facility;
00076 bool dblog;
00077 QString path;
00078 } LogPropagateOpts;
00079
00080 LogPropagateOpts logPropagateOpts;
00081 QString logPropagateArgs;
00082
00083 #define TIMESTAMP_MAX 30
00084 #define MAX_STRING_LENGTH (LOGLINE_MAX+120)
00085
00086 LogLevel_t logLevel = (LogLevel_t)LOG_INFO;
00087
00088 typedef struct {
00089 uint64_t mask;
00090 QString name;
00091 bool additive;
00092 QString helpText;
00093 } VerboseDef;
00094 typedef QMap<QString, VerboseDef *> VerboseMap;
00095
00096 typedef struct {
00097 int value;
00098 QString name;
00099 char shortname;
00100 } LoglevelDef;
00101 typedef QMap<int, LoglevelDef *> LoglevelMap;
00102
00103 bool verboseInitialized = false;
00104 VerboseMap verboseMap;
00105 QMutex verboseMapMutex;
00106
00107 LoglevelMap loglevelMap;
00108 QMutex loglevelMapMutex;
00109
00110 const uint64_t verboseDefaultInt = VB_GENERAL;
00111 const char *verboseDefaultStr = " general";
00112
00113 uint64_t verboseMask = verboseDefaultInt;
00114 QString verboseString = QString(verboseDefaultStr);
00115
00116 uint64_t userDefaultValueInt = verboseDefaultInt;
00117 QString userDefaultValueStr = QString(verboseDefaultStr);
00118 bool haveUserDefaultValues = false;
00119
00120 void verboseAdd(uint64_t mask, QString name, bool additive, QString helptext);
00121 void loglevelAdd(int value, QString name, char shortname);
00122 void verboseInit(void);
00123 void verboseHelp(void);
00124
00125 void LogTimeStamp( struct tm *tm, uint32_t *usec );
00126 char *getThreadName( LoggingItem *item );
00127 int64_t getThreadTid( LoggingItem *item );
00128 void setThreadTid( LoggingItem *item );
00129 static LoggingItem *createItem(
00130 const char *, const char *, int, LogLevel_t, int);
00131 static void deleteItem(LoggingItem *item);
00132 #ifndef _WIN32
00133 void logSighup( int signum, siginfo_t *info, void *secret );
00134 #endif
00135
00136 typedef enum {
00137 kMessage = 0x01,
00138 kRegistering = 0x02,
00139 kDeregistering = 0x04,
00140 kFlush = 0x08,
00141 kStandardIO = 0x10,
00142 } LoggingType;
00143
00144 class LoggingItem
00145 {
00146 public:
00147 LoggingItem(const char *_file, const char *_function,
00148 int _line, LogLevel_t _level, int _type) :
00149 threadId((uint64_t)(QThread::currentThreadId())),
00150 line(_line), type(_type), level(_level), file(_file),
00151 function(_function), threadName(NULL)
00152 {
00153 LogTimeStamp(&tm, &usec);
00154 message[0]='\0';
00155 message[LOGLINE_MAX]='\0';
00156 setThreadTid(this);
00157 refcount.ref();
00158 }
00159
00160 QAtomicInt refcount;
00161 uint64_t threadId;
00162 uint32_t usec;
00163 int line;
00164 int type;
00165 LogLevel_t level;
00166 struct tm tm;
00167 const char *file;
00168 const char *function;
00169 char *threadName;
00170 char message[LOGLINE_MAX+1];
00171 };
00172
00177 LoggerBase::LoggerBase(char *string, int number)
00178 {
00179 QMutexLocker locker(&loggerListMutex);
00180 if (string)
00181 {
00182 m_handle.string = strdup(string);
00183 m_string = true;
00184 }
00185 else
00186 {
00187 m_handle.number = number;
00188 m_string = false;
00189 }
00190 loggerList.append(this);
00191 }
00192
00195 LoggerBase::~LoggerBase()
00196 {
00197 QMutexLocker locker(&loggerListMutex);
00198
00199 QList<LoggerBase *>::iterator it;
00200
00201 for (it = loggerList.begin(); it != loggerList.end(); ++it)
00202 {
00203 if( *it == this )
00204 {
00205 loggerList.erase(it);
00206 break;
00207 }
00208 }
00209
00210 if (m_string)
00211 free(m_handle.string);
00212 }
00213
00214
00221 FileLogger::FileLogger(char *filename, bool progress, int quiet) :
00222 LoggerBase(filename, 0), m_opened(false), m_fd(-1),
00223 m_progress(progress), m_quiet(quiet)
00224 {
00225 if( !strcmp(filename, "-") )
00226 {
00227 m_opened = true;
00228 m_fd = 1;
00229 LOG(VB_GENERAL, LOG_INFO, "Added logging to the console");
00230 }
00231 else
00232 {
00233 m_progress = false;
00234 m_quiet = 0;
00235 m_fd = open(filename, O_WRONLY|O_CREAT|O_APPEND, 0664);
00236 m_opened = (m_fd != -1);
00237 LOG(VB_GENERAL, LOG_INFO, QString("Added logging to %1")
00238 .arg(filename));
00239 }
00240 }
00241
00243 FileLogger::~FileLogger()
00244 {
00245 if( m_opened )
00246 {
00247 if( m_fd != 1 )
00248 {
00249 LOG(VB_GENERAL, LOG_INFO, QString("Removed logging to %1")
00250 .arg(m_handle.string));
00251 close( m_fd );
00252 }
00253 else
00254 LOG(VB_GENERAL, LOG_INFO, "Removed logging to the console");
00255 }
00256 }
00257
00260 void FileLogger::reopen(void)
00261 {
00262 char *filename = m_handle.string;
00263
00264
00265 if( !strcmp(filename, "-") )
00266 return;
00267
00268 close(m_fd);
00269
00270 m_fd = open(filename, O_WRONLY|O_CREAT|O_APPEND, 0664);
00271 m_opened = (m_fd != -1);
00272 LOG(VB_GENERAL, LOG_INFO, QString("Rolled logging on %1")
00273 .arg(filename));
00274 }
00275
00278 bool FileLogger::logmsg(LoggingItem *item)
00279 {
00280 char line[MAX_STRING_LENGTH];
00281 char usPart[9];
00282 char timestamp[TIMESTAMP_MAX];
00283 char *threadName = NULL;
00284 pid_t pid = getpid();
00285
00286 if (!m_opened || m_quiet || (m_progress && item->level > LOG_ERR))
00287 return false;
00288
00289 item->refcount.ref();
00290
00291 strftime( timestamp, TIMESTAMP_MAX-8, "%Y-%m-%d %H:%M:%S",
00292 (const struct tm *)&item->tm );
00293 snprintf( usPart, 9, ".%06d", (int)(item->usec) );
00294 strcat( timestamp, usPart );
00295 char shortname;
00296
00297 {
00298 QMutexLocker locker(&loglevelMapMutex);
00299 LoglevelMap::iterator it = loglevelMap.find(item->level);
00300 if (it == loglevelMap.end())
00301 shortname = '-';
00302 else
00303 shortname = (*it)->shortname;
00304 }
00305
00306 if (item->type & kStandardIO)
00307 {
00308 snprintf( line, MAX_STRING_LENGTH, "%s", item->message );
00309 }
00310 else if (m_fd == 1)
00311 {
00312
00313 snprintf( line, MAX_STRING_LENGTH, "%s %c %s\n", timestamp,
00314 shortname, item->message );
00315 }
00316 else
00317 {
00318 threadName = getThreadName(item);
00319 pid_t tid = getThreadTid(item);
00320
00321 if( tid )
00322 snprintf( line, MAX_STRING_LENGTH,
00323 "%s %c [%d/%d] %s %s:%d (%s) - %s\n",
00324 timestamp, shortname, pid, tid, threadName, item->file,
00325 item->line, item->function, item->message );
00326 else
00327 snprintf( line, MAX_STRING_LENGTH,
00328 "%s %c [%d] %s %s:%d (%s) - %s\n",
00329 timestamp, shortname, pid, threadName, item->file,
00330 item->line, item->function, item->message );
00331 }
00332
00333 int fd = (item->type & kStandardIO) ? 1 : m_fd;
00334 int result = write( fd, line, strlen(line) );
00335
00336 deleteItem(item);
00337
00338 if( result == -1 )
00339 {
00340 LOG(VB_GENERAL, LOG_ERR,
00341 QString("Closed Log output on fd %1 due to errors").arg(m_fd));
00342 m_opened = false;
00343 if( m_fd != 1 )
00344 close( m_fd );
00345 return false;
00346 }
00347 return true;
00348 }
00349
00350
00351 #ifndef _WIN32
00352
00353
00354 SyslogLogger::SyslogLogger(int facility) : LoggerBase(NULL, facility),
00355 m_opened(false)
00356 {
00357 CODE *name;
00358
00359 m_application = strdup((char *)QCoreApplication::applicationName()
00360 .toLocal8Bit().constData());
00361
00362 openlog( m_application, LOG_NDELAY | LOG_PID, facility );
00363 m_opened = true;
00364
00365 for (name = &facilitynames[0];
00366 name->c_name && name->c_val != facility; name++);
00367
00368 LOG(VB_GENERAL, LOG_INFO, QString("Added syslogging to facility %1")
00369 .arg(name->c_name));
00370 }
00371
00373 SyslogLogger::~SyslogLogger()
00374 {
00375 LOG(VB_GENERAL, LOG_INFO, "Removing syslogging");
00376 free(m_application);
00377 closelog();
00378 }
00379
00380
00383 bool SyslogLogger::logmsg(LoggingItem *item)
00384 {
00385 if (!m_opened)
00386 return false;
00387
00388 char shortname;
00389
00390 {
00391 QMutexLocker locker(&loglevelMapMutex);
00392 LoglevelMap::iterator it = loglevelMap.find(item->level);
00393 if (it == loglevelMap.end())
00394 shortname = '-';
00395 else
00396 shortname = (*it)->shortname;
00397 }
00398
00399 char *threadName = getThreadName(item);
00400
00401 syslog( item->level, "%c %s %s:%d (%s) %s", shortname, threadName,
00402 item->file, item->line, item->function, item->message );
00403
00404 return true;
00405 }
00406 #endif
00407
00408 const int DatabaseLogger::kMinDisabledTime = 1000;
00409
00412 DatabaseLogger::DatabaseLogger(char *table) : LoggerBase(table, 0),
00413 m_opened(false),
00414 m_loggingTableExists(false)
00415 {
00416 m_query = QString(
00417 "INSERT INTO %1 "
00418 " (host, application, pid, tid, thread, filename, "
00419 " line, function, msgtime, level, message) "
00420 "VALUES (:HOST, :APP, :PID, :TID, :THREAD, :FILENAME, "
00421 " :LINE, :FUNCTION, :MSGTIME, :LEVEL, :MESSAGE)")
00422 .arg(m_handle.string);
00423
00424 LOG(VB_GENERAL, LOG_INFO, QString("Added database logging to table %1")
00425 .arg(m_handle.string));
00426
00427 m_thread = new DBLoggerThread(this);
00428 m_thread->start();
00429
00430 m_opened = true;
00431 m_disabled = false;
00432 }
00433
00435 DatabaseLogger::~DatabaseLogger()
00436 {
00437 LOG(VB_GENERAL, LOG_INFO, "Removing database logging");
00438
00439 stopDatabaseAccess();
00440 }
00441
00443 void DatabaseLogger::stopDatabaseAccess(void)
00444 {
00445 if( m_thread )
00446 {
00447 m_thread->stop();
00448 m_thread->wait();
00449 delete m_thread;
00450 m_thread = NULL;
00451 }
00452 }
00453
00456 bool DatabaseLogger::logmsg(LoggingItem *item)
00457 {
00458 if (!m_thread)
00459 return false;
00460
00461 if (!m_thread->isRunning())
00462 {
00463 m_disabled = true;
00464 m_disabledTime.start();
00465 }
00466
00467 if (!m_disabled && m_thread->queueFull())
00468 {
00469 m_disabled = true;
00470 m_disabledTime.start();
00471 LOG(VB_GENERAL, LOG_CRIT,
00472 "Disabling DB Logging: too many messages queued");
00473 return false;
00474 }
00475
00476 if (m_disabled && m_disabledTime.elapsed() > kMinDisabledTime)
00477 {
00478 if (isDatabaseReady() && !m_thread->queueFull())
00479 {
00480 m_disabled = false;
00481 LOG(VB_GENERAL, LOG_CRIT, "Reenabling DB Logging");
00482 }
00483 }
00484
00485 if (m_disabled)
00486 return false;
00487
00488 item->refcount.ref();
00489 m_thread->enqueue(item);
00490 return true;
00491 }
00492
00496 bool DatabaseLogger::logqmsg(MSqlQuery &query, LoggingItem *item)
00497 {
00498 char timestamp[TIMESTAMP_MAX];
00499 char *threadName = getThreadName(item);
00500 pid_t tid = getThreadTid(item);
00501
00502 strftime( timestamp, TIMESTAMP_MAX-8, "%Y-%m-%d %H:%M:%S",
00503 (const struct tm *)&item->tm );
00504
00505 query.bindValue(":TID", tid);
00506 query.bindValue(":THREAD", threadName);
00507 query.bindValue(":FILENAME", item->file);
00508 query.bindValue(":LINE", item->line);
00509 query.bindValue(":FUNCTION", item->function);
00510 query.bindValue(":MSGTIME", timestamp);
00511 query.bindValue(":LEVEL", item->level);
00512 query.bindValue(":MESSAGE", item->message);
00513
00514 if (!query.exec())
00515 {
00516
00517
00518
00519 QSqlError err = query.lastError();
00520 if ((err.type() != 1 || err.number() != -1) &&
00521 (!m_errorLoggingTime.isValid() ||
00522 (m_errorLoggingTime.elapsed() > 1000)))
00523 {
00524 MythDB::DBError("DBLogging", query);
00525 m_errorLoggingTime.start();
00526 }
00527 return false;
00528 }
00529
00530 deleteItem(item);
00531
00532 return true;
00533 }
00534
00537 void DatabaseLogger::prepare(MSqlQuery &query)
00538 {
00539 query.prepare(m_query);
00540 query.bindValue(":HOST", gCoreContext->GetHostName());
00541 query.bindValue(":APP", QCoreApplication::applicationName());
00542 query.bindValue(":PID", getpid());
00543 }
00544
00547 bool DatabaseLogger::isDatabaseReady(void)
00548 {
00549 bool ready = false;
00550 MythDB *db = GetMythDB();
00551
00552 if ((db) && db->HaveValidDatabase())
00553 {
00554 if ( !m_loggingTableExists )
00555 m_loggingTableExists = tableExists(m_handle.string);
00556
00557 if ( m_loggingTableExists )
00558 ready = true;
00559 }
00560
00561 return ready;
00562 }
00563
00567 bool DatabaseLogger::tableExists(const QString &table)
00568 {
00569 bool result = false;
00570 MSqlQuery query(MSqlQuery::InitCon());
00571 if (query.isConnected())
00572 {
00573 QString sql = "SELECT INFORMATION_SCHEMA.COLUMNS.COLUMN_NAME "
00574 " FROM INFORMATION_SCHEMA.COLUMNS "
00575 " WHERE INFORMATION_SCHEMA.COLUMNS.TABLE_SCHEMA = "
00576 " DATABASE() "
00577 " AND INFORMATION_SCHEMA.COLUMNS.TABLE_NAME = "
00578 " :TABLENAME "
00579 " AND INFORMATION_SCHEMA.COLUMNS.COLUMN_NAME = "
00580 " :COLUMNNAME;";
00581 if (query.prepare(sql))
00582 {
00583 query.bindValue(":TABLENAME", table);
00584 query.bindValue(":COLUMNNAME", "function");
00585 if (query.exec() && query.next())
00586 result = true;
00587 }
00588 }
00589 return result;
00590 }
00591
00592
00595 DBLoggerThread::DBLoggerThread(DatabaseLogger *logger) :
00596 MThread("DBLogger"),
00597 m_logger(logger),
00598 m_queue(new QQueue<LoggingItem *>),
00599 m_wait(new QWaitCondition()), aborted(false)
00600 {
00601 }
00602
00605 DBLoggerThread::~DBLoggerThread()
00606 {
00607 stop();
00608 wait();
00609
00610 QMutexLocker qLock(&m_queueMutex);
00611 while (!m_queue->empty())
00612 deleteItem(m_queue->dequeue());
00613 delete m_queue;
00614 delete m_wait;
00615 m_queue = NULL;
00616 m_wait = NULL;
00617 }
00618
00620 void DBLoggerThread::run(void)
00621 {
00622 RunProlog();
00623
00624
00625
00626
00627 while (true)
00628 {
00629 if ((aborted || (gCoreContext && m_logger->isDatabaseReady())))
00630 break;
00631
00632 QMutexLocker locker(&m_queueMutex);
00633 m_wait->wait(locker.mutex(), 100);
00634 }
00635
00636 if (!aborted)
00637 {
00638
00639
00640
00641 MSqlQuery *query = new MSqlQuery(MSqlQuery::InitCon());
00642 m_logger->prepare(*query);
00643
00644 QMutexLocker qLock(&m_queueMutex);
00645 while (!aborted || !m_queue->isEmpty())
00646 {
00647 if (m_queue->isEmpty())
00648 {
00649 m_wait->wait(qLock.mutex(), 100);
00650 continue;
00651 }
00652
00653 LoggingItem *item = m_queue->dequeue();
00654 if (!item)
00655 continue;
00656
00657 qLock.unlock();
00658
00659 if (item->message[0] != '\0')
00660 {
00661 if (!m_logger->logqmsg(*query, item))
00662 {
00663 qLock.relock();
00664 m_queue->prepend(item);
00665 m_wait->wait(qLock.mutex(), 100);
00666 delete query;
00667 query = new MSqlQuery(MSqlQuery::InitCon());
00668 m_logger->prepare(*query);
00669 continue;
00670 }
00671 }
00672 else
00673 {
00674 deleteItem(item);
00675 }
00676
00677 qLock.relock();
00678 }
00679
00680 delete query;
00681
00682 qLock.unlock();
00683 }
00684
00685 RunEpilog();
00686 }
00687
00689 void DBLoggerThread::stop(void)
00690 {
00691 QMutexLocker qLock(&m_queueMutex);
00692 aborted = true;
00693 m_wait->wakeAll();
00694 }
00695
00699 char *getThreadName( LoggingItem *item )
00700 {
00701 static const char *unknown = "thread_unknown";
00702 char *threadName;
00703
00704 if( !item )
00705 return( (char *)unknown );
00706
00707 if( !item->threadName )
00708 {
00709 QMutexLocker locker(&logThreadMutex);
00710 if( logThreadHash.contains(item->threadId) )
00711 threadName = logThreadHash[item->threadId];
00712 else
00713 threadName = (char *)unknown;
00714 }
00715 else
00716 {
00717 threadName = item->threadName;
00718 }
00719
00720 return( threadName );
00721 }
00722
00729 int64_t getThreadTid( LoggingItem *item )
00730 {
00731 pid_t tid = 0;
00732
00733 if( !item )
00734 return( 0 );
00735
00736 QMutexLocker locker(&logThreadTidMutex);
00737 if( logThreadTidHash.contains(item->threadId) )
00738 tid = logThreadTidHash[item->threadId];
00739
00740 return( tid );
00741 }
00742
00750 void setThreadTid( LoggingItem *item )
00751 {
00752 QMutexLocker locker(&logThreadTidMutex);
00753
00754 if( ! logThreadTidHash.contains(item->threadId) )
00755 {
00756 int64_t tid = 0;
00757
00758 #if defined(linux)
00759 tid = (int64_t)syscall(SYS_gettid);
00760 #elif defined(__FreeBSD__)
00761 long lwpid;
00762 int dummy = thr_self( &lwpid );
00763 (void)dummy;
00764 tid = (int64_t)lwpid;
00765 #elif CONFIG_DARWIN
00766 tid = (int64_t)mach_thread_self();
00767 #endif
00768 logThreadTidHash[item->threadId] = tid;
00769 }
00770 }
00771
00775 LoggerThread::LoggerThread() :
00776 MThread("Logger"),
00777 m_waitNotEmpty(new QWaitCondition()),
00778 m_waitEmpty(new QWaitCondition()),
00779 aborted(false)
00780 {
00781 char *debug = getenv("VERBOSE_THREADS");
00782 if (debug != NULL)
00783 {
00784 LOG(VB_GENERAL, LOG_NOTICE,
00785 "Logging thread registration/deregistration enabled!");
00786 debugRegistration = true;
00787 }
00788 }
00789
00791 LoggerThread::~LoggerThread()
00792 {
00793 stop();
00794 wait();
00795
00796 QMutexLocker locker(&loggerListMutex);
00797
00798 QList<LoggerBase *>::iterator it;
00799
00800 for (it = loggerList.begin(); it != loggerList.end(); ++it)
00801 {
00802 (*it)->deleteLater();
00803 }
00804
00805 delete m_waitNotEmpty;
00806 delete m_waitEmpty;
00807 }
00808
00813 void LoggerThread::run(void)
00814 {
00815 RunProlog();
00816
00817 logThreadFinished = false;
00818
00819 QMutexLocker qLock(&logQueueMutex);
00820
00821 while (!aborted || !logQueue.isEmpty())
00822 {
00823 if (logQueue.isEmpty())
00824 {
00825 m_waitEmpty->wakeAll();
00826 m_waitNotEmpty->wait(qLock.mutex(), 100);
00827 continue;
00828 }
00829
00830 LoggingItem *item = logQueue.dequeue();
00831 qLock.unlock();
00832
00833 handleItem(item);
00834 deleteItem(item);
00835
00836 qLock.relock();
00837 }
00838
00839 logThreadFinished = true;
00840
00841 qLock.unlock();
00842
00843 RunEpilog();
00844 }
00845
00846
00853 void LoggerThread::handleItem(LoggingItem *item)
00854 {
00855 if (item->type & kRegistering)
00856 {
00857 int64_t tid = getThreadTid(item);
00858
00859 QMutexLocker locker(&logThreadMutex);
00860 logThreadHash[item->threadId] = strdup(item->threadName);
00861
00862 if (debugRegistration)
00863 {
00864 snprintf(item->message, LOGLINE_MAX,
00865 "Thread 0x%" PREFIX64 "X (%" PREFIX64
00866 "d) registered as \'%s\'",
00867 (long long unsigned int)item->threadId,
00868 (long long int)tid,
00869 logThreadHash[item->threadId]);
00870 }
00871 }
00872 else if (item->type & kDeregistering)
00873 {
00874 int64_t tid = 0;
00875
00876 {
00877 QMutexLocker locker(&logThreadTidMutex);
00878 if( logThreadTidHash.contains(item->threadId) )
00879 {
00880 tid = logThreadTidHash[item->threadId];
00881 logThreadTidHash.remove(item->threadId);
00882 }
00883 }
00884
00885 QMutexLocker locker(&logThreadMutex);
00886 if (logThreadHash.contains(item->threadId))
00887 {
00888 if (debugRegistration)
00889 {
00890 snprintf(item->message, LOGLINE_MAX,
00891 "Thread 0x%" PREFIX64 "X (%" PREFIX64
00892 "d) deregistered as \'%s\'",
00893 (long long unsigned int)item->threadId,
00894 (long long int)tid,
00895 logThreadHash[item->threadId]);
00896 }
00897 item->threadName = logThreadHash[item->threadId];
00898 logThreadHash.remove(item->threadId);
00899 }
00900 }
00901
00902 if (item->message[0] != '\0')
00903 {
00904 QMutexLocker locker(&loggerListMutex);
00905
00906 QList<LoggerBase *>::iterator it;
00907 for (it = loggerList.begin(); it != loggerList.end(); ++it)
00908 (*it)->logmsg(item);
00909 }
00910 }
00911
00914 void LoggerThread::stop(void)
00915 {
00916 QMutexLocker qLock(&logQueueMutex);
00917 flush(1000);
00918 aborted = true;
00919 m_waitNotEmpty->wakeAll();
00920 }
00921
00925 bool LoggerThread::flush(int timeoutMS)
00926 {
00927 QTime t;
00928 t.start();
00929 while (!aborted && logQueue.isEmpty() && t.elapsed() < timeoutMS)
00930 {
00931 m_waitNotEmpty->wakeAll();
00932 int left = timeoutMS - t.elapsed();
00933 if (left > 0)
00934 m_waitEmpty->wait(&logQueueMutex, left);
00935 }
00936 return logQueue.isEmpty();
00937 }
00938
00939 static QList<LoggingItem*> item_recycler;
00940 static QAtomicInt item_count;
00941 static QAtomicInt malloc_count;
00942
00943 #define DEBUG_MEMORY 0
00944 #if DEBUG_MEMORY
00945 static int max_count = 0;
00946 static QTime memory_time;
00947 #endif
00948
00956 static LoggingItem *createItem(const char *_file, const char *_function,
00957 int _line, LogLevel_t _level, int _type)
00958 {
00959 LoggingItem *item = new LoggingItem(_file, _function, _line, _level, _type);
00960
00961 malloc_count.ref();
00962
00963 #if DEBUG_MEMORY
00964 int val = item_count.fetchAndAddRelaxed(1) + 1;
00965 if (val == 0)
00966 memory_time.start();
00967 max_count = (val > max_count) ? val : max_count;
00968 if (memory_time.elapsed() > 1000)
00969 {
00970 cout<<"current memory usage: "
00971 <<val<<" * "<<sizeof(LoggingItem)<<endl;
00972 cout<<"max memory usage: "
00973 <<max_count<<" * "<<sizeof(LoggingItem)<<endl;
00974 cout<<"malloc count: "<<(int)malloc_count<<endl;
00975 memory_time.start();
00976 }
00977 #else
00978 item_count.ref();
00979 #endif
00980
00981 return item;
00982 }
00983
00986 static void deleteItem(LoggingItem *item)
00987 {
00988 if (!item)
00989 return;
00990
00991 if (!item->refcount.deref())
00992 {
00993 if (item->threadName)
00994 free(item->threadName);
00995 item_count.deref();
00996 delete item;
00997 }
00998 }
00999
01004 void LogTimeStamp( struct tm *tm, uint32_t *usec )
01005 {
01006 if( !usec || !tm )
01007 return;
01008
01009 time_t epoch;
01010
01011 #if HAVE_GETTIMEOFDAY
01012 struct timeval tv;
01013 gettimeofday(&tv, NULL);
01014 epoch = tv.tv_sec;
01015 *usec = tv.tv_usec;
01016 #else
01017
01018 QDateTime date = QDateTime::currentDateTime();
01019 QTime time = date.time();
01020 epoch = date.toTime_t();
01021 *usec = time.msec() * 1000;
01022 #endif
01023
01024 localtime_r(&epoch, tm);
01025 }
01026
01038 void LogPrintLine( uint64_t mask, LogLevel_t level, const char *file, int line,
01039 const char *function, int fromQString,
01040 const char *format, ... )
01041 {
01042 va_list arguments;
01043
01044 QMutexLocker qLock(&logQueueMutex);
01045
01046 int type = kMessage;
01047 type |= (mask & VB_FLUSH) ? kFlush : 0;
01048 type |= (mask & VB_STDIO) ? kStandardIO : 0;
01049 LoggingItem *item = createItem(file, function, line, level, type);
01050 if (!item)
01051 return;
01052
01053 char *formatcopy = NULL;
01054 if( fromQString && strchr(format, '%') )
01055 {
01056 QString string(format);
01057 format = strdup(string.replace(logRegExp, "%%").toLocal8Bit()
01058 .constData());
01059 formatcopy = (char *)format;
01060 }
01061
01062 va_start(arguments, format);
01063 vsnprintf(item->message, LOGLINE_MAX, format, arguments);
01064 va_end(arguments);
01065
01066 if (formatcopy)
01067 free(formatcopy);
01068
01069 logQueue.enqueue(item);
01070
01071 if (logThread && logThreadFinished && !logThread->isRunning())
01072 {
01073 while (!logQueue.isEmpty())
01074 {
01075 item = logQueue.dequeue();
01076 qLock.unlock();
01077 logThread->handleItem(item);
01078 deleteItem(item);
01079 qLock.relock();
01080 }
01081 }
01082 else if (logThread && !logThreadFinished && (type & kFlush))
01083 {
01084 logThread->flush();
01085 }
01086 }
01087
01088 #ifndef _WIN32
01089
01090 void logSighup( int signum, siginfo_t *info, void *secret )
01091 {
01092 LOG(VB_GENERAL, LOG_INFO, "SIGHUP received, rolling log files.");
01093
01094
01095 QMutexLocker locker(&loggerListMutex);
01096
01097 QList<LoggerBase *>::iterator it;
01098 for (it = loggerList.begin(); it != loggerList.end(); ++it)
01099 {
01100 (*it)->reopen();
01101 }
01102 }
01103 #endif
01104
01108 void logPropagateCalc(void)
01109 {
01110 QString mask = verboseString.trimmed();
01111 mask.replace(QRegExp(" "), ",");
01112 mask.remove(QRegExp("^,"));
01113 logPropagateArgs = " --verbose " + mask;
01114
01115 if (logPropagateOpts.propagate)
01116 logPropagateArgs += " --logpath " + logPropagateOpts.path;
01117
01118 QString name = logLevelGetName(logLevel);
01119 logPropagateArgs += " --loglevel " + name;
01120
01121 for (int i = 0; i < logPropagateOpts.quiet; i++)
01122 logPropagateArgs += " --quiet";
01123
01124 if (!logPropagateOpts.dblog)
01125 logPropagateArgs += " --nodblog";
01126
01127 #ifndef _WIN32
01128 if (logPropagateOpts.facility >= 0)
01129 {
01130 CODE *syslogname;
01131
01132 for (syslogname = &facilitynames[0];
01133 (syslogname->c_name &&
01134 syslogname->c_val != logPropagateOpts.facility); syslogname++);
01135
01136 logPropagateArgs += QString(" --syslog %1").arg(syslogname->c_name);
01137 }
01138 #endif
01139 }
01140
01143 bool logPropagateQuiet(void)
01144 {
01145 return logPropagateOpts.quiet;
01146 }
01147
01160 void logStart(QString logfile, int progress, int quiet, int facility,
01161 LogLevel_t level, bool dblog, bool propagate)
01162 {
01163 LoggerBase *logger;
01164
01165 {
01166 QMutexLocker qLock(&logQueueMutex);
01167 if (!logThread)
01168 logThread = new LoggerThread();
01169 }
01170
01171 if (logThread->isRunning())
01172 return;
01173
01174 logLevel = level;
01175 LOG(VB_GENERAL, LOG_NOTICE, QString("Setting Log Level to LOG_%1")
01176 .arg(logLevelGetName(logLevel).toUpper()));
01177
01178 logPropagateOpts.propagate = propagate;
01179 logPropagateOpts.quiet = quiet;
01180 logPropagateOpts.facility = facility;
01181 logPropagateOpts.dblog = dblog;
01182
01183 if (propagate)
01184 {
01185 QFileInfo finfo(logfile);
01186 QString path = finfo.path();
01187 logPropagateOpts.path = path;
01188 }
01189
01190 logPropagateCalc();
01191
01192
01193 logger = new FileLogger((char *)"-", progress, quiet);
01194
01195
01196 if( !logfile.isEmpty() )
01197 logger = new FileLogger((char *)logfile.toLocal8Bit().constData(),
01198 false, false);
01199
01200 #ifndef _WIN32
01201
01202 if( facility == -1 )
01203 LOG(VB_GENERAL, LOG_CRIT,
01204 "Syslogging facility unknown, disabling syslog output");
01205 else if( facility >= 0 )
01206 logger = new SyslogLogger(facility);
01207 #endif
01208
01209
01210 if( dblog )
01211 logger = new DatabaseLogger((char *)"logging");
01212
01213 #ifndef _WIN32
01214
01215 LOG(VB_GENERAL, LOG_NOTICE, "Setting up SIGHUP handler");
01216 struct sigaction sa;
01217 sa.sa_sigaction = logSighup;
01218 sigemptyset( &sa.sa_mask );
01219 sa.sa_flags = SA_RESTART | SA_SIGINFO;
01220 sigaction( SIGHUP, &sa, NULL );
01221 #endif
01222
01223 (void)logger;
01224
01225 logThread->start();
01226 }
01227
01229 void logStop(void)
01230 {
01231 if (logThread)
01232 {
01233 logThread->stop();
01234 logThread->wait();
01235 }
01236
01237 #ifndef _WIN32
01238
01239 struct sigaction sa;
01240 sa.sa_handler = SIG_DFL;
01241 sigemptyset( &sa.sa_mask );
01242 sa.sa_flags = SA_RESTART;
01243 sigaction( SIGHUP, &sa, NULL );
01244 #endif
01245
01246 QList<LoggerBase *>::iterator it;
01247 for (it = loggerList.begin(); it != loggerList.end(); ++it)
01248 {
01249 (*it)->stopDatabaseAccess();
01250 }
01251 }
01252
01257 void loggingRegisterThread(const QString &name)
01258 {
01259 if (logThreadFinished)
01260 return;
01261
01262 QMutexLocker qLock(&logQueueMutex);
01263
01264 LoggingItem *item = createItem(__FILE__, __FUNCTION__, __LINE__,
01265 (LogLevel_t)LOG_DEBUG, kRegistering);
01266 if (item)
01267 {
01268 item->threadName = strdup((char *)name.toLocal8Bit().constData());
01269 logQueue.enqueue(item);
01270 }
01271 }
01272
01275 void loggingDeregisterThread(void)
01276 {
01277 if (logThreadFinished)
01278 return;
01279
01280 QMutexLocker qLock(&logQueueMutex);
01281
01282 LoggingItem *item = createItem(__FILE__, __FUNCTION__, __LINE__,
01283 (LogLevel_t)LOG_DEBUG, kDeregistering);
01284 if (item)
01285 logQueue.enqueue(item);
01286 }
01287
01291 int syslogGetFacility(QString facility)
01292 {
01293 #ifdef _WIN32
01294 LOG(VB_GENERAL, LOG_NOTICE,
01295 "Windows does not support syslog, disabling" );
01296 return( -2 );
01297 #else
01298 CODE *name;
01299 int i;
01300 QByteArray ba = facility.toLocal8Bit();
01301 char *string = (char *)ba.constData();
01302
01303 for (i = 0, name = &facilitynames[0];
01304 name->c_name && strcmp(name->c_name, string); i++, name++);
01305
01306 return( name->c_val );
01307 #endif
01308 }
01309
01313 LogLevel_t logLevelGet(QString level)
01314 {
01315 QMutexLocker locker(&loglevelMapMutex);
01316 if (!verboseInitialized)
01317 {
01318 locker.unlock();
01319 verboseInit();
01320 locker.relock();
01321 }
01322
01323 for (LoglevelMap::iterator it = loglevelMap.begin();
01324 it != loglevelMap.end(); ++it)
01325 {
01326 LoglevelDef *item = (*it);
01327 if ( item->name == level.toLower() )
01328 return (LogLevel_t)item->value;
01329 }
01330
01331 return LOG_UNKNOWN;
01332 }
01333
01337 QString logLevelGetName(LogLevel_t level)
01338 {
01339 QMutexLocker locker(&loglevelMapMutex);
01340 if (!verboseInitialized)
01341 {
01342 locker.unlock();
01343 verboseInit();
01344 locker.relock();
01345 }
01346 LoglevelMap::iterator it = loglevelMap.find((int)level);
01347
01348 if ( it == loglevelMap.end() )
01349 return QString("unknown");
01350
01351 return (*it)->name;
01352 }
01353
01360 void verboseAdd(uint64_t mask, QString name, bool additive, QString helptext)
01361 {
01362 VerboseDef *item = new VerboseDef;
01363
01364 item->mask = mask;
01365 name.detach();
01366
01367 name.remove(0, 3);
01368 name = name.toLower();
01369 item->name = name;
01370 item->additive = additive;
01371 helptext.detach();
01372 item->helpText = helptext;
01373
01374 verboseMap.insert(name, item);
01375 }
01376
01382 void loglevelAdd(int value, QString name, char shortname)
01383 {
01384 LoglevelDef *item = new LoglevelDef;
01385
01386 item->value = value;
01387 name.detach();
01388
01389 name.remove(0, 4);
01390 name = name.toLower();
01391 item->name = name;
01392 item->shortname = shortname;
01393
01394 loglevelMap.insert(value, item);
01395 }
01396
01398 void verboseInit(void)
01399 {
01400 QMutexLocker locker(&verboseMapMutex);
01401 QMutexLocker locker2(&loglevelMapMutex);
01402 verboseMap.clear();
01403 loglevelMap.clear();
01404
01405
01406
01407
01408
01409
01410
01411
01412
01413 #undef VERBOSEDEFS_H_
01414 #define _IMPLEMENT_VERBOSE
01415 #include "verbosedefs.h"
01416
01417 verboseInitialized = true;
01418 }
01419
01420
01423 void verboseHelp(void)
01424 {
01425 QString m_verbose = verboseString.trimmed();
01426 m_verbose.replace(QRegExp(" "), ",");
01427 m_verbose.remove(QRegExp("^,"));
01428
01429 cerr << "Verbose debug levels.\n"
01430 "Accepts any combination (separated by comma) of:\n\n";
01431
01432 for (VerboseMap::Iterator vit = verboseMap.begin();
01433 vit != verboseMap.end(); ++vit )
01434 {
01435 VerboseDef *item = vit.value();
01436 QString name = QString(" %1").arg(item->name, -15, ' ');
01437 if (item->helpText.isEmpty())
01438 continue;
01439 cerr << name.toLocal8Bit().constData() << " - " <<
01440 item->helpText.toLocal8Bit().constData() << endl;
01441 }
01442
01443 cerr << endl <<
01444 "The default for this program appears to be: '-v " <<
01445 m_verbose.toLocal8Bit().constData() << "'\n\n"
01446 "Most options are additive except for 'none' and 'all'.\n"
01447 "These two are semi-exclusive and take precedence over any\n"
01448 "other options. However, you may use something like\n"
01449 "'-v none,jobqueue' to receive only JobQueue related messages\n"
01450 "and override the default verbosity level.\n\n"
01451 "Additive options may also be subtracted from 'all' by\n"
01452 "prefixing them with 'no', so you may use '-v all,nodatabase'\n"
01453 "to view all but database debug messages.\n\n"
01454 "Some debug levels may not apply to this program.\n\n";
01455 }
01456
01460 int verboseArgParse(QString arg)
01461 {
01462 QString option;
01463
01464 if (!verboseInitialized)
01465 verboseInit();
01466
01467 QMutexLocker locker(&verboseMapMutex);
01468
01469 verboseMask = verboseDefaultInt;
01470 verboseString = QString(verboseDefaultStr);
01471
01472 if (arg.startsWith('-'))
01473 {
01474 cerr << "Invalid or missing argument to -v/--verbose option\n";
01475 return GENERIC_EXIT_INVALID_CMDLINE;
01476 }
01477
01478 QStringList verboseOpts = arg.split(QRegExp("\\W+"));
01479 for (QStringList::Iterator it = verboseOpts.begin();
01480 it != verboseOpts.end(); ++it )
01481 {
01482 option = (*it).toLower();
01483 bool reverseOption = false;
01484
01485 if (option != "none" && option.left(2) == "no")
01486 {
01487 reverseOption = true;
01488 option = option.right(option.length() - 2);
01489 }
01490
01491 if (option == "help")
01492 {
01493 verboseHelp();
01494 return GENERIC_EXIT_INVALID_CMDLINE;
01495 }
01496 else if (option == "important")
01497 {
01498 cerr << "The \"important\" log mask is no longer valid.\n";
01499 }
01500 else if (option == "extra")
01501 {
01502 cerr << "The \"extra\" log mask is no longer valid. Please try "
01503 "--loglevel debug instead.\n";
01504 }
01505 else if (option == "default")
01506 {
01507 if (haveUserDefaultValues)
01508 {
01509 verboseMask = userDefaultValueInt;
01510 verboseString = userDefaultValueStr;
01511 }
01512 else
01513 {
01514 verboseMask = verboseDefaultInt;
01515 verboseString = QString(verboseDefaultStr);
01516 }
01517 }
01518 else
01519 {
01520 VerboseDef *item = verboseMap.value(option);
01521
01522 if (item)
01523 {
01524 if (reverseOption)
01525 {
01526 verboseMask &= ~(item->mask);
01527 verboseString = verboseString.remove(' ' + item->name);
01528 verboseString += " no" + item->name;
01529 }
01530 else
01531 {
01532 if (item->additive)
01533 {
01534 if (!(verboseMask & item->mask))
01535 {
01536 verboseMask |= item->mask;
01537 verboseString += ' ' + item->name;
01538 }
01539 }
01540 else
01541 {
01542 verboseMask = item->mask;
01543 verboseString = item->name;
01544 }
01545 }
01546 }
01547 else
01548 {
01549 cerr << "Unknown argument for -v/--verbose: " <<
01550 option.toLocal8Bit().constData() << endl;;
01551 return GENERIC_EXIT_INVALID_CMDLINE;
01552 }
01553 }
01554 }
01555
01556 if (!haveUserDefaultValues)
01557 {
01558 haveUserDefaultValues = true;
01559 userDefaultValueInt = verboseMask;
01560 userDefaultValueStr = verboseString;
01561 }
01562
01563 return GENERIC_EXIT_OK;
01564 }
01565
01570 QString logStrerror(int errnum)
01571 {
01572 return QString("%1 (%2)").arg(strerror(errnum)).arg(errnum);
01573 }
01574
01575
01576
01577
01578