【Linux】简易日志系统

news2024/9/21 19:20:09

目录

一、概念

二、可变参数

三、日志系统


一、概念

一个正在运行的程序或系统就像一个哑巴,一旦开始运行我们很难知晓其内部的运行状态。

但有时在程序运行过程中,我们想知道其内部不同时刻的运行结果如何,这时一个日志系统可以有效的帮助我们监控程序的运行状态。

如果系统或程序发生了错误或存在bug,通过日志的内容我们也可以很快的知道故障的原因并定位错误的位置

一个成熟的日志至少需要包含以下信息:

  • 日志时间
  • 日志等级

根据情况可将日志划分为不同的等级,例如常规信息、警告信息、严重错误、致命错误、调试信息

  • 日志内容
  • 文件名称或行号


二、可变参数

日志的内容需要我们指定格式并传参,而参数的个数是不确定的。因此在学习编写日志系统之前,我们先了解一下可变参数的用法

以下是对可变参数进行操作时需要用到的函数/宏

#include <stdarg.h>

void va_start(va_list ap, last);
type va_arg(va_list ap, type);
void va_end(va_list ap);

我们以一个可以同时累加多个变量的函数为例:

int sum(int n, ...)
{}

形参在实例化时会从右向左进行压栈,也就是说多个参数在函数栈帧中是连续的,因此我们可以通过地址的偏移来依次访问到所有的参数

首先:

int sum(int n, ...)
{
    va_list s;
    va_start(s, n);
}

其中va_list实际上就是char*, 而va_start可以让s指向参数n的下一个参数,也就是可变参数的第一个参数的位置。此时我们就有了获取第一个参数内容的前提

这也是为什么printf等支持可变参数的函数中必须至少要有一个确定的参数,有了该参数才能找到可变参数的起始地址

