1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-02-15 04:42:04 +00:00
srs/trunk/3rdparty/srt-1-fit/srtcore/logging.h
Haibo Chen c5e067fb0b
Upgrade libsrt to v1.5.3. v5.0.183 v6.0.81 (#3808)
fix https://github.com/ossrs/srs/issues/3155
Build srt-1-fit fails with `standard attributes in middle of
decl-specifiers` on GCC 12,Arch Linux.

See https://github.com/Haivision/srt/releases/tag/v1.5.3
2023-09-21 22:23:56 +08:00

505 lines
13 KiB
C++

/*
* 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