【C++】POCO学习总结(十七):日志系统(级别、通道、格式化、记录流)

【C++】郭老二博文之:C++目录

1、Poco::Message 日志消息

1.1 说明

所有日志消息都在Poco::Message对象中存储和传输。

头文件:#include "Poco/Message.h"

一条消息包含如下内容:优先级、来源、一个文本、一个时间戳、进程和线程标识符、可选参数(名称-值对)

1.2 消息优先级

POCO定义了8个消息优先级:

  • PRIO_FATAL(最高优先级)
  • PRIO_CRITICAL
  • PRIO_ERROR
  • PRIO_WARNING
  • PRIO_NOTICE
  • PRIO_INFORMATION
  • PRIO_DEBUG
  • PRIO_TRACE(最低优先级)

设置、获取优先级

  • void setPriority(Priority prio)
  • Priority getPriority() const

1.3 消息来源

消息来源:一般用生成消息的类的名称,或者产生消息的子系统的名称

  • void setSource(const std::string& source)
  • const std::string& getSource() const

1.4 消息内容

要记录的实际消息。对格式、长度等没有要求。可以在出现在日志目的地之前由格式化程序修改

  • void setText(const std::string& text)
  • const std::string& getText() const

1.5 时间戳

创建消息的日期和时间,精度可达微秒级。

由Poco::Message的构造函数自动初始化为当前日期和时间。

  • void setTime(const Timestamp& time)
  • const Timestamp& getTime() const

1.6 进程和线程标识符

进程标识符(PID)是一个长整型值,用于存储系统的进程ID。

线程标识符(TID)也是一个长整型值,存储当前线程的序列号。

进程标识符、线程标识符和线程名称在Poco::Message的构造函数中初始化。

  • void setThread(const std::string& threadName)
  • const std::string& getThread() const
  • void setTid(long tid)
  • long getTid() const
  • void setPid(long pid)
  • long getPid() const

1.7 消息参数

消息可以存储任意数量的键值对。键和值可以是任意字符串。消息参数可以在格式化程序中引用。使用索引操作符访问消息参数。

2、Poco::Logger 日志记录

2.1 创建

Poco::Logger是日志框架的主要入口点。

头文件 #include "Poco/Logger.h"

应用程序使用Poco::Logger类的实例来生成日志消息。

每个记录器都有一个附加的通道,该通道将消息传递到它们的目的地(可能通过其他通道)。

每个记录器都有一个名称,它成为该记录器生成的所有消息的源。该名称是在创建时设置的,以后不可更改

2.2 优先级

日志记录器根据消息的优先级过滤消息。

只有优先级等于或高于日志记录器日志级别的消息才会被传播。

例如:

日志级别设置为PRIO_ERROR的日志记录器将只传播优先级为PRIO_ERROR、PRIO_CRITICAL或PRIO_FATAL的消息。

PRIO_WARNING或更低的值将被丢弃。

2.3 记录器的层次结构

根据它们的名字,记录器形成了一个树状的层次结构。

日志记录器的名称由一个或多个组件组成,以句点分隔。每个组件对应于前一个组件的名称空间中的一个名称。

一个特殊的记录器,根记录器,是一个以空字符串作为其名称的记录器。它构成了日志记录器层次结构的根。

日志记录器层次结构的最大深度没有限制。

2.4 继承层次结构

新创建的日志记录器从日志记录器层次结构中的祖先继承其日志级别和附加通道。

例如:在创建时,记录器"HTTPServer.RequestHandler.cgi"将从名为"HTTPServer.RequestHandler"的记录器继承其日志级别和附加通道。

一旦日志程序被完全创建,它就不再与它的祖先相关。换句话说,对日志记录器的日志级别或通道的更改对其已经存在的后代没有影响。

2.5 记录日志

  • void log(const Message& msg):如果消息的优先级大于或等于记录器的日志级别,则将消息传播到附加的通道。信息保持不变。

  • void log(const Exception& exc):创建并记录一条优先级为PRIO_ERROR的消息和异常的显示文本

创建并记录具有各自优先级和给定文本的消息:

  • void fatal(const std::string& text)
  • void critical(const std::string& text)
  • void error(const std::string& text)
  • void warning(const std::string& text)
  • void notice(const std::string& text)
  • void information(const std::string& text)
  • void debug(const std::string& text)
  • void trace(const std::string& text)

void dump(const std::string& text,const void* buffer, int length,Message::Priority prio = Message::PRIO_DEBUG)

用给定的优先级记录给定的消息。文本之后是给定缓冲区的十六进制转储。

2.6 日志级别

  • bool is(int level) const:如果日志记录器的日志级别至少为level,则返回true。- bool fatal() const

