【Linux修行路】实现一个简单的日志代码

目录

⛳️推荐

一、可变参数的使用

二、Log

2.1 日志打印

2.1.1 时间获取

2.1.2 日志分块打印

2.2 打印模式选择

2.3 Log 使用样例

2.4 Log 完整源码


⛳️推荐

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站【Linux修行路】动静态库详解点击跳转到网站

一、可变参数的使用

int sum(int n, ...)
{
    va_list s; // va_list 本质上就是一个指针
    va_start(s, n); 

    int sum = 0;
    while(n)
    {
        sum += va_arg(s, int);
        n--;
    }

    va_end(s);

    return sum;
}

va_list 本质上就是一个 char * 类型,va_start 的作用就是让指针 s 指向函数栈帧中第一个非可变形参。因为函数的实参是按照从右向左的顺序进行压栈的,因此只要知道了第一个非可变形参的位置,就可以找到第一个可变参数的位置,进而去解析所有的可变参数。这就要求:可变参数的左边必须要有一个具体的参数va_arg 是根据第二个参数所确定的类型来提取可变参数,va_end 是将 s 置空。

image-20240305154610604

二、Log

2.1 日志打印

日志一般包括:日志的时间、日志的等级、日志的内容、文件名称和行号。

2.1.1 时间获取

时间获取介绍time 返回值是时间戳、gettimeofday 系统调用接口、localtime 将一个时间戳转化成我们看得懂的格式。

gettimeofday:

image-20240305163715736

localtime:

image-20240305170401246

void logmessage(int level, const char *format, ...)
{
    time_t t = time(nullptr);
    struct tm *ctime = localtime(&t);
    printf("%d-%d-%d %d:%d:%d", ctime->tm_year+1900, ctime->tm_mon+1, ctime->tm_mday, ctime->tm_hour, ctime->tm_min, ctime->tm_sec); 
}
2.1.2 日志分块打印

根据日志的内容,可以将一条日志信息分成两部分:默认部分自定义部分

  • 默认部分:日志的时间、等级。

  • 自定义部分:日志的内容,需要进行格式化控制的内容。

日志等级转字符串模块:

std::string LevelToString(int level)
{
    switch (level)
    {
    case Info:
        return "Info";
    case Debug:
        return "Debug";
    case Warning:
        return "Warning";
    case Error:
        return "Error";
    case Fatal:
        return "Fatal";
    default:
        return "None";
    }
}

默认部分代码:通过 snprintf 函数将默认部分的信息写入到一个字符数组中。

void DefaultMessage(int level, char *defaultbuffer, int size)
{
    time_t t = time(nullptr);
    struct tm *ctime = localtime(&t);
    snprintf(defaultbuffer, size, "[%s][%d-%d-%d %d:%d:%d]", LevelToString(level), ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday, ctime->tm_hour, ctime->tm_min, ctime->tm_sec);
}

自定义部分代码:通过 vsnprintf 函数来帮助我们解析用户的格式化输入,将用户自定义的信息写入到一个字符数组中。

void logmessage(int level, const char *format, ...)
{
    char defaultbuffer[SIZE];  // 存储默认内容
    DefaultMessage(level, defaultbuffer, sizeof(defaultbuffer));

    char userbuffer[SIZE]; // 存储自定义内容
    va_list s;
    va_start(s, format);
    vsnprintf(userbuffer, sizeof(userbuffer), format, s);
    va_end(s);
}

将默认内容和自定义内容合并

void logmessage(int level, const char *format, ...)
{
    char defaultbuffer[SIZE];  // 存储默认内容
    DefaultMessage(level, defaultbuffer, sizeof(defaultbuffer));

    char userbuffer[SIZE]; // 存储自定义内容
    va_list s;
    va_start(s, format);
    vsnprintf(userbuffer, sizeof(userbuffer), format, s);
    va_end(s);

    char logtxt[SIZE*2];
    snprintf(logtxt, sizeof(logtxt), "%s %s", defaultbuffer, userbuffer);
    std::cout << logtxt << std::endl;
}

