muduo网络库学习之Logger类、LogStream类、LogFile类封装中的知识点


一、Logger类、LogStream类

1、日志作用

开发过程中:
调试错误
更好的理解程序
运行过程中:
诊断系统故障并处理
记录系统运行状态

2、日志级别

TRACE
指出比DEBUG粒度更细的一些信息事件(开发过程中使用)
DEBUG
指出细粒度信息事件对调试应用程序是非常有帮助的。(开发过程中使用)
INFO
表明消息在粗粒度级别上突出强调应用程序的运行过程。
WARN
系统能正常运行,但可能会出现潜在错误的情形。
ERROR
指出虽然发生错误事件,但仍然不影响系统的继续运行。
FATAL
指出每个严重的错误事件将会导致应用程序的退出。


class  Logger
{
public:
enum  LogLevel
  {
    TRACE,
    DEBUG,
    INFO,
    WARN,
    ERROR,
    FATAL,
    NUM_LOG_LEVELS,
  };
// compile time calculation of basename of source file
class  SourceFile { };
private:
       class  Impl { };
};
muduo网络库学习之Logger类、LogStream类、LogFile类封装中的知识点_第1张图片
muduo网络库学习之Logger类、LogStream类、LogFile类封装中的知识点_第2张图片
template<int SIZE>
class  FixedBuffer  : boost::noncopyable
muduo网络库学习之Logger类、LogStream类、LogFile类封装中的知识点_第3张图片
class  LogStream : boost::noncopyable
{
typedef LogStream  self;
public:                                        // 4000
 typedef detail::FixedBuffer<detail::kSmallBuffer>  Buffer;
};
muduo网络库学习之Logger类、LogStream类、LogFile类封装中的知识点_第4张图片
class Fmt // : boost::noncopyable
{
public:
  template<typename T>
  Fmt(const char* fmt, T val)
  {     // 按照fmt 格式将val 格式化成字符串放入buf_中
      length_ = snprintf(buf_, sizeof buf_, fmt, val);
  };
};

3、formatInteger()  //是把数字或者指针的值当作字符串写入
 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
 

const  char digits[] =  "9876543210123456789";
const  char* zero = digits +  9;
const  char digitsHex[] =  "0123456789ABCDEF";

LogStream& LogStream:: operator<<( int v)
{
  formatInteger(v);
   return * this;
}

template< typename T>
void LogStream::formatInteger(T v)
{                        //32
   if (buffer_.avail() >= kMaxNumericSize)
  {
    size_t len = convert(buffer_.current(), v);
    buffer_.add(len);
  }
}

// Efficient Integer to String Conversions, by Matthew Wilson.
template< typename T>
size_t convert( char buf[], T value)
{
  T i = value;
   char* p = buf;

   do
  {
     int lsd =  static_cast< int>(i %  10);
    i /=  10;
    *p++ = zero[lsd];
  }  while (i !=  0);

   if (value <  0)
  {
    *p++ =  '-';
  }
  *p =  '\0';
  std::reverse(buf, p);

   return p - buf;
}

LogStream& LogStream:: operator<<( const  void* p)
{
  uintptr_t v =  reinterpret_cast<uintptr_t>(p);
   if (buffer_.avail() >= kMaxNumericSize)
  {
     char* buf = buffer_.current();
    buf[ 0] =  '0';
    buf[ 1] =  'x';
    size_t len = convertHex(buf+ 2, v);
    buffer_.add(len+ 2);
  }
   return * this;
}

// Efficient Pointer to String Conversions
// uintptr_t on 32bit as unsigned int; on 64bit as unsigned long int
size_t convertHex( char buf[], uintptr_t value)
{
  uintptr_t i = value;
   char* p = buf;

   do
  {
     int lsd = i %  16;
    i /=  16;
    *p++ = digitsHex[lsd];
  }  while (i !=  0);

  *p =  '\0';
  std::reverse(buf, p);

   return p - buf;
}


4、Logger使用时序图:

