Linux:日志的编写及使用

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)
相关推荐
A小辣椒1 天前
TShark:Wireshark CLI 功能
linux
A小辣椒1 天前
TShark:基础知识
linux
AlfredZhao1 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao2 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334662 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪2 天前
linux 拷贝文件或目录到指定的位置
linux
大树883 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质3 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
bush43 天前
嵌入式linux学习记录十四、术语
linux·嵌入式