用python做网站不常见,什么网站容易做,新开传奇手游发布网站,wordpress 支付宝付款#x1f466;个人主页#xff1a;Weraphael ✍#x1f3fb;作者简介#xff1a;目前正在学习c和算法 ✈️专栏#xff1a;Linux #x1f40b; 希望大家多多支持#xff0c;咱一起进步#xff01;#x1f601; 如果文章有啥瑕疵#xff0c;希望大佬指点一二 如果文章对… 个人主页Weraphael ✍作者简介目前正在学习c和算法 ✈️专栏Linux 希望大家多多支持咱一起进步 如果文章有啥瑕疵希望大佬指点一二 如果文章对你有帮助的话 欢迎 评论 点赞 收藏 加关注 目录 一、日志的概念二、储备知识之C式风格的可变参数三、获取时间四、实现打印日志函数五、封装成类并实现将日志信息打印到文件里完整代码 一、日志的概念
在编程中日志是指程序在运行时生成的记录信息和生成对应记录的时间。这些记录信息可以包括程序的状态、错误消息、警告、调试信息等。通过日志程序员可以更轻松地跟踪程序的执行过程、诊断问题并监视系统的运行情况。
常见的日志等级包括 info常规信息 warning指示可能会引起问题的情况但程序仍然可以继续执行。 error指示程序发生了错误可能需要立即处理但程序仍然能够继续执行。 fatal指示程序出现了致命问题可能导致程序无法继续执行。 debug提供关于程序详细执行过程的信息通常用于调试目的。
二、储备知识之C式风格的可变参数
在C语言中可变参数函数是一种允许函数接受不定数量参数的机制。比方说printf就可以接受不定数量参数。
#include stdio.h
int printf(const char *format, ...);实现这种功能需要使用stdarg.h头文件提供的一些宏。这些宏包括
va_list可以理解为一个用于存储所以可变参数的容器。va_start是一个宏函数它的作用是初始化一个va_list对象使其指向可变参数列表的第一个参数。以下是它的原型
void va_start(va_list ap, last_arg);其中 第一个参数是类型为va_list的对象 第二个参数是一个固定参数即可变参数列表之前的那个参数。因此可变参数之前必须要有至少一个具体的参数。
va_arg是一个宏函数访问可变参数列表中的下一个参数它的具体实现会有指针的自增操作。
type va_arg(va_list ap, type);第一个参数是类型为va_list的对象。 第二个参数是你希望从可变参数列表中获取的参数类型。
va_end清理va_list对象。
比方说定义一个可变参数函数计算所有参数的和用于演示如何编写和使用可变参数函数 【程序结果】 三、获取时间
日志中包含时间是非常重要的因为它可以帮助程序员准确地定位和跟踪问题。获取时间的方法有很多种如time函数、clock 函数、gettimeofday 函数、strftime 函数等。
这里我以localtime函数为例以上函数的具体用法大家可以自行搜索。
#include time.h
struct tm *localtime(const time_t *timep);localtime函数可以将time_t类型的时间戳转换为struct tm类型而struct tm类型有如下成员变量 需要注意的是在C语言的struct tm结构体中年份(tm_year)的起始值为1900月份(tm_mon)的起始值为0。这意味着如果你想要获取实际的年份和月份需要对tm_year和tm_mon进行一些调整。 tm_year表示从1900年开始经过的年数。因此要获取实际的年份需要将其加上1900即tm_year 1900。 tm_mon表示月份范围从0到11其中0表示一月1表示二月以此类推。因此要获取实际的月份需要将其加上1即tm_mon 1。
以下是代码示例 【程序结果】 四、实现打印日志函数
有了以上的知识我们就可以开始实现打印日志函数了。
首先规定日志的格式[时间] [等级] [用户自定义内容]
代码如下含详细注释 【函数解析】 snprintf函数用于将格式化的数据写入字符数组中。它的声明通常如下 int snprintf(char *str, size_t size, const char *format, ...);str: 指向存储输出的字符数组。size: 字符数组的大小。format: 传递给格式化字符串的数据格式与printf类似。...: 可变数量的参数这些参数根据格式字符串进行格式化。 vsnprintf函数与snprintf类似但它使用va_list类型的参数列表。这对于在函数内部处理可变参数特别有用。其声明通常如下 int vsnprintf(char *str, size_t size, const char *format, va_list ap);str: 指向存储输出的字符数组。size: 字符数组的大小format: 传递给格式化字符串的数据格式与printf类似。ap: va_list类型的参数列表由va_start、va_arg和va_end宏管理。 【复制即可用】
#pragma once
#include iostream
#include string
#include stdarg.h// 将日志等级用整数表示
#define Info 0 // 常规
#define Debug 1 // 调试
#define Warning 2 // 警告
#define Error 3 // 错误
#define Fatal 4 // 致命// 因为我们这里的日志等级是用一个整数表示的
// 而最后日志打印时需要有具体是什么日志等级
// 因此我们可以封装一个函数将日志等级转化为字符串
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;}
}// level - 日志等级
// format - 格式化字符串的数据格式。类似于printf前半部分
// ... - 表示可变参数
void logmessage(int level, const char *format, ...)
{// 默认部分日志等级 时间 time_t _timestamp time(NULL); // time函数会返回时间戳// 再将time_t类型转化为struct tm类型struct tm *_tm localtime(_timestamp);char defaultPart[1024]; // 默认部分// 打印的日志格式[日志等级][时间]snprintf(defaultPart, sizeof(defaultPart), [%s][%d-%d-%d:%d:%d:%d],levelToString(level).c_str(), _tm-tm_year 1900, _tm-tm_mon 1, _tm-tm_mday,_tm-tm_hour, _tm-tm_min, _tm-tm_sec);// 自定义部分format内容 可变参数... char self[1024];va_list s;va_start(s, format);vsnprintf(self, sizeof(self), format, s);va_end(s);// 将默认部分和自定义部分整合 char logtxt[2048];snprintf(logtxt, sizeof(logtxt), %s %s\n, defaultPart, self);// 信息全部在logtxt中你可以打印出来或者写到一个文件里 printf(%s, logtxt);
}五、封装成类并实现将日志信息打印到文件里完整代码
#pragma once
#include iostream
#include string
#include stdarg.h
#include sys/types.h
#include unistd.h
#include sys/stat.h
#include fcntl.h// 将日志等级用整数表示
#define Info 0 // 常规
#define Debug 1 // 调试
#define Warning 2 // 警告
#define Error 3 // 错误
#define Fatal 4 // 致命#define Screen 1
#define OneFile 2
#define ClassFile 3class log
{
public:// 写一个默认构造函数, 默认打印是向屏幕打印log(){printMethod Screen;logdir ./logdir/; // 你需要保证当前路径下有目录名为logdir}// 让用户选择打印方式void Enable(int method){printMethod method;}// 因为我们这里的日志等级是用一个整数表示的// 而最后日志打印时需要有具体是什么日志等级// 因此我们可以封装一个函数将日志等级转化为字符串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;}}// level - 日志等级// format - 格式化字符串的数据格式。类似于printf前半部分// ... - 表示可变参数void logmessage(int level, const char *format, ...){// 默认部分日志等级 时间 time_t _timestamp time(NULL); // time函数会返回时间戳// 再将time_t类型转化为struct tm类型struct tm *_tm localtime(_timestamp);char defaultPart[1024]; // 默认部分// 打印的日志格式[日志等级][时间]snprintf(defaultPart, sizeof(defaultPart), [%s][%d-%d-%d:%d:%d:%d],levelToString(level).c_str(), _tm-tm_year 1900, _tm-tm_mon 1, _tm-tm_mday,_tm-tm_hour, _tm-tm_min, _tm-tm_sec);// 自定义部分format内容 可变参数... char self[1024];va_list s;va_start(s, format);vsnprintf(self, sizeof(self), format, s);va_end(s);// 将默认部分和自定义部分整合 char logtxt[2048];snprintf(logtxt, sizeof(logtxt), %s %s\n, defaultPart, self);// 信息全部在logtxt中你可以打印出来或者写到一个文件里 // printf(%s, logtxt); // 直接打印printLog(level, logtxt);}// 封装打印日志文件的方法1. 向屏幕打印 2. 向文件打印 3. 分类打印void printLog(int level, const std::string logtxt){switch (printMethod){case Screen:std::cout logtxt std::endl;break;case OneFile:printOneFile(log.txt, logtxt);break;case ClassFile:printClassFile(level, logtxt);break;default:break;}}// 向一个文件写void printOneFile(const std::string filename, const std::string logtxt){std::string _filename logdir filename; // ./logdir/log.txtint fd open(_filename.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666);if (fd 0){return;}write(fd, logtxt.c_str(), logtxt.size());close(fd);}// 文件分类写。比如Info信息放在一个文件中Errno放在一个文件中...void printClassFile(int level, const std::string logtxt){std::string filename log.txt;filename .;filename levelToString(level);printOneFile(filename, logtxt);}private:int printMethod;std::string logdir; // 日志文件存放目录
};