reSIProcate学习笔记(二)


Log

rutil目录下的Logger.hxx声明了一系列的Logging宏,分别代表不同的debug level。

/* @Usage

Each source file which uses logging facilities, must set RESIPROCATE_SUBSYSTEM
preprocessor define to one of resip subsystems. This define will be used by
logging macros to mark log entries, generated from this source file, with
appropriate subsystem tag. For the list of available resip subsystems refer
to static variables list in resip::Subsystem class. Note, that those standard
resip::Subsystem variables are just the most commonly used ones. Nothing
prevents you from creating your own resip::Subsystem instance and set
RESIPROCATE_SUBSYSTEM to point to it. That custom resip::Subsystem variable
can even be local to a file or private class member variable, you just must
ensure that it exist in all places you use logging this file. Creating own
resip::Subsystem will allow you to create own subsystem tag and set specific
logging level for it.

Once you have RESIPROCATE_SUBSYSTEM defined you can start logging with any
of StackLog(), DebugLog(), InfoLog(), WarningLog(), ErrLog(), CritLog(). These
preprocessor macros provide a convenient way to log your data. Look at this
piece of code as an example:

<code>
#include Logger.hxx
#define RESIPROCATE_SUBSYSTEM resip::Subsystem::SIP

DebugLog(<< “hi there ” << mix << 4 << types);  // note leading << and no endl
</code>
*/

不同调试等级的宏定义:

// unconditionally output to cerr -- easily change back and forth
#define CerrLog(args_)     resip::Log::tags(resip::Log::StdErr, RESIPROCATE_SUBSYSTEM, \
               __FILE__, __LINE__, resipCerr) << ' ' << '|' << ' ' args_ << std::endl;

#define StackLog(args_)    GenericLog(RESIPROCATE_SUBSYSTEM, resip::Log::Stack, args_)

#define DebugLog(args_)    GenericLog(RESIPROCATE_SUBSYSTEM, resip::Log::Debug, args_)

#define InfoLog(args_)     GenericLog(RESIPROCATE_SUBSYSTEM, resip::Log::Info, args_)

#define WarningLog(args_)  GenericLog(RESIPROCATE_SUBSYSTEM, resip::Log::Warning, args_)

#define ErrLog(args_)      GenericLog(RESIPROCATE_SUBSYSTEM, resip::Log::Err, args_)

#define CritLog(args_)     GenericLog(RESIPROCATE_SUBSYSTEM, resip::Log::Crit, args_)

而宏GenericLog的定义为:

// do/while allows a {} block in an expression
#define GenericLog(system_, level_, args_)                                       \
do                                                                               \
{                                                                                \
    if (genericLogCheckLevel(level_, system_))                                   \
    {                                                                            \
        resip::Log::Guard _resip_log_guard(level_, system_, __FILE__, __LINE__); \
        _resip_log_guard.asStream()  args_;                                      \
    }                                                                            \
} while (false)

以注释中的例子为例,args_就是<< “hi there ” << mix << 4 << types,因此代入之后是GenericLog(RESIPROCATE_SUBSYSTEM, resip::Log::Debug, << “hi there ” << mix << 4 << types),再按宏定义展开结果如下:

do
{
    if (genericLogCheckLevel(resip::Log::Debug, RESIPROCATE_SUBSYSTEM))
    {
        resip::Log::Guard _resip_log_guard(resip::Log::Debug, RESIPROCATE_SUBSYSTEM, __FILE__, __LINE__);
        _resip_log_guard.asStream() << "hi there " << mix << 4 << types;
    }
} while (false)

下面先追究下函数genericLogCheckLevel(),其定义如下:

static inline bool genericLogCheckLevel(resip::Log::Level level, const resip::Subsystem& sub)
{
   return resip::Log::isLogging(level, sub);
}

类Log中包含的公有枚举成员决定调试的种类及等级:

enum Type
{
    Cout = 0,
    Syslog, 
    File, 
    Cerr,
    VSDebugWindow,        // Use only for Visual Studio Debug Window logging - WIN32 must be defined
    OnlyExternal,         // log messages are only written to external logger
    OnlyExternalNoHeaders // same as OnlyExternal, only the messageWithHeaders param of the ExternalLogger
                          // will be empty.  This parameter usually contains a pre-formatted log entry.
};

enum Level
{
    None = -1,
#ifdef WIN32
    Crit = 2,
    Err = 3,
    Warning = 4,
    Info = 6,
    Debug = 7,
#else
    Crit = LOG_CRIT,
    Err,
    Warning = LOG_WARNING,
    Info = LOG_INFO,
    Debug = LOG_DEBUG,
#endif
    Stack = 8,
    StdErr = 9,
    Bogus = 666
};

