热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

一、Logger类、LogStream类1、日志作用开发过程中:调试错误更好的理解程序运行过程中:诊断系统故障并处理记录系统运行状态2、日志级别TRAC



一、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
class FixedBuffer : boost::noncopyable



class LogStream : boost::noncopyable
{
typedef LogStream self;
public:                                        // 4000
typedef detail::FixedBuffer Buffer;
};



class Fmt // : boost::noncopyable
{
public:
template
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() >&#61; kMaxNumericSize)
  {
    size_t len &#61; 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 &#61; value;
  char* p &#61; buf;

  do
  {
    int lsd &#61; static_cast<int>(i % 10);
    i /&#61; 10;
    *p&#43;&#43; &#61; zero[lsd];
  } while (i !&#61; 0);

  if (value < 0)
  {
    *p&#43;&#43; &#61; &#39;-&#39;;
  }
  *p &#61; &#39;\0&#39;;
  std::reverse(buf, p);

  return p - buf;
}

LogStream& LogStream::operator<<(const void* p)
{
  uintptr_t v &#61; reinterpret_cast(p);
  if (buffer_.avail() >&#61; kMaxNumericSize)
  {
    char* buf &#61; buffer_.current();
    buf[0] &#61; &#39;0&#39;;
    buf[1] &#61; &#39;x&#39;;
    size_t len &#61; convertHex(buf&#43;2, v);
    buffer_.add(len&#43;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 &#61; value;
  char* p &#61; buf;

  do
  {
    int lsd &#61; i % 16;
    i /&#61; 16;
    *p&#43;&#43; &#61; digitsHex[lsd];
  } while (i !&#61; 0);

  *p &#61; &#39;\0&#39;;
  std::reverse(buf, p);

  return p - buf;
}


4、Logger使用时序图:




#define LOG_INFO if (muduo::Logger::logLevel() <&#61; muduo::Logger::INFO) \
  muduo::Logger(__FILE__, __LINE__).stream()

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

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

Logger &#61;> Impl &#61;> LogStream &#61;> operator< g_output &#61;> g_flush

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

如果没有调用g_flush&#xff0c;会一直输出到缓冲区&#xff08;标准输出缓冲区&#xff0c;文件FILE缓冲区&#xff09;满才会真的输出在标准输出&#xff0c;或者写入到文件中去。注&#xff1a;可以使用setvbuf设置缓冲区的大小。

int setvbuf ( FILE * stream, char * buffer, int mode, size_t size );


默认日志信息输出到标准输出&#xff08;g_output &#61; defaultOutput、g_flush &#61; defaultFlush&#xff09;&#xff0c;也可以输出到文件&#xff0c;使用SetOutput、SetFlush 设置。



测试程序&#xff1a;
C&#43;&#43; 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 
#include 
#include 

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 &#61; ::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 &#61; 13;
    LOG_SYSERR << "syserr ...";
    //LOG_SYSFATAL<<"sysfatal ...";

    ::fclose(g_file);

    return 0;
}

程序执行后查看写入文件的内容&#xff1a;
simba&#64;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&#61;13) syserr ... - Log_test2.cc:35
simba&#64;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.

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

class StringPiece {
private:
  const char*   ptr_;
  int           length_;
....
C&#43;&#43; 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 &#61; memcmp(ptr_, x.ptr_, length_ < x.length_ ? length_ : x.length_); \
    return ((r auxcmp 0) || ((r &#61;&#61; 0) && (length_ cmp x.length_)));          \
  }
STRINGPIECE_BINARY_PREDICATE( < ,  < );
STRINGPIECE_BINARY_PREDICATE( <&#61; , < );
STRINGPIECE_BINARY_PREDICATE( >&#61; , > );
STRINGPIECE_BINARY_PREDICATE( > ,  > );
#undef STRINGPIECE_BINARY_PREDICATE

};

二、LogFile类

日志滚动条件
文件大小&#xff08;例如每写满1G换下一个文件&#xff09;
时间&#xff08;每天零点新建一个日志文件&#xff0c;不论前一个文件是否写满&#xff09;
一个典型的日志文件名
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_; // 开始记录日志时间&#xff08;调整到零时时间&#xff09;

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

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

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

// not thread safe
class LogFile::File : boost::noncopyable

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

测试程序&#xff1a;

LogFile_test.cc&#xff1a;

C&#43;&#43; 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 
#include 

boost::scoped_ptr g_logFile;

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

void flushFunc()
{
    g_logFile->flush();
// scoped_ptr 重载operator->&#xff0c;调用LogFile::flush(), 
//间接调用File::flush()&#xff0c;最后::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 &#61; "1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ ";

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

        usleep(1000);
    }
}

执行程序后查看创建的日志文件&#xff1a;

simba&#64;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 &#61; 196k&#xff0c;所以现在即使没有到另一个零时&#xff0c;因为文件大小已到上限&#xff0c;也会自动滚动文件。





参考&#xff1a;
muduo manual.pdf
《linux 多线程服务器编程&#xff1a;使用muduo c&#43;&#43;网络库》





推荐阅读
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 电话号码的字母组合解题思路和代码示例
    本文介绍了力扣题目《电话号码的字母组合》的解题思路和代码示例。通过使用哈希表和递归求解的方法,可以将给定的电话号码转换为对应的字母组合。详细的解题思路和代码示例可以帮助读者更好地理解和实现该题目。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 关于我们EMQ是一家全球领先的开源物联网基础设施软件供应商,服务新产业周期的IoT&5G、边缘计算与云计算市场,交付全球领先的开源物联网消息服务器和流处理数据 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • ***byte(字节)根据长度转成kb(千字节)和mb(兆字节)**parambytes*return*publicstaticStringbytes2kb(longbytes){ ... [详细]
  • 使用nodejs爬取b站番剧数据,计算最佳追番推荐
    本文介绍了如何使用nodejs爬取b站番剧数据,并通过计算得出最佳追番推荐。通过调用相关接口获取番剧数据和评分数据,以及使用相应的算法进行计算。该方法可以帮助用户找到适合自己的番剧进行观看。 ... [详细]
author-avatar
大头莎LALA
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有