日志封装log(c++)

demo.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int add(void)
{
    return 20;
}

void test01()
{
    // int res = add(12, 23, 12);
    // printf("%d", res);
}


int main()
{
    setbuf(stdout, NULL);
    test01();
    return 0;
}

logger.cpp

#include "logger.h"

#include <cstring>
#include <cstdarg>
#include <ctime>
#include <algorithm>
#include <sys/stat.h>
#include <io.h>

using namespace std;


ofstream Logger::s_ofs;

string Logger::s_projectName("zhifubao");

string Logger::s_logDirPath;

string Logger::s_logDirName("output");

void Logger::WriteLogger(const char *filename, const char *funcname, int line, const char *level, const char *fmt, ...)
{
    // 获取当前的日志文件夹路径
    string currentFilePath = __FILE__;
    size_t pos = currentFilePath.find_last_of("/\\");
    s_logDirPath = currentFilePath.substr(0, pos) + "/" + s_logDirName;

    if (!s_ofs.is_open()) {
        struct stat dirInfo;
        string logFileName;
        // 判断日志文件夹是否存在,如果不存在,则创建一个文件夹
        if (stat(s_logDirPath.c_str(), &dirInfo) != 0) { // 不存在,则创建一个新的日志文件夹
            mkdir(s_logDirPath.c_str());
            logFileName = GenLogFileName();
        } else {
            // 如果存在
            const vector<std::string> &files = GetLogFiles();
            if (files.empty()) { // 如果为空,则创建一个新的
                logFileName = GenLogFileName();
            } else { // 如果不为空,则找到最近的那个日志文件
                logFileName = files.back();
            }
        }
        s_ofs.open(s_logDirPath + "/" + logFileName, ios::app);
    }

    // 解析不定义参数
    va_list vaList;
    va_start(vaList, fmt);
    char logMsg[LOG_MSG_MAX_LEN] = {0};
    // int vsprintf (char *__stream, const char *__format, __builtin_va_list __local_argv)
    vsprintf(logMsg, fmt, vaList);
    va_end(vaList);

    // (1)构造一条完整的日志
    // 2023-10-19 10:06:32 [login.c] [LoginCheck:50] [INFO] user admin login success
    char *p = strrchr(filename, '/');
    if (p == nullptr) {
        p = strrchr(filename, '\\');
    }
    // (2)获取当前的时间
    string currentTime = GetCurrentTime("%Y-%m-%d %H:%M:%S");
    char fullMsg[LOG_FULL_MSG_MAX_LEN] = {0};
    sprintf(fullMsg, "%s [%s] [%s:%d] [%s] %s", currentTime.c_str(), p + 1, funcname, line, level, logMsg);
    // 判断文件大小,如果超过最大大小,则把上个文件关闭,重新打开文件
    if (s_ofs.tellp() >= LOG_FILE_MAX_SIZE) {
        s_ofs.close();
        // 打开新的文件之前,要判断文件的个数,如果最大个数,则删除最远的那个日志文件
        const vector<std::string> &files = GetLogFiles();
        if (files.size() >= LOG_FILE_MAX_NUM) {
            string delFilePath = s_logDirPath + "/" + files.front();
            remove(delFilePath.c_str());
        }
        string newFileName = GenLogFileName();
        s_ofs.open(s_logDirPath + "/" + newFileName, ios::app);
    }
    // (3)写入文件中
    s_ofs << fullMsg << endl;
    s_ofs.flush(); // 刷新缓冲区,立即会写入文件
}

string Logger::GetCurrentTime(const std::string &fmt)
{
    time_t tt = time(nullptr);
    // struct tm * localtime(const time_t *_Time)
    struct tm *tmPtr = localtime(&tt);
    // size_t strftime(char *_Buf,size_t _SizeInBytes,const char *_Format,const struct tm *_Tm);
    char buffer[LOG_TIME_BUFFER_LEN] = {0};
    strftime(buffer, sizeof(buffer), fmt.c_str(), tmPtr);
    return buffer;
}

std::string Logger::GenLogFileName()
{
    // 项目名称_YYMMDD_HHMMSS.log
    string currentTime = GetCurrentTime("%Y%m%d_%H%M%S");
    string filename = s_projectName + "_" + currentTime + ".log";
    return filename;
}