如果日志记录器的日志级别至少是相应的级别,则返回true:

  • bool critical() const
  • bool error() const
  • bool warning() const
  • bool notice() const
  • bool information() const
  • bool debug() const
  • bool trace() const

2.7 记录器

POCO管理一个全局的记录器映射。

不要自己创建记录器。要使用POCO提供记录器的引用。POCO会根据需要动态创建新的记录器。

static Logger& get(const std::string& name)

返回对具有给定名称的记录器的引用。如果没有会自动创建日志记录器。这是线程安全的。

2.8 示例

cpp 复制代码
#include "Poco/Logger.h"
using Poco::Logger;
int main(int argc, char** argv)
{
	Logger& logger = Logger::get("TestLogger");
	logger.information("This is an informational message");
	logger.warning("This is a warning message");
	return 0;
}

3、Poco::Channel 通道

3.1 说明

Poco::Channel的子类负责将消息(Poco::Message)传递到它们的目的地(例如,控制台或日志文件)。

每个Poco::Logger(它本身是Poco::Channel的子类)都连接到Poco::Channel。POCO提供了POCO::Channel的各种子类,这些子类将消息传递到控制台、日志文件或系统的日志记录设施。

可以定义你自己的通道类。

通道使用引用计数进行内存管理。

3.2 信道特性

通道可以支持任意数量的属性(键值对),这些属性用于配置通道。

属性是通过setProperty()成员函数设置的:

属性的值可以通过getProperty()获得:

这两个函数是在Poco::Configurable类中定义的,它是Poco::Channel的超类。

  • void setProperty(const std::string& name, const std::string& value)
  • std::string getProperty(const sdt::string& name)

3.3 Poco::ConsoleChannel 控制台通道

Poco::ConsoleChannel 是最基本的通道实现。

头文件:#include "Poco/ConsoleChannel.h"

Poco::ConsoleChannel 只是将接收到的任何消息的文本写入标准输出(std::clog)。

Poco::ConsoleChannel 没有可配置的属性。

Poco::ConsoleChannel 是根记录器的默认通道。

3.4 Poco::WindowsConsoleChannel Windows控制台

Poco::WindowsConsoleChannel类似于ConsoleChannel,但直接写入Windows控制台,而不是std::clog。

头文件:#include "Poco/WindowsConsoleChannel.h"

Poco::WindowsConsoleChannel 只是将接收到的任何消息的文本写入Windows控制台。

Poco::WindowsConsoleChannel 没有可配置的属性。

Poco::WindowsConsoleChannel 支持> UTF-8编码的文本。

3.5 Poco::NullChannel 空通道

Poco::NullChannel丢弃发送给它的所有消息。

头文件:#include "Poco/NullChannel.h"

3.6 Poco::SimpleFileChannel 最简日志文件

3.6.1 说明

Poco::SimpleFileChannel是编写日志文件最简单的方法。

消息的文本被附加到一个文件中,后跟一个换行符。

支持可选的简单日志文件轮换:在主日志文件和备日志文件之间轮流写,当一旦主日志文件超过一定大小,将创建一个备日志文件(如果已经存在,则将其清空)。如果备日志文件超过最大大小,则清空主日志文件,并继续记录主日志文件,依此类推。

3.6.2 属性

1)path:主日志文件路径。

2)secondaryPath:备日志文件路径。默认为path.1

  1. rotation:日志文件大小
  • never:默认的,一直记录在主日志文件
  • n:当文件大小超过n字节时轮换
  • n K:当文件大小超过n Kb时轮换
  • n M:当文件大小超过n Mb时轮换

3.6.3 示例

cpp 复制代码
#include "Poco/Logger.h"
#include "Poco/SimpleFileChannel.h"
#include "Poco/AutoPtr.h"
using Poco::Logger;
using Poco::SimpleFileChannel;
using Poco::AutoPtr;
int main(int argc, char** argv)
{
	AutoPtr<SimpleFileChannel> pChannel(new SimpleFileChannel);
	pChannel->setProperty("path", "sample.log");
	pChannel->setProperty("rotation", "2 K");
	Logger::root().setChannel(pChannel);
	Logger& logger = Logger::get("TestLogger"); // 继承根通道
	for (int i = 0; i < 100; ++i)
		logger.information("Testing SimpleFileChannel");
	return 0;
}

3.7 Poco::FileChannel 文件通道

3.7.1 说明

Poco::FileChannel提供了全面的日志文件支持。

头文件:#include "Poco/FileChannel.h"

消息的文本被附加到一个文件中,后跟一个换行符。

Poco::FileChannel 支持根据文件大小或时间间隔轮换日志文件。

Poco::FileChannel 支持归档日志文件的自动归档(不同的文件命名策略)、压缩(gzip)和清除(根据归档文件的时间或归档文件的数量)。

3.7.2 属性

1)path:日志文件路径

