服务器的模块中,日志系统是必不可少的
服务器的程序特点: 没有终端 守护进程 7 * 24小时
如何去设计一个日志系统?
一条日志的产生,到最终被保存下来的全过程
日志来源:一个服务器的程序是有多个模块,需要知道一条日志由哪一个模块产生的
日志布局: 对一条日志的格式进行控制
日志过滤器: 不同的用户看到不同类型的日志信息 通过日志的优先级
日志目的地: 终端、文件、远程服务器
日志分类
- 系统日志
- 业务日志
已经有的日志系统
log4cpp
1. 安装
$ tar xzvf log4cpp-1.1.4rc3.tar.gz
$ cd log4cpp
$ ./configure
$ make
$ sudo make install
$ sudo ldconfig
2.安装后验证是否安装成功
测试程序
#include "log4cpp/Category.hh"
#include "log4cpp/Appender.hh"
#include "log4cpp/FileAppender.hh"
#include "log4cpp/OstreamAppender.hh"
#include "log4cpp/Layout.hh"
#include "log4cpp/BasicLayout.hh"
#include "log4cpp/Priority.hh"
int main(int argc, char** argv) {
log4cpp::Appender *appender1 = new log4cpp::OstreamAppender("console", &std::cout);
appender1->setLayout(new log4cpp::BasicLayout());
log4cpp::Appender *appender2 = new log4cpp::FileAppender("default", "program.log");
appender2->setLayout(new log4cpp::BasicLayout());
log4cpp::Category& root = log4cpp::Category::getRoot();
root.setPriority(log4cpp::Priority::WARN);
root.addAppender(appender1);
log4cpp::Category& sub1 = log4cpp::Category::getInstance(std::string("sub1"));
sub1.addAppender(appender2);
// use of functions for logging messages
root.error("root error");
root.info("root info");
sub1.error("sub1 error");
sub1.warn("sub1 warn");
// printf-style for logging variables
root.warn("%d + %d == %s ?", 1, 1, "two");
// use of streams for logging messages
root << log4cpp::Priority::ERROR << "Streamed root error";
root << log4cpp::Priority::INFO << "Streamed root info";
sub1 << log4cpp::Priority::ERROR << "Streamed sub1 error";
sub1 << log4cpp::Priority::WARN << "Streamed sub1 warn";
// or this way:
root.errorStream() << "Another streamed error";
return 0;
}
找不到动态库
成功:
Category
日志来源: Category 日志是由Category对象产生的
每一次记录一条日志是由其成员函数进行的,表示该条日志的优先级是debug/info/…
Category对象是可以设置自己的优先级,
每一条日志也有自己的优先级
当每一条日志的优先级大于等于Category的优先级时,该条日志才会被记录
设置多个目的地:
Category具有层级概念
root设置的优先级会被子Category继承
Priority
优先级的值越小的,优先级别越高
值越大的,优先级别越低
参考: Linux 环境下 syslog函数
Layout
void setConversionPattern(const std::string & conversionPattern)
patternLayout 支持以下格式字符集:
%%-单个百分号
%c - 类别
%d - 日期\n 日期格式:日期格式字符可以用于
例如,%d{%h:%m:%s,%l} 或 %d{%d %m %y 7%y %h:
使用以下格式:“Wed Jan 02 02:03:55 1980”.the date forn
function strftime 的函数,增加了 1 个。添加的是
%m 消息
%n 特定于平台的行分隔符
%p 优先级
%r - 自创建此布局以来的毫秒数。
%R - 自 1970 年 1 月 1 日以来的秒数
%u-自 PE 进程启动以来的 clock 时钟周期
%x - NDC
%t 线程名称
默认情况下,patternLayout 的 conversionPattern 设置为 “%m%n”。
#include<iostream>
#include<log4cpp/Category.hh>
#include<log4cpp/OstreamAppender.hh>
#include<log4cpp/FileAppender.hh>
#include<log4cpp/Priority.hh>
#include<log4cpp/PatternLayout.hh>
using std::cout;
using std::endl;
using namespace log4cpp;
void test0()
{
PatternLayout * ptnLayout=new PatternLayout();
ptnLayout->setConversionPattern("%d %c [%p] %m%n");
PatternLayout * ptnLayout2=new PatternLayout();
ptnLayout2->setConversionPattern("%d %c [%p] %m%n");
OstreamAppender * pOsApp=new OstreamAppender("console",&cout);
pOsApp->setLayout(ptnLayout);
FileAppender * pFileApp=new FileAppender("fileApp","wd.log");
pFileApp->setLayout(ptnLayout2);
Category & mycat=Category::getRoot().getInstance("salesDapart");
mycat.setPriority(Priority::DEBUG);
mycat.addAppender(pOsApp);
mycat.addAppender(pFileApp);
mycat.emerg("this is an emerg message");
mycat.fatal("this is a fatal message");
mycat.alert("this is an alert message");
mycat.crit("this is a crit message");
mycat.error("this is an error message");
mycat.warn("this an error message");
mycat.info("this is an info message");
mycat.notice("this is a notice message");
mycat.debug("this a debug message");
Category::shutdown();
}
int main()
{
test0();
}
g++ log4cpptest.cc -llog4cpp -lpthread//顺序不能反,不然会编译出错
Appender
一个目的地对应一个日志布局
void setLayout (Layout *layout)//一个目的地对应一个日志布局
OstreamAppender (const std::string &name, std::ostream *stream)
FileAppender (const std::string &name, const std::string &fileName, bool append=true, mode_t mode=00644)//append:以追加方式写入文件
bug
回卷文件
目的: 节省空间
RollingFileAppender (const std::string &name, const std::string &fileName, size_t maxFileSize=10 *1024 *1024, unsigned int maxBackupIndex=1, bool append=true, mode_t mode=00644)
//name:文件的名字 //maxFileSize:一个文件的最大字节数 //maxBackupIndex:回卷的文件数
比如一个服务器设定系统日志文件占据的空间的就限定为1G,1个日志文件最多只能占据的空间是100M,日志文件就有10个,当10个文件填满之后,最新的数据用来覆盖最旧的数据
#include<iostream>
#include<log4cpp/Category.hh>
#include<log4cpp/OstreamAppender.hh>
#include<log4cpp/FileAppender.hh>
#include<log4cpp/RollingFileAppender.hh>
#include<log4cpp/Priority.hh>
#include<log4cpp/PatternLayout.hh>
using std::cout;
using std::endl;
using namespace log4cpp;
void test0()
{
PatternLayout * ptnLayout=new PatternLayout();
ptnLayout->setConversionPattern("%d %c [%p] %m%n");
PatternLayout * ptnLayout2=new PatternLayout();
ptnLayout2->setConversionPattern("%d %c [%p] %m%n");
PatternLayout * ptnLayout3=new PatternLayout();
ptnLayout3->setConversionPattern("%d %c [%p] %m%n");
OstreamAppender * pOsApp=new OstreamAppender("console",&cout);
pOsApp->setLayout(ptnLayout);
FileAppender * pFileApp=new FileAppender("fileApp","wd.log");
pFileApp->setLayout(ptnLayout2);
auto pRollingFileApp=new RollingFileAppender("rollingfileApp","rollingwd.log",5*1024,3);
pRollingFileApp->setLayout(ptnLayout3);
Category & mycat=Category::getRoot().getInstance("salesDapart");
mycat.setPriority(Priority::DEBUG);
mycat.addAppender(pOsApp);
mycat.addAppender(pFileApp);
mycat.addAppender(pRollingFileApp);
for(int i=0;i<100;++i){
mycat.emerg("this is an emerg message");
mycat.fatal("this is a fatal message");
mycat.alert("this is an alert message");
mycat.crit("this is a crit message");
mycat.error("this is an error message");
mycat.warn("this an error message");
mycat.info("this is an info message");
mycat.notice("this is a notice message");
mycat.debug("this a debug message");
}
Category::shutdown();
}
int main()
{
test0();
}
尝试将 优先级等 写入配置文件,使得修改优先级不需要修改源码:
# log4cpp.properties
log4cpp.rootCategory=DEBUG, rootAppender
log4cpp.category.sub1=DEBUG, A1, A2
log4cpp.category.sub1.sub2=DEBUG, A3
log4cpp.appender.rootAppender=ConsoleAppender
log4cpp.appender.rootAppender.layout=PatternLayout
log4cpp.appender.rootAppender.layout.ConversionPattern=%d [%p] %m%n
log4cpp.appender.A1=FileAppender
log4cpp.appender.A1.fileName=A1.log
log4cpp.appender.A1.layout=BasicLayout
log4cpp.appender.A2=FileAppender
log4cpp.appender.A2.threshold=WARN
log4cpp.appender.A2.fileName=A2.log
log4cpp.appender.A2.layout=PatternLayout
log4cpp.appender.A2.layout.ConversionPattern=%d [%p] %m%n
log4cpp.appender.A3=RollingFileAppender
log4cpp.appender.A3.fileName=A3.log
log4cpp.appender.A3.maxFileSize=200
log4cpp.appender.A3.maxBackupIndex=1
log4cpp.appender.A3.layout=PatternLayout
log4cpp.appender.A3.layout.ConversionPattern=%d [%p] %m%n
#include <log4cpp/Category.hh>
#include <log4cpp/PropertyConfigurator.hh>
int main(int argc, char* argv[])
{
std::string initFileName = "log4cpp.conf";
log4cpp::PropertyConfigurator::configure(initFileName);
log4cpp::Category& root = log4cpp::Category::getRoot();
log4cpp::Category& sub1 =
log4cpp::Category::getInstance(std::string("sub1"));
log4cpp::Category& sub2 =
log4cpp::Category::getInstance(std::string("sub1.sub2"));
root.warn("Storm is coming");
sub1.debug("Received storm warning");
sub1.info("Closing all hatches");
sub2.debug("Hiding solar panels");
sub2.error("Solar panels are blocked");
sub2.debug("Applying protective shield");
sub2.warn("Unfolding protective shield");
sub2.info("Solar panels are shielded");
sub1.info("All hatches closed");
root.info("Ready for storm.");
log4cpp::Category::shutdown();
return 0;
}
封装 log4cpp:
#ifndef __WD_Mylogger_HPP__//如果没有定义一个宏,就会定义一个宏
#define __WD_Mylogger_HPP__//在同一个*.cc文件中加载多次时,不会报重复定义的错误
//预处理操作是针对于一个*.cc文件的,当头文件在一个*.cc文件出现多次时,只有一份Mylogger源码出现
//keep ypur code clean
#include<log4cpp/Category.hh>
using std::string;
namespace wd
{
class Mylogger
{
public:
static Mylogger *getInstance();//2.定义了一个静态的成员函数用来初始化
static void destroy();
void error(const char *msg);
void warn(const char *msg);
void info(const char *msg);
void debug(const char *msg);
private:
Mylogger();//1、构造函数私有化
~Mylogger();
private:
log4cpp::Category & _mycat;
//log4cpp::OstreamAppender * _pos;
static Mylogger * _pInstance;3、定义一个静态的能够存储本类型对象的指针
};
#define addprefix(msg) string ("[").append(__FILE__).append(":")\
.append(__FUNCTION__).append(":")\
.append(std::to_string(__LINE__))\
.append("]").append(msg).c_str()
#define LogError(msg) wd::Mylogger::getInstance()->error(addprefix(msg))
#define LogWarn(msg) wd::Mylogger::getInstance()->warn(addprefix(msg))
#define LogInfo(msg) wd::Mylogger::getInstance()->info(addprefix(msg))
#define LogDebug(msg) wd::Mylogger::getInstance()->debug(addprefix(msg))
}//end of namespace wd_
#endif//之前的代码都会衔接到被引用的文件中
#include"Mylogger.hpp"
#include<log4cpp/Priority.hh>
#include<log4cpp/OstreamAppender.hh>
#include<log4cpp/FileAppender.hh>
#include<log4cpp/PatternLayout.hh>
#include<iostream>
using std::cout;
using std::endl;
using namespace log4cpp;
namespace wd
{
Mylogger * Mylogger::_pInstance=nullptr;
Mylogger * Mylogger:: getInstance()
{
if(_pInstance==nullptr){
_pInstance=new Mylogger();
}
return _pInstance;
}
void Mylogger::destroy()
{
if(_pInstance){
delete _pInstance;
_pInstance=nullptr;//safe delete
}
}
Mylogger::Mylogger()
:_mycat(Category::getRoot().getInstance("mycat"))//这里的getRoot是为了强调层级概念
{
//C++11新特性
auto ptnLayout1=new PatternLayout();
ptnLayout1->setConversionPattern("%d %c [%p] %m%n");
auto ptnLayout2=new PatternLayout();
ptnLayout2->setConversionPattern("%d%c [%p] %m%n");
auto posApp=new OstreamAppender("console",&cout);
posApp->setLayout(ptnLayout1);
auto pfileApp=new FileAppender("pfileApp","wd.log");
pfileApp->setLayout(ptnLayout2);
_mycat.setPriority(Priority::DEBUG);
_mycat.addAppender(posApp);
_mycat.addAppender(pfileApp);
cout<<"Mylogger()"<<endl;
}
Mylogger::~Mylogger()
{
Category::shutdown();
cout<<"~Mylogger()"<<endl;
}
void Mylogger::error(const char * msg)
{ _mycat.error(msg);}
void Mylogger::warn(const char * msg)
{ _mycat.warn(msg);}
void Mylogger::info(const char * msg)
{ _mycat.info(msg);}
void Mylogger::debug(const char * msg)
{ _mycat.debug(msg);}
}//end of namespace
#include"Mylogger.hpp"
#include<iostream>
//#include<string.h>
using std::cout;
using std::endl;
using std::string;
void test0()
{
wd::Mylogger::getInstance()->error("this is an error msg");
wd::Mylogger::getInstance()->warn("this is an warn msg");
wd::Mylogger::getInstance()->info("this is an info msg");
wd::Mylogger::getInstance()->debug("this is an debug msg");
}
void test1()
{
cout<<__FILE__<<endl;//宏在预编译时会替换成当前的源文件名
cout<<__LINE__<<endl;//宏在预编译时会替换成当前的行号
cout<<__FUNCTION__<<endl;//宏在预编译时会替换成当前的函数名称
cout<<__func__<<endl;
}
void test2()
{
cout<<addprefix("this is a test msg")<<endl;
cout<<addprefix("this is a test2 msg")<<endl;
}
void test3()
{
wd::Mylogger::getInstance()->error(addprefix("this is an error msg"));
wd::Mylogger::getInstance()->warn(addprefix("this is a warn msg"));
wd::Mylogger::getInstance()->info(addprefix("this is an info msg"));
wd::Mylogger::getInstance()->debug(addprefix("this is a debug msg"));
}
void test4()
{
LogError("this is an error message");//%d
LogWarn("this is a warn message");
LogInfo("this is an info message");
LogDebug("this is a debug message");
}
int main()
{
//test0();
//wd::Mylogger::destroy();
//test1();
//test2();
test4();
wd::Mylogger::destroy();
return 0;
}
遇到的 bug:
Mylogger * Mylogger::_pInstance=nullptr;
放头文件,被多个文件引用
g++ *.cc -llog4cpp -lpthread