日志文件
日志文件开发中必不可少的东西,他可以帮我们定位代码 bug,收集埋点数据和信息等等,Java 中有大名鼎鼎的 log4j ,而 cpp 中负责日志组件也有很多,log4cpp 算是其中一个较为有名的,在开始介绍之前,我们可以首先做一个关于日志的一个讨论,我们的需要日志有什么功能。
我们能想到最直接的就是可以输出到控制台上,用代码表示就是 printf 的函数。除了控制台,一些用户行为我们也可输出到文件中,以网站为例,那就是我们用户的登录日志我们是需要的。
如果只是输出控制台以及写入文件貌似并没有什么难度,即使不使用组件也可以完成,但是我们还有另外的要求,那就是我们希望我们自己可以选择输出什么内容,错误信息我们肯定要输出,但是我们是不是希望一些警告消息可以输出,因此我们希望日志文件可以进行分级,根据我们设定的级别来对日志进行选择性输出。
至此只是一个简单的日志文件设想,如果深入细节其实有更多可以讨论的,比方说我们上述进行开发时候要怎样进行组织,我们的级别应该在什么地方进行定义。
log4cpp
文件数据信息有一个分类器,一个分类下有多个 Appender ,每个 appender 通过 layout 实现输出。
关于这样的日志文件不再多讲,直接上代码
在日志文件设置上,我们们可以在代码中定义设置的各种参数,也可以通过读取配置文件来对其进行设置,首先就是在代码中进行设置。
关于 log4cpp 的头文件可以对源码进行编译即可,当然其中可以会有错误
代码语言:c 复制StringUtil::vform(const char*, __va_list_tag*)’:
StringUtil.cpp:45:23: error: incompatible types in assignment of ‘__va_list_tag*’ to ‘va_list {aka __va_list_tag [1]}’
args_copy = args;
^
Makefile:615: recipe for target 'StringUtil.lo' failed
make[1]: *** [StringUtil.lo] Error 1
make[1]: Leaving directory '/home/germer/Desktop/log4cpp/src'
Makefile:552: recipe for target 'all-recursive' failed
make: *** [all-recursive] Error 1
如果编译过程中出现上述错误,只需要将 StringUtil.cpp 文件中 arg_copy = args 换成 va_copy(arg_copy, args) 或者直接注释掉就可以编译成功。
代码语言:c 复制#include "log4cpp/Category.hh"
#include "log4cpp/FileAppender.hh"
#include "log4cpp/BasicLayout.hh"
int main(int argc, char* argv[])
{
log4cpp::Layout *layout = new log4cpp::BasicLayout();
log4cpp::Appender *appender = new log4cpp::FileAppender("FileAppender", "./test_log4cpp.log"); //声明一个新的日志文件
appender->setLayout(layout); //appender 设置 layout
log4cpp::Category &warn_log = log4cpp::Category::getInstance("ge3m0r"); //声明一个分类器
warn_log.setAdditivity(false);
/*每个Category都有⼀个additivity属性,该属性默认值为true。
如果值为true,则该Category的Appender包含了⽗Category的Appender。
如果值为false,则该Category的Appender取代了⽗Category的Appender*/
warn_log.setAppender(appender); //设置该分类的 appender
warn_log.setPriority(log4cpp::Priority::WARN); //设置优先级
warn_log.info("Program info which cannot be wirten"); //输出
warn_log.debug("This debug message will fail to write");
warn_log.alert("Alert info");
warn_log.log(log4cpp::Priority::WARN, "This will be a logged waring");
log4cpp::Priority::PriorityLevel priority;
bool this_is_critical = true;
if(this_is_critical)
priority = log4cpp::Priority::CRIT;
else
priority = log4cpp::Priority::DEBUG;
warn_log.log(priority, "Importance depends on context");
warn_log.critStream() << "This will show up << as "<< 1 << " critical message";
// clean up and flush all appenders
log4cpp::Category::shutdown();
return 0;
}
log4cpp 设置了 10 种类型的日志
代码语言:c 复制typedef enum {
EMERG = 0,
FATAL = 0,
ALERT = 100,
CRIT = 200,
ERROR = 300,
WARN = 400,
NOTICE = 500,
INFO = 600,
DEBUG = 700,
NOTSET = 800
} PriorityLevel;
其中设置的数组越大,输出的日志信息越详细,当然在开发中我们既需要考虑日志的详细程度用来获取足够信息又需要保证不会因为日志输出影响我们呢系统性能。
从上述可以看出 log4cpp 的内容相对来说较为简单,在对象和方法上分为三大类,分别是 Category 类以及跟他相关的方法,appender 跟他相关的类和方法,layout 和他相关的类和方法。
从上述代码可以看出,我们在 Category 上设置级别,并使用其添加 appender 然后输出相关日志内容。
而 appender 负责日志的类型和输出方式。可以在其上添加相关的内容,代码中是将输出到相关文件中。
layout 是一个布局,负责日志输出的格式,其中我们可以自定义,也可以让组件本身进行限制。
配置文件加代码
代码语言:c 复制# a simple test config
#定义了3个category sub1, sub2, sub1.sub2
log4j.rootCategory=DEBUG, rootAppender
log4j.category.sub1=,A1
log4j.category.sub2=INFO
log4j.category.sub1.sub2=ERROR, A2
# 设置sub1.sub2 的additivity属性
log4j.additivity.sub1.sub2=false
#定义rootAppender类型和layout属性
log4j.appender.rootAppender=org.apache.log4j.ConsoleAppender
log4j.appender.rootAppender.layout=org.apache.log4j.BasicLayout
#定义A1的属性
log4j.appender.A1=org.apache.log4j.FileAppender
log4j.appender.A1.filename=A1.log
log4j.appender.A1.layout=org.apache.log4j.SimpleLayout
#定义A2的属性
log4j.appender.A2=org.apache.log4j.ConsoleAppender
log4j.appender.A2.layout=org.apache.log4j.PatternLayout
#log4j.appender.A2.layout.ConversionPattern=The message '%m' at time%d%n
log4j.appender.A2.layout.ConversionPattern=%d %m %n
上述是一个简单的配置文件,使用的 conf 格式文件,设置了根的分类,以及子分类。代码如下:
代码语言:c 复制#include "log4cpp/Category.hh"
#include "log4cpp/PropertyConfigurator.hh"
int main(int argc, char* argv[])
{
try{
log4cpp::PropertyConfigurator::configure("./test_log4cpp2.conf"); //读取配置文件
}catch(log4cpp::ConfigureFailure& f){
std::cout<<" Configure Problem"<<f.what() << std::endl;
return -1
}
log4cpp::Category& root = log4cpp::Category::getRoot(); //获取 categroy ,因为配置文件已经设置了 appender 和 layout 所以可以直接使用来进行日志输出
log4cpp::Category& sub1 = log4cpp::Category::getInstance(std::string("sub1"));
log4cpp::Category& sub3 = log4cpp::Category::getInstance(std::string("sub1.sub2"));
sub1.info("This is some info");
sub1.alert("A warning");
sub3.debug("This debug message will fail to write");
sub3.alert("All hands abandon ship");
sub3.critStream << "This will show up << as"<< 1 << " critical message";
sub3 << log4cpp::Priority::ERROR << "And this will be an error";
sub3.log(log4cpp::Priority::WARN, "This will be a logged warning");
return 0;
}
log4cpp 整体来说使用的话比较简单,并没有太多难点,后边有机会继续会介绍 log4cplus 这个从log4j 改过来的组件。