首页 > 系统相关 >【Linux修行路】实现一个简单的日志代码

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

时间:2024-09-02 17:51:34浏览次数:10  
标签:修行 ctime level int tm Linux return 日志

目录

⛳️推荐

一、可变参数的使用

二、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;
};

标签:修行,ctime,level,int,tm,Linux,return,日志
From: https://blog.csdn.net/m0_68662723/article/details/141784994

相关文章

  • linux shell grep命令单字匹配
    grep.sh#!/bin/bashE_BADARGS=65if[-z"$1"];thenecho"Usage:`basename$0`pattern"exit$E_BADARGSfiechoforfilein*dooutput=$(sed-n/"$1"/p$file)if[!-z"$output"];thenecho-n"......
  • Linux目录结构基础和Linux核心命令
     前言 想要了解Linux系统,我们先从它的目录结构部分说起。一、Linux目录概述Linux的目录是什么,类似于windows中的文件夹,但是它是以根目录为起始向下延伸,它的表示形式为'/'。 1.1.Linux的核心目录(根下)   根下的核心目录说明/etc/系统服务的配置文件,主要存放一些......
  • linux定时重启jar包项目
    1、创建.sh文件touchgtws.sh//创建文件vitouchgtws.sh //编辑文件#!/bin/bash#停止现有程序pkill-fdp-upload.jar#等待三秒时间sleep3#重启项目包nohupjava-jardp-upload.jar>dp-upload.log&#输出提示信息echo"定时任务重启成功"exit注意......
  • 2.Linux编程-库
    命名(习惯):静态库:.lib/.a动态库.dll/.so静态库的命名一般分为三个部分:前缀:lib库名称:自定义即可,如test后缀:.a共享库的命名一般分为三个部分:前缀:lib库名称:自己定义即可,如test后缀:.so库制作完成后,如何给用户使用头文件—包含了库函数的声明库文件—包含了库函数的代......
  • 【树莓派开发】使用树莓派在Linux环境下编写C语言代码
    文章目录前言1.创建test.c文件2.编译运行该文件3.编译并链接两个源文件结语前言如何使用树莓派编译C语言代码呢?21年暑假的时候,学习编程的劲头高涨,然后冲动消费买了个树莓派4B……结果压根不会用,吃灰了半年不过现在已经学完了C语言,也接触了一丢丢Linux系统下的gcc指令,可以尝试用它......
  • linux PS1
    最近不小心动了环境变量的PS1老是感觉很奇怪然后研究了一下PS1可以支持以下这些选项:\d:显示曰期,格式为"星期月日"。\H:显示完整的主机名。如默认主机名"localhost.localdomain"。\h:显示简写的主机名。如默认主机名"localhost"。\t:显示24小时制时间,格式为"HH:MM:SS"。......
  • Archlinux的安装
    事先申明archlinux是一个自由度很高的系统,这篇文章仅是我个人习惯!!1.U盘的制作archlinux系统的下载直接上官网上找哈!ArchLinux-Downloads下载好了以后呢,我们还需要一个U盘,大小嘛16G以上吧(我个人认为哈)我们要创作启动盘哈在windows系统里我们要先下一个烧录软件Index......
  • 阿里云上,给Linux系统增加SSH密钥对认证
    背景:当前使用密码认证。准备:确保能够使用root进行SSH登录。绑定密钥对:在阿里云控制台,打开ecs实例详情,点击全部操作按钮,选择绑定密钥对,该密钥对的公钥会被自动上传到/.ssh/authorized_keys.在putty上配置私钥,并使用root远程登陆。将密钥对的公钥复制到普通用户user1的目录下,......
  • WEB渗透Linux提权篇-提权工具合集
     往期文章WEB渗透Linux提权篇-环境变量提权-CSDN博客工具合集工具名称下载地址工具描述BadPotatohttps://github.com/BeichenDream/BadPotatoWindows权限提升BadPotatoDatabasetoolshttps://github.com/Hel10-Web/Databasetools一款用Go语言编写的数据库自动化提权工具,......
  • 折腾 Quickwit,Rust 编写的分布式搜索引擎 - 可观测性之日志管理
    Quickwit从底层构建,旨在高效地索引非结构化数据,并在云存储上轻松搜索这些数据。此外,Quickwit开箱即支持OpenTelemetrygRPC和HTTP(仅protobuf)协议,并提供了一个RESTAPI,可以接收任何JSON格式的日志。这让Quickwit成为了日志的理想选择!.https://quickwit.io/docs/guides/......