日志Log程序(C++)

17 篇文章 2 订阅

        本篇将使用 C/C++ 代码实现一个日志程序,便于我们在调试程序的时候,打印出对应的日志信息,同时也可以重定向然后记录我们的日志信息。

        日志:软件运行的记录信息,可以向显示器打印,也可以向文件中打印。

        本篇中设置出的日志特定格式:

        [日志等级][pid][filename][filenumber][time] 日志内容(支持可变参数)

        其中日志等级分为:DEBUG(调试信息)、INFO(正常信息)、WARNING(存在问题,但不影响正常运行)、ERROR(存在错误,但还是希望尽量不要退出程序)、FATAL(致命错误,直接退出)。

        实现的日志程序如下:

        Log.hpp:

#pragma once
#include <iostream>
#include <string>
#include <cstdarg>
#include <cstring>
#include <fstream>
#include <sys/types.h>
#include <pthread.h>
#include <unistd.h>

namespace log_ns {
    enum { DEBUG = 1, INFO, WARNING, ERROR, FATAL };

    // 定义日子真正需要记录的信息
    struct LogMessage {
        std::string _level;
        int _id;
        std::string _filename;
        int _filenumber;
        std::string _curtime;
        std::string _log_message;
    };

    #define SCREEN_TYPE 1
    #define FILE_TYPE   2

    const std::string defaultlogfile = "./log.txt";

    pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER;

    class Log {
    private:
        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 CurTime() {
            // 获取当前的时间戳
            time_t curtime = time(nullptr);
            // 将当前时间戳转换成结构体
            struct tm* now = localtime(&curtime);
            char buff[128];
            snprintf(buff, sizeof(buff), "%d-%02d-%02d %02d:%02d:%02d", 
                now->tm_year + 1900,
                now->tm_mon + 1,
                now->tm_mday,
                now->tm_hour,
                now->tm_min,
                now->tm_sec
            );
            return buff;
        }

        void Flush(const LogMessage& lg) {
            // 打印日志的时候可能存在线程安全,使用锁lock住
            pthread_mutex_lock(&log_lock);
            switch(_type) {
                case SCREEN_TYPE:
                    FlushToScreen(lg);
                    break;
                case FILE_TYPE:
                    FlushToFile(lg);
                    break;
            }
            pthread_mutex_unlock(&log_lock);
        }

        void FlushToFile(const LogMessage& lg) {
            std::ofstream out;
            out.open(_logfile, std::ios::app); // 文件的操作使用追加
            if (!out.is_open()) return;
            
            char buff[2024];
            snprintf(buff ,sizeof(buff), "[%s][%d][%s][%d][%s] %s",
                lg._level.c_str(),
                lg._id,
                lg._filename.c_str(),
                lg._filenumber,
                lg._curtime.c_str(),
                lg._log_message.c_str()
            );            

            out.write(buff, strlen(buff));

            out.close();
        }

        void FlushToScreen(const LogMessage& lg) {
            printf("[%s][%d][%s][%d][%s] %s",
                lg._level.c_str(),
                lg._id,
                lg._filename.c_str(),
                lg._filenumber,
                lg._curtime.c_str(),
                lg._log_message.c_str()
            );
        }

    public:
        Log(std::string logfile = defaultlogfile)
            : _type(SCREEN_TYPE),
              _logfile(logfile)
        {}

        void Enable(int type) {
            _type = type;
        }

        void LoadMessage(std::string filename, int filenumber, int level, const char* format, ...) {
            LogMessage lg;
            lg._level = LevelToString(level);
            lg._filename = filename;
            lg._filenumber = filenumber;
            // 获取当前时间
            lg._curtime = CurTime();
            // std::cout << lg._curtime << std::endl;
            lg._id = getpid();

            // 获取可变参数
            va_list ap;
            va_start(ap, format);
            char buff[2048];
            vsnprintf(buff, sizeof(buff), format, ap);
            va_end(ap);
            lg._log_message = buff;
            // std::cout << lg._log_message;
            Flush(lg);
        }

        void ClearOurFile() {
            std::ofstream out;
            out.open(_logfile);
            out.close();
        }

        ~Log() {}
    private:
        int _type;
        std::string _logfile;
    };

    Log lg;

// LOG 宏
#define LOG(level, format, ...)                                           \
    do                                                                    \
    {                                                                     \
        lg.LoadMessage(__FILE__, __LINE__, level, format, ##__VA_ARGS__); \
    } while (0)

#define EnableToScreen()        \
    do                          \
    {                           \
        lg.Enable(SCREEN_TYPE); \
    } while (0)

#define EnableToFile()        \
    do                        \
    {                         \
        lg.Enable(FILE_TYPE); \
    } while (0)

// 清理文件
#define ClearFile()        \
    do                     \
    {                      \
        lg.ClearOurFile(); \
    } while (0)
}

        实现如上代码之后,我们只需要将在需要进行日志打印出直接使用宏 LOG 就可以进行日志打印了(同时该 LOG 支持类似与 printf 函数的可变参数打印),同时宏 EnableToFile 和 EnableToScreen 可以选择是在屏幕上打印还是在文件中打印(文件打印可以指定文件位置,不指定则是默认的当前路径下的文件)。

        以后想要打印 debug 程序完全就可以使用 LOG 宏代替我们的打印语句,测试如下:

        可以自动的记录当前打印的 LOG 宏在哪个文件,哪一行,以及当前进程的 pid。

        将其放入到我们的线程池程序中进行测试,如下:

  • 13
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值