std::vector<std::string> Logger::GetLogFiles()
{
    vector<string> files;
    // 如何查找某个文件夹下的日志文件
    intptr_t fileHandle;
    struct _finddata_t fileInfo;
    string dirPath = s_logDirPath + "/*.log";
    if ((fileHandle = _findfirst(dirPath.c_str(), &fileInfo)) != -1) {
        do {
            files.emplace_back(fileInfo.name);
        } while (_findnext(fileHandle, &fileInfo) == 0);
        _findclose(fileHandle);
    }
    sort(files.begin(), files.end());
    return files;
}

logger.h

#ifndef CPP0717_LOGGER_H
#define CPP0717_LOGGER_H

#include <string>
#include <fstream>
#include <vector>

#define LOGGER_INFO(fmt, ...) Logger::WriteLogger(__FILE__, __FUNCTION__, __LINE__, "INFO", fmt, ##__VA_ARGS__)
#define LOGGER_ERROR(fmt, ...) Logger::WriteLogger(__FILE__, __FUNCTION__, __LINE__, "ERROR", fmt, ##__VA_ARGS__)
#define LOGGER_WARNING(fmt, ...) Logger::WriteLogger(__FILE__, __FUNCTION__, __LINE__, "WARNING", fmt, ##__VA_ARGS__)
#define LOGGER_FATAL(fmt, ...) Logger::WriteLogger(__FILE__, __FUNCTION__, __LINE__, "FATAL", fmt, ##__VA_ARGS__)
#define LOGGER_DEBUG(fmt, ...) Logger::WriteLogger(__FILE__, __FUNCTION__, __LINE__, "DEBUG", fmt, ##__VA_ARGS__)

class Logger {
public:
    static void
    WriteLogger(const char *filename, const char *funcname, int line, const char *level, const char *fmt, ...);

private:
    // 获取当前的时间
    static std::string GetCurrentTime(const std::string &fmt);

    // 获取日志输出文件夹下的文件
    static std::vector<std::string> GetLogFiles();

    // 生成新的日志文件名称
    static std::string GenLogFileName();

    // 字节换算单位
    static const int BYTE_UNIT = 1024;
    // 最大的日志文件个数
    static const int LOG_FILE_MAX_NUM = 10;

    // 日志文件最大大小
    static const int LOG_FILE_MAX_SIZE = 10 * BYTE_UNIT * BYTE_UNIT;

    // 日志可变长参数的长度
    static const int LOG_MSG_MAX_LEN = 512;

    // 一条完整的日志最大长度
    static const int LOG_FULL_MSG_MAX_LEN = 1024;

    // 日志中时间字符串的长度
    static const int LOG_TIME_BUFFER_LEN = 32;

    // 日志写入文件流
    static std::ofstream s_ofs;

    // 项目名称
    static std::string s_projectName;

    // 日志输出的文件夹路径
    static std::string s_logDirPath;

    // 日志输出的文件夹名称
    static std::string s_logDirName;
};

#endif //CPP0717_LOGGER_H

logger_test.cpp

#include <iostream>
#include <string>
#include <vector>
#include <cstdarg>
#include <io.h>

#include "logger.h"

using namespace std;

/*
函数的不定长参数:
    ...
如何在不定长参数的函数中获取参数的值
    (1)按照地址偏移方式[本质]
    (2)借助头文件中的
        #include <stdarg.h>
        #include <cstdarg>
编码规范:
    函数的参数个数一般不超过5个
为什么:
    栈:函数的返回值,函数的形参,局部变量
    实际中,函数的形参如果比较少的时候,一般是放在寄存器中,
    由于寄存器的访问速度比栈访问速度快。所以建议函数的形参个数不宜太多
*/
int add(int a, int b, int c, ...)
{
    int *p1 = &a;
    cout << p1 << endl;
    int *p2 = &b;
    cout << p2 << endl;
    int *p3 = &c;
    cout << p3 << endl;
    cout << *(p3 + 2) << endl;
    cout << *(p3 + 4) << endl;
    return a + b;
}

int sum(int n, ...)
{
    int *p = &n;
    int total = 0;
    for (int i = 0; i < n; ++i) {
        total += *(p + 2 * (i + 1)); // 2 4 6 8 10
    }
    return total;
}