2)rotation:日志文件的轮换方式

  • never 不轮换
    • n:当文件大小超过n字节时轮换
  • n K:当文件大小超过n Kb时轮换
  • n M:当文件大小超过n Mb时轮换
  • [day,][hh:][mm]:在指定的星期/时间轮换
  • daily/weekly/monthly:每天/每周/每月
  • n hours/weeks/months:每n个小时/周/月

3)archive:归档日志文件的命名

  • number:一个自动递增的数字,从0开始,附加到日志文件名后。最新存档的文件总是0。
  • timestamp: 以 YYYYMMDDHHMMSS 格式的时间戳附加到日志文件中。

4)times:指定轮换时间是作为本地时间还是UTC时间处理。有效值为local和utc。

5)compress:自动压缩归档文件。指定true或false。

6)purgeAge:指定归档日志文件的最大保存时间。超过这个时间的文件将被清除。

7)purgeCount:指定归档日志文件的最大数目。如果达到这个数字,那么最旧的归档日志文件将被清除。

3.7.3 示例

cpp 复制代码
#include "Poco/Logger.h"
#include "Poco/FileChannel.h"
#include "Poco/AutoPtr.h"
using Poco::Logger;
using Poco::FileChannel;
using Poco::AutoPtr;
int main(int argc, char** argv)
{
	AutoPtr<FileChannel> pChannel(new FileChannel);
	pChannel->setProperty("path", "sample.log");
	pChannel->setProperty("rotation", "2 K");
	pChannel->setProperty("archive", "timestamp");
	Logger::root().setChannel(pChannel);
	Logger& logger = Logger::get("TestLogger"); // 继承根通道
	for (int i = 0; i < 100; ++i)
		logger.information("Testing FileChannel");
	return 0;
}

3.8 Poco::EventLogChannel Windows专用

Poco::EventLogChannel:仅在Windows NT平台上使用,记录Windows事件日志。

Poco::EventLogChannel将PocoFoundation.dll注册为Windows事件日志中的消息定义资源DLL。

当查看Windows事件日志时,事件查看器应用程序必须能够找到PocoFoundation.dll,否则日志消息将不会按预期显示。

3.9 Poco::SyslogChannel Linux专用

仅在Unix平台上可用的Poco::SyslogChannel将日志记录到本地Syslog守护进程。

Net库包含一个RemoteSyslogChannel类,它使用基于udp的Syslog协议与远程Syslog守护进程一起工作。

3.10 Poco::AsyncChannel 异步通道

Poco::AsyncChannel允许在单独的线程中运行通道。

将产生日志消息的线程与传递日志消息的线程解耦。

所有日志消息都存储在FIFO队列中。

一个单独的线程从队列中提取消息并将它们发送到另一个通道。

cpp 复制代码
#include "Poco/Logger.h"
#include "Poco/AsyncChannel.h"
#include "Poco/ConsoleChannel.h"
#include "Poco/AutoPtr.h"
using Poco::Logger;
using Poco::AsyncChannel;
using Poco::ConsoleChannel;
using Poco::AutoPtr;
int main(int argc, char** argv)
{
	AutoPtr<ConsoleChannel> pCons(new ConsoleChannel);
	AutoPtr<AsyncChannel> pAsync(new AsyncChannel(pCons));
	Logger::root().setChannel(pAsync);
	Logger& logger = Logger::get("TestLogger");
	for (int i = 0; i < 10; ++i)
		logger.information("This is a test");
	return 0;
}

3.11 Poco::SplitterChannel 分发通道

Poco::SplitterChannel将消息转发到一个或多个其他通道。

void addChannel(Channel* pChannel)为Poco::SplitterChannel添加一个新通道

cpp 复制代码
#include "Poco/Logger.h"
#include "Poco/SplitterChannel.h"
#include "Poco/ConsoleChannel.h"
#include "Poco/SimpleFileChannel.h"
#include "Poco/AutoPtr.h"
using Poco::Logger;
using Poco::SplitterChannel;
using Poco::ConsoleChannel;
using Poco::SimpleFileChannel;
using Poco::AutoPtr;
int main(int argc, char** argv)
{
	AutoPtr<ConsoleChannel> pCons(new ConsoleChannel);
	AutoPtr<SimpleFileChannel> pFile(new SimpleFileChannel("test.log"));
	AutoPtr<SplitterChannel> pSplitter(new SplitterChannel);
	pSplitter->addChannel(pCons);
	pSplitter->addChannel(pFile);
	Logger::root().setChannel(pSplitter);
	Logger::root().information("This is a test");
	return 0;
}

4、Poco::LogStream 日志流

Poco::LogStream为Logger提供了ostream接口。

流的所有特性都可用于格式化日志消息。

日志消息必须以std::endl(或CR或LF字符)结束。

