Linux -- 日志

2024-05-10 1396阅读

一 日志的重要性

  在之前的编程经历中,如果我们的程序运行出现了问题,都是通过 标准输出 或 标准错误 将 错误信息 直接输出到屏幕上,以此来排除程序中的错误。

  这在我们以往所写的程序中使用没啥问题,但如果出错的是一个不断在运行中的服务,那问题就大了,因为服务器是不间断运行中,直接将 错误信息 输出到屏幕上,会导致错误排查变得极为困难。

  其实,我们可以将各种 错误信息 组织管理,使 每种错误有属于自己的格式(包括时间、文件名及行号、错误等级等),利于排查问题,同时,把这些错误写入一个单独的地方,便于我们查找和阅读(因为错误信息繁多,我们一般写入文件中)。

  这种错误信息的集合,我们便称为日志。

所以接下来我们将会实现一个简易版日志器,用于定向输出我们的日志信息。

二 可变参数

日志需要我们指定格式并输出,依赖于可变参数。

因此我们需要认识一下可变参数的使用,主要是几个宏。

#include 
 
va_list 	// 指向可变参数列表的指针
va_start()	// 将指针指向起始地址
va_arg()	// 根据类型,提取可变参数列表中的参数
va_end()	// 将指针置为空 

示例,我们通过可变参数实现参数遍历:

 

#include 
#include 
void foreach(int format, ...){
    va_list p;
    va_start(p, format);
    // 接下来就是获取其中的每一个参数
    for(int i = 0; i  

这种依靠自己动手的方法比较麻烦,我们也可以借助标准库提供的 vsnprintf() 函数进行参数解析

//头文件:
#include 
//函数声明:
int vsnprintf(char* str, size_t size, const char* format, va_list ap);
  1. char *str ,  把生成的格式化的字符串存放在这里.
  2. size_t size , str可接受的最大字符数 ,防止产生数组越界.
  3. const char *format , 指定输出格式的字符串,它决定了你需要提供的可变参数的类型、个数和顺序。
  4. va_list ap , va_list变量. 

函数功能:将可变参数格式化输出到一个字符数组。

返回值:执行成功,返回最终生成字符串的长度,若生成字符串的长度大于size,则将字符串的前size个字符复制到str,同时将原串的长度返回(不包含终止符);执行失败,返回负值,并置errno. 

#include
#include
#include 
using namespace std;
void logtest(int format,...){
    va_list a;
    va_start(a,format);
    char msg[1024];
    int n = vsnprintf(msg,sizeof(msg),"%d-%d-%d-%d-%d",a);
    if(n tm_mon + 1, st->tm_mday, st->tm_hour, st->tm_min, st->tm_sec); 
    return buff;
}

3.3 日志格式

  日志的格式我们一般可以自己规定,这里我们规定我们日志的格式为:

[时间] [PID] {消息体}

  接下来就是获取进程 PID,这个简单,直接使用 getpid() 函数获取即可,最后是解析参数,需要用到 vsnprintf() 函数,只要传入缓冲区和 va_list 指针,该函数就可以自动解析出参数,并存入缓冲区中  。

void logMessage(int level, const char* format, ...){
    //截获主体消息
    char msgbuff[1024];
    va_list p;
    va_start(p, format);    //将 p 定位至 format 的起始位置
    vsnprintf(msgbuff, sizeof(msgbuff), format, p); //自动根据格式进行读取
    va_end(p);
}

形成测试版日志信息函数

//处理信息
void logMessage(int level, const char* format, ...){
    //日志格式: [时间] [PID] {消息体}
    string logmsg = getLevel(level);    //获取日志等级
    logmsg += " " + getTime();  //获取时间
    logmsg += " [" + to_string(getpid()) + "]";    //获取进程PID
    //截获主体消息
    char msgbuff[1024];
    va_list p;
    va_start(p, format);    //将 p 定位至 format 的起始位置
    vsnprintf(msgbuff, sizeof(msgbuff), format, p); //自动根据格式进行读取
    va_end(p);
    logmsg += " {" + string(msgbuff) + "}";    //获取主体消息
    printf("%s\n", logmsg); //这里先打印 方便进行测试
} 

  为什么日志消息最后还是向屏幕输出?这样组织日志消息的好处是什么?
  因为现在还在测试阶段,等测试完成后,可以将日志消息存入文件中,做到持久化存储;至于统一组织的好处不言而喻,能够确保每条日志消息都包含必要信息,便于排查错误

3.4 Log.hpp 头文件代码

#pragma once
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
enum{
    
    Debug = 0,
    Info,
    Warning,
    Error,
    Fatal
};
string getLevel(int level){
    vector vs = {"", "", "", "", "", ""};
    //避免非法情况
    if(level = vs.size() - 1) {
      return vs[vs.size() - 1];
    }
    return vs[level];
}
string getTime(){
    time_t t = time(nullptr);   //获取时间戳
    struct tm *st = localtime(&t);    //获取时间相关的结构体
    char buff[128];
    snprintf(buff, sizeof(buff), "%d-%d-%d %d:%d:%d", st->tm_year + 1900, st->tm_mon + 1, st->tm_mday, st->tm_hour, st->tm_min, st->tm_sec);
    return buff;
}
//处理信息
void logMessage(int level, const char* format, ...){
    //日志格式: [时间] [PID] {消息体}
    string logmsg = getLevel(level);    //获取日志等级
    logmsg += " " + getTime();  //获取时间
    logmsg += " [" + to_string(getpid()) + "]";    //获取进程PID
    //截获主体消息
    char msgbuff[1024];
    va_list p;
    va_start(p, format);    //将 p 定位至 format 的起始位置
    vsnprintf(msgbuff, sizeof(msgbuff), format, p); //自动根据格式进行读取
    va_end(p);
    logmsg += " {" + string(msgbuff) + "}";    //获取主体消息
    cout
VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]