感谢sylar,感谢开源笔记的所有人~
- 知识点备忘录
- switch结合宏定义简化
- 获取时间戳
- 获取行号
- 获取线程ID
知识点备忘录
上一篇适配器后得到的输出是下面这样,在main函数中定义了需要的一切,和项目所需要的还相差很远,比如日志级别需要显示名称,而不是1234,时间没有输出,文件名称,行号,线程和协程ID是自己输入定义的,而不是自动获取的,综上所述,还需改进
switch结合宏定义简化
日志级别的代码是:
class LogLevel{
public:
enum Level{
UNKNOW = 0, //起手先来个未知级别兜底
DEBUG = 1, //调试级别
INFO = 2, //普通信息级别
WARN = 3, //警告信息
ERROR = 4, //错误信息
FATAL = 5 //灾难级信息
};
}
输出的就是012345,现在我们不想要012345,只想要UNKNOW,DEBUG…
简单来说就是希望数据存储的时候存数值,打印输出的是对应的枚举字段
其实用switch就好了,很简单的C++语法而已,但是那样不够优雅,可以结合使用宏定义简化,看起来更酷
先解释一下宏定义怎么用:
宏定义被用来减少冗余代码。#define XX(name) 定义了一个宏,用于简化 switch-case 语句中的重复代码。
#define XX(name) \
case LogLevel::name: \
return #name; \
break;
XX(name)
是宏定义的名字,name是参数;case LogLevel::name:
:这个宏会将参数 name 插入到 switch-case 语句中,并匹配对应的 enum。return #name;
:#name 会将参数 name 转换为字符串。例如,如果 name 是 DEBUG,#name
会返回 “DEBUG” 这个字符串。break;
:匹配成功后退出switch
结合switch:
class LogLevel{
public:
enum Level{
UNKNOW = 0, //起手先来个未知级别兜底
DEBUG = 1, //调试级别
INFO = 2, //普通信息级别
WARN = 3, //警告信息
ERROR = 4, //错误信息
FATAL = 5 //灾难级信息
};
static const char* ToString(LogLevel::Level level);
};
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";
}
宏定义的写法是一种预处理器技巧,在简化switch-case语句、减少代码重复时非常好用,可以理解成一种文本替换工具。
定义好了,在派生类输出到控制行的log()中相应改一下:
<< "["
<< LogLevel::ToString(event->getLevel())
<< "] "
获取时间戳
输出当前时间,格式为 “%Y-%m-%d %H:%M:%S”,即年-月-日 时:分:秒 的格式。
int main(int argc,char** argv){
const std::string m_format = "%Y-%m-%d %H:%M:%S";
struct tm tm;
time_t t = time(0);
localtime_r(&t, &tm);
char buf[64];
strftime(buf, sizeof(buf), m_format.c_str(), &tm);
std::cout << buf << std::endl;
return 0;
}
const std::string m_format = "%Y-%m-%d %H:%M:%S";
:这里定义了一个格式化字符串m_format,表示想要的时间格式,这个格式字符串会用于格式化时间输出- tm 结构体是一个标准库结构,包含关于时间的详细信息,如年份、月份、日期、小时、分钟、秒等。
struct tm tm;
:定义了一个tm结构体,用来保存转换后的本地时间信息 time_t t = time(0);
:time(0)返回当前时间,time_t 是用于表示时间的整数类型localtime_r(&t, &tm);
:localtime_r 函数将 time_t 类型的 t 转换为本地时间,存储在 tm 结构体中。char buf[64];
:声明一个 char 数组 buf,用于存储格式化后的时间字符串。64 表示这个缓冲区最多可以存储 64 个字符。strftime(buf, sizeof(buf), m_format.c_str(), &tm);
:strftime 函数根据指定的格式将时间格式化成字符串,存储在 buf 中。
这段代码几乎是死的,不咋改,拿来就用
在派生类输出到控制行的log()方法中加上
void StdoutLogAppender::log(LogEvent::ptr event){
//格式化时间
const std::string format = "%Y-%m-%d %H:%M:%S";
struct tm tm;
time_t t = event->getTime();
localtime_r(&t, &tm);
char tm_buf[64];
strftime(tm_buf, sizeof(tm_buf), format.c_str(), &tm);
std::cout
//<< event->getTime() << " "
<< tm_buf << " "
<< event->getThreadId() << " "
<< event->getFiberId() << " "
<< "["
<< LogLevel::ToString(event->getLevel())
<< "] "
<< event->getFile() << ":" << event->getLine() << " "
<< "输出到控制台的信息"
<< std::endl;
}
获取行号
文件名和行号的自动获取,可以用预处理宏来解决
__FILE__
:一个预定义的预处理器宏,它会在编译时被替换为当前源文件的文件名。__LINE__
:一个预定义的预处理器宏,它会在编译时被替换为当前代码所在的行号。
在日志事件中演示一下:
int main(int argc,char** argv){
//创建一个日志事件(这里的内容随便定义,因为我们没有真正用到它)
LogEvent::ptr event(new LogEvent(
LogLevel::INFO, //日志级别
__FILE__, //文件名称
__LINE__, //行号
1234567, //运行时间
0, //线程ID
0, //协程ID
time(0) //当前时间
));
Logger::ptr lg(new Logger("XYZ"));
//添加控制台输出适配器
StdoutLogAppender::ptr stdApd(new StdoutLogAppender());
lg->addAppender(stdApd);
//输出
lg->log(event);
return 0;
}
输出:
2024-04-20 04:53:35 2 3 [INFO] /apps/sylar/tests/test.cc:115 输出到控制台的信息
获取线程ID
和linux中的PID对应
只有一行代码:
syscall(SYS_gettid)
和行号一个用法
就写这么多了,优化的部分不写了