高性能服务器---日志系统

日志系统采用字符串解析进行日志消息的输出,基于日志级别,当前采用的是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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值