muduoログライブラリ学習(一)


muduoのログ・ライブラリは、LogStream{.h,.cc}、Logging{.h,.cc}、LogFile{.h,.cc}、AsyncLogging{.h,.cc}から構成されています.ここでは,これらのファイル(主にファイル内の対応するクラス)間がどのように関連し,連携して動作しているかを主に説明する.
 
LogStreamクラスにはBufferメンバー(muduo::Bufferクラスではなくテンプレートクラス)があります.このクラスは主に記録するログの内容をこのBufferに入れることを担当しています.文字列、整数、doubleタイプ(整数とdoubleはまず文字型に変換し、bufferに入れます).このクラスはこれらのタイプに対して再ロードされました<
        Logging.hファイルはLoggingクラスではなくloggerクラスを定義する.ログクラスは列挙タイプでログレベルを定義します.
enum LogLevel
  {
    TRACE,
    DEBUG,
    INFO,
    WARN,
    ERROR,
    FATAL,
    NUM_LOG_LEVELS
  };

ロギングでhファイルには、一連のマクロ定義も定義されている.
#define LOG_TRACE if (muduo::Logger::logLevel() <= muduo::Logger::TRACE) \
  muduo::Logger(__FILE__, __LINE__, muduo::Logger::TRACE, __func__).stream()
#define LOG_DEBUG if (muduo::Logger::logLevel() <= muduo::Logger::DEBUG) \
  muduo::Logger(__FILE__, __LINE__, muduo::Logger::DEBUG, __func__).stream()
#define LOG_INFO if (muduo::Logger::logLevel() <= muduo::Logger::INFO) \
  muduo::Logger(__FILE__, __LINE__).stream()
#define LOG_WARN muduo::Logger(__FILE__, __LINE__, muduo::Logger::WARN).stream()
#define LOG_ERROR muduo::Logger(__FILE__, __LINE__, muduo::Logger::ERROR).stream()
#define LOG_FATAL muduo::Logger(__FILE__, __LINE__, muduo::Logger::FATAL).stream()
#define LOG_SYSERR muduo::Logger(__FILE__, __LINE__, false).stream()
#define LOG_SYSFATAL muduo::Logger(__FILE__, __LINE__, true).stream()

ログ機能を使用すると、これらのマクロが使用されます.たとえば
LOG_TRACE<

これらのマクロ定義をよく見ると、それは
muduo::Logger(__FILE__,__LINE__,muduo::Logger::TRACE,__func__).stream()<

一方、Loggerクラスが返すstreamはLogStreamオブジェクトであり、これらのマクロによるログ記録はLogStreamオブジェクトの1つのbufferに格納されます.前述したLogStream関数は実際のIO操作を行わないため、LogStreamに書き込まれたデータはbuffer()関数で取得される.
ログクラスには、ログの出力位置を設定する2つの関数ポインタも定義されています.この設定は、オブジェクトベースではなくクラスベースであるため、自然に2つの静的変数g_があるoutput,g_flushは出力関数とリフレッシュ関数をそれぞれ格納します.
typedef void (*OutputFunc)(const char* msg, int len);
typedef void (*FlushFunc)();
デフォルトでは、stdout(すなわち、標準出力)に出力されます.
void defaultOutput(const char* msg, int len)
{
  size_t n = fwrite(msg, 1, len, stdout);
  //FIXME check n
  (void)n;
}

void defaultFlush()
{
  fflush(stdout);
}

Logger::OutputFunc g_output = defaultOutput;
Logger::FlushFunc g_flush = defaultFlush;

さらに、このクラスには、静的変数g_を設定するための2つの静的関数も提供される.outputとg_flushの.
static void setOutput(OutputFunc);
static void setFlush(FlushFunc); 

言うまでもなく、この2つの関数は、出力関数を変更したり、ログ出力にリダイレクトしたりすることができるので、自分で書いた出力関数に変更することができ、この出力関数では、出力したいキャッシュやファイルに出力することができます.muduoでは、一般的に、ログをAsyncLoggingオブジェクトにリダイレクトします.具体的には、AsyncLoggingオブジェクトのキャッシュに保存してから、AsyncLoggingによって非同期にファイルに書き込むことになります.
Loggerクラスのもう1つの特徴は、Implメソッドを使用して、プライベートなImplクラスを定義することです.このImplクラスにはLogStreamクラスがあります.ほとんどのImplメソッドと同様に,Loggerクラスの具体的な動作はこのImplクラスによって達成される.ただし、LoggerクラスのImplメンバーはポインタではありません.Implメソッドを使用する大きな理由は,実装コードだけでなくクラスのプライベートメンバー関数も非表示にするために,ソースを閉じるためである.しかしmuduoというオープンソースライブラリにとっては意味がありません.またポインタを使うと、newのときにスタックにスペースを申請する必要があり、運転速度が低下するに違いありません.
ロガークラスは間接的にIOを行う.Loggerの構造関数は次のとおりです.
Logger::~Logger()
{
  impl_.finish();// buffer 。
  constLogStream::Buffer& buf(stream().buffer());
 
 g_output(buf.data(), buf.length());
  if (impl_.level_== FATAL)
  {
    g_flush();
    abort();
  }
}

解析関数では、出力関数g_が呼び出されます.outputは、ログデータ(bufに格納されている)を出力します.
 
 
今、LoggerクラスとLogStreamクラスがどのように協力して働いているのかを説明します.
LOG_*の使用などのマクロは、一時的な匿名Loggerオブジェクトを作成します.このオブジェクトにはImplオブジェクトがあり、ImplオブジェクトにはLogStreamオブジェクトがあります.LOG_*マクロは、LogStreamオブジェクトの参照を返します.LogStreamの1つのbufferにコンテンツを入力します.Loggerの構造関数では、LogStreamに保存されているbufferのログコンテンツが出力されます.
        LOG_*のマクロは、一時匿名Loggerオブジェクトを作成し、一時匿名が重要です.一時匿名オブジェクトは使用が終わるとすぐに破棄され、構造関数を呼び出すためです.一方、C++はスタック内の名前付きオブジェクトに対して、先に作成した後に破棄します.これにより、後に作成されたLoggerオブジェクトは、先に作成されたLoggerオブジェクトよりも先に破棄されます.すなわち、構造関数を呼び出してログを出力すると、ログコンテンツが逆シーケンス(具体的には{}に含まれるブロック内の逆シーケンス)になります.一時匿名Loggerオブジェクトを使用する効果は、LOG_*です.この行のコードにはログの内容だけでなく、すぐにログを出力します.
 
これで、基本的なログ機能は完了しましたが、非同期ではありません.