QsLog.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. // Copyright (c) 2013, Razvan Petru
  2. // All rights reserved.
  3. // Redistribution and use in source and binary forms, with or without modification,
  4. // are permitted provided that the following conditions are met:
  5. // * Redistributions of source code must retain the above copyright notice, this
  6. // list of conditions and the following disclaimer.
  7. // * Redistributions in binary form must reproduce the above copyright notice, this
  8. // list of conditions and the following disclaimer in the documentation and/or other
  9. // materials provided with the distribution.
  10. // * The name of the contributors may not be used to endorse or promote products
  11. // derived from this software without specific prior written permission.
  12. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  13. // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  14. // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  15. // IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
  16. // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  17. // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  18. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  19. // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
  20. // OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  21. // OF THE POSSIBILITY OF SUCH DAMAGE.
  22. #include "QsLog.h"
  23. #include "QsLogDest.h"
  24. #ifdef QS_LOG_SEPARATE_THREAD
  25. #include <QThreadPool>
  26. #include <QRunnable>
  27. #endif
  28. #include <QMutex>
  29. #include <QVector>
  30. #include <QDateTime>
  31. #include <QtGlobal>
  32. #include <cassert>
  33. #include <cstdlib>
  34. #include <stdexcept>
  35. namespace QsLogging
  36. {
  37. typedef QVector<DestinationPtr> DestinationList;
  38. static const char TraceString[] = "TRACE";
  39. static const char DebugString[] = "DEBUG";
  40. static const char InfoString[] = "INFO ";
  41. static const char WarnString[] = "WARN ";
  42. static const char ErrorString[] = "ERROR";
  43. static const char FatalString[] = "FATAL";
  44. // not using Qt::ISODate because we need the milliseconds too
  45. static const QString fmtDateTime("yyyy-MM-dd hh:mm:ss");
  46. static Logger* sInstance = 0;
  47. static const char* LevelToText(Level theLevel)
  48. {
  49. switch (theLevel) {
  50. case TraceLevel:
  51. return TraceString;
  52. case DebugLevel:
  53. return DebugString;
  54. case InfoLevel:
  55. return InfoString;
  56. case WarnLevel:
  57. return WarnString;
  58. case ErrorLevel:
  59. return ErrorString;
  60. case FatalLevel:
  61. return FatalString;
  62. case OffLevel:
  63. return "";
  64. default: {
  65. assert(!"bad log level");
  66. return InfoString;
  67. }
  68. }
  69. }
  70. #ifdef QS_LOG_SEPARATE_THREAD
  71. class LogWriterRunnable : public QRunnable
  72. {
  73. public:
  74. LogWriterRunnable(QString message, Level level);
  75. virtual void run();
  76. private:
  77. QString mMessage;
  78. Level mLevel;
  79. };
  80. #endif
  81. class LoggerImpl
  82. {
  83. public:
  84. LoggerImpl();
  85. #ifdef QS_LOG_SEPARATE_THREAD
  86. QThreadPool threadPool;
  87. #endif
  88. QMutex logMutex;
  89. Level level;
  90. DestinationList destList;
  91. ProcessingCallback process;
  92. };
  93. #ifdef QS_LOG_SEPARATE_THREAD
  94. LogWriterRunnable::LogWriterRunnable(QString message, Level level)
  95. : QRunnable()
  96. , mMessage(message)
  97. , mLevel(level)
  98. {
  99. }
  100. void LogWriterRunnable::run()
  101. {
  102. Logger::instance().write(mMessage, mLevel);
  103. }
  104. #endif
  105. LoggerImpl::LoggerImpl()
  106. : level(InfoLevel)
  107. , process(0)
  108. {
  109. // assume at least file + console
  110. destList.reserve(2);
  111. #ifdef QS_LOG_SEPARATE_THREAD
  112. threadPool.setMaxThreadCount(1);
  113. threadPool.setExpiryTimeout(-1);
  114. #endif
  115. }
  116. Logger::Logger()
  117. : d(new LoggerImpl)
  118. {
  119. }
  120. Logger& Logger::instance()
  121. {
  122. if (!sInstance)
  123. sInstance = new Logger;
  124. return *sInstance;
  125. }
  126. void Logger::destroyInstance()
  127. {
  128. delete sInstance;
  129. sInstance = 0;
  130. }
  131. // tries to extract the level from a string log message. If available, conversionSucceeded will
  132. // contain the conversion result.
  133. Level Logger::levelFromLogMessage(const QString& logMessage, bool* conversionSucceeded)
  134. {
  135. if (conversionSucceeded)
  136. *conversionSucceeded = true;
  137. if (logMessage.startsWith(QLatin1String(TraceString)))
  138. return TraceLevel;
  139. if (logMessage.startsWith(QLatin1String(DebugString)))
  140. return DebugLevel;
  141. if (logMessage.startsWith(QLatin1String(InfoString)))
  142. return InfoLevel;
  143. if (logMessage.startsWith(QLatin1String(WarnString)))
  144. return WarnLevel;
  145. if (logMessage.startsWith(QLatin1String(ErrorString)))
  146. return ErrorLevel;
  147. if (logMessage.startsWith(QLatin1String(FatalString)))
  148. return FatalLevel;
  149. if (conversionSucceeded)
  150. *conversionSucceeded = false;
  151. return OffLevel;
  152. }
  153. Logger::~Logger()
  154. {
  155. #ifdef QS_LOG_SEPARATE_THREAD
  156. d->threadPool.waitForDone();
  157. #endif
  158. delete d;
  159. d = 0;
  160. }
  161. void Logger::addDestination(DestinationPtr destination)
  162. {
  163. QMutexLocker lock(&d->logMutex);
  164. assert(destination.data());
  165. d->destList.push_back(destination);
  166. }
  167. void Logger::setLoggingLevel(Level newLevel)
  168. {
  169. d->level = newLevel;
  170. }
  171. Level Logger::loggingLevel() const
  172. {
  173. return d->level;
  174. }
  175. void Logger::setProcessingCallback(ProcessingCallback cb)
  176. {
  177. d->process = cb;
  178. }
  179. //! creates the complete log message and passes it to the logger
  180. void Logger::Helper::writeToLog()
  181. {
  182. const char* const levelName = LevelToText(level);
  183. const QString completeMessage(QString("%1 [ %2 ] %3")
  184. .arg(QDateTime::currentDateTime().toString(fmtDateTime))
  185. .arg(levelName)
  186. .arg(buffer)
  187. );
  188. Logger::instance().enqueueWrite(completeMessage, level);
  189. }
  190. Logger::Helper::~Helper()
  191. {
  192. try {
  193. writeToLog();
  194. }
  195. catch(std::exception&) {
  196. // you shouldn't throw exceptions from a sink
  197. assert(!"exception in logger helper destructor");
  198. throw;
  199. }
  200. }
  201. //! directs the message to the task queue or writes it directly
  202. void Logger::enqueueWrite(QString message, Level level)
  203. {
  204. if (d->process)
  205. d->process(message);
  206. #ifdef QS_LOG_SEPARATE_THREAD
  207. LogWriterRunnable *r = new LogWriterRunnable(message, level);
  208. d->threadPool.start(r);
  209. #else
  210. write(message, level);
  211. #endif
  212. }
  213. //! Sends the message to all the destinations. The level for this message is passed in case
  214. //! it's useful for processing in the destination.
  215. void Logger::write(const QString& message, Level level)
  216. {
  217. QMutexLocker lock(&d->logMutex);
  218. for (DestinationList::iterator it = d->destList.begin(),
  219. endIt = d->destList.end();it != endIt;++it) {
  220. (*it)->write(message, level);
  221. }
  222. }
  223. } // end namespace