Log.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. /*
  2. * Copyright (c) 2013-2016, The PurpleI2P Project
  3. *
  4. * This file is part of Purple i2pd project and licensed under BSD3
  5. *
  6. * See full license text in LICENSE file at top of project tree
  7. */
  8. #include "Log.h"
  9. //for std::transform
  10. #include <algorithm>
  11. namespace i2p {
  12. namespace log {
  13. static Log logger;
  14. /**
  15. * @brief Maps our loglevel to their symbolic name
  16. */
  17. static const char * g_LogLevelStr[eNumLogLevels] =
  18. {
  19. "none", // eLogNone
  20. "error", // eLogError
  21. "warn", // eLogWarn
  22. "info", // eLogInfo
  23. "debug" // eLogDebug
  24. };
  25. /**
  26. * @brief Colorize log output -- array of terminal control sequences
  27. * @note Using ISO 6429 (ANSI) color sequences
  28. */
  29. #ifdef _WIN32
  30. static const char *LogMsgColors[] = { "", "", "", "", "", "" };
  31. #else /* UNIX */
  32. static const char *LogMsgColors[] = {
  33. [eLogNone] = "\033[0m", /* reset */
  34. [eLogError] = "\033[1;31m", /* red */
  35. [eLogWarning] = "\033[1;33m", /* yellow */
  36. [eLogInfo] = "\033[1;36m", /* cyan */
  37. [eLogDebug] = "\033[1;34m", /* blue */
  38. [eNumLogLevels] = "\033[0m", /* reset */
  39. };
  40. #endif
  41. #ifndef _WIN32
  42. /**
  43. * @brief Maps our log levels to syslog one
  44. * @return syslog priority LOG_*, as defined in syslog.h
  45. */
  46. static inline int GetSyslogPrio (enum LogLevel l) {
  47. int priority = LOG_DEBUG;
  48. switch (l) {
  49. case eLogNone : priority = LOG_CRIT; break;
  50. case eLogError : priority = LOG_ERR; break;
  51. case eLogWarning : priority = LOG_WARNING; break;
  52. case eLogInfo : priority = LOG_INFO; break;
  53. case eLogDebug : priority = LOG_DEBUG; break;
  54. default : priority = LOG_DEBUG; break;
  55. }
  56. return priority;
  57. }
  58. #endif
  59. Log::Log():
  60. m_Destination(eLogStdout), m_MinLevel(eLogInfo),
  61. m_LogStream (nullptr), m_Logfile(""), m_HasColors(true), m_TimeFormat("%H:%M:%S"),
  62. m_IsRunning (false), m_Thread (nullptr)
  63. {
  64. }
  65. Log::~Log ()
  66. {
  67. delete m_Thread;
  68. }
  69. void Log::Start ()
  70. {
  71. if (!m_IsRunning)
  72. {
  73. m_IsRunning = true;
  74. m_Thread = new std::thread (std::bind (&Log::Run, this));
  75. }
  76. }
  77. void Log::Stop ()
  78. {
  79. switch (m_Destination)
  80. {
  81. #ifndef _WIN32
  82. case eLogSyslog :
  83. closelog();
  84. break;
  85. #endif
  86. case eLogFile:
  87. case eLogStream:
  88. if (m_LogStream) m_LogStream->flush();
  89. break;
  90. default:
  91. /* do nothing */
  92. break;
  93. }
  94. m_IsRunning = false;
  95. m_Queue.WakeUp ();
  96. if (m_Thread)
  97. {
  98. m_Thread->join ();
  99. delete m_Thread;
  100. m_Thread = nullptr;
  101. }
  102. }
  103. std::string str_tolower(std::string s) {
  104. std::transform(s.begin(), s.end(), s.begin(),
  105. // static_cast<int(*)(int)>(std::tolower) // wrong
  106. // [](int c){ return std::tolower(c); } // wrong
  107. // [](char c){ return std::tolower(c); } // wrong
  108. [](unsigned char c){ return std::tolower(c); } // correct
  109. );
  110. return s;
  111. }
  112. void Log::SetLogLevel (const std::string& level_) {
  113. std::string level=str_tolower(level_);
  114. if (level == "none") { m_MinLevel = eLogNone; }
  115. else if (level == "error") { m_MinLevel = eLogError; }
  116. else if (level == "warn") { m_MinLevel = eLogWarning; }
  117. else if (level == "info") { m_MinLevel = eLogInfo; }
  118. else if (level == "debug") { m_MinLevel = eLogDebug; }
  119. else {
  120. LogPrint(eLogError, "Log: unknown loglevel: ", level);
  121. return;
  122. }
  123. LogPrint(eLogInfo, "Log: min messages level set to ", level);
  124. }
  125. const char * Log::TimeAsString(std::time_t t) {
  126. if (t != m_LastTimestamp) {
  127. strftime(m_LastDateTime, sizeof(m_LastDateTime), m_TimeFormat.c_str(), localtime(&t));
  128. m_LastTimestamp = t;
  129. }
  130. return m_LastDateTime;
  131. }
  132. /**
  133. * @note This function better to be run in separate thread due to disk i/o.
  134. * Unfortunately, with current startup process with late fork() this
  135. * will give us nothing but pain. Maybe later. See in NetDb as example.
  136. */
  137. void Log::Process(std::shared_ptr<LogMsg> msg)
  138. {
  139. if (!msg) return;
  140. std::hash<std::thread::id> hasher;
  141. unsigned short short_tid;
  142. short_tid = (short) (hasher(msg->tid) % 1000);
  143. switch (m_Destination) {
  144. #ifndef _WIN32
  145. case eLogSyslog:
  146. syslog(GetSyslogPrio(msg->level), "[%03u] %s", short_tid, msg->text.c_str());
  147. break;
  148. #endif
  149. case eLogFile:
  150. case eLogStream:
  151. if (m_LogStream)
  152. *m_LogStream << TimeAsString(msg->timestamp)
  153. << "@" << short_tid
  154. << "/" << g_LogLevelStr[msg->level]
  155. << " - " << msg->text << std::endl;
  156. break;
  157. case eLogStdout:
  158. default:
  159. std::cout << TimeAsString(msg->timestamp)
  160. << "@" << short_tid
  161. << "/" << LogMsgColors[msg->level] << g_LogLevelStr[msg->level] << LogMsgColors[eNumLogLevels]
  162. << " - " << msg->text << std::endl;
  163. break;
  164. } // switch
  165. }
  166. void Log::Run ()
  167. {
  168. Reopen ();
  169. while (m_IsRunning)
  170. {
  171. std::shared_ptr<LogMsg> msg;
  172. while ((msg = m_Queue.Get ()))
  173. Process (msg);
  174. if (m_LogStream) m_LogStream->flush();
  175. if (m_IsRunning)
  176. m_Queue.Wait ();
  177. }
  178. }
  179. void Log::Append(std::shared_ptr<i2p::log::LogMsg> & msg)
  180. {
  181. m_Queue.Put(msg);
  182. }
  183. void Log::SendTo (const std::string& path)
  184. {
  185. if (m_LogStream) m_LogStream = nullptr; // close previous
  186. if (m_MinLevel == eLogNone) return;
  187. auto flags = std::ofstream::out | std::ofstream::app;
  188. auto os = std::make_shared<std::ofstream> (path, flags);
  189. if (os->is_open ())
  190. {
  191. m_HasColors = false;
  192. m_Logfile = path;
  193. m_Destination = eLogFile;
  194. m_LogStream = os;
  195. return;
  196. }
  197. LogPrint(eLogError, "Log: can't open file ", path);
  198. }
  199. void Log::SendTo (std::shared_ptr<std::ostream> os) {
  200. m_HasColors = false;
  201. m_Destination = eLogStream;
  202. m_LogStream = os;
  203. }
  204. #ifndef _WIN32
  205. void Log::SendTo(const char *name, int facility) {
  206. if (m_MinLevel == eLogNone) return;
  207. m_HasColors = false;
  208. m_Destination = eLogSyslog;
  209. m_LogStream = nullptr;
  210. openlog(name, LOG_CONS | LOG_PID, facility);
  211. }
  212. #endif
  213. void Log::Reopen() {
  214. if (m_Destination == eLogFile)
  215. SendTo(m_Logfile);
  216. }
  217. Log & Logger() {
  218. return logger;
  219. }
  220. } // log
  221. } // i2p