类Log的静态成员函数isLogging定义如下:

bool Log::isLogging(Log::Level level, const resip::Subsystem&amp; sub)
{
   if (sub.getLevel() != Log::None)
   {
      return level <= sub.getLevel();
   }
   else
   {
      return (level <= Log::getLoggerData().mLevel);
   }
}

类Subsystem用于区分特定区域代码属于哪个子系统,类注释说明得非常清楚:

/* @brief  Class used to specify what sub-system given sections of code belong to, for use by the logging system.

@note   The logging macros defined in Logger.hxx assume that the preprocessor macro RESIPROCATE_SUBSYSTEM is defined to an instance of this class. The logging macro uses this object to determine what logging level should be used, and what sub-system the logging statement should come from. So, in your code you might do something like the following:

@code
#define RESIPROCATE_SUBSYSTEM Subsystem::APP
//…
void doStuff() { DebugLog(<< “Doing some stuff…”); }
@endcode

This would cause your log statement to be marked as coming from Subsystem::APP.
*/

整个类的完整定义:

class Subsystem 
{
   public:
      // Add new systems below
      static Subsystem APP;
      static Subsystem CONTENTS;
      static Subsystem DNS;
      static Subsystem DUM;
      static Subsystem NONE; // default subsystem
      static Subsystem PRESENCE; 
      static Subsystem SDP;
      static Subsystem SIP;  // SIP Stack / Parser
      static Subsystem TEST;   
      static Subsystem TRANSACTION;
      static Subsystem TRANSPORT;
      static Subsystem STATS;
      static Subsystem REPRO;
      
      const Data& getSubsystem() const;
      Log::Level getLevel() const { return mLevel; }
      void setLevel(Log::Level level) { mLevel = level; }
   protected:
      explicit Subsystem(const char* rhs) : mSubsystem(rhs), mLevel(Log::None) {};
      explicit Subsystem(const Data& rhs) : mSubsystem(rhs), mLevel(Log::None) {};
      Subsystem& operator=(const Data&amp; rhs);

      Data mSubsystem;
      Log::Level mLevel;

      friend EncodeStream& operator<<(EncodeStream& strm, const Subsystem& ss);
};

于是Log::isLogging()函数中sub.getLevel()返回sub的mLevel成员,而函数Log::getLoggerData()定义如下:

static ThreadData &getLoggerData()
{
    ThreadData* pData = static_cast<ThreadData*>(ThreadIf::tlsGetValue(*Log::mLocalLoggerKey));
    return pData ? *pData : mDefaultLoggerData;
}

ThreadData是Log内部的一个嵌套类,其含有类型为Log::Level的成员声明:

volatile Level mLevel;

因此,若getLoggerData()函数return的是*pData,则在isLogging中Log::getLoggerData().mLevel就是pData->mLevel;而mDefaultLoggerData是Log.cxx中的一个全局静态变量,完整定义为:

Log::ThreadData Log::mDefaultLoggerData(0, Log::Cout, Log::Info, NULL, NULL);

ThreadData的构造函数,从中可以看出mDefaultLoggerData均采用了默认值:

ThreadData(LocalLoggerId id, Type type = Cout, Level level = Info, 
           const char *logFileName = NULL, ExternalLogger *pExternalLogger = NULL)
               : mLevel(level),
                 mMaxLineCount(0),
                 mMaxByteCount(0),
                 mExternalLogger(pExternalLogger),
                 mId(id),
                 mType(type),
                 mLogger(NULL),
                 mLineCount(0)
{
    if (logFileName)
    {
        mLogFileName = logFileName;
    }
}

追究完了函数genericLogCheckLevel(),现在来看Log类中的嵌套类Guard,完整定义如下:

class Guard
{
    public:
        /* Remember the logging values and be a a stream to receive the log contents. */
        Guard(Level level, const Subsystem&amp; system, const char* file, int line);
        /* Commit logging */
        ~Guard();
        EncodeStream& asStream() {return mStream;}
        operator EncodeStream&() {return mStream;}
    private:
        resip::Log::Level mLevel;
        const resip::Subsystem& mSubsystem;
        resip::Data::size_type mHeaderLength;
        const char* mFile;
        int mLine;
        char mBuffer[128];
        Data mData;
        oDataStream mStream;
        Guard& operator=(const Guard&);
};

在先前宏展开的最后有_resip_log_guard.asStream() << “hi there ” << mix << 4 << types;

