精通
英语
和
开源
,
擅长
开发
与
培训
,
胸怀四海
第一信赖
锐英源精品开源,禁止转载和任何形式的非法内容使用,违者必究
Plog用于生成日志内容,从www.codeproject.com上下载。把它的目录加入到项目里,可以直接使用,目录里是头文件。它用流的方式来生成日志,操作方便。日志内容里还有写日志的函数信息,很直观。跨线程使用也支持,我在主线程和子线程里都进行了日志输出,可以正常完成。Plog可以指定文件组来当做输出目标,当一个文件满了或占用时,自动生成新的日志文件,此功能体现了Plog的全面性。
Plog is a C++ logging library that is designed to be as simple, small and flexible as possible. It is created as an alternative to existing large libraries and provides some unique features as CSV log format and automatic 'this' pointer capture. Plog是一个C ++ 日志库,设计目标是尽可能简单、小巧和灵活。它是作为现有大型库的替代创建的,并提供一些独特的功能,如 CSV 日志格式 和 自动”this”指针捕获。
Here is a minimal hello log sample: 这是一个最小的hello 日志示例:
#include <plog/log.h> // Step1: include the header. int main() { plog::init(plog::debug, "Hello.txt"); // Step2: initialize the logger. // Step3: write log messages using a special macro. // There are several log macros, use the macro you liked the most. logD << "Hello log!"; // short macro log_DEBUG << "Hello log!"; // long macro log(plog::debug) << "Hello log!"; // function-style macro return 0; }
And its output:
2015-05-18 23:12:43.921 DEBUG [21428] [main@13] Hello log!
2015-05-18 23:12:43.968 DEBUG [21428] [main@14] Hello log!
2015-05-18 23:12:43.968 DEBUG [21428] [main@15] Hello log!
To start using plog you need to make 3 simple steps. 3个简单步骤
At first your project needs to know about plog. For that you have to: 首先,您的项目需要了解p log。为此你必须:
The next step is to initialize the Logger. This is done by the following plog::init function: 下一步是初始化 Logger。这是通过以下功能完成的:plog::init
maxSeverity is the logger severity upper limit. All log messages have its own severity and if it is higher than the limit those messages are dropped. Plog defines the following severity levels: maxSeverity是logger严重性上限。所有日志消息都有自己的严重性,如果高于限制,则丢弃这些消息。P log定义以下严重性级别:
enum Severity
{
none = 0,
fatal = 1,
error = 2,
warning = 3,
info = 4,
debug = 5,
verbose = 6
};
The log format is determined automatically by fileName file extension: 该日志格式由fileName 文件的扩展名自动确定 :
The rolling behavior is controlled by maxFileSize and maxFiles parameters: 切换行为由maxFileSize 和 maxFiles 参数控制 :
If one of them is zero then log rolling is disabled.
如果其中一个为零,则禁用日志文件切换。
Sample:
plog::init(plog::warning, "c:\\logs\\log.csv", 1000000, 5);
Here the logger is initialized to write all messages with up to warning severity to a file in csv format. Maximum log file size is set to 1'000'000 bytes and 5 log files are kept. 这里初始化logger以将具有高达警告严重性的所有消息写入csv格式的文件。最大日志文件大小设置为1'000'000字节,并保留5个日志文件。
logging is performed with the help of special macros. A log message is constructed using stream output operators <<. Thus it is type-safe and extendable in contrast to a format string output. logging是在特殊宏的帮助下执行的。日志消息是使用流输出操作符构成 <<。因此,与格式字符串输出相比,它是类型安全的和可扩展的。
This is the most used type of logging macros. They do unconditional logging. 这是最常用的日志宏类型。他们是无条件的日志。
Long macros:
log_VERBOSE << "verbose";
log_DEBUG << "debug";
log_INFO << "info";
log_WARNING << "warning";
log_ERROR << "error";
log_FATAL << "fatal";
Short macros:
logV << "verbose";
logD << "debug";
logI << "info";
logW << "warning";
logE << "error";
logF << "fatal";
Function-style macros:
log(severity) << "msg";
These macros are used to do a conditional logging. They accept a condition as a parameter and perform logging if the condition is true. 这些宏用于执行条件日志。它们接受条件作为参数,并在条件为真时执行写日志。
Long macros:
log_VERBOSE_IF(cond) << "verbose";
log_DEBUG_IF(cond) << "debug";
log_INFO_IF(cond) << "info";
log_WARNING_IF(cond) << "warning";
log_ERROR_IF(cond) << "error";
log_FATAL_IF(cond) << "fatal";
Short macros:
logV_IF(cond) << "verbose";
logD_IF(cond) << "debug";
logI_IF(cond) << "info";
logW_IF(cond) << "warning";
logE_IF(cond) << "error";
logF_IF(cond) << "fatal";
Function-style macros:
log_IF(severity, cond) << "msg";
In some cases there is a need to perform a group of actions depending on the current logger severity level. There is a special macro for that. It helps to minimize performance penalty when the logger is inactive. 在某些情况下,需要根据当前日志严重性级别执行一组操作。有一个特殊的宏。当日志没激活使用时,它有助于最小化性能损失。
IF_log(severity)
Sample:
IF_log(plog::debug) // we want to execute the following statements only at debug severity (and higher)
{
for (int i = 0; i < vec.size(); ++i)
{
logD << "vec[" << i << "]: " << vec[i];
}
}
It is possible to set the maximum severity not only at the logger initialization time but at any time later. There are special accessor methods: 可以不仅在日志初始化时间而且在以后的任何时间设置最大严重性。有特殊的访问方法:
Severity logger::getMaxSeverity() const;
logger::setMaxSeverity(Severity severity);
To get the logger use plog::get function:
logger* get();
Sample:
plog::get()->setMaxSeverity(plog::debug);
Non-typical log cases require the use of custom initialization. It is done by the following plog::init function: 非典型的日志案例需要使用自定义初始化。它由以下功能完成:plog::init
logger& init(Severity maxSeverity = none, IAppender* appender = NULL);
You have to construct an Appender parameterized with a Formatter and pass it to the plog::init function.
Note: a lifetime of the appender should be static!
您必须构造一个 使用Formatter参数化 的 Appender并将其传递给函数plog::init
注意:appender的生命周期应该是静态的!
Sample:
static plog::ConsoleAppender<plog::TxtFormatter> consoleAppender;
plog::init(plog::debug, &consoleAppender);
It is possible to have multiple Appenders within a single Logger. In such case log message will be written to all of them. Use the following method to accomplish that: 在单个 Logger中可以有多个 Appender。在这种情况下,所有这些追加器都会写日志消息。使用以下方法来实现:
logger& logger::addAppender(IAppender* appender);
Sample:
static plog::RollingFileAppender<plog::CsvFormatter> fileAppender("MultiAppender.csv", 8000, 3); // Create the 1st appender.
static plog::ConsoleAppender<plog::TxtFormatter> consoleAppender; // Create the 2nd appender.
plog::init(plog::debug, &fileAppender).addAppender(&consoleAppender); // Initialize the logger with the both appenders.
Here the logger is initialized in the way when log messages are written to both a file and a console. 这里logger以日志消息写入文件和控制台的方式初始化。
Multiple Loggers can be used simultaneously each with their own separate configuration. The Loggers differ by their instance number (that is implemented as a template parameter). The default instance is zero. Initialization is done by the appropriate template plog::initfunctions: 多个 Loggers 可以同时使用,每个都有自己独立的配置。日志之间区别在于它们的实例数目不同(用模板参数来实现)。默认实例为零。初始化由适当的模板函数完成:plog::init
logger<instance>& init<instance>(...);
To get a logger use plog::get function (returns NULL if the logger is not initialized): 获取日志使用plog::get函数(如果日志未初始化则返回 NULL)
logger<instance>* get<instance>();
All logging macros have their special versions that accept an instance parameter. These kind of macros have an underscore at the end: 所有日志宏都有它们接受实例参数的特殊版本。这些宏最后有一个下划线:
logD_(instance) << "debug";
logD_IF_(instance, condition) << "conditional debug";
IF_log_(instance, severity)
Sample:
enum // Define log instances. Default is 0 and is omitted from this enum.
{
Secondlog = 1
};
int main()
{
plog::init(plog::debug, "MultiInstance-default.txt"); // Initialize the default logger instance.
plog::init<Secondlog>(plog::debug, "MultiInstance-second.txt"); // Initialize the 2nd logger instance.
// Write some messages to the default log.
logD << "Hello default log!";
// Write some messages to the 2nd log.
logD_(Secondlog) << "Hello second log!";
return 0;
}
A Logger can work as an Appender for another Logger. So you can chain several loggers together. This is useful for streaming log messages from a shared library to the main application binary. 一个日志实例可作为其它日志追加器。因此,您可以将多个日志链接在一起。这对于将日志消息从共享库流式传输到主应用程序二进制文件非常有用。
Sample:
// shared library
// Function that initializes the logger in the shared library.
extern "C" void EXPORT initialize(plog::Severity severity, plog::IAppender* appender)
{
plog::init(severity, appender); // Initialize the shared library logger.
}
// Function that produces a log message.
extern "C" void EXPORT foo()
{
logI << "Hello from shared lib!";
}
// main app
// Functions imported form the shared library.
extern "C" void initialize(plog::Severity severity, plog::IAppender* appender);
extern "C" void foo();
int main()
{
plog::init(plog::debug, "ChainedApp.txt"); // Initialize the main logger.
logD << "Hello from app!"; // Write a log message.
initialize(plog::debug, plog::get()); // Initialize the logger in the shared library. Note that it has its own severity.
foo(); // Call a function from the shared library that produces a log message.
return 0;
}
Architecture架构
Overview
Plog is designed to be small but flexible, so it prefers templates to interface inheritance. All main entities are shown on the following UML diagram: Plog设计得小而灵活,因此它更喜欢模板来继承接口。所有主要实体都显示在以下UML图中:
There are 5 functional parts:
logger
Logger is a center object of the whole logging system. It is a singleton and thus it forms a known single entry point for configuration and processing log data. Logger can act as Appender for another Logger because it implements IAppenderinterface. Also there can be several independent loggers that are parameterized by an integer instance number. The default instance is 0. Logger 是整个日志系统的中心对象。它是一个单例,因此它形成了一个已知的单一入口点,用于配置和处理日志数据。 logger 可以作为 另一个Logger的Appender, 因为它实现了 接口。此外,可以有几个独立的日志gers,它们由整数实例编号参数化。默认实例为0。IAppender
template<int instance>
class logger : public util::Singleton<logger<instance> >, public IAppender
{
public:
logger(Severity maxSeverity = none);
logger& addAppender(IAppender* appender);
Severity getMaxSeverity() const;
void setMaxSeverity(Severity severity);
bool checkSeverity(Severity severity) const;
virtual void write(const Record& record);
void operator+=(const Record& record);
};
Record
Record stores all log data. It includes:
Also Record has a number of overloaded stream output operators to construct a message. 具有多个重载流输出操作符,用于构造消息
class Record
{
public:
Record(Severity severity, const char* func, size_t line, const void* object);
//////////////////////////////////////////////////////////////////////////
// Stream output operators
Record& operator<<(char data);
Record& operator<<(wchar_t data);
template<typename T>
Record& operator<<(const T& data);
//////////////////////////////////////////////////////////////////////////
// Getters
const util::Time& getTime() const;
Severity getSeverity() const;
unsigned int getTid() const;
const void* getObject() const;
size_t getLine() const;
const util::nstring getMessage() const;
std::string getFunc() const;
};
Formatter
Formatter is responsible for formatting log data from Record into various string representations (binary forms can be used too). There is no base class for formatters, they are implemented as classes with static functions formatand header:
Formatter 负责将Record中的日志数据 格式化 为各种字符串表示形式(也可以使用二进制形式)。格式化程序没有基类,它们实现为具有静态函数的类,这些静态函数为:format和header
class Formatter
{
public:
static util::nstring header();
static util::nstring format(const Record& record);
};
TxtFormatter
This is a classic log format available in almost any log library. It is good for console output and it is easy to read without any tools.
这是几乎所有日志中都可用的经典日志格式。它适用于控制台输出,无需任何工具即可轻松阅读。
2014-11-11 00:29:06.245 FATAL [4460] [main@22] fatal
2014-11-11 00:29:06.261 ERROR [4460] [main@23] error
2014-11-11 00:29:06.261 INFO [4460] [main@24] info
2014-11-11 00:29:06.261 WARN [4460] [main@25] warning
2014-11-11 00:29:06.261 DEBUG [4460] [main@26] debug
2014-11-11 00:29:06.261 INFO [4460] [main@32] This is a message with "quotes"!
2014-11-11 00:29:06.261 DEBUG [4460] [Object::Object@8]
2014-11-11 00:29:06.261 DEBUG [4460] [Object::~Object@13]
CsvFormatter
This is the most powerful log format. It can be easily read without any tools (but slighlty harder than TXT format) and can be heavily analyzed if it is opened with a CSV-aware tool (like Excel). One rows can be highlighted according to their cell values, another rows can be hidden, columns can be manipulated and you can even run SQL queries on log data! This is a recommended format if logs are big and require heavy analysis. Also 'this' pointer is shown so object instances can be told apart.
这是最强大的日志格式。它可以在没有任何工具的情况下轻松读取(但比TXT格式更难 ),并且如果使用支持CSV的工具(如Excel)打开它,则可以进行大量分析。可以根据单元格值突出显示一行,可以隐藏另一行,可以操作列,甚至可以对日志数据运行SQL查询!如果log很大并需要大量分析,这是推荐的格式。此外,还显示了“this”指针,因此可以将对象实例分开。
Date;Time;Severity;TID;This;Function;Message
2014/11/14;15:22:25.033;FATAL;4188;00000000;main@22;"fatal"
2014/11/14;15:22:25.033;ERROR;4188;00000000;main@23;"error"
2014/11/14;15:22:25.033;INFO;4188;00000000;main@24;"info"
2014/11/14;15:22:25.033;WARN;4188;00000000;main@25;"warning"
2014/11/14;15:22:25.048;DEBUG;4188;00000000;main@26;"debug"
2014/11/14;15:22:25.048;INFO;4188;00000000;main@32;"This is a message with ""quotes""!"
2014/11/14;15:22:25.048;DEBUG;4188;002EF4E3;Object::Object@8;
2014/11/14;15:22:25.048;DEBUG;4188;002EF4E3;Object::~Object@13;
Note: message size is limited to 32000 chars.
FuncMessageFormatter
This format is designed to be used with appenders that provide their own timestamps (like AndroidAppender or linux syslog facility).
此格式旨在与提供自己时间戳的appender一起使用(如 AndroidAppender 或linux sys log facility)。
main@22: fatal
main@23: error
main@24: info
main@25: warning
main@26: debug
main@32: This is a message with "quotes"!
Object::Object@8:
Converter
Converter is responsible for conversion of Formatter output data to a raw buffer (represented as std::string). It is used by RollingFileAppender to perform a conversion before writing to a file. There is no base class for converters, they are implemented as classes with static functions convert and header:
Converter 负责将Formatter 输出数据转换为 原始缓冲区(表示为 std::string)。RollingFileAppender使用它 在写入文件之前执行转换。转换器没有基类,它们被实现为具有静态函数的类, 静态函数为:convert和 header:
class Converter
{
public:
static std::string header(const util::nstring& str);
static std::string convert(const util::nstring& str);
};
UTF8Converter
UTF8Converter is the only converter available in plog out of the box. It converts string data to UTF-8 with BOM.
UTF8Converter 是plog直接即可使用的唯一转换器。它使用BOM将字符串数据转换为UTF-8。
Appender
Appender uses Formatter and Converter to get a desired representation of log data and outputs (appends) it to a file/console/etc. All appenders must implement IAppenderinterface (the only interface in plog):
Appender 使用 Formatter 和 Converter 来获得所需的日志数据表示,并将其输出(附加)到文件/控制台/等。所有appender都必须实现 IAppender接口(p log中唯一的接口):
class IAppender
{
public:
virtual ~IAppender();
virtual void write(const Record& record) = 0;
};
RollingFileAppender
This appender outputs log data to a file with rolling behaviour. As template parameters it accepts both Formatter and Converter.
此appender将日志数据输出到具有切换行为的文件。作为模板参数,它接受 Formatter 和 Converter。
RollingFileAppender<Formatter, Converter>::RollingFileAppender(const char* fileName, size_t maxFileSize = 0, int maxFiles = 0);
If maxFileSizeor maxFilesis 0 then rolling behaviour is turned off.
The sample file names produced by this appender:
Note: the lowest maxFileSize is 1000 bytes.
ConsoleAppender
This appender outputs log data to stdout. As a template parameter it accepts Formatter.
ConsoleAppender<Formatter>::ConsoleAppender();
AndroidAppender
AndroidAppender uses Android logging system to output log data. It can be viewed with logcat or in a logwindow of Android IDEs. As a template parameter this appender accepts Formatter (usually FuncMessageFormatter).
AndroidAppender<Formatter>::AndroidAppender(const char* tag);
Miscellaneous notes
Lazy stream evaluation
log messages are constructed using lazy stream evaluation. It means that if a log message will be dropped (because of its severity) then stream output operators are not executed. Thus performance penalty of unprinted log messages is negligible.
logD << /* the following statements will be executed only when the logger severity is debug or higher */ ...
Stream improvements over std::ostream
Stream output in plog has several improvements over the standard std::ostream:
Automatic 'this' pointer capture
'This' pointer is captured automatically to log data and can be printed by CsvFormatter. Unfortunately this feature is supported only on msvc 2010 and higher.
Headers to include
The core plog functionality is provided by inclusion of plog/log.h file. Extra components require inclusion of corresponding extra headers after plog/log.h.
Unicode
Plog is unicode aware and wide string friendly. All messages are converted to a system native char type:
Also char is treated as:
Internally plog uses nstringand nstringstream('n' for native) that are defined as:
#ifdef _WIN32
typedef std::wstring nstring;
typedef std::wstringstream nstringstream;
#else
typedef std::string nstring;
typedef std::stringstream nstringstream;
#endif
By default all log files are stored in UTF-8 with BOM thanks to UTF8Converter.
Note: on Android wide string support in plog is disabled.
Performance
Plog is not using any asynchronous techniques so it may slow down your application on large volumes of logmessages.
Producing a single log message takes the following amount of time:
CPU |
OS |
Time per a log call, microsec |
AMD Phenom II 1055T @3.5GHz |
Windows 2008 R2 |
12 |
AMD Phenom II 1055T @3.5GHz |
Linux Mint 17.1 |
8 |
Intel Core i3-3120M @2.5GHz |
Windows 2012 R2 |
25 |
Intel Core i5-2500K @4.2GHz |
Windows 2008 R2 |
8 |
Intel Atom N270 @1.6GHz |
Windows 2003 |
68 |
Assume 20 microsec per a log call then 500 log calls per a second will slow down an application by 1%. It is acceptable for the most use cases.
Extending
Plog can be easily extended to support new:
Custom data type
To output a custom data type to a log message implement the following function:
namespace plog
{
Record& operator<<(Record& record, const MyType& t);
}
Custom appender
A custom appender must implement IAppender interface. Also it may accept Formatter and Converter as template parameters however this is optional.
namespace plog
{
template<class Formatter>
class MyAppender : public IAppender
{
public:
virtual void write(const Record& record);
};
}
Custom formatter
A formatter that is compatible with existing appenders must be a class with 2 static methods:
namespace plog
{
class MyFormatter
{
public:
static util::nstring header();
static util::nstring format(const Record& record);
};
}
Custom converter
A converter must be a class with 2 static methods:
namespace plog { class MyConverter {
public:
static std::string header(const util::nstring& str);
static std::string convert(const util::nstring& str);
};
}