logging.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. /*
  2. * SRT - Secure, Reliable, Transport
  3. * Copyright (c) 2018 Haivision Systems Inc.
  4. *
  5. * This Source Code Form is subject to the terms of the Mozilla Public
  6. * License, v. 2.0. If a copy of the MPL was not distributed with this
  7. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  8. *
  9. */
  10. /*****************************************************************************
  11. written by
  12. Haivision Systems Inc.
  13. *****************************************************************************/
  14. #ifndef INC_SRT_LOGGING_H
  15. #define INC_SRT_LOGGING_H
  16. #include <iostream>
  17. #include <iomanip>
  18. #include <set>
  19. #include <sstream>
  20. #include <cstdarg>
  21. #ifdef _WIN32
  22. #include "win/wintime.h"
  23. #include <sys/timeb.h>
  24. #else
  25. #include <sys/time.h>
  26. #endif
  27. #include "srt.h"
  28. #include "utilities.h"
  29. #include "threadname.h"
  30. #include "logging_api.h"
  31. #include "srt_compat.h"
  32. #include "sync.h"
  33. #ifdef __GNUC__
  34. #define PRINTF_LIKE __attribute__((format(printf,2,3)))
  35. #else
  36. #define PRINTF_LIKE
  37. #endif
  38. #if ENABLE_LOGGING
  39. // GENERAL NOTE: All logger functions ADD THEIR OWN \n (EOL). Don't add any your own EOL character.
  40. // The logging system may not add the EOL character, if appropriate flag was set in log settings.
  41. // Anyway, treat the whole contents of eventually formatted message as exactly one line.
  42. // LOGC uses an iostream-like syntax, using the special 'log' symbol.
  43. // This symbol isn't visible outside the log macro parameters.
  44. // Usage: LOGC(gglog.Debug, log << param1 << param2 << param3);
  45. #define LOGC(logdes, args) if (logdes.CheckEnabled()) \
  46. { \
  47. srt_logging::LogDispatcher::Proxy log(logdes); \
  48. log.setloc(__FILE__, __LINE__, __FUNCTION__); \
  49. const srt_logging::LogDispatcher::Proxy& log_prox SRT_ATR_UNUSED = args; \
  50. }
  51. // LOGF uses printf-like style formatting.
  52. // Usage: LOGF(gglog.Debug, "%s: %d", param1.c_str(), int(param2));
  53. // NOTE: LOGF is deprecated and should not be used
  54. #define LOGF(logdes, ...) if (logdes.CheckEnabled()) logdes().setloc(__FILE__, __LINE__, __FUNCTION__).form(__VA_ARGS__)
  55. // LOGP is C++11 only OR with only one string argument.
  56. // Usage: LOGP(gglog.Debug, param1, param2, param3);
  57. #define LOGP(logdes, ...) if (logdes.CheckEnabled()) logdes.printloc(__FILE__, __LINE__, __FUNCTION__,##__VA_ARGS__)
  58. #define IF_LOGGING(instr) instr
  59. #if ENABLE_HEAVY_LOGGING
  60. #define HLOGC LOGC
  61. #define HLOGP LOGP
  62. #define HLOGF LOGF
  63. #define IF_HEAVY_LOGGING(instr) instr
  64. #else
  65. #define HLOGC(...)
  66. #define HLOGF(...)
  67. #define HLOGP(...)
  68. #define IF_HEAVY_LOGGING(instr) (void)0
  69. #endif
  70. #else
  71. #define LOGC(...)
  72. #define LOGF(...)
  73. #define LOGP(...)
  74. #define HLOGC(...)
  75. #define HLOGF(...)
  76. #define HLOGP(...)
  77. #define IF_HEAVY_LOGGING(instr) (void)0
  78. #define IF_LOGGING(instr) (void)0
  79. #endif
  80. namespace srt_logging
  81. {
  82. struct LogConfig
  83. {
  84. typedef std::bitset<SRT_LOGFA_LASTNONE+1> fa_bitset_t;
  85. fa_bitset_t enabled_fa; // NOTE: assumed atomic reading
  86. LogLevel::type max_level; // NOTE: assumed atomic reading
  87. std::ostream* log_stream;
  88. SRT_LOG_HANDLER_FN* loghandler_fn;
  89. void* loghandler_opaque;
  90. srt::sync::Mutex mutex;
  91. int flags;
  92. LogConfig(const fa_bitset_t& efa,
  93. LogLevel::type l = LogLevel::warning,
  94. std::ostream* ls = &std::cerr)
  95. : enabled_fa(efa)
  96. , max_level(l)
  97. , log_stream(ls)
  98. , loghandler_fn()
  99. , loghandler_opaque()
  100. , flags()
  101. {
  102. }
  103. ~LogConfig()
  104. {
  105. }
  106. SRT_ATTR_ACQUIRE(mutex)
  107. void lock() { mutex.lock(); }
  108. SRT_ATTR_RELEASE(mutex)
  109. void unlock() { mutex.unlock(); }
  110. };
  111. // The LogDispatcher class represents the object that is responsible for
  112. // a decision whether to log something or not, and if so, print the log.
  113. struct SRT_API LogDispatcher
  114. {
  115. private:
  116. int fa;
  117. LogLevel::type level;
  118. static const size_t MAX_PREFIX_SIZE = 32;
  119. char prefix[MAX_PREFIX_SIZE+1];
  120. LogConfig* src_config;
  121. bool isset(int flg) { return (src_config->flags & flg) != 0; }
  122. public:
  123. LogDispatcher(int functional_area, LogLevel::type log_level, const char* your_pfx,
  124. const char* logger_pfx /*[[nullable]]*/, LogConfig& config):
  125. fa(functional_area),
  126. level(log_level),
  127. src_config(&config)
  128. {
  129. // XXX stpcpy desired, but not enough portable
  130. // Composing the exact prefix is not critical, so simply
  131. // cut the prefix, if the length is exceeded
  132. // See Logger::Logger; we know this has normally 2 characters,
  133. // except !!FATAL!!, which has 9. Still less than 32.
  134. // If the size of the FA name together with severity exceeds the size,
  135. // just skip the former.
  136. if (logger_pfx && strlen(prefix) + strlen(logger_pfx) + 1 < MAX_PREFIX_SIZE)
  137. {
  138. #if defined(_MSC_VER) && _MSC_VER < 1900
  139. _snprintf(prefix, MAX_PREFIX_SIZE, "%s:%s", your_pfx, logger_pfx);
  140. #else
  141. snprintf(prefix, MAX_PREFIX_SIZE + 1, "%s:%s", your_pfx, logger_pfx);
  142. #endif
  143. }
  144. else
  145. {
  146. #ifdef _MSC_VER
  147. strncpy_s(prefix, MAX_PREFIX_SIZE + 1, your_pfx, _TRUNCATE);
  148. #else
  149. strncpy(prefix, your_pfx, MAX_PREFIX_SIZE);
  150. prefix[MAX_PREFIX_SIZE] = '\0';
  151. #endif
  152. }
  153. }
  154. ~LogDispatcher()
  155. {
  156. }
  157. bool CheckEnabled();
  158. void CreateLogLinePrefix(std::ostringstream&);
  159. void SendLogLine(const char* file, int line, const std::string& area, const std::string& sl);
  160. // log.Debug("This is the ", nth, " time"); <--- C++11 only.
  161. // log.Debug() << "This is the " << nth << " time"; <--- C++03 available.
  162. #if HAVE_CXX11
  163. template <class... Args>
  164. void PrintLogLine(const char* file, int line, const std::string& area, Args&&... args);
  165. template<class Arg1, class... Args>
  166. void operator()(Arg1&& arg1, Args&&... args)
  167. {
  168. PrintLogLine("UNKNOWN.c++", 0, "UNKNOWN", arg1, args...);
  169. }
  170. template<class Arg1, class... Args>
  171. void printloc(const char* file, int line, const std::string& area, Arg1&& arg1, Args&&... args)
  172. {
  173. PrintLogLine(file, line, area, arg1, args...);
  174. }
  175. #else
  176. template <class Arg>
  177. void PrintLogLine(const char* file, int line, const std::string& area, const Arg& arg);
  178. // For C++03 (older) standard provide only with one argument.
  179. template <class Arg>
  180. void operator()(const Arg& arg)
  181. {
  182. PrintLogLine("UNKNOWN.c++", 0, "UNKNOWN", arg);
  183. }
  184. void printloc(const char* file, int line, const std::string& area, const std::string& arg1)
  185. {
  186. PrintLogLine(file, line, area, arg1);
  187. }
  188. #endif
  189. #if ENABLE_LOGGING
  190. struct Proxy;
  191. friend struct Proxy;
  192. Proxy operator()();
  193. #else
  194. // Dummy proxy that does nothing
  195. struct DummyProxy
  196. {
  197. DummyProxy(LogDispatcher&)
  198. {
  199. }
  200. template <class T>
  201. DummyProxy& operator<<(const T& ) // predicted for temporary objects
  202. {
  203. return *this;
  204. }
  205. // DEPRECATED: DO NOT use LOGF/HLOGF macros anymore.
  206. // Use iostream-style formatting with LOGC or a direct argument with LOGP.
  207. SRT_ATR_DEPRECATED_PX DummyProxy& form(const char*, ...) SRT_ATR_DEPRECATED
  208. {
  209. return *this;
  210. }
  211. DummyProxy& vform(const char*, va_list)
  212. {
  213. return *this;
  214. }
  215. DummyProxy& setloc(const char* , int , std::string)
  216. {
  217. return *this;
  218. }
  219. };
  220. DummyProxy operator()()
  221. {
  222. return DummyProxy(*this);
  223. }
  224. #endif
  225. };
  226. #if ENABLE_LOGGING
  227. struct LogDispatcher::Proxy
  228. {
  229. LogDispatcher& that;
  230. std::ostringstream os;
  231. // Cache the 'enabled' state in the beginning. If the logging
  232. // becomes enabled or disabled in the middle of the log, we don't
  233. // want it to be partially printed anyway.
  234. bool that_enabled;
  235. int flags;
  236. // CACHE!!!
  237. const char* i_file;
  238. int i_line;
  239. std::string area;
  240. Proxy& setloc(const char* f, int l, std::string a)
  241. {
  242. i_file = f;
  243. i_line = l;
  244. area = a;
  245. return *this;
  246. }
  247. // Left for future. Not sure if it's more convenient
  248. // to use this to translate __PRETTY_FUNCTION__ to
  249. // something short, or just let's leave __FUNCTION__
  250. // or better __func__.
  251. std::string ExtractName(std::string pretty_function);
  252. Proxy(LogDispatcher& guy);
  253. // Copy constructor is needed due to noncopyable ostringstream.
  254. // This is used only in creation of the default object, so just
  255. // use the default values, just copy the location cache.
  256. Proxy(const Proxy& p): that(p.that), area(p.area)
  257. {
  258. i_file = p.i_file;
  259. i_line = p.i_line;
  260. that_enabled = false;
  261. flags = p.flags;
  262. }
  263. template <class T>
  264. Proxy& operator<<(const T& arg) // predicted for temporary objects
  265. {
  266. if ( that_enabled )
  267. {
  268. os << arg;
  269. }
  270. return *this;
  271. }
  272. ~Proxy()
  273. {
  274. if ( that_enabled )
  275. {
  276. if ( (flags & SRT_LOGF_DISABLE_EOL) == 0 )
  277. os << std::endl;
  278. that.SendLogLine(i_file, i_line, area, os.str());
  279. }
  280. // Needed in destructor?
  281. //os.clear();
  282. //os.str("");
  283. }
  284. Proxy& form(const char* fmts, ...) PRINTF_LIKE
  285. {
  286. if ( !that_enabled )
  287. return *this;
  288. if ( !fmts || fmts[0] == '\0' )
  289. return *this;
  290. va_list ap;
  291. va_start(ap, fmts);
  292. vform(fmts, ap);
  293. va_end(ap);
  294. return *this;
  295. }
  296. Proxy& vform(const char* fmts, va_list ap)
  297. {
  298. char buf[512];
  299. #if defined(_MSC_VER) && _MSC_VER < 1900
  300. _vsnprintf(buf, sizeof(buf) - 1, fmts, ap);
  301. #else
  302. vsnprintf(buf, sizeof(buf), fmts, ap);
  303. #endif
  304. size_t len = strlen(buf);
  305. if ( buf[len-1] == '\n' )
  306. {
  307. // Remove EOL character, should it happen to be at the end.
  308. // The EOL will be added at the end anyway.
  309. buf[len-1] = '\0';
  310. }
  311. os << buf;
  312. return *this;
  313. }
  314. };
  315. #endif
  316. class Logger
  317. {
  318. int m_fa;
  319. LogConfig& m_config;
  320. public:
  321. LogDispatcher Debug;
  322. LogDispatcher Note;
  323. LogDispatcher Warn;
  324. LogDispatcher Error;
  325. LogDispatcher Fatal;
  326. Logger(int functional_area, LogConfig& config, const char* logger_pfx = NULL):
  327. m_fa(functional_area),
  328. m_config(config),
  329. Debug ( m_fa, LogLevel::debug, " D", logger_pfx, m_config ),
  330. Note ( m_fa, LogLevel::note, ".N", logger_pfx, m_config ),
  331. Warn ( m_fa, LogLevel::warning, "!W", logger_pfx, m_config ),
  332. Error ( m_fa, LogLevel::error, "*E", logger_pfx, m_config ),
  333. Fatal ( m_fa, LogLevel::fatal, "!!FATAL!!", logger_pfx, m_config )
  334. {
  335. }
  336. };
  337. inline bool LogDispatcher::CheckEnabled()
  338. {
  339. // Don't use enabler caching. Check enabled state every time.
  340. // These assume to be atomically read, so the lock is not needed
  341. // (note that writing to this field is still mutex-protected).
  342. // It's also no problem if the level was changed at the moment
  343. // when the enabler check is tested here. Worst case, the log
  344. // will be printed just a moment after it was turned off.
  345. const LogConfig* config = src_config; // to enforce using const operator[]
  346. int configured_enabled_fa = config->enabled_fa[fa];
  347. int configured_maxlevel = config->max_level;
  348. return configured_enabled_fa && level <= configured_maxlevel;
  349. }
  350. #if HAVE_CXX11
  351. //extern std::mutex Debug_mutex;
  352. inline void PrintArgs(std::ostream&) {}
  353. template <class Arg1, class... Args>
  354. inline void PrintArgs(std::ostream& serr, Arg1&& arg1, Args&&... args)
  355. {
  356. serr << arg1;
  357. PrintArgs(serr, args...);
  358. }
  359. template <class... Args>
  360. inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int line SRT_ATR_UNUSED, const std::string& area SRT_ATR_UNUSED, Args&&... args SRT_ATR_UNUSED)
  361. {
  362. #ifdef ENABLE_LOGGING
  363. std::ostringstream serr;
  364. CreateLogLinePrefix(serr);
  365. PrintArgs(serr, args...);
  366. if ( !isset(SRT_LOGF_DISABLE_EOL) )
  367. serr << std::endl;
  368. // Not sure, but it wasn't ever used.
  369. SendLogLine(file, line, area, serr.str());
  370. #endif
  371. }
  372. #else
  373. template <class Arg>
  374. inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int line SRT_ATR_UNUSED, const std::string& area SRT_ATR_UNUSED, const Arg& arg SRT_ATR_UNUSED)
  375. {
  376. #ifdef ENABLE_LOGGING
  377. std::ostringstream serr;
  378. CreateLogLinePrefix(serr);
  379. serr << arg;
  380. if ( !isset(SRT_LOGF_DISABLE_EOL) )
  381. serr << std::endl;
  382. // Not sure, but it wasn't ever used.
  383. SendLogLine(file, line, area, serr.str());
  384. #endif
  385. }
  386. #endif
  387. // SendLogLine can be compiled normally. It's intermediately used by:
  388. // - Proxy object, which is replaced by DummyProxy when !ENABLE_LOGGING
  389. // - PrintLogLine, which has empty body when !ENABLE_LOGGING
  390. inline void LogDispatcher::SendLogLine(const char* file, int line, const std::string& area, const std::string& msg)
  391. {
  392. src_config->lock();
  393. if ( src_config->loghandler_fn )
  394. {
  395. (*src_config->loghandler_fn)(src_config->loghandler_opaque, int(level), file, line, area.c_str(), msg.c_str());
  396. }
  397. else if ( src_config->log_stream )
  398. {
  399. (*src_config->log_stream) << msg;
  400. (*src_config->log_stream).flush();
  401. }
  402. src_config->unlock();
  403. }
  404. }
  405. #endif