欢迎来到博主的专栏:从0开始linux
博主ID:代码小豪
在较为大型的项目当中,如果发生了程序崩溃,使用gdp或者vs当中的调试模式排查报错原因有点太慢了,而且如果是系统调用出现错误的话,有可能会出现此次运行失败,下次运行就没问题的情况出现,因此一个日志系统是必不可少的,因此博主给大家设计出一个日志系统,在后续博主的文章(linux网络编程)当中,也会采取日志的方式查看运行信息,因此给大家了解一下博主的日志系统。这里不解释日志的运行原理,因为这些接口在前面的文章中都有介绍。
首先日志可以通过控制台输出的方式(即屏幕),或者文件的方式,因此我们的日志类需要定义出这两种不同的策略,这里博主选择使用宏来确定日志信息的输出方式。
#define CONVERT_CONSOLE_LOG() logmodule::logger.ConverConsoleStrategy()//向控制台输出日志
#define CONVERT_FILE_LOG() logmodule::logger.ConverFileStrategy()//向文件输出日志
首先要将该日志系统的头文件加在项目当中,后续如果我们想让日志向控制台输出日志,则使用宏CONVERT_CONSOLE_LOG()
,若是向文件输出日志,则使用宏CONVERT_FILE_LOG()
,如果我们选择向文件输出日志,则会在当前文件夹当中出现一个叫做log的文件夹,在文件夹当中会包含一个log.txt的文件,文件当中的内容则是日志信息。
博主设计了五个日志等级,分别是DEBUG,INFO,WARRING,ERROR,FATAL。其中DEBUG表示该日志是调试信息,INFO表示正常运行,WARRING表示运行出现了可能导致崩溃的警告,ERROR表示运行出现错误(导致运行结果出错的错误),FATAL表示运行出现了巨大的错误(导致程序崩溃的错误)
enum class loglevel{
DEBUG=1,
INFO,
WARRING,
ERROR,
FATAL
};
#define DEBUG logmodule::loglevel::DEBUG
#define INFO logmodule::loglevel::INFO
#define WARRING logmodule::loglevel::WARRING
#define ERROR logmodule::loglevel::ERROR
#define FATAL logmodule::loglevel::FATAL
最后博主定义了一个宏,通过这个宏可以输出日志信息。
#define LOG(LEVEL) (logmodule::logger(LEVEL,__FILE__,__LINE__))
下面是该日志系统使用的范例代码:
#include"log.hpp"
using namespace logmodule;
int main()
{
CONVERT_FILE_LOG();//向文件输出日志
LOG(DEBUG)<<"hello FILE";//向日志文件输出hello file
LOG(DEBUG)<<"hello FILE";
LOG(DEBUG)<<"hello FILE";
LOG(DEBUG)<<"hello FILE";
CONVERT_CONSOLE_LOG();//向控制台输出日志
LOG(DEBUG)<<"hello world";//向控制台输出hello world
LOG(DEBUG)<<"hello world";
LOG(DEBUG)<<"hello world";
LOG(DEBUG)<<"hello world";
return 0;
}
使用方式为:LOG(日志等级)<<“日志信息”
,可以使用多个<<来输出更多的信息,类似于cout的使用方式,但是要注意,endl无法用来作为换行符。
#pragma once
#include "mutex.hpp"
#include
#include
#include
#include //c++17
#include
#include
#include
namespace logmodule
{
std::string defaultpath = "./log"; // 日志默认生成路径
std::string defaultfile = "/log.txt"; // 日志默认生成文件
enum class loglevel{
DEBUG=1,
INFO,
WARRING,
ERROR,
FATAL
};
std::string currtime()//获取当前时间
{
char buf[1024];
time_t timestamp=time(nullptr);
struct tm time;
localtime_r(×tamp,&time);
snprintf(buf,sizeof(buf),"%d-%d-%d %d:%d:%d",\
time.tm_year+1900,time.tm_mon+1,time.tm_mday,\
time.tm_hour,time.tm_min,time.tm_sec);
return std::string(buf);
}
std::string leveltostring(loglevel level)
{
std::string str;
switch (level)
{
case loglevel::DEBUG:str="DEBUG";
break;
case loglevel::ERROR:str="ERROR";
break;
case loglevel::FATAL:str="FATAL";
break;
case loglevel::WARRING:str="WARRING";
break;
case loglevel::INFO:str="INFO";
break;
}
return str;
}
class LogStrategy // 接口类
{
public:
virtual void LogStrategysync(std::string &message) = 0; // 刷新日志
virtual ~LogStrategy(){}
};
class ConsoleLogStrategy : public LogStrategy // 继承接口类,向控制台输出日志
{
public:
void LogStrategysync(std::string &message)
{
mutexmodule::lockgroud lock(_mutex);
std::cerr << message << std::endl;
}
~ConsoleLogStrategy()
{
}
private:
mutexmodule::mutex _mutex;
};
class FileLogStrategy : public LogStrategy // 继承接口类,向文件输出日志
{
public:
FileLogStrategy() : _filename(defaultfile), _filepath(defaultpath)
{
mutexmodule::lockgroud lock(_mutex);
if (std::filesystem::exists(_filepath))
return;
try
{
std::filesystem::create_directory(_filepath);
}
catch (std::filesystem::filesystem_error &e)
{
std::cerr << e.what() << std::endl;
}
}
FileLogStrategy(const std::string &filepath, std::string &filename) : _filename(filename), _filepath(filepath)
{
mutexmodule::lockgroud lock(_mutex);
if (std::filesystem::exists(_filepath))
return;
try
{
std::filesystem::create_directory(_filepath);
}
catch (std::filesystem::filesystem_error &e)
{
std::cerr << e.what() << std::endl;
}
}
void LogStrategysync(std::string &message)
{
mutexmodule::lockgroud lock(_mutex);
std::string file = _filepath + _filename;
std::ofstream out(file.c_str(), std::ofstream::app);
out << message << '\n';
out.close();
}
private:
std::string _filepath;
std::string _filename;
mutexmodule::mutex _mutex;
};
class Logger
{
public:
Logger(){
_strategy=std::make_shared<ConsoleLogStrategy>();//默认策略是是使用控制台输出日志信息
}
void ConverConsoleStrategy(){
_strategy=std::make_shared<ConsoleLogStrategy>();
}
void ConverFileStrategy(std::string filepath,std::string filename){
_strategy=std::make_shared<FileLogStrategy>(filepath,filename);
}
void ConverFileStrategy(){
_strategy=std::make_shared<FileLogStrategy>();
}
//日志信息封装成类
class LogMessage
{
public:
LogMessage(loglevel level,std::string filename,int line,Logger& log)//构造出message的大部分固定内容
:_time(currtime())
,_level(level)
,_pid(::getpid())
,_filename(filename)
,_line(line)
,_log(log)
{
std::stringstream ss;
ss<<'['<<_time<<"] ["<<leveltostring(_level)<<"] ["<<_pid<<"] ["<<_filename<<"] ["<<_line<<"] -";
message=ss.str();
}
template<class T>
LogMessage& operator<<(const T& data)
{
std::stringstream ss;
ss<<data;
message+=ss.str();
return *this;
}
~LogMessage(){
_log._strategy->LogStrategysync(message);
}
private:
std::string _time;//当前时间
loglevel _level;
pid_t _pid;
std::string _filename;
int _line;
std::string message;
Logger& _log;
};
LogMessage operator()(loglevel level,std::string filename,int line)
{
return LogMessage(level,filename,line,*this);
}
~Logger()=default;
private:
std::shared_ptr<LogStrategy> _strategy;
};
Logger logger;
}
#define LOG(LEVEL) (logmodule::logger(LEVEL,__FILE__,__LINE__))
#define CONVERT_CONSOLE_LOG() logmodule::logger.ConverConsoleStrategy()
#define CONVERT_FILE_LOG() logmodule::logger.ConverFileStrategy()
#define DEBUG logmodule::loglevel::DEBUG
#define INFO logmodule::loglevel::INFO
#define WARRING logmodule::loglevel::WARRING
#define ERROR logmodule::loglevel::ERROR
#define FATAL logmodule::loglevel::FATAL
这里的mutex类是博主自己封装的互斥锁库,大家可以使用c++里面的mutex类对象来替换,或者也引用博主自己封装的mutex.hpp库,源码如下:
#pragma once
#include
#include
namespace mutexmodule
{
class mutex
{
public:
mutex(){
pthread_mutex_init(&_mutex,nullptr);
}
void lock()
{
pthread_mutex_lock(&_mutex);
}
pthread_mutex_t* addr()
{
return &_mutex;
}
void unlock()
{
pthread_mutex_unlock(&_mutex);
}
~mutex(){
pthread_mutex_destroy(&_mutex);
}
private:
pthread_mutex_t _mutex;
};
class lockgroud
{
public:
lockgroud(mutex& lock):_mutex(lock){
_mutex.lock();
}
~lockgroud(){
_mutex.unlock();
// std::cout<<"开锁成功"<
}
private:
mutex& _mutex;
};
}