Torc  0.1
torcloggingimp.cpp
Go to the documentation of this file.
1 // Std
2 #include <stdlib.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <stdio.h>
6 #include <iostream>
7 
8 // Qt
9 #include <QtGlobal>
10 #include <QMutex>
11 #include <QList>
12 #include <QTime>
13 #include <QRegExp>
14 #include <QHash>
15 #include <QMap>
16 #include <QByteArray>
17 #include <QFileInfo>
18 #include <QStringList>
19 #include <QQueue>
20 
21 // Torc
22 #include "torcexitcodes.h"
23 #include "torccompat.h"
24 #include "torclogging.h"
25 #include "torcloggingimp.h"
26 
27 // Various ways to get to thread's tid
28 #ifdef Q_OS_LINUX
29 #include <unistd.h>
30 #include <sys/syscall.h>
31 #elif defined(__FreeBSD__)
32 extern "C" {
33 #include <sys/ucontext.h>
34 #include <sys/thr.h>
35 }
36 #elif defined(Q_OS_MAC)
37 #include <mach/mach.h>
38 #endif
39 
40 class LoggingThread;
41 class LogItem;
42 
43 using namespace std;
44 
46 QList<LoggerBase *> gLoggerList;
48 QQueue<LogItem *> gLogQueue;
49 QRegExp gLogRexExp = QRegExp("[%]{1,2}");
51 QHash<uint64_t, char *> gLogThreadHash;
53 QHash<uint64_t, int64_t> gLogThreadtidHash;
55 bool gLogThreadFinished = false;
56 
57 typedef enum {
58  kMessage = 0x01,
59  kRegistering = 0x02,
61  kFlush = 0x08,
62  kStandardIO = 0x10,
63 } LoggingType;
64 
65 char* GetThreadName(LogItem *item);
66 int64_t GetThreadTid(LogItem *item);
67 
68 class LogItem
69 {
70  public:
71  static LogItem* Create(const char* File, const char* Function,
72  int Line, LogLevel Level, int Type, quint64 Mask)
73  {
74  return new LogItem(File, Function, Line, Level, Type, Mask);
75  }
76 
77  static void Delete(LogItem *Item)
78  {
79  if (Item && !Item->refCount.deref())
80  {
81  if (Item->threadName)
82  free(Item->threadName);
83  delete Item;
84  }
85  }
86 
87  protected:
88  LogItem(const char *File, const char *Function,
89  int Line, LogLevel Level, int Type, quint64 Mask)
90  : refCount(),
91  threadId((uint64_t)(QThread::currentThreadId())),
92  usec(0),
93  line(Line), type(Type),
94  level(Level),
95  tm(),
96  file(File),
97  function(Function), threadName(nullptr),
98  mask(Mask)
99  {
100  SetTime();
101  SetThreadTid();
102 
103  message[0]='\0';
104  message[LOGLINE_MAX]='\0';
105 
106  refCount.ref();
107  }
108 
109  public:
110  void SetTime(void)
111  {
112  time_t epoch;
113 
114 #if HAVE_GETTIMEOFDAY
115  struct timeval tv;
116  gettimeofday(&tv, nullptr);
117  epoch = tv.tv_sec;
118  usec = tv.tv_usec;
119 #else
120  QDateTime date = QDateTime::currentDateTime();
121  QTime time = date.time();
122  epoch = date.toTime_t();
123  usec = time.msec() * 1000;
124 #endif
125 
126  localtime_r(&epoch, &tm);
127  }
128 
129  void SetThreadTid(void)
130  {
131  QMutexLocker locker(&gLogThreadTidLock);
132 
133  if (!gLogThreadtidHash.contains(this->threadId))
134  {
135  int64_t tid = 0;
136 
137 #ifdef Q_OS_LINUX
138  tid = (int64_t)syscall(SYS_gettid);
139 #elif defined(__FreeBSD__)
140  long lwpid;
141  int dummy = thr_self( &lwpid );
142  (void)dummy;
143  tid = (int64_t)lwpid;
144 #elif defined(Q_OS_MAC)
145  tid = (int64_t)mach_thread_self();
146 #endif
147  gLogThreadtidHash[this->threadId] = tid;
148  }
149  }
150 
151  QAtomicInt refCount;
152  uint64_t threadId;
153  uint32_t usec;
154  int line;
155  int type;
156  LogLevel level;
157  struct tm tm;
158  const char *file;
159  const char *function;
160  char *threadName;
161  char message[LOGLINE_MAX+1];
162  quint64 mask;
163 };
164 
166 {
167  Initialise();
168 
169  gLogThreadFinished = false;
170 
171  QMutexLocker lock(&gLogQueueLock);
172 
173  while (!m_aborted || !gLogQueue.isEmpty())
174  {
175  if (gLogQueue.isEmpty())
176  {
177  m_waitEmpty.wakeAll();
178  m_waitNotEmpty.wait(lock.mutex(), 100);
179  continue;
180  }
181 
182  LogItem *item = gLogQueue.dequeue();
183  lock.unlock();
184 
185  HandleItem(item);
186  LogItem::Delete(item);
187 
188  lock.relock();
189  }
190 
191  gLogThreadFinished = true;
192 
193  lock.unlock();
194 
195  Deinitialise();
196 }
197 
199 {
200 }
201 
203 {
204 }
205 
207 {
208  if (m_aborted)
209  return;
210 
211  {
212  QMutexLocker lock(&gLogQueueLock);
213  Flush(1000);
214  m_aborted = true;
215  }
216  m_waitNotEmpty.wakeAll();
217 }
218 
219 bool LoggingThread::Flush(int TimeoutMS)
220 {
221  QTime t;
222  t.start();
223  while (!m_aborted && !gLogQueue.isEmpty() && t.elapsed() < TimeoutMS)
224  {
225  m_waitNotEmpty.wakeAll();
226  int left = TimeoutMS - t.elapsed();
227  if (left > 0)
228  m_waitEmpty.wait(&gLogQueueLock, left);
229  }
230  return gLogQueue.isEmpty();
231 }
232 
234 {
235  if (Item->type & kRegistering)
236  {
237  QMutexLocker locker(&gLogThreadLock);
238  if (gLogThreadHash.contains(Item->threadId))
239  free(gLogThreadHash.take(Item->threadId));
240 
241  gLogThreadHash[Item->threadId] = strdup(Item->threadName);
242  }
243  else if (Item->type & kDeregistering)
244  {
245  {
246  QMutexLocker locker(&gLogThreadTidLock);
247  if (gLogThreadtidHash.contains(Item->threadId))
248  gLogThreadtidHash.remove(Item->threadId);
249  }
250 
251  QMutexLocker locker(&gLogThreadLock);
252  if (gLogThreadHash.contains(Item->threadId))
253  {
254  char* threadname = gLogThreadHash.take(Item->threadId);
255  free(threadname);
256  }
257  }
258 
259  if (Item->message[0] != '\0')
260  {
261  QMutexLocker locker(&gLoggerListLock);
262 
263  QList<LoggerBase *>::iterator it;
264  for (it = gLoggerList.begin(); it != gLoggerList.end(); ++it)
265  (*it)->Logmsg(Item);
266  }
267 }
268 
269 typedef struct {
270  bool propagate;
271  int quiet;
272  QString path;
274 
275 LogPropagateOpts gLogPropagationOpts = { false, 0, QStringLiteral("") };
277 
278 #define TIMESTAMP_MAX 30
279 #define MAX_STRING_LENGTH (LOGLINE_MAX+120)
280 
281 LogLevel gLogLevel = (LogLevel)LOG_INFO;
282 
283 typedef struct {
284  uint64_t mask { 0 };
285  QString name { QStringLiteral("") };
286  bool additive { false };
287  QString helpText { QStringLiteral("") };
288 } VerboseDef;
289 
290 typedef QMap<QString, VerboseDef> VerboseMap;
291 
292 typedef struct {
293  int value { 0 };
294  QString name { QStringLiteral("") };
295  char shortname { '?' };
296 } LoglevelDef;
297 
298 typedef QMap<int, LoglevelDef> LoglevelMap;
299 
304 
305 bool gVerboseInitialised = false;
306 const uint64_t gVerboseDefaultInt = VB_GENERAL;
307 QString gVerboseDefaultStr = QStringLiteral(" general");
313 
314 void AddVerbose(uint64_t mask, QString name, bool additive, QString helptext);
315 void AddLogLevel(int value, QString name, char shortname);
316 void InitVerbose(void);
317 void VerboseHelp(void);
318 
319 LoggerBase::LoggerBase(const QString &FileName) : m_fileName(FileName)
320 {
321  QMutexLocker locker(&gLoggerListLock);
322  gLoggerList.append(this);
323 }
324 
325 QByteArray LoggerBase::GetLine(LogItem *Item)
326 {
327  if (!Item)
328  return QByteArray();
329 
330  Item->refCount.ref();
331 
332  QByteArray line(MAX_STRING_LENGTH, ' ');
333  //char line[MAX_STRING_LENGTH];
334  char timestamp[TIMESTAMP_MAX];
335  char usPart[9];
336  strftime(timestamp, TIMESTAMP_MAX-8, "%Y-%m-%d %H:%M:%S",
337  (const struct tm *)&Item->tm );
338  snprintf(usPart, 5, ".%03d", (int)(Item->usec));
339  strcat(timestamp, usPart);
340  char shortname;
341 
342  {
343  QMutexLocker locker(&gLoglevelMapLock);
344  LoglevelMap::iterator it = gLoglevelMap.find(Item->level);
345  if (it == gLoglevelMap.end())
346  shortname = '-';
347  else
348  shortname = (*it).shortname;
349  }
350 
351  if (Item->type & kStandardIO)
352  {
353  snprintf(line.data(), MAX_STRING_LENGTH, "%s", Item->message);
354  }
355  else
356  {
357  char fileline[50];
358  snprintf(fileline, 50, "%s (%s:%d)", Item->function, Item->file, Item->line);
359  char* threadName = GetThreadName(Item);
360  pid_t tid = GetThreadTid(Item);
361 
362  snprintf(line.data(), MAX_STRING_LENGTH, "%s %c [%6d/%6d] %-11s %-50s - %s\n",
363  timestamp, shortname, getpid(), tid, threadName, fileline,
364  Item->message);
365  }
366 
367  Item->refCount.deref();
368  return line.trimmed();
369 }
370 
371 FileLogger::FileLogger(const QString &Filename, bool ErrorsOnly, int Quiet)
372  : LoggerBase(Filename),
373  m_opened(false),
374  m_file(),
375  m_errorsOnly(ErrorsOnly),
376  m_quiet(Quiet)
377 {
378  if (m_fileName.isEmpty())
379  {
380  m_opened = true;
381  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Logging to the console"));
382  }
383  else
384  {
385  if (QFile::exists(m_fileName))
386  {
387  QString old = m_fileName + ".old";
388 
389  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Moving '%1' to '%2'").arg(m_fileName, old));
390 
391  QFile::remove(old);
392  QFile::rename(m_fileName, old);
393  }
394 
395  m_errorsOnly = false;
396  m_quiet = false;
397  m_file.setFileName(m_fileName);
398  if (!m_file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Truncate | QIODevice::Text | QIODevice::Unbuffered))
399  {
400  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Failed to open %1 for logging").arg(m_fileName));
401  return;
402  }
403 
404  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Logging to '%1'").arg(m_fileName));
405  m_opened = true;
406  }
407 }
408 
410 {
411  if (m_opened)
412  {
413  LogItem *item = LogItem::Create(__FILE__, __FUNCTION__,
414  __LINE__, LOG_INFO, kMessage, VB_GENERAL);
415 
416  strcpy(item->message, m_file.isOpen() ? "Closing file logger." : "Closing console logger.");
417  FileLogger::Logmsg(item);
418  LogItem::Delete(item);
419  //m_file.flush();
420  m_file.close();
421  }
422  m_opened = false;
423 }
424 
426 {
427  if (!m_opened || m_quiet || (m_errorsOnly && (Item->level > LOG_ERR)))
428  return false;
429 
430  Item->refCount.ref();
431  QByteArray line = GetLine(Item);
432  LogItem::Delete(Item);
433  return PrintLine(line);
434 }
435 
436 bool FileLogger::PrintLine(QByteArray &Line)
437 {
438  int error = m_file.isOpen() ? m_file.write(Line) : write(1, Line, Line.size() /*strlen(line)*/);
439  if (error == -1)
440  {
441  LOG(VB_GENERAL, LOG_ERR,
442  QStringLiteral("Closed Log output due to errors"));
443  m_opened = false;
444  return false;
445  }
446 
447  return true;
448 }
449 
459 WebLogger::WebLogger(const QString &Filename)
460  : QObject(),
461  TorcHTTPService(this, QStringLiteral("log"), QStringLiteral("log"), WebLogger::staticMetaObject, QStringLiteral("event")),
462  FileLogger(Filename, false, false),
463  log(),
464  tail(),
465  changed(false)
466 {
467  // we rate limit the LogChanged signal to once every 10 seconds
468  startTimer(10000);
469 }
470 
471 bool WebLogger::event(QEvent *event)
472 {
473  if (event && event->type() == QEvent::Timer)
474  {
475  m_httpServiceLock.lockForRead();
476  bool haschanged = changed;
477  m_httpServiceLock.unlock();
478 
479  if (haschanged)
480  {
481  m_httpServiceLock.lockForWrite();
482  changed = false;
483  m_httpServiceLock.unlock();
484  emit logChanged();
485  }
486  }
487  return QObject::event(event);
488 }
489 
490 void WebLogger::SubscriberDeleted(QObject *Subscriber)
491 {
493 }
494 
495 QByteArray WebLogger::GetLog(void)
496 {
497  QFile log(m_file.fileName());
498  if (!log.open(QIODevice::ReadOnly))
499  return QByteArray();
500  QByteArray result = log.readAll();
501  log.close();
502  return result;
503 }
504 
505 QByteArray WebLogger::GetTail(void)
506 {
507  QReadLocker locker(&m_httpServiceLock);
508  return tail;
509 }
510 
512 {
513  if (!m_opened || m_quiet || (m_errorsOnly && (Item->level > LOG_ERR)))
514  return false;
515 
516  bool filter = (Item->mask & VB_NETWORK) && (Item->level >= LOG_DEBUG);
517 
518  Item->refCount.ref();
519  QByteArray line = GetLine(Item);
520  LogItem::Delete(Item);
521 
522  if (filter)
523  return true;
524 
525  m_httpServiceLock.lockForWrite();
526  changed = true;
527  tail = line;
528  m_httpServiceLock.unlock();
529 
530  emit tailChanged();
531  return PrintLine(line);
532 }
533 
534 char *GetThreadName(LogItem *Item)
535 {
536  static const char *unknown = "QRunnable";
537 
538  if (!Item)
539  return (char*)unknown;
540 
541  char *threadName;
542 
543  if (!Item->threadName)
544  {
545  QMutexLocker locker(&gLogThreadLock);
546  if (gLogThreadHash.contains(Item->threadId))
547  threadName = gLogThreadHash[Item->threadId];
548  else
549  threadName = (char*)unknown;
550  }
551  else
552  {
553  threadName = Item->threadName;
554  }
555 
556  return threadName;
557 }
558 
559 int64_t GetThreadTid(LogItem *Item)
560 {
561  if (!Item)
562  return 0;
563 
564  pid_t tid = 0;
565 
566  QMutexLocker locker(&gLogThreadTidLock);
567  if (gLogThreadtidHash.contains(Item->threadId))
568  tid = gLogThreadtidHash[Item->threadId];
569 
570  return(tid);
571 }
572 
574  : TorcQThread(QStringLiteral("Logger")),
575  m_waitNotEmpty(),
576  m_waitEmpty(),
577  m_aborted(false)
578 {
579 }
580 
582 {
583  Stop();
584  quit();
585  wait();
586 }
587 
588 void PrintLogLine(uint64_t Mask, LogLevel Level, const char *File, int Line,
589  const char *Function, const char *Format, ... )
590 {
591  int type = kMessage;
592  type |= (Mask & VB_FLUSH) ? kFlush : 0;
593  type |= (Mask & VB_STDIO) ? kStandardIO : 0;
594  LogItem *item = LogItem::Create(File, Function, Line, Level, type, Mask);
595  if (!item)
596  return;
597 
598  va_list arguments;
599  va_start(arguments, Format);
600  vsnprintf(item->message, LOGLINE_MAX, Format, arguments);
601  va_end(arguments);
602 
603  QMutexLocker lock(&gLogQueueLock);
604 
605  gLogQueue.enqueue(item);
606 
607  if (gLogThread && gLogThreadFinished && !gLogThread->isRunning())
608  {
609  while (!gLogQueue.isEmpty())
610  {
611  item = gLogQueue.dequeue();
612  lock.unlock();
613  gLogThread->HandleItem(item);
614  LogItem::Delete(item);
615  lock.relock();
616  }
617  }
618  else if (gLogThread && !gLogThreadFinished && (type & kFlush))
619  {
620  gLogThread->Flush();
621  }
622 }
623 
625 {
626  QString mask = gVerboseString.trimmed();
627  mask.replace(QRegExp(" "), QStringLiteral(","));
628  mask.remove(QRegExp("^,"));
629  gLogPropagationArgs = QStringLiteral(" --verbose ") + mask;
630 
631  if (gLogPropagationOpts.propagate)
632  gLogPropagationArgs += QStringLiteral(" --logpath ") + gLogPropagationOpts.path;
633 
634  QString name = GetLogLevelName(gLogLevel);
635  gLogPropagationArgs += QStringLiteral(" --loglevel ") + name;
636 
637  for (int i = 0; i < gLogPropagationOpts.quiet; i++)
638  gLogPropagationArgs += QStringLiteral(" --quiet");
639 }
640 
642 {
643  return gLogPropagationOpts.quiet;
644 }
645 
646 void StartLogging(const QString &Logfile, int progress, int quiet,
647  const QString &Level, bool Propagate)
648 {
650 
651  LogLevel level = GetLogLevel(Level);
652 
653  {
654  QMutexLocker lock(&gLogQueueLock);
655  if (!gLogThread)
656  gLogThread = new LoggingThread();
657  if (!gLogThread)
658  return;
659  }
660 
661  if (gLogThread->isRunning())
662  return;
663 
664  gLogLevel = level;
665  LOG(VB_GENERAL, LOG_NOTICE, QStringLiteral("Setting level to LOG_%1")
666  .arg(GetLogLevelName(gLogLevel).toUpper()));
667 
668  gLogPropagationOpts.propagate = Propagate;
669  gLogPropagationOpts.quiet = quiet;
670 
671  if (Propagate)
672  {
673  QFileInfo finfo(Logfile);
674  QString path = finfo.path();
675  gLogPropagationOpts.path = path;
676  }
677 
679 
680  new FileLogger(QStringLiteral(""), progress, quiet);
681 
682  if (!Logfile.isEmpty())
683  new WebLogger(Logfile);
684 
685  gLogThread->start();
686 }
687 
688 void StopLogging(void)
689 {
690  if (gLogThread)
691  {
692  gLogThread->Stop();
693  gLogThread->quit();
694  gLogThread->wait();
695  }
696 
697  {
698  QMutexLocker lock(&gLogQueueLock);
699  delete gLogThread;
700  gLogThread = nullptr;
701  }
702 
703  {
704  QMutexLocker locker(&gLoggerListLock);
705  foreach (LoggerBase* logger, gLoggerList)
706  delete logger;
707  gLoggerList.clear();
708  }
709 
710  // this cleans up the last thread name - the logging thread itself - which is deregistered
711  // after the logging thread runloop has finished...
712  {
713  QMutexLocker locker(&gLogThreadLock);
714  QHash<uint64_t, char *>::iterator it = gLogThreadHash.begin();
715  for ( ; it != gLogThreadHash.end(); ++it)
716  free(it.value());
717  gLogThreadHash.clear();
718  }
719 
720  {
721  QMutexLocker locker(&gLogThreadTidLock);
722  gLogThreadtidHash.clear();
723  }
724 
725  gLogThreadFinished = false; // is this safe? required to restart logging
726 }
727 
729 {
730  if (gLogThreadFinished)
731  return;
732 
733  QMutexLocker lock(&gLogQueueLock);
734 
735  LogItem *item = LogItem::Create(__FILE__, __FUNCTION__, __LINE__,
736  (LogLevel)LOG_DEBUG, kRegistering, VB_GENERAL);
737  if (item)
738  {
739  item->threadName = strdup((char *)QThread::currentThread()->objectName().toLocal8Bit().constData());
740  gLogQueue.enqueue(item);
741  }
742 }
743 
745 {
746  if (gLogThreadFinished)
747  return;
748 
749  QMutexLocker lock(&gLogQueueLock);
750 
751  LogItem *item = LogItem::Create(__FILE__, __FUNCTION__, __LINE__,
752  (LogLevel)LOG_DEBUG, kDeregistering, VB_GENERAL);
753  if (item)
754  gLogQueue.enqueue(item);
755 }
756 
757 LogLevel GetLogLevel(const QString &level)
758 {
759  QMutexLocker locker(&gLoglevelMapLock);
760  if (!gVerboseInitialised)
761  {
762  locker.unlock();
763  InitVerbose();
764  locker.relock();
765  }
766 
767  for (LoglevelMap::iterator it = gLoglevelMap.begin();
768  it != gLoglevelMap.end(); ++it)
769  {
770  if ((*it).name == level.toLower() )
771  return (LogLevel)(*it).value;
772  }
773 
774  return LOG_UNKNOWN;
775 }
776 
777 QString GetLogLevelName(LogLevel level)
778 {
779  QMutexLocker locker(&gLoglevelMapLock);
780  if (!gVerboseInitialised)
781  {
782  locker.unlock();
783  InitVerbose();
784  locker.relock();
785  }
786  LoglevelMap::iterator it = gLoglevelMap.find((int)level);
787 
788  if ( it == gLoglevelMap.end() )
789  return QStringLiteral("unknown");
790 
791  return (*it).name;
792 }
793 
794 void AddVerbose(uint64_t mask, QString name, bool additive, QString helptext)
795 {
796  VerboseDef item;
797 
798  item.mask = mask;
799  name.detach();
800  // VB_GENERAL -> general
801  name.remove(0, 3);
802  name = name.toLower();
803  item.name = name;
804  item.additive = additive;
805  helptext.detach();
806  item.helpText = helptext;
807 
808  gVerboseMap.insert(name, item);
809 }
810 
811 void AddLogLevel(int value, QString name, char shortname)
812 {
813  LoglevelDef item;
814 
815  item.value = value;
816  name.detach();
817  // LOG_CRIT -> crit
818  name.remove(0, 4);
819  name = name.toLower();
820  item.name = name;
821  item.shortname = shortname;
822 
823  gLoglevelMap.insert(value, item);
824 }
825 
826 void InitVerbose(void)
827 {
828  QMutexLocker locker(&gVerboseMapLock);
829  QMutexLocker locker2(&gLoglevelMapLock);
830  gVerboseMap.clear();
831  gLoglevelMap.clear();
832 
833 #undef TORCLOGGINGDEFS_H_
834 #define _IMPLEMENT_VERBOSE
835 #include "torcloggingdefs.h"
836 
837  gVerboseInitialised = true;
838 }
839 
840 void VerboseHelp(void)
841 {
842  QString m_verbose = gVerboseString.trimmed();
843  m_verbose.replace(QRegExp(" "), QStringLiteral(","));
844  m_verbose.remove(QRegExp("^,"));
845 
846  cerr << "Verbose debug levels.\n"
847  "Accepts any combination (separated by comma) of:\n\n";
848 
849  for (VerboseMap::Iterator vit = gVerboseMap.begin();
850  vit != gVerboseMap.end(); ++vit )
851  {
852  QString name = QStringLiteral(" %1").arg(vit.value().name, -15, ' ');
853  if (vit.value().helpText.isEmpty())
854  continue;
855  cerr << name.toLocal8Bit().constData() << " - " <<
856  vit.value().helpText.toLocal8Bit().constData() << endl;
857  }
858 
859  cerr << endl <<
860  "The default for this program appears to be: '-v " <<
861  m_verbose.toLocal8Bit().constData() << "'\n\n"
862  "Most options are additive except for 'none' and 'all'.\n"
863  "These two are semi-exclusive and take precedence over any\n"
864  "other options. However, you may use something like\n"
865  "'-v none,jobqueue' to receive only JobQueue related messages\n"
866  "and override the default verbosity level.\n\n"
867  "Additive options may also be subtracted from 'all' by\n"
868  "prefixing them with 'no', so you may use '-v all,nodatabase'\n"
869  "to view all but database debug messages.\n\n"
870  "Some debug levels may not apply to this program.\n\n";
871 }
872 
873 int ParseVerboseArgument(const QString &arg)
874 {
875  QString option;
876 
877  if (!gVerboseInitialised)
878  InitVerbose();
879 
880  QMutexLocker locker(&gVerboseMapLock);
881 
884 
885  if (arg.startsWith('-'))
886  {
887  cerr << "Invalid or missing argument to -v/--verbose option\n";
889  }
890 
891  QStringList verboseOpts = arg.split(QRegExp("\\W+"));
892  for (QStringList::Iterator it = verboseOpts.begin();
893  it != verboseOpts.end(); ++it )
894  {
895  option = (*it).toLower();
896  bool reverseOption = false;
897 
898  if (option != QStringLiteral("none") && option.left(2) == QStringLiteral("no"))
899  {
900  reverseOption = true;
901  option = option.right(option.length() - 2);
902  }
903 
904  if (option == QStringLiteral("help"))
905  {
906  VerboseHelp();
908  }
909  else if (option == QStringLiteral("important"))
910  {
911  cerr << "The \"important\" log mask is no longer valid.\n";
912  }
913  else if (option == QStringLiteral("extra"))
914  {
915  cerr << "The \"extra\" log mask is no longer valid. Please try "
916  "--loglevel debug instead.\n";
917  }
918  else if (option == QStringLiteral("default"))
919  {
921  {
924  }
925  else
926  {
929  }
930  }
931  else
932  {
933  if (gVerboseMap.contains(option))
934  {
935  VerboseDef item = gVerboseMap.value(option);
936  if (reverseOption)
937  {
938  gVerboseMask &= ~(item.mask);
939  gVerboseString = gVerboseString.remove(' ' + item.name);
940  gVerboseString += " no" + item.name;
941  }
942  else
943  {
944  if (item.additive)
945  {
946  if (!(gVerboseMask & item.mask))
947  {
948  gVerboseMask |= item.mask;
949  gVerboseString += ' ' + item.name;
950  }
951  }
952  else
953  {
954  gVerboseMask = item.mask;
955  gVerboseString = item.name;
956  }
957  }
958  }
959  else
960  {
961  cerr << "Unknown argument for -v/--verbose: " <<
962  option.toLocal8Bit().constData() << endl;;
964  }
965  }
966  }
967 
969  {
970  gHaveUserDefaultValues = true;
973  }
974 
975  return TORC_EXIT_OK;
976 }
977 
978 QString LogErrorToString(int errnum)
979 {
980  return QStringLiteral("%1 (%2)").arg(strerror(errnum)).arg(errnum);
981 }
982 
983 // vim:ts=4:sw=4:ai:et:si:sts=4
984 
QMutex gLoggerListLock
LoggingType
QMutex gLoglevelMapLock
LoggingThread * gLogThread
uint32_t usec
bool Flush(int TimeoutMS=200000)
LoggerBase(const QString &FileName)
void VerboseHelp(void)
QAtomicInt refCount
#define TORC_EXIT_INVALID_CMDLINE
Definition: torcexitcodes.h:7
void Finish(void)
QString gVerboseString
QByteArray log
QMutex gLogThreadTidLock
LogLevel level
void InitVerbose(void)
void SubscriberDeleted(QObject *Subscriber)
void PrintLogLine(uint64_t Mask, LogLevel Level, const char *File, int Line, const char *Function, const char *Format,...)
void AddLogLevel(int value, QString name, char shortname)
QMutex gLogThreadLock
QHash< uint64_t, int64_t > gLogThreadtidHash
int ParseVerboseArgument(const QString &arg)
QString m_fileName
static void Delete(LogItem *Item)
bool Logmsg(LogItem *Item) override
QQueue< LogItem * > gLogQueue
bool PrintLine(QByteArray &Line) override
QList< LoggerBase * > gLoggerList
void tailChanged(void)
quint64 mask
WebLogger(const QString &Filename)
QByteArray GetLog(void)
#define LOGLINE_MAX
Definition: torcloggingimp.h:4
int64_t GetThreadTid(LogItem *item)
QByteArray GetLine(LogItem *Item)
const char * file
Serves log content to registered subscribers and HTTP clients.
LogLevel gLogLevel
QString gLogPropagationArgs
bool event(QEvent *event) override
void CalculateLogPropagation(void)
uint64_t gVerboseMask
QString GetLogLevelName(LogLevel level)
char * GetThreadName(LogItem *item)
QString gVerboseDefaultStr
bool Logmsg(LogItem *Item) override
QMap< int, LoglevelDef > LoglevelMap
const uint64_t gVerboseDefaultInt
VerboseMap gVerboseMap
QMutex gVerboseMapLock
bool GetQuietLogPropagation(void)
QByteArray GetTail(void)
QHash< uint64_t, char * > gLogThreadHash
void HandleSubscriberDeleted(QObject *Subscriber)
#define MAX_STRING_LENGTH
QReadWriteLock m_httpServiceLock
bool m_errorsOnly
LogPropagateOpts gLogPropagationOpts
void SetTime(void)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: torclogging.h:20
void logChanged(void)
#define TORC_EXIT_OK
Definition: torcexitcodes.h:4
char * threadName
void RegisterLoggingThread(void)
FileLogger(const QString &Filename, bool ErrorsOnly, int Quiet)
uint64_t gUserDefaultValueInt
QMap< QString, VerboseDef > VerboseMap
void StartLogging(const QString &Logfile, int progress, int quiet, const QString &Level, bool Propagate)
QRegExp gLogRexExp
LoglevelMap gLoglevelMap
LogLevel GetLogLevel(const QString &level)
QMutex gLogQueueLock
LogItem(const char *File, const char *Function, int Line, LogLevel Level, int Type, quint64 Mask)
static LogItem * Create(const char *File, const char *Function, int Line, LogLevel Level, int Type, quint64 Mask)
void HandleItem(LogItem *Item)
QString gUserDefaultValueStr
void Start(void)
uint64_t threadId
const char * function
bool gHaveUserDefaultValues
char message[LOGLINE_MAX+1]
QString LogErrorToString(int errnum)
bool gVerboseInitialised
struct tm tm
QByteArray tail
A Torc specific wrapper around QThread.
Definition: torcqthread.h:7
QString helpText
void AddVerbose(uint64_t mask, QString name, bool additive, QString helptext)
void DeregisterLoggingThread(void)
void SetThreadTid(void)
bool gLogThreadFinished
#define TIMESTAMP_MAX
void StopLogging(void)