当前位置: 首页 > news >正文

『 Linux 』简单日志插件

文章目录

    • 什么是日志
    • 应用日志的设计要点
    • 简单应用日志插件设计编码
      • 大概框架及所需头文件与定义
      • 日志信息的输出
      • 日志的输出方式
      • operator()重载


什么是日志

请添加图片描述

日志(log)是一种记录和存储系统运行状态,事件,和操作历史的文件和数据库,这些记录通常以时间顺序排列且详细记录系统中发生的各种活动;

日志的类型常见有以下几种:

  • 系统日志

    记录操作系统级别的事件,如启动,关机,错误和警告等;

  • 应用日志:

    记录应用程序运行过程中的事件,如用户操作,错误,状态变更等;

  • 安全日志

    记录系统和应用程序的安全相关事件,如登录尝试,权限变更等;

日志的作用为以下:

  • 问题诊断和故障排查

    日志可以帮助系统管理员和开发人员诊断和解决问题;

  • 系统监控和维护

    通过分析日志,管理员可以监控系统的运行状态并且识别潜在的问题和异常;

  • 安全审计和合规性

    日志记录系统和应用程序的各种安全事件以帮助进行安全审计,同时确保系统符合安全和法规要求;

  • 性能分析和优化

    日志数据可以用于分析系统和应用程序的性能,并间接提升系统效率和响应速度;

  • 历史记录和追踪

    日志提供了系统和应用程序运行过程中的详细历史记录,可以用于追踪和回溯系统的操作和变更;

日志记录通常包含以下基本信息:

  • 时间戳

    记录事件发生的具体事件;

  • 事件类型

    记录事件的类型,如错误,警告,信息等;

  • 事件来源

    知名事件是由哪个系统组件或者是应用程序生成的;

  • 事件描述

    详细描述事件的内容和相关信息;

本文主要简单设计一个简单应用日志;


应用日志的设计要点

请添加图片描述

应用日志专门记录应用程序正在运行过程中发生的各种事件,错误,状态变化等信息;

应用日志可以帮助跟踪程序的执行过程并进行诊断从而了解具体问题;

应用日志的日志级别主要如下:

  • DEBUG

    详细的信息,一般用于诊断问题,经常用于开发过程;

  • INFO

    普通操作信息,用于记录应用程序的正常运行情况;

  • WARNING

    警告信息,标明看那会出现问题的情况,但程序仍可以正常运行;

  • ERROR

    错误信息,标明程序中的某些功能出现问题需要引起注意;

  • CRITICAL/FATAL

    严重错误(致命)信息,标明程序可能无法继续运行,需要立即解决;

日志的统一格式有助于日志的解析和分析;

常见的格式包括JSON,XML或简单的纯文本格式;

如:

2024-07-15 10:00:00 [INFO] [module_name] - This is an info message.
2024-07-15 10:01:00 [ERROR] [module_name] - An error occurred.

日志的存储通常有以下几种:

  • 文件

    将日志记录到文件中,方便查看和归档;

  • 数据块

    将日志存储到数据库中,便于查询和分析;

  • 日志管理系统

    使用专门的日志管理工具,如ELK堆栈,进行集中化管理和分析;


简单应用日志插件设计编码

请添加图片描述

主要思路为封装一个为log的类;

在类中实现根据错误编码产生的日志并使用对应的方式打印至显示器或写入文件中;

文件为:

  • log.hpp

    该文件主要封装日志插件并实现;

  • makefile

    自动化构建过程文件;

  • test.cc

    测试文件;


大概框架及所需头文件与定义

请添加图片描述

