【Linux取经路】一个简单的日志模块

文章目录

  • 一、可变参数的使用
  • 二、Log
    • 2.1 日志打印
      • 2.1.1 时间获取
      • 2.1.2 日志分块打印
    • 2.2 打印模式选择
    • 2.3 Log 使用样例
    • 2.4 Log 完整源码
  • 三、结语

在这里插入图片描述

一、可变参数的使用

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

三、结语

今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,春人的主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是春人前进的动力!

在这里插入图片描述

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

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

相关文章

计算机毕业设计 | node.js(Express)+vue影院售票商城 电影放映购物系统(附源码+论文)

1&#xff0c;绪论 1.1 项目背景 最近几年&#xff0c;我国影院企业发展迅猛&#xff0c;各大电影院不断建设新的院线&#xff0c;每年新投入使用的荧幕数目逐年显著上升。这离不开人们的观影需求及对观影的过程要求的不断进步。广大观影消费者需要知道自己的空闲时间&#x…

Django中使用Celery(通用方案、官方方案)

Django中使用Celery&#xff08;通用方案、官方方案&#xff09; 目录 Django中使用Celery&#xff08;通用方案、官方方案&#xff09;通用方案场景前置准备完整代码 Celery官方方案【1】注册celery配置【2】创建celery文件【3】init注册【4】添加任务【5】启动worker异步任务…

设计模式六大原则之依赖倒置原则

文章目录 概念逻辑关系 小结 概念 依赖倒置原则指在设计代码架构时&#xff0c;高层模块不应该依赖底层模块&#xff0c;二者都应该依赖抽象。抽象不应该依赖于细节&#xff0c;细节应该依赖于抽象。 逻辑关系 如上图所示&#xff0c;逻辑应该就是这样&#xff0c;高层依赖于…

解决Wordpress中Cravatar头像无法访问问题

一、什么是Cravatar Gravatar是WordPress母公司Automattic推出的一个公共头像服务&#xff0c;也是WordPress默认的头像服务。但因为长城防火墙的存在&#xff0c;Gravatar在中国时不时就会被墙一下&#xff0c;比如本次从2021年2月一直到8月都是不可访问状态。 在以往的时候&…

R语言:单细胞pcoa降维和去批次

#生成随机颜色 > randomColor <- function() { paste0("#",paste0(sample(c(0:9, letters[1:6]), 6, replace TRUE),collapse "")) } # 生成100个随机颜色 > randomColors <- replicate(100,randomColor()) > seuratreadRDS("seu…

前端javascript包管理,npm升级用pnpm

一 pnpm 介绍 pnpm&#xff08;Package Manager&#xff09;是一个快速、节省磁盘空间的 JavaScript 包管理器&#xff0c;它是 Node.js 生态系统中 npm 的一个替代品。pnpm 解决了传统包管理工具在处理依赖时的一些痛点&#xff0c;特别是关于存储空间使用和依赖地狱的问题。…

如何将Google Search Console添加到WordPress和GA4

您想知道如何将 Google Search Console 添加到您的 Google Analytics 帐户和 WordPress 网站吗&#xff1f; 作为网站主&#xff0c;Google Search Console 是一款不能不使用的工具。对于任何想要确保其网站在 Google 搜索结果中表现良好的人来说&#xff0c;这绝对是一个必不…

leetCode-hot100-数组专题之区间问题

数组专题之区间问题 知识点&#xff1a;解决思路&#xff1a;例题56.合并区间57.插入区间253.会议室 Ⅱ485.无重叠区间 数组区间问题是算法中常见的一类问题&#xff0c;它们通常涉及对数组中的区间进行排序、合并、插入或删除操作。无论是合并区间、插入区间还是删除重复空间&…

【HarmonyOS尝鲜课】- 下载、安装DevEco Studio以及配置环境、创建运行HarmonyOS项目

下载、安装开发工具 进入DevEco Studio下载官网&#xff0c;单击“立即下载”进入下载页面。 这里以Windows为例进行安装&#xff0c;可以根据操作系统选择对应的版本进行下载。 下载完成后解压一下&#xff0c;进入文件里&#xff0c;双击应用程序&#xff0c;打开安装向导&a…