2.2 打印模式选择

可以通过设置选项,将日志信息打印到:**显示器、一个文件、多个文件(同等级的在一个文件)。**抽象一个 Log 类,在里面设置一个成员变量来选择日志的输出。

模式设置:

void SetStyle(int style)
{
    _outputstyle = style;
}

模式选择:

void OutPutLog(int level, const std::string &message)
{
    switch (_outputstyle)
    {
        case Screen: // 向显示器打印
            OutPutToScreen(message);
            return;
        case Onefile: // 向一个文件中打印
            OutPutToOnefile(_logpath, message);
            return;
        case Classfile: // 向多个文件中打印
            OutPutToClassfile(level, message);
            return;
    }
}

向显示器打印:

// 将日志信息打印到屏幕
void OutPutToScreen(const std::string &message)
{
    std::cout << message << std::endl;
}

向一个文件中写入:

// 将日志信息保存到一个文件中
void OutPutToOnefile(const std::string &path, const std::string &message)
{
    // 打开文件
    std::fstream fp;
    fp.open(path, std::ios::app);
    if (!fp.is_open())
    {
        std::cout << path << " open faile" << std::endl;
    }

    // 向文件写入
    fp << message << std::endl;

    // 关闭文件
    fp.close();
}

向多个文件中写入:

// 将日志信息按照等级保存到不同文件中
void OutPutToClassfile(int level, const std::string &message)
{
    switch (level)
    {
        case Info:
            OutPutToOnefile(INFO_LOG_PATH, message);
            break;
        case Debug:
            OutPutToOnefile(DEBUG_LOG_PATH, message);
            break;
        case Warning:
            OutPutToOnefile(WARING_LOG_PATH, message);
            break;
        case Error:
            OutPutToOnefile(ERROR_LOG_PATH, message);
            break;
        case Fatal:
            OutPutToOnefile(FATAL_LOG_PATH, message);
            break;
        default:
            break;
    }

    return;
}

operator() 让调用显得更加优雅:

void operator()(int level, const char *format, ...)
{
    char defaultbuffer[SIZE]; // 存储默认内容
    DefaultMessage(level, defaultbuffer, sizeof(defaultbuffer));

    char userbuffer[SIZE]; // 存储自定义内容
    va_list s;
    va_start(s, format);
    vsnprintf(userbuffer, sizeof(userbuffer), format, s);
    va_end(s);

    char logtxt[SIZE * 2];
    snprintf(logtxt, sizeof(logtxt), "%s %s", defaultbuffer, userbuffer);
    // std::cout << logtxt << std::endl;

    OutPutLog(level, logtxt);
}

2.3 Log 使用样例

#include "log.hpp"
#include <stdlib.h>
#include <unistd.h>

int main()
{
    Log log;
    int cnt = 10;
    while (cnt--)
    {
        if(cnt == 5) log.SetStyle(Classfile);
        log(Info, "I am %d %s %f", 2, "wuchengyang", 3.14);
        log(Debug, "I am %d %s %f", 3, "wuchengyang", 4.78);
        log(Fatal, "I am %d %s %f", 4, "wuchengyang", 5.32);
        sleep(1);
    }

    return 0;
}

2.4 Log 完整源码

#pragma once
#include <stdarg.h>
#include <iostream>
#include <time.h>
#include <fstream>

#define SIZE 1024
// 定义日志等级
#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4

// 日志输出
#define Screen 1
#define Onefile 2
#define Classfile 3

// 存储日志信息的目录
#define LOG_PATH "./Log/log.txt"
#define INFO_LOG_PATH "./Log/InfoLog.txt"
#define DEBUG_LOG_PATH "./Log/DebugLog.txt"
#define WARING_LOG_PATH "./Log/WaringLog.txt"
#define ERROR_LOG_PATH "./Log/ErrorLog.txt"
#define FATAL_LOG_PATH "./Log/FatalLog.txt"

