cpp
#pragma once
#include <iostream>
#include <fstream>
#include <cstdio>
#include <string>
#include <ctime>
#include <cstdarg>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include "LockGuard.hpp"
bool gIsSave=false;//默认false是向显示器进行打印,true是向文件进行写入
const std::string logname="log.txt";
//1.日志是有等级的
enum Level
{
DEBUG=0,//调试
INFO,//正常
WARNING,//警告
ERROR,//错误
FATAL//严重的错误
};
void SaveFile(const std::string &filename,const std::string &message)//改变日志的打印目标
{
std::ofstream out(filename,std::ios::app);
if(!out.is_open())
{
return;
}
out<<message;
out.close();
}
std::string LevelToString(int level)//将等级转换成字符串
{
switch(level)
{
case DEBUG:
return "Debug";
case INFO:
return "INFO";
case WARNING:
return "Warning";
case ERROR:
return "Error";
case FATAL:
return "Fatal";
default:
return "Unknown";
}
}
std::string GetTimeString()//以字符串形式获取当前时间
{
time_t curr_time=time(nullptr);
struct tm *format_time=localtime(&curr_time);//tm是time库函数中的一个结构体类型,内部可以存储日期时间等信息
if(format_time==nullptr)
{
return "None";
}
char time_buffer[1024];
snprintf(time_buffer,sizeof(time_buffer),"%d-%d-%d %d:%d:%d",
format_time->tm_year+1900,
format_time->tm_mon+1,
format_time->tm_mday,
format_time->tm_hour,
format_time->tm_min,
format_time->tm_sec);
return time_buffer;
}
pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;
//2.日志是有格式的
//日志等级 时间 代码所在的文件名/行数 日志的内容
//filename 需要写入的目标文件名
//line 写入该条日志信息的所在行数
//issave 是否向文件进行写入
//level 日志等级
//
void LogMessage(std::string filename,int line,bool issave,int level,const char*format)
{
std::string levelstr=LevelTostring(level);//等级
std::string timestr=GetTimeString();//时间信息
pid_t selfid=getpid();//当前线程PID
char buffer[1024];
va_list arg;//本质上是一个指针
va_start(arg,format);//用format的地址来初始化arg(即让其指向可变参数部分),arg本质上是一个
//void类型的指针
vsnprintf(buffer,sizeof(buffer),format,arg);
va_end(arg);//将arg进行清空
std::string message="["+timestr+"]"+"["+levelstr+"]"+
"["+std::to_string(selfid)+"]"+
"["+filename+"]"+"["+std::to_string(line)+"]"+buffer+"\n";
LockGuard lockguard(&lock);
if(!issave)
{
std::cout<<message;
}
else
{
SaveFile(logname,message);
}
}
// C99新特性__VA_ARGS__
#define LOG(level,format,...)
do
{
LogMessage(__FILE__,__LINE__,gIsSave,level,format,##__VA_ARGS__);
}while(0)
#define EnableFile()
do
{
gIsSave=true;
}while(0)
#define EnableScreen()
do
{
gIsSave=false;
}while(0)
va_start和va_arg
cpp
va_start(arg,num);
va_arg(arg,int);
1、arg指针可以根据num的地址然后减去num类型的大小指向num之后一个元素的地址。
2、data进行读取数据时将arg强转成int*然后进行解引用,重复以上的操作,这样就可以将可变参数全部读取完毕。
vsprintf/vsnprintf
将内容printf到第一个参数所指向的字符串中,长度为第二个参数size,
将指向可变部分ap以format格式,将格式化完成后的字符串存入第一个参数,str字符串中。vsnprintf则是更安全的方式,按照用户要求的长度将其放到str中。
宏替换的使用及C++14语法的使用
考虑到用户便捷和使用的方便,可以使用宏的方式对日志的调用以及传参进行替换,结合C++14的语法特性对其进行do while操作。
cpp
#define LOG(level,format,...)
do
{
LogMessage(__FILE__,__LINE__,gIsSave,level,format,##__VA_ARGS__);
}while(0)
//通过宏来直接对gIs进行修改
#define EnableFile()//向文件写入日志
do
{
gIsSave=true;
}while(0)
#define EnableScreen()//向显示器写入日志
do
{
gIsSave=false;
}while(0)