void test01()
{
    int res = add(1, 2, 3, 4, 5);
    cout << res << endl;

    int arr[] = {1, 2, 3, 4, 5};
    int *p = arr;
    /*
    指针+n,实际偏移的字节数为指针所指向的内存块类型占用的字节数 * n,
    即sizeof(类型) * n个字节
    */
    p + 1;
}

void test02()
{
    int res = sum(5, 1, 2, 3, 4, 5);
    cout << res << endl;
    res = sum(3, 1, 2, 3);
    cout << res << endl;
}

int sum2(int n, ...)
{
    va_list vaList;
    va_start(vaList, n); // 将变量n的下一个地址赋值给vaList
    int total = 0;
    for (int i = 0; i < n; ++i) {
        total += va_arg(vaList, int);
    }
    va_end(vaList); // 将vaList置为空指针
    return total;
}

void test03()
{
    int res = sum2(5, 1, 2, 3, 4, 5);
    cout << res << endl;
    // res = sum2(3, 1, 2, 3);
    // cout << res << endl;
}

// 使用stdarg获取不同类型的变量值
void func(int arg, ...)
{
    va_list vaList;
    va_start(vaList, arg);
    int a = va_arg(vaList, int);
    cout << a << endl;
    char b = va_arg(vaList, int);
    cout << b << endl;
    float c = va_arg(vaList, double);
    cout << c << endl;
    double d = va_arg(vaList, double);
    cout << d << endl;
    char *e = va_arg(vaList, char *);
    cout << e << endl;
    va_end(vaList);
}

void test04()
{
    int a = 10;
    char b = '*';
    float c = 3.14f;
    double d = 3.14;
    char e[] = "hello";
    func(5, a, b, c, d, e);
}

// int printf(const char *__format, ...)
// int vprintf(const char *__format, __builtin_va_list __local_argv)
// typedef __builtin_va_list __gnuc_va_list;
// typedef __gnuc_va_list va_list;
void info(const char *fmt, ...)
{
    va_list p;
    va_start(p, fmt);
    vprintf(fmt, p);
    va_end(p);
}

void test05()
{
    info("%d %d\n", 10, 20);
    info("%s %d %.2f\n", "china", 20, 3.1415926);
}

void test_log()
{
    LOGGER_INFO("start...");
    for (int i = 0; i < 2000000; ++i) {
        LOGGER_INFO("maping: %d", i);
    }
    LOGGER_INFO("end...");
    LOGGER_INFO("data: %s %d %.2f", "zhaosi", 10, 3.1415926);

    LOGGER_ERROR("file[%s] is not found", "zhaosi.txt");
}

/*
限制文件的个数:
    10个文件
    当前已经写了9个满的,1个正在写入
    当你要重新产生一个新的日志文件时,首先得统计一下当前一共有几个日志文件
    如果当前的文件个数>=10, 应该把最远的那个文件给删除吧
*/


int main()
{
    setbuf(stdout, nullptr);
    // test01();
    // test02();
    // test03();
    // test04();
    // test05();
    test_log();
    return 0;
}

readme.md


日志:
	为了记录程序运行时的一些关键的逻辑、警告、错误信息的。方便日后的问题定位
日志的级别:
	DEBUG: 调试日志
	INFO: 详细的常规日志
	WARNING: 警告日志
	ERROR: 常规错误日志
	FATAL: 致命错误日志
日志文件格式:
	文件格式一般都是以.log结尾
日志信息格式:
	日期 时间 模块 文件名 函数名 行号 级别 具体的信息
	2023-10-19 10:06:32 [login] [login.c] [LoginCheck:50] [INFO] user admin login success
	2023-10-19 10:06:32 [module:login] [filename:login.c] [funcname:LoginCheck] [line:50] [INFO] user admin login success

规则:
	(1)日志内容中不能出现敏感信息【手机号、密码、工号、邮箱、token、session、证书】
	(2)日志内容中不能有绝对路径
	(3)日志单个文件大小需要进行限制。10M/50M
	(4)日志需要具备转储功能,同时日志文件的个数也需要限制(7个/10个)。

注意:
	日志的打印功能需要考虑线程安全问题
```

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值