muduo网络库学习之Logger类、LogStream类、LogFile类封装中的知识点_第5张图片
#define LOG_INFO if (muduo::Logger::logLevel() <= muduo::Logger::INFO) \
  muduo::Logger(__FILE__, __LINE__).stream()

LOG_INFO<<“info ...”;     // 使用方式
muduo::Logger(__FILE__, __LINE__).stream()<<“info”;

// LogStream& stream() { return impl_.stream_; }

Logger => Impl => LogStream => operator<< FixedBuffer => g_output => g_flush

栈上匿名的Logger对象使用完就要析构,在~Logger()中调用 g_output,即 g_output(buf.data(), buf.length());
如果是FATAL错误,还要调用g_flush,最后abort()程序。

如果没有调用g_flush,会一直输出到缓冲区(标准输出缓冲区,文件FILE缓冲区)满才会真的输出在标准输出,或者写入到文件中去。注:可以使用setvbuf设置缓冲区的大小。
int setvbuf ( FILE * stream, char * buffer, int mode, size_t size );

默认日志信息输出到标准输出(g_output =  defaultOutput、g_flush = defaultFlush) ,也可以输出到文件,使用SetOutput、SetFlush 设置。

测试程序:
 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
 
#include <muduo/base/Logging.h>
#include <errno.h>
#include <stdio.h>

using  namespace muduo;

FILE *g_file;

void dummyOutput( const  char *msg,  int len)
{
     if (g_file)
    {
        fwrite(msg,  1, len, g_file);
    }
}

void dummyFlush()
{
    fflush(g_file);
}

int main()
{
    g_file = ::fopen( "/tmp/muduo_log""ae");
    Logger::setOutput(dummyOutput);
    Logger::setFlush(dummyFlush);

    LOG_TRACE <<  "trace ...";
    LOG_DEBUG <<  "debug ...";
    LOG_INFO <<  "info ...";
    LOG_WARN <<  "warn ...";
    LOG_ERROR <<  "error ...";
     //LOG_FATAL<<"fatal ...";
    errno =  13;
    LOG_SYSERR <<  "syserr ...";
     //LOG_SYSFATAL<<"sysfatal ...";

    ::fclose(g_file);

     return  0;
}

程序执行后查看写入文件的内容:
simba@ubuntu:~/Documents/build/debug/bin$ cat /tmp/muduo_log 
20131102 12:33:29.812878Z  4021 TRACE main trace ... - Log_test2.cc:28
20131102 12:33:29.813108Z  4021 DEBUG main debug ... - Log_test2.cc:29
20131102 12:33:29.813112Z  4021 INFO  info ... - Log_test2.cc:30
20131102 12:33:29.813114Z  4021 WARN  warn ... - Log_test2.cc:31
20131102 12:33:29.813117Z  4021 ERROR error ... - Log_test2.cc:32
20131102 12:33:29.813120Z  4021 ERROR Permission denied (errno=13) syserr ... - Log_test2.cc:35
simba@ubuntu:~/Documents/build/debug/bin$ 

5、StringPiece类

 // We provide non-explicit singleton constructors so users can pass
  // in a "const char*" or a "string" wherever a "StringPiece" is
  // expected.

只是复制字符串的指针,故不涉及具体字符串内存的拷贝,高效地传递字符串。

class StringPiece {
private:
  const char*   ptr_;
  int           length_;
....
 C++ Code 
1
2
3
4
5
6
7
8
9
10
 
#define STRINGPIECE_BINARY_PREDICATE(cmp,auxcmp)                             \
   bool  operator cmp ( const StringPiece& x)  const {                           \
     int r = memcmp(ptr_, x.ptr_, length_ < x.length_ ? length_ : x.length_); \
     return ((r auxcmp  0) || ((r ==  0) && (length_ cmp x.length_)));          \
  }
STRINGPIECE_BINARY_PREDICATE( < ,  < );
STRINGPIECE_BINARY_PREDICATE( <= , < );
STRINGPIECE_BINARY_PREDICATE( >= , > );
STRINGPIECE_BINARY_PREDICATE( > ,  > );
#undef STRINGPIECE_BINARY_PREDICATE

};