class Log
{
public:
    Log(const std::string &logpath = LOG_PATH, int style = Onefile)
        : _logpath(logpath),
          _outputstyle(style)
    {
    }

private:
    std::string LevelToString(int level)
    {
        switch (level)
        {
        case Info:
            return "Info";
        case Debug:
            return "Debug";
        case Warning:
            return "Warning";
        case Error:
            return "Error";
        case Fatal:
            return "Fatal";
        default:
            return "None";
        }
    }

    void DefaultMessage(int level, char *defaultbuffer, int size)
    {
        time_t t = time(nullptr);
        struct tm *ctime = localtime(&t);
        snprintf(defaultbuffer, size, "[%s][%d-%d-%d %d:%d:%d]", LevelToString(level).c_str(), ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday, ctime->tm_hour, ctime->tm_min, ctime->tm_sec);
    }

    // 将日志信息打印到屏幕
    void OutPutToScreen(const std::string &message)
    {
        std::cout << message << std::endl;
    }

    // 将日志信息保存到一个文件中
    void OutPutToOnefile(const std::string &path, const std::string &message)
    {
        // 打开文件
        std::fstream fp;
        fp.open(path, std::ios::app);
        if (!fp.is_open())
        {
            std::cout << path << " open faile" << std::endl;
        }

        // 向文件写入
        fp << message << std::endl;

        // 关闭文件
        fp.close();
    }

    // 将日志信息按照等级保存到不同文件中
    void OutPutToClassfile(int level, const std::string &message)
    {
        switch (level)
        {
        case Info:
            OutPutToOnefile(INFO_LOG_PATH, message);
            break;
        case Debug:
            OutPutToOnefile(DEBUG_LOG_PATH, message);
            break;
        case Warning:
            OutPutToOnefile(WARING_LOG_PATH, message);
            break;
        case Error:
            OutPutToOnefile(ERROR_LOG_PATH, message);
            break;
        case Fatal:
            OutPutToOnefile(FATAL_LOG_PATH, message);
            break;
        default:
            break;
        }

        return;
    }

    void OutPutLog(int level, const std::string &message)
    {
        switch (_outputstyle)
        {
        case Screen: // 向显示器打印
            OutPutToScreen(message);
            return;
        case Onefile: // 向一个文件中打印
            OutPutToOnefile(_logpath, message);
            return;
        case Classfile: // 向多个文件中打印
            OutPutToClassfile(level, message);
            return;
        }
    }

public:
    void SetStyle(int style)
    {
        _outputstyle = style;
    }

    // void logmessage(int level, const char *format, ...)
    // {
    //     char defaultbuffer[SIZE]; // 存储默认内容
    //     DefaultMessage(level, defaultbuffer, sizeof(defaultbuffer));

    //     char userbuffer[SIZE]; // 存储自定义内容
    //     va_list s;
    //     va_start(s, format);
    //     vsnprintf(userbuffer, sizeof(userbuffer), format, s);
    //     va_end(s);

    //     char logtxt[SIZE * 2];
    //     snprintf(logtxt, sizeof(logtxt), "%s %s", defaultbuffer, userbuffer);
    //     // std::cout << logtxt << std::endl;

    //     OutPutLog(level, logtxt);
    // }

    void operator()(int level, const char *format, ...)
    {
        char defaultbuffer[SIZE]; // 存储默认内容
        DefaultMessage(level, defaultbuffer, sizeof(defaultbuffer));

        char userbuffer[SIZE]; // 存储自定义内容
        va_list s;
        va_start(s, format);
        vsnprintf(userbuffer, sizeof(userbuffer), format, s);
        va_end(s);

        char logtxt[SIZE * 2];
        snprintf(logtxt, sizeof(logtxt), "%s %s", defaultbuffer, userbuffer);
        // std::cout << logtxt << std::endl;

        OutPutLog(level, logtxt);
    }

    ~Log()
    {
    }

private:
    int _outputstyle;
    std::string _logpath;
};

🎁结语:

        今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,您的支持就是我前进的动力!

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

库库的里昂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值