日志系统采用字符串解析进行日志消息的输出,基于日志级别,当前采用的是DEBUG,调用util.h间接调用syscall系统调用进行tid的输出,当然也可以采用系统调用函数syscall(SYS_gettid)。时间采用localtime_t 可重入版本进行时间戳的转换为时间的格式。代码如下,test.cc是测试文件
log.h
#ifndef _LOG_H__
#define _LOG_H__
#include<string>
#include<cstdint>
#include<iostream>
#include<vector>
#include<memory>
#include<list>
#include<fstream> //文件流
#include<sstream>
#include<time.h>
#include<string.h>
//#include<stringstream> //字符串流
namespace moxue{
//提前声明
class Logger;
//日志事件
class LogEvent{
public:
typedef std::shared_ptr<LogEvent> ptr;
LogEvent(const char* file,int32_t line,uint32_t elapse,uint32_t thread_id,uint32_t fiber_id,uint64_t time);
const char* getFile() const {return m_file; }
int32_t getLine() const {return m_line; }
uint32_t getElapse() const {return m_elapse; }
uint32_t getthread_id() const { return m_threadid; }
uint32_t getfiber_id() const { return m_fiberid; }
uint32_t gettime() const { return m_time; }
std::string getcontent() const { return m_ss.str(); }
std::stringstream & getSS() { return m_ss; }
private:
const char* m_file=nullptr; //文件名
int32_t m_line=0;//行号
uint32_t m_elapse=0; //程序启动开始到现在的毫秒数
uint32_t m_threadid=0;//线程id
uint32_t m_fiberid=0;//协程号id
uint32_t m_time;//时间戳
//std::string m_content;//信息
std::stringstream m_ss;
};
//日志级别
class LogLevel{
public:
//日志级别
enum Level{
DEBUG=1,
INFO,
WARN,
ERROR,
FATAL
};
static const char* ToString(LogLevel::Level level);
};
//日志格式器
class LogFormatter{
public:
typedef std::shared_ptr<LogFormatter> ptr;
LogFormatter(const std::string & pattern); //根据pattern的格式解析items的信息
std::string format(std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event);
public:
class FormatItem{ //日志格式项
public:
typedef std::shared_ptr<FormatItem> ptr;
//FormatItem(const std::string & str){}
virtual ~FormatItem() {};
virtual void format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event) = 0;
};
class MessageFormatItem : public FormatItem{
public:
MessageFormatItem(const std::string &str){
}
void format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event) override;
};
class LevelFormatItem : public FormatItem{
public:
LevelFormatItem(const std::string &str){
}
void format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event) override;
};
class ElapseFormatItem : public FormatItem{
public:
ElapseFormatItem(const std::string &str){
}
void format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event) override;
};
class ThreadidFormatItem : public FormatItem{
public:
ThreadidFormatItem(const std::string &str){
}
void format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event) override;
};
class FiberidFormatItem : public FormatItem{
public:
FiberidFormatItem(const std::string &str){
}
void format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event) override;
};
class DateTimeFormatItem : public FormatItem{
public:
DateTimeFormatItem(const std::string& format = "%Y:%m:%d %H:%M:%s")
:m_format(format){
if(m_format.empty()){
m_format="%Y-%m-%d %H:%M:%S";
}
} //默认的时间格式
void format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event) override;
private:
std::string m_format;
};
class FilenameFormatItem : public FormatItem{
public:
FilenameFormatItem(const std::string &str){
}
void format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event) override;
};
class LineFormatItem : public FormatItem{
public:
LineFormatItem(const std::string &str){
}
void format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event) override;
};
class NewLineFormatItem : public FormatItem{
public:
NewLineFormatItem(const std::string &str){
}
void format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event) override;
};
class StringFormatItem : public FormatItem{
public:
StringFormatItem(const std::string &str)
:m_string(str){
}
void format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event) override;
private:
std::string m_string;
};
class TabFormatItem : public FormatItem{
public:
TabFormatItem(const std::string& str=""){} //常量引用绑定到常量对象
void format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event) override;
};
void init(); //init做pattern的解析
private:
std::string m_pattern; //模式
std::vector<FormatItem::ptr> m_items;
};
//日志输出地
class LogAppender{
public:
typedef std::shared_ptr<LogAppender> ptr;
virtual ~LogAppender() {} //因为日志输出地有很多 存在继承关系
virtual void log(std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event) = 0; //纯虚函数 子类必须实现
void setFormattwer(LogFormatter::ptr val) { m_formatter=val; } //设置Formatter
LogFormatter::ptr getFormatter() const { return m_formatter; }
protected:
LogFormatter::ptr m_formatter;
LogLevel::Level m_level=LogLevel::DEBUG;
};
//日志器
class Logger : public std::enable_shared_from_this<Logger>{
public:
typedef std::shared_ptr<Logger> ptr;
Logger(const std::string name= "root" );
void log(LogLevel::Level level,LogEvent::ptr event);
void debug(LogEvent::ptr event);
void debug(LogLevel::Level level,LogEvent::ptr event);
void info(LogEvent::ptr event);
void warn(LogEvent::ptr event);
void error(LogEvent::ptr event);
void fatal(LogEvent::ptr event);
//添加Appender
void addAppender(LogAppender::ptr appender);
void delAppender(LogAppender::ptr appender);
LogLevel::Level getlevel() const { return m_level; }
void setlevel(LogLevel::Level val) { m_level=val; }
const std::string get_name() const { return m_name; }
private:
std::string m_name; //日志名称
LogLevel::Level m_level; //满足这个级别的日志才会输出到里面去
std::list<LogAppender::ptr> m_apperders; //Appender集合
LogFormatter::ptr m_formatter;
};
//输出到控制台的Appender
class StdoutLogAppender : public LogAppender{
public:
typedef std::shared_ptr<StdoutLogAppender> ptr;//StdoutLogAppender实例化对象的ptr ptr是类型
virtual void log(std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event) override;//参数1:日志级别 参数2:日志事件
};
//输出到文件的Appender
class FileLogAppender : public LogAppender{
public:
typedef std::shared_ptr<FileLogAppender> ptr;
virtual void log(std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event) override;
FileLogAppender(const std::string filename);
bool reopen(); //如果文件已经打开,我们进行关闭并再次打开 重新打开成功返回true
private:
std::ofstream m_filestream;
std::string m_filename;
};
}
#endif
log.cc
#include"log.h"
#include<ctype.h>
#include<map>
#include<functional>
/*#--宏定义中,将后面的内容进行字符串化*/
namespace moxue{
const char* LogLevel::ToString(LogLevel::Level level){
switch (level)
{
#define XX(name) \
case LogLevel::name: \
return #name; \
break;
XX(DEBUG);
XX(INFO);
XX(WARN);
XX(ERROR);
XX(FATAL);
#undef XX
default:
return "UNKNOW";
}
return "UNKNOW";
}
LogEvent::LogEvent(const char* file,int32_t line,uint32_t elapse,uint32_t thread_id,uint32_t fiber_id,
uint64_t time):m_file(file),m_elapse(elapse),m_fiberid(fiber_id),m_line(line),m_threadid(thread_id),m_time(time)
{
}
Logger::Logger(const std::string name):m_name(name),m_level(LogLevel::DEBUG){
m_formatter.reset(new LogFormatter("%d%T%t%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"));
}
void Logger::log(LogLevel::Level level,LogEvent::ptr event){
if(level>=m_level){
auto self=shared_from_this(); //在自己的成员函数中获取自己的指针
for(auto & i: m_apperders){
i->log(self,level,event); //
}
}
}
void Logger::debug(LogEvent::ptr event){
log(LogLevel::DEBUG,event);
}
void Logger::info(LogEvent::ptr event){
log(LogLevel::INFO,event);
}
void Logger::warn(LogEvent::ptr event){
log(LogLevel::WARN,event);
}
void Logger::error(LogEvent::ptr event){
log(LogLevel::ERROR,event);
}
void Logger::fatal(LogEvent::ptr event){
log(LogLevel::FATAL,event);
}
void Logger::addAppender(LogAppender::ptr appender){
if(!appender->getFormatter()){
appender->setFormattwer(m_formatter); /*如果输出没有Formater的话 将我们自己的formater 保证每一个输出都有
自己的Formater*/
}
m_apperders.push_back(appender);
}
void Logger::delAppender(LogAppender::ptr appender){
for(std::list<LogAppender::ptr>::iterator it=m_apperders.begin();it!=m_apperders.end();++it){
if(*it==appender){
m_apperders.erase(it);
break;
}
}
}
FileLogAppender::FileLogAppender(const std::string filename):m_filename(filename){
}
void StdoutLogAppender::log(std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event){
if(level>=m_level){
std::string str=m_formatter->format(logger,level,event);
std::cout<<str;//<<std::endl;
}
}
void FileLogAppender::log(std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event){
if(level>=m_level){
m_filestream << m_formatter->format(logger,level,event)<<std::endl;
}
}
bool FileLogAppender::reopen(){
if(m_filestream){ //如果是已经打开的
m_filestream.close();
}
m_filestream.open(m_filename);
return !!m_filestream;
}
std::string LogFormatter::format(std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event){
std::stringstream ss;
//std::cout<<"此时的m_items的大小为"<<m_items.size()<<std::endl;
for(auto & i : m_items){
i->format(ss,logger,level,event);
}
return ss.str();
}
LogFormatter::LogFormatter(const std::string & pattern)
:m_pattern(pattern) {
init();
}
void LogFormatter::init(){ //解析日志输出
//str format type
std::vector<std::tuple<std::string,std::string,int>> vec; //tuple (string string int)
std::string nstr;
for(size_t i=0;i<m_pattern.size();++i){ //m_pattern是一个模式string
if(m_pattern[i]!='%'){
nstr.append(1,m_pattern[i]);//追加一个字
continue;
}
if((i+1)<m_pattern.size()){ //%%
if(m_pattern[i+1]=='%'){
nstr.append(1,'%');
continue;
}
}
size_t n=i+1;
size_t fmt_begin=0;
int format_status=0;//格式状态
std::string fmt;
std::string str;
while(n<m_pattern.size()){
if(!std::isalpha(m_pattern[n]) && m_pattern[n]!='{' &&
m_pattern[n]!='}'){ //只接受字母
str=m_pattern.substr(i+1,n-i-1);
break; //日志格式出现非法字符 直接break
}
if(format_status == 0){ //尚未遇到{
if(m_pattern[n] == '{'){
str=m_pattern.substr(i+1,n-i-1);
format_status = 1; //解析格式
fmt_begin=n;
++n;
continue;
}
}
else if (format_status==1)
{
if(m_pattern[n]== '}' ){
fmt=m_pattern.substr(fmt_begin+1,n-fmt_begin-1);
format_status=0; // / /==2代表{}字符匹配结束
++n;
break;
}
}
++n;
if(n==m_pattern.size()){
if(str.empty()){
str=m_pattern.substr(i+1);
}
}
}
if(format_status==0){
if(!nstr.empty()){
vec.push_back(std::make_tuple(nstr,std::string(),0));
nstr.clear(); //删除nstr 将空间的首字符置为0
}
//str=m_pattern.substr(i+1,n-i-1);
vec.push_back(std::make_tuple(str,fmt,1));
i=n-1;
}
//格式错误
else if(format_status==1){
std::cout<<"pattern parse error "<<m_pattern<<" - "<<m_pattern.substr(i)<<std::endl;
vec.push_back(std::make_tuple("<<pattern_error>>",fmt,1));
}
// else if(format_status==2){
// if(!nstr.empty()){
// vec.push_back(std::make_tuple(nstr,"",0));
// nstr.clear();
// }
// vec.push_back(std::make_tuple(str,fmt,1));
// i=n-1;
// }
}
if(!nstr.empty()){
vec.push_back(std::make_tuple(nstr,"",0));
}
const std::string m="m",p="p",c="c",r="r",t="t",n="n",d="d",l="l",f="f",T="T",F="F";
static std::map<std::string,std::function<FormatItem::ptr (const std::string & str)>>
s_format_items = {
#define XX(str,C) { \
str,[](const std::string & fmt){ return FormatItem::ptr(new C(fmt)); } \
}
XX(f,FilenameFormatItem),
XX(m,MessageFormatItem),
XX(p,LevelFormatItem),
XX(r,ElapseFormatItem),
XX(c,FilenameFormatItem),
XX(t,ThreadidFormatItem),
XX(n,NewLineFormatItem),
XX(d,DateTimeFormatItem),
XX(l,LineFormatItem),
XX(T,TabFormatItem),
XX(F,FiberidFormatItem)
#undef XX //取消定义宏
};
/*%m --消息体
%p --level
%r --启动后的时间
%c --日志名称
%t --线程id
%n --回车换行
%d --时间
%f --文件名
%l --行号*/
for(auto & i : vec){
if(std::get<2>(i)==0){ //string
m_items.push_back(FormatItem::ptr(new StringFormatItem(std::get<0>(i))));
}
else{
auto it = s_format_items.find(std::get<0>(i));
if(it == s_format_items.end()){
m_items.push_back(FormatItem::ptr(new StringFormatItem("<<error_format %"+
std::get<0>(i)+">>")));
}
else{
m_items.push_back(it->second(std::get<1>(i)));
}
}
//std::cout<<"{"<<std::get<0>(i)<<"} - {"<<std::get<1>(i)<<"} - {"<<std::get<2>(i)<<"}"<<std::endl;
}
//std::cout<<"size="<<m_items.size()<<std::endl;
}
void LogFormatter::MessageFormatItem::format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event)
{
os<<event->getcontent();
}//直接输出event里面
void LogFormatter::LevelFormatItem::format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event){
os<<LogLevel::ToString(level);
}
void LogFormatter::ElapseFormatItem::format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event){
os<<event->getElapse();
}
void LogFormatter::ThreadidFormatItem::format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event){
os<<event->getthread_id();
}
void LogFormatter::FiberidFormatItem::format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event){
os<<event->getfiber_id();
}
void LogFormatter::DateTimeFormatItem::format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event){
struct tm tm;
time_t time=event->gettime();
localtime_r(&time,&tm);
char buf[1024];
strftime(buf,sizeof buf,m_format.c_str(),&tm);
os<<buf;
}
void LogFormatter::FilenameFormatItem::format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event){
os<<event->getFile();
}
void LogFormatter::LineFormatItem::format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event){
os<<" "<<event->getLine();
}
void LogFormatter::NewLineFormatItem::format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event){
os<<std::endl;
}
void LogFormatter::StringFormatItem::format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event){
os<<m_string;
}
void LogFormatter::TabFormatItem::format(std::ostream & os,std::shared_ptr<Logger> logger,LogLevel::Level level,LogEvent::ptr event){
os<<"\t";
}
}
test.cc
#include<iostream>
#include"../src/log.h"
#include"../src/util.h"
/*输出是通过Logger本体进行输出的*/
#include<thread>
int main(){
moxue::Logger::ptr logger(new moxue::Logger);
logger->addAppender(moxue::LogAppender::ptr(new moxue::StdoutLogAppender));
// moxue::LogEvent::ptr event(new moxue::LogEvent(__FILE__,__LINE__,0,1,2,time(0)));
// std::thread::id threadId = std::this_thread::get_id();
// size_t threadIdHash = std::hash<std::thread::id>{}(threadId);
// int threadIdInt = static_cast<int>(threadIdHash);
moxue::LogEvent::ptr event(new moxue::LogEvent(__FILE__, __LINE__, 0, moxu::GetThread_Id(), 2, time(0)));
event->getSS()<<"Hello moxue log"<<std::endl;
logger->log(moxue::LogLevel::DEBUG,event);
// std::cout<<"The log is successed"<<std::endl;
return 0;
}
注:ununtu环境下使用syscall系统函数包含zconf.h或者unistd.h即可,centos 下应该是只有unistd.h