Redis主从、哨兵、集群讲解

一、Redis主从 大家在面试中可能经常会被问到Redis的高可用问题。Redis高可用回答包括两个层面&#xff0c;一个就是数据不能丢失&#xff0c;或者说尽量减少丢失 ;另外一个就是保证Redis服务不中断 。 对于尽量减少数据丢失&#xff0c;可以通过AOF和RDB保证。 对于保证服务…

linux---线程控制

线程和进程 以前我们要同时跑多个程序&#xff0c;可以通过fork()多个子进程&#xff0c;然后通过系统函数进行程序的替换&#xff0c;但是创建进程代价大&#xff0c;不仅要拷贝一份父进程的地址空间&#xff0c;页表&#xff0c;文件表述符表等。但是线程不需要因为是进程的…

windows docker desktop 更换镜像存储目录

windows docker desktop 更换镜像存储目录 方法&#xff1a;如图&#xff0c;Browse浏览一个新的目录并选中&#xff0c;确定后&#xff0c;程序会开始stop&#xff0c;在stop完成前&#xff0c;会持续迁移原有镜像到新的位置&#xff0c;你会发现目标位置的磁盘占用空间越来越…

Mac网线连接windows本【局域网互传文件】

Mac网线连接windows本【局域网互传文件】 两台电脑网线互联 Mac->网络->USP TCP/IP 手动配置IP&#xff0c;子网掩码&#xff0c;路由器 windows 网络和Internet配置->更改适配器选项->以太网->Internet协议版本4&#xff08;TCP/IPv4&#xff09;->属性 …

K8S/ hpa分享

在 Kubernetes 中&#xff0c;HorizontalPodAutoscaler 自动更新工作负载资源 &#xff08;例如 Deployment 或者 StatefulSet&#xff09;&#xff0c; 目的是自动扩缩工作负载以满足需求。 hpa的使用本身还是很简单的 示例如下&#xff1a; 官网示例 apiVersion: apps/v1 k…

NetCore发布的时候怎么去除生成的多余的语言文件夹cs,de...,Microsoft.CodeAnalysis语言资源文件

1、问题&#xff1a; .NetCore 3.1 发布出来的publish目录一大堆杂七杂八的文件夹如下图&#xff1a; 2、产生原因 由Microsoft.VisualStudio.Web.CodeGeneration.Design包导致 3、解决方法&#xff1a; 如果确实需要某种语言资源文件&#xff0c;可以这样&#xff0c;右键编…

压摆率SR、增益带宽积GBP、开环增益Aol

运放的选型对运放电路的实际效果非常关键&#xff0c;一定要理解运放重要参数的概念。下面几天将对运放的选型进行系统学习并做实验 运放的压摆率&#xff08;Slew Rate&#xff0c;简称SR&#xff09;是指闭环放大器在输入为阶跃信号时&#xff0c;输出电压时间变化率的平均值…

vue打包部署到springboot,通过tomcat运行

tomcat默认端口 8080springboot端口 9132vue 端口 9131 框架 项目是基于SpringBootVue前后端分离的仓库管理系统 后端&#xff1a;SpringBoot MybatisPlus前端&#xff1a;Node.js Vue element-ui数据库&#xff1a;mysql 一. 打包Vue项目 cmd中输入命令 npm run build 后…

深度学习之基于YoloV5-Deepsort人物识别与追踪项目

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景与目标 本项目旨在利用深度学习技术&#xff0c;结合YoloV5和Deepsort算法&#xff0c;开发一个高效、…

前端:音频可视化(H5+js版本)

一、效果展示 HTML5JS实现一个简单的音频可视化 二、代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><title>音频可视化</title><style></style></head><body><divs…

非平稳信号的傅里叶变换与短时傅里叶变换

一、仿真一个非平稳的时间序列。 N 10000; t 0:N-1; z1 4.2*sin(2*pi/20.*t5); z2 2.2*sin(2*pi/100.*(10.001*t).*t8); w1 randn(length(t),1); yz1z2w1; figure;plot(y,LineWidth,1.5);grid on; ylabel(Signal); xlabel(Time); 二、傅里叶变换&#xff08;FFT&#xff…