#pragma once#include <fcntl.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>#include <iostream>#define INFO 0     // 常规信息
#define DEBUG 1    // debug信息
#define WARNING 2  // 警告信息
#define ERROR 3    // 错误信息
#define FATAL 4    // 致命错误信息#define SCREEN 1    // 向显示器文件写入
#define ONEFILE 2   // 向单个文件进行写入
#define CLASSIFY 3  // 分类将日志进行写入#define LOG_BUFFER_SIZE 1024  // 日志所需大小#define LogFile "log.txt"  // 默认日志文件文件名class Log {public:Log() {  // 构造函数 用于初始化默认打印方式以及设置默认路径printMethod = SCREEN;path = "./log/";}~Log() { ; }  // 析构函数 未使用private:int printMethod;   // 打印风格std::string path;  // 默认路径
};

构造函数用于初始化默认打印风格以及初始化默认路径;

利用 #define 定义出可能需要使用的常量同时包含所需头文件;


日志信息的输出

请添加图片描述

此处使用的日志输出格式为:

[loglevel][time] logmassage

日志的格式主要为 : 默认部分 + 自定义部分;

默认部分一般为 日志等级日志时间 ;

  • 日志等级

    日志等级需要以字符串的形式进行显示,则可以使用switch() case:对日志等级进行返回;

      const 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";}}
    

    需要注意此处返回的是std::string类型,若是需要打印字符串或是当做字符串使用需要调用其string::c_str();

  • 日志时间

    日志时间一般采用时间戳的方式获取;

    常见的系统调用接口有time()系统调用接口与gettimeofday()系统调用接口,此处使用time();

    NAMEtime - get time in secondsSYNOPSIS#include <time.h>time_t time(time_t *t);DESCRIPTIONtime() returns the time as the number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).If t is non-NULL, the return value is also stored in the memory pointed to by t.RETURN VALUEOn  success,  the  value  of  time in seconds since the Epoch is returned.  On error, ((time_t) -1) is returned, and errno is setappropriately.
    

    调用time()系统调用接口将会返回time_t类型,实际打印的是未格式化的时间戳;

    若是需要格式化需要调用localtime()接口将时间戳进行实例化;

    NAMEasctime,  ctime,  gmtime,  localtime,  mktime, asctime_r, ctime_r, gmtime_r, localtime_r - transform date and time to broken-downtime or ASCIISYNOPSIS#include <time.h>struct tm *localtime(const time_t *timep);

    其会返回一个struct tm类型的结构体,其结构体构造如下:

               struct tm {int tm_sec;         /* seconds */int tm_min;         /* minutes */int tm_hour;        /* hours */int tm_mday;        /* day of the month */int tm_mon;         /* month */int tm_year;        /* year */int tm_wday;        /* day of the week */int tm_yday;        /* day in the year */int tm_isdst;       /* daylight saving time */};
    

    可根据需要获取结构中数据;

    可用printf打印进行debug显示数据是否正确:

     printf("%d-%d-%d %d:%d:%d\n", ctime->tm_year + 1900, ctime->tm_mon + 1,ctime->tm_mday, ctime->tm_hour, ctime->tm_min, ctime->tm_sec);
    

    time()系统调用接口中介绍了时间戳的开始时间,为了显示正确时间需要加上对应的数值;

自定义部分需要使用可变参数列表即...;

需要调用vsnprintf()来获取用户自定义的内容:

NAMEprintf, fprintf, sprintf, snprintf, vprintf, vfprintf, vsprintf, vsnprintf - formatted output conversionSYNOPSIS#include <stdarg.h>int vsnprintf(char *str, size_t size, const char *format, va_list ap);Feature Test Macro Requirements for glibc (see feature_test_macros(7)):snprintf(), vsnprintf():_BSD_SOURCE || _XOPEN_SOURCE >= 500 || _ISOC99_SOURCE || _POSIX_C_SOURCE >= 200112L;or cc -std=c99DESCRIPTIONThe  functions  in  the  printf()  family  produce  output  according to a format as described below.  The functions printf() andvprintf() write output to stdout, the standard output stream; fprintf() and vfprintf() write output to the given  output  stream;sprintf(), snprintf(), vsprintf() and vsnprintf() write to the character string str.

在使用可变参数的时候需要定义va_list变量来表示可变参数;

