123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505 |
- /*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
- /*****************************************************************************
- written by
- Haivision Systems Inc.
- *****************************************************************************/
- #ifndef INC_SRT_LOGGING_H
- #define INC_SRT_LOGGING_H
- #include <iostream>
- #include <iomanip>
- #include <set>
- #include <sstream>
- #include <cstdarg>
- #ifdef _WIN32
- #include "win/wintime.h"
- #include <sys/timeb.h>
- #else
- #include <sys/time.h>
- #endif
- #include "srt.h"
- #include "utilities.h"
- #include "threadname.h"
- #include "logging_api.h"
- #include "srt_compat.h"
- #include "sync.h"
- #ifdef __GNUC__
- #define PRINTF_LIKE __attribute__((format(printf,2,3)))
- #else
- #define PRINTF_LIKE
- #endif
- #if ENABLE_LOGGING
- // GENERAL NOTE: All logger functions ADD THEIR OWN \n (EOL). Don't add any your own EOL character.
- // The logging system may not add the EOL character, if appropriate flag was set in log settings.
- // Anyway, treat the whole contents of eventually formatted message as exactly one line.
- // LOGC uses an iostream-like syntax, using the special 'log' symbol.
- // This symbol isn't visible outside the log macro parameters.
- // Usage: LOGC(gglog.Debug, log << param1 << param2 << param3);
- #define LOGC(logdes, args) if (logdes.CheckEnabled()) \
- { \
- srt_logging::LogDispatcher::Proxy log(logdes); \
- log.setloc(__FILE__, __LINE__, __FUNCTION__); \
- const srt_logging::LogDispatcher::Proxy& log_prox SRT_ATR_UNUSED = args; \
- }
- // LOGF uses printf-like style formatting.
- // Usage: LOGF(gglog.Debug, "%s: %d", param1.c_str(), int(param2));
- // NOTE: LOGF is deprecated and should not be used
- #define LOGF(logdes, ...) if (logdes.CheckEnabled()) logdes().setloc(__FILE__, __LINE__, __FUNCTION__).form(__VA_ARGS__)
- // LOGP is C++11 only OR with only one string argument.
- // Usage: LOGP(gglog.Debug, param1, param2, param3);
- #define LOGP(logdes, ...) if (logdes.CheckEnabled()) logdes.printloc(__FILE__, __LINE__, __FUNCTION__,##__VA_ARGS__)
- #define IF_LOGGING(instr) instr
- #if ENABLE_HEAVY_LOGGING
- #define HLOGC LOGC
- #define HLOGP LOGP
- #define HLOGF LOGF
- #define IF_HEAVY_LOGGING(instr) instr
- #else
- #define HLOGC(...)
- #define HLOGF(...)
- #define HLOGP(...)
- #define IF_HEAVY_LOGGING(instr) (void)0
- #endif
- #else
- #define LOGC(...)
- #define LOGF(...)
- #define LOGP(...)
- #define HLOGC(...)
- #define HLOGF(...)
- #define HLOGP(...)
- #define IF_HEAVY_LOGGING(instr) (void)0
- #define IF_LOGGING(instr) (void)0
- #endif
- namespace srt_logging
- {
- struct LogConfig
- {
- typedef std::bitset<SRT_LOGFA_LASTNONE+1> fa_bitset_t;
- fa_bitset_t enabled_fa; // NOTE: assumed atomic reading
- LogLevel::type max_level; // NOTE: assumed atomic reading
- std::ostream* log_stream;
- SRT_LOG_HANDLER_FN* loghandler_fn;
- void* loghandler_opaque;
- srt::sync::Mutex mutex;
- int flags;
- LogConfig(const fa_bitset_t& efa,
- LogLevel::type l = LogLevel::warning,
- std::ostream* ls = &std::cerr)
- : enabled_fa(efa)
- , max_level(l)
- , log_stream(ls)
- , loghandler_fn()
- , loghandler_opaque()
- , flags()
- {
- }
- ~LogConfig()
- {
- }
- SRT_ATTR_ACQUIRE(mutex)
- void lock() { mutex.lock(); }
- SRT_ATTR_RELEASE(mutex)
- void unlock() { mutex.unlock(); }
- };
- // The LogDispatcher class represents the object that is responsible for
- // a decision whether to log something or not, and if so, print the log.
- struct SRT_API LogDispatcher
- {
- private:
- int fa;
- LogLevel::type level;
- static const size_t MAX_PREFIX_SIZE = 32;
- char prefix[MAX_PREFIX_SIZE+1];
- LogConfig* src_config;
- bool isset(int flg) { return (src_config->flags & flg) != 0; }
- public:
- LogDispatcher(int functional_area, LogLevel::type log_level, const char* your_pfx,
- const char* logger_pfx /*[[nullable]]*/, LogConfig& config):
- fa(functional_area),
- level(log_level),
- src_config(&config)
- {
- // XXX stpcpy desired, but not enough portable
- // Composing the exact prefix is not critical, so simply
- // cut the prefix, if the length is exceeded
- // See Logger::Logger; we know this has normally 2 characters,
- // except !!FATAL!!, which has 9. Still less than 32.
- // If the size of the FA name together with severity exceeds the size,
- // just skip the former.
- if (logger_pfx && strlen(prefix) + strlen(logger_pfx) + 1 < MAX_PREFIX_SIZE)
- {
- #if defined(_MSC_VER) && _MSC_VER < 1900
- _snprintf(prefix, MAX_PREFIX_SIZE, "%s:%s", your_pfx, logger_pfx);
- #else
- snprintf(prefix, MAX_PREFIX_SIZE + 1, "%s:%s", your_pfx, logger_pfx);
- #endif
- }
- else
- {
- #ifdef _MSC_VER
- strncpy_s(prefix, MAX_PREFIX_SIZE + 1, your_pfx, _TRUNCATE);
- #else
- strncpy(prefix, your_pfx, MAX_PREFIX_SIZE);
- prefix[MAX_PREFIX_SIZE] = '\0';
- #endif
- }
- }
- ~LogDispatcher()
- {
- }
- bool CheckEnabled();
- void CreateLogLinePrefix(std::ostringstream&);
- void SendLogLine(const char* file, int line, const std::string& area, const std::string& sl);
- // log.Debug("This is the ", nth, " time"); <--- C++11 only.
- // log.Debug() << "This is the " << nth << " time"; <--- C++03 available.
- #if HAVE_CXX11
- template <class... Args>
- void PrintLogLine(const char* file, int line, const std::string& area, Args&&... args);
- template<class Arg1, class... Args>
- void operator()(Arg1&& arg1, Args&&... args)
- {
- PrintLogLine("UNKNOWN.c++", 0, "UNKNOWN", arg1, args...);
- }
- template<class Arg1, class... Args>
- void printloc(const char* file, int line, const std::string& area, Arg1&& arg1, Args&&... args)
- {
- PrintLogLine(file, line, area, arg1, args...);
- }
- #else
- template <class Arg>
- void PrintLogLine(const char* file, int line, const std::string& area, const Arg& arg);
- // For C++03 (older) standard provide only with one argument.
- template <class Arg>
- void operator()(const Arg& arg)
- {
- PrintLogLine("UNKNOWN.c++", 0, "UNKNOWN", arg);
- }
- void printloc(const char* file, int line, const std::string& area, const std::string& arg1)
- {
- PrintLogLine(file, line, area, arg1);
- }
- #endif
- #if ENABLE_LOGGING
- struct Proxy;
- friend struct Proxy;
- Proxy operator()();
- #else
- // Dummy proxy that does nothing
- struct DummyProxy
- {
- DummyProxy(LogDispatcher&)
- {
- }
- template <class T>
- DummyProxy& operator<<(const T& ) // predicted for temporary objects
- {
- return *this;
- }
- // DEPRECATED: DO NOT use LOGF/HLOGF macros anymore.
- // Use iostream-style formatting with LOGC or a direct argument with LOGP.
- SRT_ATR_DEPRECATED_PX DummyProxy& form(const char*, ...) SRT_ATR_DEPRECATED
- {
- return *this;
- }
- DummyProxy& vform(const char*, va_list)
- {
- return *this;
- }
- DummyProxy& setloc(const char* , int , std::string)
- {
- return *this;
- }
- };
- DummyProxy operator()()
- {
- return DummyProxy(*this);
- }
- #endif
- };
- #if ENABLE_LOGGING
- struct LogDispatcher::Proxy
- {
- LogDispatcher& that;
- std::ostringstream os;
- // Cache the 'enabled' state in the beginning. If the logging
- // becomes enabled or disabled in the middle of the log, we don't
- // want it to be partially printed anyway.
- bool that_enabled;
- int flags;
- // CACHE!!!
- const char* i_file;
- int i_line;
- std::string area;
- Proxy& setloc(const char* f, int l, std::string a)
- {
- i_file = f;
- i_line = l;
- area = a;
- return *this;
- }
- // Left for future. Not sure if it's more convenient
- // to use this to translate __PRETTY_FUNCTION__ to
- // something short, or just let's leave __FUNCTION__
- // or better __func__.
- std::string ExtractName(std::string pretty_function);
- Proxy(LogDispatcher& guy);
- // Copy constructor is needed due to noncopyable ostringstream.
- // This is used only in creation of the default object, so just
- // use the default values, just copy the location cache.
- Proxy(const Proxy& p): that(p.that), area(p.area)
- {
- i_file = p.i_file;
- i_line = p.i_line;
- that_enabled = false;
- flags = p.flags;
- }
- template <class T>
- Proxy& operator<<(const T& arg) // predicted for temporary objects
- {
- if ( that_enabled )
- {
- os << arg;
- }
- return *this;
- }
- ~Proxy()
- {
- if ( that_enabled )
- {
- if ( (flags & SRT_LOGF_DISABLE_EOL) == 0 )
- os << std::endl;
- that.SendLogLine(i_file, i_line, area, os.str());
- }
- // Needed in destructor?
- //os.clear();
- //os.str("");
- }
- Proxy& form(const char* fmts, ...) PRINTF_LIKE
- {
- if ( !that_enabled )
- return *this;
- if ( !fmts || fmts[0] == '\0' )
- return *this;
- va_list ap;
- va_start(ap, fmts);
- vform(fmts, ap);
- va_end(ap);
- return *this;
- }
- Proxy& vform(const char* fmts, va_list ap)
- {
- char buf[512];
- #if defined(_MSC_VER) && _MSC_VER < 1900
- _vsnprintf(buf, sizeof(buf) - 1, fmts, ap);
- #else
- vsnprintf(buf, sizeof(buf), fmts, ap);
- #endif
- size_t len = strlen(buf);
- if ( buf[len-1] == '\n' )
- {
- // Remove EOL character, should it happen to be at the end.
- // The EOL will be added at the end anyway.
- buf[len-1] = '\0';
- }
- os << buf;
- return *this;
- }
- };
- #endif
- class Logger
- {
- int m_fa;
- LogConfig& m_config;
- public:
- LogDispatcher Debug;
- LogDispatcher Note;
- LogDispatcher Warn;
- LogDispatcher Error;
- LogDispatcher Fatal;
- Logger(int functional_area, LogConfig& config, const char* logger_pfx = NULL):
- m_fa(functional_area),
- m_config(config),
- Debug ( m_fa, LogLevel::debug, " D", logger_pfx, m_config ),
- Note ( m_fa, LogLevel::note, ".N", logger_pfx, m_config ),
- Warn ( m_fa, LogLevel::warning, "!W", logger_pfx, m_config ),
- Error ( m_fa, LogLevel::error, "*E", logger_pfx, m_config ),
- Fatal ( m_fa, LogLevel::fatal, "!!FATAL!!", logger_pfx, m_config )
- {
- }
- };
- inline bool LogDispatcher::CheckEnabled()
- {
- // Don't use enabler caching. Check enabled state every time.
- // These assume to be atomically read, so the lock is not needed
- // (note that writing to this field is still mutex-protected).
- // It's also no problem if the level was changed at the moment
- // when the enabler check is tested here. Worst case, the log
- // will be printed just a moment after it was turned off.
- const LogConfig* config = src_config; // to enforce using const operator[]
- int configured_enabled_fa = config->enabled_fa[fa];
- int configured_maxlevel = config->max_level;
- return configured_enabled_fa && level <= configured_maxlevel;
- }
- #if HAVE_CXX11
- //extern std::mutex Debug_mutex;
- inline void PrintArgs(std::ostream&) {}
- template <class Arg1, class... Args>
- inline void PrintArgs(std::ostream& serr, Arg1&& arg1, Args&&... args)
- {
- serr << arg1;
- PrintArgs(serr, args...);
- }
- template <class... Args>
- 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)
- {
- #ifdef ENABLE_LOGGING
- std::ostringstream serr;
- CreateLogLinePrefix(serr);
- PrintArgs(serr, args...);
- if ( !isset(SRT_LOGF_DISABLE_EOL) )
- serr << std::endl;
- // Not sure, but it wasn't ever used.
- SendLogLine(file, line, area, serr.str());
- #endif
- }
- #else
- template <class Arg>
- 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)
- {
- #ifdef ENABLE_LOGGING
- std::ostringstream serr;
- CreateLogLinePrefix(serr);
- serr << arg;
- if ( !isset(SRT_LOGF_DISABLE_EOL) )
- serr << std::endl;
- // Not sure, but it wasn't ever used.
- SendLogLine(file, line, area, serr.str());
- #endif
- }
- #endif
- // SendLogLine can be compiled normally. It's intermediately used by:
- // - Proxy object, which is replaced by DummyProxy when !ENABLE_LOGGING
- // - PrintLogLine, which has empty body when !ENABLE_LOGGING
- inline void LogDispatcher::SendLogLine(const char* file, int line, const std::string& area, const std::string& msg)
- {
- src_config->lock();
- if ( src_config->loghandler_fn )
- {
- (*src_config->loghandler_fn)(src_config->loghandler_opaque, int(level), file, line, area.c_str(), msg.c_str());
- }
- else if ( src_config->log_stream )
- {
- (*src_config->log_stream) << msg;
- (*src_config->log_stream).flush();
- }
- src_config->unlock();
- }
- }
- #endif
|