一,实用类设计
该类主要是提前完成一些后面会用到的零碎通用的功能接口,主要有下面几个
1.获取系统时间:这里我们直接用time()函数获取时间返回
2.判断文件是否存在:判断文件我们调用系统接口,stat(),如下所示,第一个为文件路径,第二个是一个结构体,创建传入指针即可,返回0说明存在,-1说明不存在或者错误。
3.获取文件的所在目录路径:以''./abc/a.txt''为例,该文件路径其实就是最后一个'/'以前(包括'/'),这里我们可以直接用string提供的搜索接口即可
4.创建目录:以"./abc/ad/a.txt"为例,创建目录要一个一个建,先创建/abc,再依次进行,创建目录我们可以调用系统接口mkdir(),如下所示,第一个参数是所建目录路径,第二个参数为该目录权限
代码如下,注意后面三个功能都是关于文件路径的,因此我们将后面三个放到了一个类中实现
#ifndef _M_UTIL_H_
#define _M_UTIL_H_
#include <iostream>
#include <ctime>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
namespace mjw
{
namespace util
{
//设计两个类,时间一个类,后面的文件存在判断,文件目录路径,创建目录分为一个类
class gettime
{
public:
static size_t nowtime()
{
return (size_t)time(nullptr);
}
};
class FileDirectory
{
public:
//1.判断文件是否存在
static bool exist(const std::string& filename)
{
struct stat sm;
return stat(filename.c_str(),&sm)==0;
}
//2.获取文件路径
static std::string getDirectory(const std::string& filename)
{
if(filename.empty()) return ".";
int pos=filename.find_last_of("/\\");
if(pos==std::string::npos)//pos==npos说明没有找到
{
return ".";
}
return filename.substr(0,pos+1);
}
//3.创建目录
static void createDirectory(const std::string& path)
{
// ./abc/ab/c.txt
if(path.empty()) return;
if(exist(path)) return;
int pos=0,indox=0;
while(indox<path.size())
{
pos=path.find_first_of("/\\",indox);
//当pos==npos时,说明pos到indox已经没有/,说该创建最后一个文件,创建完break即可
if(pos==std::string::npos)
{
mkdir(path.c_str(),0777);
break;
}
if(!exist(path.substr(0,pos))) mkdir(path.substr(0,pos).c_str(),0777);
indox=pos+1;
}
}
};
}
}
#endif
二,日志等级类设计
首先我们需要明确划分日志等级的目的,就是在输出时按照等级进行过滤输出,也就是说每一个项目都会设置一个默认的输出等级,只有输出的日志等级大于等于默认输出等级才会输出。
而日志等级类有下面两个功能
1.使用枚举类的方法定义出所有的输出等级
等级如上所示,这里有一个问题为什么OFF可以关闭日志?当我们将默认输出等级设计成OFF时,此时只有等级大于等于OFF的信息可以输出,但是OFF等级是最大的,这样日志的输出就被关闭了。
2.提供一个接口,将对应的枚举等级转换成字符串
DEBUG ->"DEBUG"
模块代码如下
#ifndef _M_LEVEL_H_
#define _M_LEVEL_H_
#include <iostream>
namespace mjwlog
{
class LogLevel
{
public:
enum class level
{
UNKNOW=0, //未知
DEBUG, //调试
INFO, //提示
WARN, //警告
ERROR, //错误
FATAL, //致命
OFF //关闭日志
};
static const char* LeveltoString(LogLevel::level l)
{
switch(l)
{
case LogLevel::level::DEBUG:return "DEBUG";
case LogLevel::level::INFO:return "INFO";
case LogLevel::level::WARN:return "WARN";
case LogLevel::level::ERROR:return "ERROR";
case LogLevel::level::FATAL:return "FATAL";
case LogLevel::level::OFF:return "OFF";
}
return "UNKNOW";
}
};
}
#endif
测试:
三,日志消息类设计
意义:定义出一条日志存储时所需要的各项要素,同时完成该类构造
关键要素 | 作用 |
时间 | 用来过滤日志输出时间 |
等级 | 过滤日志信息,多等级输出 |
源文件名称 | 快速定位错误出现的位置 |
源代码行号 | 快速定位错误出现的位置 |
线程ID | 快速定位错误出现的线程 |
日志器名称 | 支持多日志器共同使用,因此日志器信息也需要过滤 |
实际日志主题信息 | 主题信息 |
代码如下:
#ifndef _M_MESSAGE_H_
#define _M_MESSAGE_H_
/*
时间 用来过滤日志输出时间
等级 过滤日志信息,多等级输出
源文件名称 快速定位错误出现的位置
源代码行号 快速定位错误出现的位置
线程ID 快速定位错误出现的线程
日志器名称 支持多日志器共同使用,因此日志器信息也需要过滤
实际日志主题信息 主题信息
*/
#include <iostream>
#include <thread>
#include "level.hpp"
#include "util.hpp"
namespace mjwlog
{
struct message//struct方便访问类内成员
{
size_t _time; //时间
size_t _line; //行号
LogLevel::level _level; //等级
std::thread::id _id; //线程id
std::string _filename; //文件名
std::string _logger; //日志器名称
std::string _msg; //主体信息
//构造
message(size_t time=util::gettime::nowtime(),
size_t line,
LogLevel::level level,
std::string filename,
std::string logger,
std::string msg)
:_time(time),
_line(line),
_level(level),
_id(std::this_thread::get_id()),
_filename(filename),
_logger(logger),
_msg(msg)
{}
};
}
#endif