int sum(int n, ...)
{
    va_list s;
    va_start(s, n);

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

其中,va_arg传入s和可变参数的类型,用于提取s指向的参数,并且移动s到下一个参数的位置

va_end将s置为空

测试效果:

拓展问题:如果可变参数中,不同参数有不同的类型怎么办?

这也是为什么printf的第一个参数需要传入一个用于控制格式的字符串,通过遍历字符串就能知道可变参数中有哪些类型了


三、日志系统

本文实现的日志系统具备以下功能: 

  • 包含日志等级、日志时间、日志内容
  • 将日志功能封装成类,并重载了函数调用运算符
  • 可以选择将日志输出到终端、输出到同一文件或按照日志等级分类输出到不同文件
  • 用户可自定义日志内容格式

如果要让日志包含文件名和行号,则可以通过宏定义__FILE__和__LINE__获取文件名和行号

接下来是完整代码(附注释)

#pragma once

#include <iostream>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>

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

#define SIZE 1024 // 缓冲区大小

// 日志的输出方式
#define Screen 1    // 输出到显示器
#define Same_file 2 // 输出到同一文件
#define Diff_file 3 // 按照等级输出到不同文件

#define Filename "log.txt"

class Log
{
public:
    Log()
    {
        _method = Screen; // 默认输出到显示器
    }

    void output(int method) // 更改输出方式
    {
        _method = method;
    }

    std::string level2string(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 operator()(int level, const char *format, ...)
    {
        va_list s;
        va_start(s, format); // s指向可变参数
        messagehandle(level, format, s);
    }

    void messagehandle(int level, const char *format, va_list s) // 整合日志字符串
    {
        time_t t = time(nullptr);         // 获取时间戳
        struct tm *ctime = localtime(&t); // 将时间戳转换为时间
        char levelAndtime[SIZE];          // 日志等级和时间部分
        snprintf(levelAndtime, sizeof(levelAndtime), "[%s][%d-%d-%d %02d:%02d:%02d]", level2string(level).c_str(),
                 ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday, ctime->tm_hour, ctime->tm_min, ctime->tm_sec);

        char content[SIZE]; // 用户自定义的内容部分
        vsnprintf(content, sizeof(content), format, s);
        va_end(s);

        char message[SIZE * 2]; // 整合所有部分
        snprintf(message, sizeof(message), "%s %s\n", levelAndtime, content);

        OutputLog(level, message); // 将整合后的日志输出
    }

    void OutputLog(int level, const std::string &logmessage)
    {
        switch (_method) // 根据输出方式进行调整
        {
        case Screen: // 输出到显示器
            std::cout << logmessage << std::endl;
            break;
        case Same_file: // 输出到同一文件
            SamefileOutput(Filename, logmessage);
            break;
        case Diff_file: // 输出到不同文件
            DiffileOutput(level, logmessage);
            break;
        default:
            break;
        }
    }

    void SamefileOutput(const std::string &filename, const std::string &logmessage)
    {
        int fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); //打开文件
        if(fd < 0) //打开失败
            return;
        write(fd, logmessage.c_str(), logmessage.size()); //写入日志
        close(fd); //关闭文件描述符
    }

    void DiffileOutput(int level, const std::string &logmessage)
    {
        std::string filename = Filename;
        filename += ".";
        filename += level2string(level); //根据日志等级调整文件名
        SamefileOutput(filename, logmessage); //复用SamefileOutput函数
    }

    ~Log()
    {}

private:
    int _method; // 输出方式
};

测试:

向显示器输出日志(n%5用于模拟不同日志等级)

向同一文件中输出日志

向不同文件中输出日志

完.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2153106.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【算法】2022年第十三届蓝桥杯大赛软件类省赛Java大学C组真题

个人主页&#xff1a;NiKo 算法专栏&#xff1a;算法设计与分析 目录 题目 2680:纸张尺寸 题目 2664:求和 题目 2681: 矩形拼接 题目 2665: 选数异或 题目 2682: GCD 题目 2667: 青蛙过河 题目 2683: 因数平方和 题目 2668: 最长不下降子序列 题目 2680:纸张尺寸 题目…

2024普通高校大学生竞赛

2024年A类竞赛(全国普通高校大学生竞赛)目录 结合A类竞赛目录选择一些大学高质量竞赛 大学生竞赛 大学生竞赛中国国际大学生创新大赛ACM-ICPC国际大学生程序设计竞赛中国大学生计算机设计大赛中国高校计算机大赛蓝桥杯全国软件和信息技术专业人才大赛百度之星程序设计大赛全国…

2016年国赛高教杯数学建模A题系泊系统的设计解题全过程文档及程序

2016年国赛高教杯数学建模 A题 系泊系统的设计 近浅海观测网的传输节点由浮标系统、系泊系统和水声通讯系统组成&#xff08;如图1所示&#xff09;。某型传输节点的浮标系统可简化为底面直径2m、高2m的圆柱体&#xff0c;浮标的质量为1000kg。系泊系统由钢管、钢桶、重物球、…

KDD 2024论文分享┆STAMP:一种基于时空图神经网络的微服务工作负载预测方法

论文分享简介 本推文详细介绍了一篇最新论文成果《Integrating System State into Spatio Temporal Graph Neural Network for Microservice Workload Prediction》&#xff0c;论文的作者包括&#xff1a;上海交通大学先进网络实验室: 罗旸、高墨涵、余哲梦&#xff0c;高晓沨…

gh-ost

优质博文&#xff1a;IT-BLOG-CN 一、gh-ost的作用 gh-ost是由Github提供的Online DDL工具&#xff0c;使用binlog代替之前的触发器做异步增量数据同步&#xff0c;从而降低主库负载。 基于触发器的Online DDL工具原理&#xff1a; 【1】根据原表结构执行alter语句&#xff…

抖音矩阵系统源码搭建,矩阵系统贴牌,矩阵工具开源

1. 抖音短视频矩阵系统 抖音短视频矩阵系统&#xff0c;是指通过抖音平台&#xff0c;以矩阵的形式进行短视频创作、发布和传播的一种模式。它以多样化的内容、丰富的表现形式、高度的专业化和协同性&#xff0c;吸引了大量用户和创作者的关注。 2. 短视频矩阵系统的优势 2.1 …

从技术打磨到产品验证:读《程序员修炼之道》的务实之道

在编程世界里&#xff0c;技术的打磨往往像是工匠雕琢作品&#xff0c;但若无法转化为产品的成功&#xff0c;所有的精致都不过是空中楼阁。读《程序员修炼之道》时&#xff0c;我深刻意识到&#xff0c;务实不仅仅是技术的选择&#xff0c;更是产品迭代和商业模式成功的关键。…

C# .net6 开发数据采集软件(一)

功能&#xff1a; 数据采集&#xff1a;采集任务 数据分析&#xff1a;数据可视化 其他功能&#xff1a;数据上传、数据下拉、软件更新 软件设置&#xff1a;PLC配置、任务配置、软件配置、可视化配置 更多功能&#xff1a;其他软件的入口&#xff0c;或者小工具的使用。比…

打印机无法打印是怎么回事_打印机无法打印多种解决方法

打印机无法打印是怎么回事&#xff1f;我们在使用打印机的时候&#xff0c;可能会遇到打印机无法打印的问题&#xff0c;该问题原因有很多。下面小编就教大家打印机无法打印多种解决方法。 打印机无法打印多种解决方法&#xff1a; 打印机无法打印解决方法一&#xff1a;纸张问…

iOS 超强插件注入神器,Trollfools迎来二次进化

长期以来&#xff0c;注入插件是越狱iPhone的专利。对巨魔玩家来说&#xff0c;越狱用户是如同“上游供应链”一样的存在。 很多增强版的APP&#xff0c;其实都是越狱玩家制作、分享的。直到Trollfools诞生&#xff0c;才彻底扭转了这一现状。 Trollfools是什么&#xff1f;简…

吴津雨银洁刘雅雯获得国际超模大赛四川总决赛网络组三甲

9月8日众人期盼已久的都江堰杯2024国际超模大赛四川总决赛在三遗之城都江堰落下帷幕。国际超模大赛已经举办第12个年头&#xff0c;每年为时尚界、模特界输送无数的优秀时尚模特人才&#xff0c;让世界超模中出现更多的中国面孔。大赛在全球已经布局多个国家及地区&#xff0c;…

攻防世界---->Windows_Reverse1

学习笔记。 前言&#xff1a;不会&#xff0c;代码越简洁&#xff0c;越难受 T ^ T 下载 查壳。 UPX脱壳。 此题脱壳后的程序&#xff0c;是不能运行的。 网上wp&#xff0c;说是因为作者采用了ASLR(地址随机化) 解决方法&#xff1a;一&#xff1a;用XP运行调试。 方法二&a…

基于单片机汽车驾驶防瞌睡防疲劳报警器自动熄火设计

文章目录 前言资料获取设计介绍功能介绍设计程序具体实现截图设计获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师&#xff0c;一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对…

【算法竞赛】栈

栈的特点是"先进后出"。 栈在生活中的原型有:坐电梯,先进电梯的被挤在最里面,只能最后出来&#xff1b;一管泡腾片,最先放进管子的药片位于最底层&#xff0c;最后被拿出来。 栈只有唯一的出入口,从这个口进入,也从这个口弹出,这是它与队列最大的区别。 队列有一个入…

李宏毅机器学习2023HW12—Reinforcement Learning强化学习

文章目录 TaskBaselineSimpleMedium Baseline—Policy GradientStrong Baseline——Actor-CriticBoss Baseline—Mask Task 实现深度强化学习方法: Policy GradientActor-Critic 环境&#xff1a;月球着陆器 Baseline Simple 定义优势函数(Advantage function)为执行完ac…

传统到AI 大数据分析的演变,颠覆智慧水电的未来?

传统到AI 大数据分析的演变&#xff0c;颠覆智慧水电的未来&#xff1f; 前言传统到AI 大数据分析的演变 前言 水电作为一种重要的能源形式&#xff0c;一直在我们的生活中扮演着至关重要的角色。而如今&#xff0c;随着科技的飞速发展&#xff0c;智慧水电和 AI 大数据应用的…

服务器安全,你必须知道的六个知识点

服务器安全 如今没有什么是安全的。各种系统安全漏洞的数量呈爆炸式增长。令人担忧的主要原因之一是服务器安全性。 接下来&#xff0c;就如何提升服务器安全&#xff0c;写几点见解。 虽然很多企业在服务器的安全性方面做了足够多&#xff0c;但是&#xff0c;黑客仍然能够…

Java项目实战II基于Java+Spring Boot+MySQL的卓越导师双选系统设计与实现(源码+数据库+文档)

目录 一、前言 二、技术介绍 三、系统实现 四、论文参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者 一、前言 在当今高等教育环境中&#xff0c;师生之间的有效沟通与匹配对于促进学生发展、提升教学质量至关重要…

蓝桥杯—STM32G431RBT6(ADC数模转换,从原理到应用)

一、什么是ADC&#xff1f; ADC&#xff08;Analog-to-Digital Converter&#xff09;即模数转换器。它是一种将模拟信号转换为数字信号的电子器件。在电子系统中&#xff0c;ADC 起着至关重要的作用&#xff0c;它能将连续变化的模拟量&#xff08;如电压、电流等&#xff09;…

openstack中的rabbitmq

基本概念 基础介绍 exchange&#xff1a;用于分发信息&#xff0c;有direct、fanout、topic、headers&#xff1b; binding&#xff1a;exchange、queue之间的虚拟连接&#xff0c;由一个或者多个routing key组成&#xff1b; queues&#xff1a;用来暂存消息&#xff0c;供…