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 { };

};

template<int SIZE> class FixedBuffer : boost::noncopyable

class LogStream : boost::noncopyable

{

typedef LogStream self;

public:                                        // 4000

 typedef detail::FixedBuffer<detail::kSmallBuffer> Buffer;

};

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()  //是把数字或者指针的值当作字符串写入

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使用时序图:

#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 设置。

测试程序:

#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_;

....

#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

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

::setbuffer(fp_, buffer_, sizeof buffer_);

测试程序:

LogFile_test.cc:

#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++网络库》

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏沃趣科技

ASM 翻译系列第三十四弹:ASM磁盘组重要属性介绍

原作者:Bane Radulovic 译者: 邱大龙 审核: 魏兴华 DBGeeK社区联合出品 原文链接:http://asmsupportguy....

3206
来自专栏开源优测

python unittest使用基本过程

前言 unittest是python的标准的单元测试框架,能够很好的和自动化测试相结合,并有独立的测试报告框架。 unittest提供了一系列类让测试变得更加容...

38611
来自专栏安富莱嵌入式技术分享

【RL-TCPnet网络教程】第28章 RL-TCPnet之DNS应用

本章节为大家讲解RL-TCPnet的DNS应用,学习本章节前,务必要优先学习第27章的DNS基础知识。有了这些基础知识之后,再搞本章节会有事半功倍的效果。

785
来自专栏精讲JAVA

怎样编写高质量的Java代码

代码质量概述 怎样辨别一个项目代码写得好还是坏?优秀的代码和腐化的代码区别在哪里?怎么让自己写的代码既漂亮又有生命力?接下来将对代码质量的问题进行一些粗略的介绍...

41510
来自专栏24K纯开源

用Qt写软件系列一:QCacheViewer(浏览器缓存查看器)

介绍      Cache技术广泛应用于计算机行业的软硬件领域。该技术既是人们对新技术探讨的结果,也是对当前软硬件计算能力的一种妥协。在浏览器中使用cache技...

1735
来自专栏顶级程序员

写一个网页进度 loading

来自:简书 作者:jack_lo 原文:www.jianshu.com/p/4c93f5bd9861 loading随处可见,比如一个app经常会有下拉...

3499
来自专栏IMWeb前端团队

版本号学习笔记

版本号 作为一名前端工程师,相信大家对npm等包管理工具都比较熟悉,这里抛出一个先前碰到的问题: 冲突 假设我们存在一个多个项目共用的构建环境runtime,构...

1830
来自专栏大数据文摘

手把手 | 嫌Python太慢?并行运算Process Pools三行代码给你4倍提速!

1674
来自专栏数据和云

innodb实例损坏情况下恢复数据及相关工具的开发

作者介绍:谢浩,现任职于云和恩墨(北京)信息技术有限公司,具有多年oracle数据库企业级运维经验,擅长结合业务、硬件系统制定各种项目方案,具有丰富mysql相...

2958
来自专栏大闲人柴毛毛

使用Eclipse插件提高Java编码质量

代码质量概述 ? 怎样辨别一个项目代码写得好还是坏?优秀的代码和腐化的代码区别在哪里?怎么让自己写的代码既漂亮又有生命力?接下来将对代码质量的问题进行...

2747

扫码关注云+社区