【Linux】Linux下的日志(日常级)

日志是日后工作中非常重要的一部分,现在写一份简单的日志项目可以帮助我们熟悉并理解原理。

设计思路:

图示是我们的最终目的。
在这里插入图片描述

  1. 设计一个类,这个类中存放着以上数据。
  2. 设计一个Log类,这个类中有一个Print函数进行按照指定格式(文件或显示器)输出,我们向这个成员函数中传参进行打印。

一些实现细节:

  • 获取时间:可以使用time函数获取时间戳,由localtime进行格式转换为我们指定的样式。
  • printf的格式输出:printf("%02d", 10);代表域宽为2,数字在右边,空余补0。
  • 可变参数的处理:

有很多的方法:
比如自己逐个提取或者利用现有的接口提取。
这并不是重点,会用即可。
可变参数的博客

我们还是最好使用接口进行提取,避免看很多麻烦的操作,那就是在这里插入图片描述
与snprintf使用方法类似,可以直接将转换后的直接写入指定字符串中!

  • 关于封装:不同功能的函数可以在成员函数与非成员函数之间自由切换(根据对成员变量的需求…)
  • 宏中的可变参数:这个__VA_ARGS__链接包含了一些宏的用法。

代码:

#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <stdarg.h>
#include <fstream>
#include <pthread.h>

namespace log_ns
{

#define SCREE_TYPE 1
#define FILE_TYPE 2

    const char *gfilename = "log.txt";
    pthread_mutex_t gmutex = PTHREAD_MUTEX_INITIALIZER;

    enum
    {
        DEBUG = 1,
        INFO,
        WARNNING,
        ERROR,
        FATAL
    };

    std::string LevelTostrint(int level)
    {
        switch (level)
        {
        case DEBUG:
            return "DEBUG";
            break;
        case INFO:
            return "INFO";
            break;
        case WARNNING:
            return "WARNING";
            break;
        case ERROR:
            return "ERROR";
            break;
        case FATAL:
            return "FATAL";
            break;
        default:
            return "Unknow";
        }
    }

    class LogMessage
    {
    public:
        std::string _level;
        pid_t _id;
        std::string _filaname;
        int _filenumber;
        std::string _curr_time;
        std::string _logmsg;
    };

    std::string GetCurTime()
    {
        time_t timestamp = time(nullptr);
        tm *ptm = localtime(&timestamp);
        char buffer[128];
        snprintf(buffer, sizeof(buffer), "%d/%02d/%02d %02d:%02d:%02d",
                 ptm->tm_year + 1900,
                 ptm->tm_mon + 1,
                 ptm->tm_mday,
                 ptm->tm_hour,
                 ptm->tm_min,
                 ptm->tm_sec);

        return buffer;
    }

    class Log
    {
    public:
        Log(int type = SCREE_TYPE, std::string FILEName = gfilename)
            : _type(type), _FILEName(gfilename)
        {}
        ~Log()
        {}
        void Print(int level, const char *filename, int filenumber, const char *msg, ...)
        {
            LogMessage lmsg;

            lmsg._level = LevelTostrint(level);
            lmsg._id = getpid();
            lmsg._filaname = filename;
            lmsg._filenumber = filenumber;
            lmsg._curr_time = GetCurTime();
            // 处理可变参数
            va_list ap;
            va_start(ap, msg);
            char buffer[1024];
            vsnprintf(buffer, sizeof(buffer), msg, ap);
            lmsg._logmsg = buffer;
            va_end(ap);
            // 打印到指定文件
            pthread_mutex_lock(&gmutex);
            Flush(lmsg);
            pthread_mutex_unlock(&gmutex);
        }
        void Enable(int type)
        {
            _type = type;
        }
        void FlushScree(const LogMessage &logMsg)
        {
            printf("[%s][%d][%s][%d][%s] %s",
                   logMsg._level.c_str(),
                   logMsg._id,
                   logMsg._filaname.c_str(),
                   logMsg._filenumber,
                   logMsg._curr_time.c_str(),
                   logMsg._logmsg.c_str());
        }
        void FlushFILE(const LogMessage &logMsg)
        {
            std::ofstream out(_FILEName.c_str(), std::fstream::app | std::fstream::out);
            if (!out.is_open())
            {
                perror("open fail");
                return;
            }
            char buffer[2048];
            snprintf(buffer, sizeof(buffer), "[%s][%d][%s][%d][%s] %s",
                     logMsg._level.c_str(),
                     logMsg._id,
                     logMsg._filaname.c_str(),
                     logMsg._filenumber,
                     logMsg._curr_time.c_str(),
                     logMsg._logmsg.c_str());
            out << buffer;
            out.close();
        }
        void Flush(const LogMessage &logMsg)
        {
            switch (_type)
            {
            case SCREE_TYPE:
                FlushScree(logMsg);
                break;
            case FILE_TYPE:
                FlushFILE(logMsg);
                break;
            }
        }

    private:
        int _type;
        std::string _FILEName;
    };

    Log lg;
#define LOG(LEVEL, format, ...)                                     \
    do                                                              \
    {                                                               \
        lg.Print(LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__); \
    } while (0)

#define EnableScree()          \
    do                         \
    {                          \
        lg.Enable(SCREE_TYPE); \
    } while (0)

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

整体来说并不是很难,但是这里的一些知识点对于有些同学过于偏僻,导致了整个处理过程有点无措。

日志的使用方法:

我们定义了宏,就避免了一些繁琐的步骤,比如创建一个对象再去调用。

另外,我们的宏也提供了改变输出文件的,使用也更方便。

#include "Log.hpp"

using namespace log_ns;

int main()
{
    EnableScree();
    LOG(DEBUG, "hello world%d\n", 666);
    EnableFILE();
    LOG(DEBUG, "hello world%d\n", 666);
    LOG(DEBUG, "hello world%d\n", 666);
    LOG(DEBUG, "hello world%d\n", 666);

    return 0;
}

当然,创建对象去调用Print也是可以的。

完~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值