二、LogFile类

日志滚动条件
文件大小(例如每写满1G换下一个文件)
时间(每天零点新建一个日志文件,不论前一个文件是否写满)
一个典型的日志文件名
logfile_test.20130411-115604.popo.7743.log
// 运行程序.时间.主机名.线程名.log

class  LogFile  : boost::noncopyable
muduo网络库学习之Logger类、LogStream类、LogFile类封装中的知识点_第6张图片
const string basename_; // 日志文件 basename

const size_t rollSize_; // 日志文件达到rollSize_换一个新文件

const int flushInterval_; // 日志写入文件间隔时间

time_t startOfPeriod_; // 开始记录日志时间(调整到零时时间)

time_t lastRoll_; // 上一次滚动日志文件时间

time_t lashFlush_; // 上一次日志写入文件时间

                                  // 60*60*24
time_t start = now / kRollPerSeconds_ * kRollPerSeconds_;
表示start对齐到kR的整数倍,也就是时间调整到当天零时

// not thread safe
class LogFile:: File  : boost::noncopyable
muduo网络库学习之Logger类、LogStream类、LogFile类封装中的知识点_第7张图片
  ::setbuffer(fp_, buffer_, sizeof buffer_);

测试程序:

LogFile_test.cc:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 
#include <muduo/base/LogFile.h>
#include <muduo/base/Logging.h>

boost::scoped_ptr<muduo::LogFile> g_logFile;

void outputFunc( const  char *msg,  int len)
{
    g_logFile->append(msg, len);
// scoped_ptr<T>  重载operator->,调用LogFile::append(), 
// 间接调用File ::append(); 最后 ::fwrite_unlocked(fp_);
}

void flushFunc()
{
    g_logFile->flush();
// scoped_ptr<T>  重载operator->,调用LogFile::flush(), 
//间接 调用File::flush(),最后 ::fflush(fp_);
}

int main( int argc,  char *argv[])
{
     char name[ 256];
    strncpy(name, argv[ 0],  256);
    g_logFile.reset( new muduo::LogFile(::basename(name),  200 *  1000));
    muduo::Logger::setOutput(outputFunc);
    muduo::Logger::setFlush(flushFunc);

    muduo::string line =  "1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ ";

     for ( int i =  0; i <  10000; ++i)
    {
        LOG_INFO << line << i;
// 不断地构造匿名的Logger对象,在~Logger()中调用dummyOutput,将日志信息写入文件

        usleep( 1000);
    }
}

执行程序后查看创建的日志文件:

simba@ubuntu:~/Documents/build/debug/bin$ ls -lh *.log
-rw-rw-r-- 1 simba simba 196K Nov  2 05:37 logfile_test.20131102-123753.ubuntu.4044.log
-rw-rw-r-- 1 simba simba 196K Nov  2 05:37 logfile_test.20131102-123756.ubuntu.4044.log
-rw-rw-r-- 1 simba simba 196K Nov  2 05:38 logfile_test.20131102-123759.ubuntu.4044.log
-rw-rw-r-- 1 simba simba 196K Nov  2 05:38 logfile_test.20131102-123802.ubuntu.4044.log
-rw-rw-r-- 1 simba simba 196K Nov  2 05:38 logfile_test.20131102-123805.ubuntu.4044.log
-rw-rw-r-- 1 simba simba 196K Nov  2 05:38 logfile_test.20131102-123808.ubuntu.4044.log
-rw-rw-r-- 1 simba simba  87K Nov  2 05:38 logfile_test.20131102-123811.ubuntu.4044.log

因为我们设置的滚动日志文件大小为200 *1000/1024 = 196k,所以现在即使没有到另一个零时,因为文件大小已到上限,也会自动滚动文件。


参考:
muduo manual.pdf
《linux 多线程服务器编程:使用muduo c++网络库》


你可能感兴趣的:(logfile,muduo,日志滚动)