可以设置消息的优先级

  • LogStream& priority(Message::Priority prio)
  • LogStream& fatal()
  • LogStream& critical()
  • LogStream& error()
  • LogStream& warning()
  • LogStream& notice()
  • LogStream& information()
  • LogStream& debug()
  • LogStream& trace
cpp 复制代码
#include "Poco/LogStream.h"
#include "Poco/Logger.h"
using Poco::Logger;
using Poco::LogStream;
int main(int argc, char** argv)
{
	Logger& logger = Logger::get("TestLogger");
	LogStream lstr(logger);
	lstr << "This is a test" << std::endl;
	return 0;
}

5、日志格式化

5.1 说明

Poco::FormattingChannel 和 Poco::Formatter 负责格式化日志消息。

在将消息传播到下一个通道之前,Poco::FormattingChannel 将它接收到的每个消息传递给 Poco::Formatter。

Formatter是所有格式化器类的基类。

与通道一样,格式化器也可以使用属性进行配置。

5.2 PatternFormatter

PatternFormatter根据printstyle打印模式格式化消息

cpp 复制代码
#include "Poco/ConsoleChannel.h"
#include "Poco/FormattingChannel.h"
#include "Poco/PatternFormatter.h"
#include "Poco/Logger.h"
#include "Poco/AutoPtr.h"
using Poco::ConsoleChannel;
using Poco::FormattingChannel;
using Poco::PatternFormatter;
using Poco::Logger;
using Poco::AutoPtr;
int main(int argc, char** argv)
{
	AutoPtr<ConsoleChannel> pCons(new ConsoleChannel);
	AutoPtr<PatternFormatter> pPF(new PatternFormatter);
	pPF->setProperty("pattern", "%Y-%m-%d %H:%M:%S %s: %t");
	AutoPtr<FormattingChannel> pFC(new FormattingChannel(pPF, pCons));
	Logger::root().setChannel(pFC);
	Logger::get("TestChannel").information("This is a test");
	return 0;
}

6、性能

创建消息需要花费一些时间(必须确定当前时间、当前进程ID和当前线程ID)。

创建一个有意义的消息可能需要更多的时间,因为字符串连接和数字格式等是必要的。

消息通常通过引用在通道链中传递。

特别的:formatingchannel和AsyncChannel创建消息的副本。

对于每个日志记录器,总是分别启用或禁用日志记录(或者更准确地说,设置日志级别),因此,一旦有了对日志记录器的引用,确定为特定日志记录器设置哪个日志级别是一个常数时间操作(简单的内联整数比较)。

获取对记录器的引用是一个具有对数复杂度的操作(基本上是std::map查找)。记录器的名称用作查找中的键,因此记录器名称的长度线性地影响查找时间(字符串比较)。然而,这一点可能可以忽略不计。

通常,对记录器(logger::get())的引用只在应用程序中获得一次。例如,在一个类中,可以在类的构造函数中获得对其日志记录器的引用,然后从此只使用该引用。

应该避免频繁调用Logger::get()。最好是对将要使用的每个记录器调用它一次,然后存储对记录器的引用以供以后使用。

日志性能取决于你要使用的通道。实际信道性能是高度依赖于系统的。

构造日志消息通常是一个耗时的操作,包括字符串创建、字符串连接、数字格式化等。

在这种情况下,使用is()、fatal()、critical()等方法,在构造消息之前检查消息是否会被记录是一个好主意。

还有一些宏在构造消息之前做检查:poco_fatal(msg), poco_critical(msg), poco_error(msg)

cpp 复制代码
if (logger.warning())
{
	std::string msg("This is a warning");
	logger.warning(msg);
}

等同于:poco_warning(logger, "This is a warning");

相关推荐
axxy20004 分钟前
leetcode之hot100---24两两交换链表中的节点(C++)
c++·leetcode·链表
若亦_Royi1 小时前
C++ 的大括号的用法合集
开发语言·c++
ragnwang4 小时前
C++ Eigen常见的高级用法 [学习笔记]
c++·笔记·学习
lqqjuly7 小时前
特殊的“Undefined Reference xxx“编译错误
c语言·c++
冰红茶兑滴水8 小时前
云备份项目--工具类编写
linux·c++
刘好念8 小时前
[OpenGL]使用 Compute Shader 实现矩阵点乘
c++·计算机图形学·opengl·glsl
酒鬼猿9 小时前
C++进阶(二)--面向对象--继承
java·开发语言·c++
姚先生979 小时前
LeetCode 209. 长度最小的子数组 (C++实现)
c++·算法·leetcode
小王爱吃月亮糖10 小时前
QT开发【常用控件1】-Layouts & Spacers
开发语言·前端·c++·qt·visual studio
aworkholic10 小时前
opencv sdk for java中提示无stiching模块接口的问题
java·c++·opencv·jni·opencv4android·stiching