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)
相关推荐
gywl7 分钟前
openEuler VM虚拟机操作(期末考试)
linux·服务器·网络·windows·http·centos
青木沐7 分钟前
Jenkins介绍
运维·jenkins
WTT001142 分钟前
2024楚慧杯WP
大数据·运维·网络·安全·web安全·ctf
苹果醋31 小时前
React源码02 - 基础知识 React API 一览
java·运维·spring boot·mysql·nginx
了一li1 小时前
Qt中的QProcess与Boost.Interprocess:实现多进程编程
服务器·数据库·qt
日记跟新中1 小时前
Ubuntu20.04 修改root密码
linux·运维·服务器
唐小旭2 小时前
服务器建立-错误:pyenv环境建立后python版本不对
运维·服务器·python
码农君莫笑2 小时前
信管通低代码信息管理系统应用平台
linux·数据库·windows·低代码·c#·.net·visual studio
明 庭2 小时前
Ubuntu下通过Docker部署NGINX服务器
服务器·ubuntu·docker