现在知道这个Guard对象_resip_log_guard调用了成员函数EncodeStream& asStream() { return mStream; },EncodeStream是一个宏,在文件resipfaststreams.hxx中有如下宏:

#define EncodeStream std::ostream
#define DecodeStream std::istream
#define resipCerr std::cerr
#define resipCout std::cout

成员函数asStream()返回的是oDataStream类型的对象并强制转换成了std::ostream类型的引用,由此可以猜测这是基类引用派生类对象,果然,观察oDataStream类的定义,其公有继承了std::ostream

/* @brief An ostream that operates on an existing Data.
   The data is appended to the reference passed to the constructor. The data is valid after DataStream's destructor is called. */
class oDataStream : private DataBuffer, public EncodeStream 
{
   public:
      /* Constructs a oDataStream with a Data to operate on.
          @param str A Data to operate on. */
      oDataStream(Data& str);
      /* Calls flush on itself to force the update to the Data reference passed into the constructor. */
      ~oDataStream();
      /* Clears the underlying Data reference. */
      void reset();
   private:
      oDataStream(const oDataStream&);
      oDataStream& operator=(const oDataStream&);
};

到此真相大白,最后_resip_log_guard.asStream() << “hi there ” << mix << 4 << types; 这句就好比std::ostream << “hi there ” << mix << 4 << types; 于是,调试信息就输出来了。

Log::initialize()

/* Logging may be used without (or prior to) initialization, in which case all log data will be printed right to console (to std::cout). Likely, you will want to use more advanced logging features like output to syslog or a file. In this case you need to call Log::initialize() with appropriate parameters. E.g., following example tells logger to write data to a file with the name “resip.log”. It will write only entries with logging priority Log::Info or higher. Application name is taken from argv[0], and no external logger is specified (last parameter is NULL).

<code>
   Log::initialize(Log::File, Log::Info, argv[0], “resip.log”, NULL);
</code>

*/

如果不想在终端中看到一堆的调试信息,就使用此函数调用将信息输出至文件,该函数有4个重载形式:

static void initialize(Type type, Level level, const Data& appName, const char * logFileName = 0, ExternalLogger* externalLogger = 0, const Data& syslogFacility = “LOG_DAEMON”);
static void initialize(const Data& type, const Data& level, const Data& appName, const char * logFileName = 0, ExternalLogger* externalLogger = 0, const Data& syslogFacility = “LOG_DAEMON”);
static void initialize(const char* type, const char* level, const char* appName, const char * logFileName = 0, ExternalLogger* externalLogger = 0, const char* syslogFacility = “LOG_DAEMON”);
static void initialize(Type type, Level level, const Data& appName, ExternalLogger& logger, const Data& syslogFacility = “LOG_DAEMON”);

如果要为不同的子系统使用不同的调试等级,可以使用Log::setLevel()函数。

/* You may set logging level to output for a specific subsystem inside resip. The recommended way to do this is with <code>Log::setLevel(level, subsystem)</code> static function. If you set a concrete logging level for the subsystem, it will be used instead of all other logging level settings, like global logging level setting and thread local logger level setting. To set subsystem-specific logging level back to default logging level, call <code>Log::setLevel(Log::None, subsystem)</code> */

所有该函数的重载形式如下:

/* @brief Set logging level for current thread. If thread has no local logger attached, then set global logging level. */
static void setLevel(Level level);
/* @brief Set logging level for given subsystem. */
static void setLevel(Level level, Subsystem& s);
/* Set logging level for given local logger. Use 0 to set global logging level. */
static void setLevel(Level level, LocalLoggerId loggerId);

Thread local loggers

/* If your application uses several threads for resip, e.g. uses separate thread for each resip instance, then you may want to split your log streams to be separate too. E.g. you may use different log files for different instances. In this case you need to use thread local loggers.

First, you need to create a thread local logger with Log::localLoggerCreate() static function call with desired parameters (they’re closely following Log::initialize() meaning). You will receive LocalLoggerId in response which you will use later to refer to created local logger. If you need to change local logger’s parameters later, you should use Log::localLoggerReinitialize() static function. And when you’re done with it, free it with Log::localLoggerRemove() static function. To actually use a created local logger, you need to call Log::setThreadLocalLogger() from the target thread context. If you need to remove thread local logger from a thread, just call
<code>Log::setThreadLocalLogger(0)</code>

Note, that thread local logger may be safely used from multiple threads. So if each of your resip instances have two threads, both of them can just share the same local logger – just pass its LocalLoggerId to them both. */

rutil/test/testLogger.cxx

未完待续..

Leave a comment

邮箱地址不会被公开。 必填项已用*标注