并且使用va_start()来初始化一个va_list类型的变量,这个变量用来访问传递给函数的可变参数;

结束使用后需要用va_end()结束对可变参数的访问;

    va_list s;va_start(s, format);char custombuffer[LOG_BUFFER_SIZE];vsnprintf(custombuffer, sizeof(custombuffer), format, s);va_end(s);

当获取结束默认部分与自定义部分后调用snprintf()将两个信息组合成一条信息,并根据接下来的需求输出日志信息;

  • 输出日志信息代码

      void logmessage(int level, const char* format, ...) {// 获取时间time_t currentTime = time(nullptr);struct tm* ctime = localtime(&currentTime);// 格式: 默认部分 + 自定义部分// 默认部分char defaultbuffer[LOG_BUFFER_SIZE];snprintf(defaultbuffer, sizeof(defaultbuffer), "[%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);// 自定义部分va_list s;va_start(s, format);char custombuffer[LOG_BUFFER_SIZE];vsnprintf(custombuffer, sizeof(custombuffer), format, s);va_end(s);// 组合char logtxt[LOG_BUFFER_SIZE * 2];snprintf(logtxt, sizeof(logtxt), "%s %s\n", defaultbuffer, custombuffer);// 组合完毕后可直接打印也可直接写到文件中// std::cout << logtxt << std::endl; - 暂时打印至显示器中;printLog(level, logtxt);}
    

日志的输出方式

请添加图片描述

可根据已定义的日志输出方式使用switch()case:分类进行输出;

  void Enable(int method) { printMethod = method; }  // 更改日志输出方式

Enable()函数用来根据用户需求更改日志的打印方式;

  void printLog(int level, const std::string& logtxt) {  // 不同方式输出日志switch (printMethod) {case SCREEN:/* code */std::cout << logtxt << std::endl;break;case ONEFILE:printOneFile(LogFile, logtxt);break;case CLASSIFY:printClassify(level, logtxt);break;default:break;}}

printLog()通过使用switch()case:对日志信息输出进行控制,输出至屏幕时直接std::cout打印至屏幕即可;

  • 将日志信息写入至同一日志文件log.txt

    思路为:

    打开文件,将日志信息进行写入最后关闭文件;

      void printOneFile(const std::string& logname,const std::string& logtxt) {  // 单个文件输出日志std::string _logname = path + logname;int fd = open(_logname.c_str(), O_CREAT | O_APPEND | O_WRONLY, 0666);if (fd < 0) {std::cerr << "open fail" << std::endl;return;}write(fd, logtxt.c_str(), logtxt.size());close(fd);}
    
  • 将日志信息进行分类归档

    思路与写进同一文件思路相同;

    创建一个std::string对象为默认的文件名,并调用levelToString()对文件名进行修改;

    最后调用printOneFile()函数即可;

     void printClassify(int level,const std::string& logtxt) {  // 分类形式输出日志std::string filename = LogFile;filename += ".";filename += levelToString(level);printOneFile(filename, logtxt);}
    

需要注意,尽量在makefile中使用mkdir命令来优先创建log目录,否则会打开文件失败;


operator()重载

请添加图片描述

调用过麻烦的话可将void logmessage(int level, const char\* format, ...)函数封装为void operator()(int level, const char* format,...)以减少调用繁琐;

void operator()(int level, const char* format,...) {  // 重载()操作符并将原本接口进行封装// 获取时间time_t currentTime = time(nullptr);struct tm* ctime = localtime(&currentTime);// printf("%d-%d-%d %d:%d:%d\n", ctime->tm_year + 1900, ctime->tm_mon + 1,//        ctime->tm_mday, ctime->tm_hour, ctime->tm_min, ctime->tm_sec);//        //打印 debug// 格式: 默认部分 + 自定义部分// 默认部分char defaultbuffer[LOG_BUFFER_SIZE];snprintf(defaultbuffer, sizeof(defaultbuffer),"[%s][%d-%02d-%02d %02d:%02d:%02d]", levelToString(level).c_str(),ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);// 自定义部分va_list s;va_start(s, format);char custombuffer[LOG_BUFFER_SIZE];vsnprintf(custombuffer, sizeof(custombuffer), format, s);va_end(s);// 组合char logtxt[LOG_BUFFER_SIZE * 2];snprintf(logtxt, sizeof(logtxt), "%s %s\n", defaultbuffer, custombuffer);// 组合完毕后可直接打印也可直接写到文件中// std::cout << logtxt << std::endl;printLog(level, logtxt);}

完整代码(供参考):

[参考代码(gitee) - DIo夹心小面包 (半介莽夫)]

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • IDEA自带的Maven 3.9.x无法刷新http nexus私服
  • Ubuntu 24.04安装Jellyfin媒体服务器图解教程
  • 【大型实战】企业网络实验(华为核心交换、ESXI7.0vmware虚拟机、DHCP中继、服务端网络及用户端网络配置)
  • 【JAVA poi-tl-ext 富文本转word】
  • GuLi商城-商品服务-API-品牌管理-JSR303自定义校验注解
  • 【JVM实战篇】内存调优:内存问题诊断+案例实战
  • 微调 Florence-2 - 微软的尖端视觉语言模型
  • 6.Dockerfile及Dockerfile常用指令
  • Windows与Ubuntu安装ffmpeg
  • 【Linux】调试器 gdb使用
  • 各个系统配置端口转发
  • [Labview] 表格单元格外边框 二维图片叠加绘图
  • 新手小白的pytorch学习第五弹-----pytorch的工作流
  • STM32智能交通监测系统教程
  • Docker存储目录问题,如何修改Docker默认存储位置?(Docker存储路径、Docker存储空间)etc/docker/daemon.json
  • 08.Android之View事件问题
  • Consul Config 使用Git做版本控制的实现
  • laravel with 查询列表限制条数
  • Shell编程
  • 那些年我们用过的显示性能指标
  • 突破自己的技术思维
  • 网页视频流m3u8/ts视频下载
  • 微信支付JSAPI,实测!终极方案
  • 译米田引理
  • 曾刷新两项世界纪录,腾讯优图人脸检测算法 DSFD 正式开源 ...
  • !! 2.对十份论文和报告中的关于OpenCV和Android NDK开发的总结
  • #Linux(Source Insight安装及工程建立)
  • #pragma pack(1)
  • (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
  • (pt可视化)利用torch的make_grid进行张量可视化
  • (react踩过的坑)antd 如何同时获取一个select 的value和 label值
  • (附源码)springboot 校园学生兼职系统 毕业设计 742122
  • (附源码)ssm失物招领系统 毕业设计 182317
  • (四)库存超卖案例实战——优化redis分布式锁
  • (已解决)vue+element-ui实现个人中心,仿照原神
  • . Flume面试题
  • .net core + vue 搭建前后端分离的框架
  • .NET 发展历程
  • .Net6使用WebSocket与前端进行通信
  • .NET处理HTTP请求
  • .NET多线程执行函数
  • .sh文件怎么运行_创建优化的Go镜像文件以及踩过的坑
  • @JsonSerialize注解的使用
  • [ vulhub漏洞复现篇 ] Celery <4.0 Redis未授权访问+Pickle反序列化利用
  • []C/C++读取串口接收到的数据程序
  • [000-01-022].第06节:RabbitMQ中的交换机介绍
  • [android]-如何在向服务器发送request时附加已保存的cookie数据
  • [AR]Vumark(下一代条形码)
  • [AutoSar]状态管理(五)Dcm与BswM、EcuM的复位实现
  • [BZOJ 3282] Tree 【LCT】
  • [Django 0-1] Core.Handlers 模块
  • [Django开源学习 1]django-vue-admin
  • [go 反射] 进阶
  • [godot] 采用状态机时,如何处理攻击时移动?如“冲撞”
  • [HITCON 2017]SSRFme 1