简单免费自建网站,济南做网站比较好的,有利于seo的网站底部,南安市网站建设个人主页#xff1a;Lei宝啊
愿所有美好如期而遇 目录
日志是什么#xff1f;
为什么需要日志#xff1f;
实现一个简单日志
时间戳
clock_gettime
time localtime
可变模板参数(使用C语言)#xff0c;va_start va_end vsprintf
宏 __LINE__…
个人主页Lei宝啊
愿所有美好如期而遇 目录
日志是什么
为什么需要日志
实现一个简单日志
时间戳
clock_gettime
time localtime
可变模板参数(使用C语言)va_start va_end vsprintf
宏 __LINE__ __FILE__ ##__VA_ARGS__
小技巧
代码实现 日志是什么
日志Log 是记录系统、应用或设备在运行过程中产生的各种事件、状态、错误等信息的文件或数据流。这些日志信息对于了解系统的运行状况、排查问题、优化性能以及安全审计等方面都非常重要。
为什么需要日志
问题排查当系统或应用出现故障时通过查看日志可以快速定位问题发生的原因和位置从而加快故障解决的速度。性能监控通过分析日志可以了解系统的运行状况包括请求量、响应时间、资源使用情况等从而发现性能瓶颈并进行优化。安全审计日志中记录了用户的操作行为、系统的访问情况等信息这些信息可以用于安全审计检查是否有异常操作或入侵行为。合规性要求在某些行业或地区法律法规要求企业或组织必须保留一定期限的日志记录以便在需要时进行审计或调查。系统恢复在某些情况下如系统崩溃或数据丢失时可以通过日志中的信息来恢复系统或数据。版本追踪对于软件来说日志可以记录软件的版本变化、功能更新等信息方便开发人员进行版本追踪和回滚。用户行为分析通过分析用户的操作日志可以了解用户的使用习惯、喜好等信息从而优化产品设计和提高用户体验
实现一个简单日志
实现日志前我们先介绍几个函数以及几个小技巧
时间戳
时间戳Timestamp 是一个表示特定时间点的数字或字符串。它通常表示从某个固定时间点1970年1月1日00:00:00 UTC也被称为Unix纪元或Unix时间戳到特定事件发生时经过的秒数。
我们在Linux系统中有这样几个函数
clock_gettime 第一个参数我们填CLOCK_REALTIME即可表示系统实时时间。
#include stdio.h
#include time.h int main()
{ struct timespec ts; if (clock_gettime(CLOCK_REALTIME, ts) 0) { printf(Current time: %ld.%09ld\n, ts.tv_sec, ts.tv_nsec); } else { perror(clock_gettime); return 1; } return 0;
}
time localtime time函数获取当前时间的时间戳。
locltime函数将时间戳转换成我们平时使用的年月日时分秒。
可变模板参数(使用C语言)va_start va_end vsprintf
void message(const char* format, ...){} //可变模板参数
我们如何获取可变模板参数中的参数呢
使用vsprintf函数 这个函数用来将可变模板参数以format格式将其写入到str指向的char类型数组中。
而可变模板参数将会使用va_list类型变量指向实际上他是一个void*类型指针我们定义后还需要使用函数初始化以及销毁。
va_list args;
va_start(args, format);
//...
va_end(args); va_start初始化va_list类型变量last就是可变模板参数左边第一个参数这样这个函数就可以将args初始化为指向可变模板参数的指针。
va_end将va_list类型变量清空。
char buffer[1024];
vsprintf(buffer, format, args);
//这样就将可变模板参数中的内容以我们希望的格式获取到
//类似于printf(%d %s, xx,xx); %d %s就是格式后面就是可变模板参数
宏 __LINE__ __FILE__ ##__VA_ARGS__
__LINE__ 当前代码所在行数__FILE__ 当前代码所在文件__VA_ARGS__ C99及C支持的宏可变模板参数 //##__VA_ARGS__ 中的 ## 操作符。当 __VA_ARGS__ 为空时即没有额外的参数 ## 操作符会将其前面的逗号去掉以防止在编译时产生语法错误。
简单来说就是没有可变模板参数只有格式那么格式后面还有个逗号##操作符会将其去掉。
小技巧
在宏替换中关于语句的替换以及多语句的替换我们可以将其写在do{}while(0)中。
如果上述讲解没有理解那么可以在下面的代码中进行理解所有讲解都会在下面代码中有体现。
代码实现
#include iostream
#include string
#include ctime
#include cstdarg
#include pthread.h
#include fstream
using namespace std;//##__VA_ARGS__ 中的 ## 操作符。当 __VA_ARGS__ 为空时即没有额外的参数## 操作符会将其前面的逗号去掉以防止在编译时产生语法错误。
#define Log(level, format, ...) do{LogMessage(__FILE__, __LINE__, level, format, ##__VA_ARGS__);}while(0)
#define YSave do{IsSave true;}while(0)
#define NSave do{IsSave false;}while(0)pthread_mutex_t log_mutex PTHREAD_MUTEX_INITIALIZER;
string file_name_path log.txt;
bool IsSave false; enum Level
{Debug 0,Info,Warning,Error,Fatal
};string LevelToString(int level)
{string name;switch (level){case Debug : return Debug;case Info : return Info;case Warning : return Warning;case Error : return Error;case Fatal : return Fatal;default: return 阿西吧;}
}string TimeToString()
{time_t Time; time(Time);struct tm* attr localtime(Time);char buffer[1024];sprintf(buffer,%4d-%2d-%2d %2d:%2d:%2d,attr-tm_year1900,attr-tm_mon1,attr-tm_mday,attr-tm_hour,attr-tm_min,attr-tm_sec);return buffer;
}void Save(string ret)
{fstream out(file_name_path.c_str(), ios::app); if(out.is_open()){out ret;}else{std::cerr 无法打开文件: file_name_path std::endl;}out.close();
}// 2. 日志是有格式的
// 日志等级 时间 代码所在的文件名/行数 日志的内容
void LogMessage(string filename, int line, int level, const char* format, ...)
{string levelname LevelToString(level);string timename TimeToString();va_list args; //一个void*指针va_start(args, format); //将args初始化指向可变参数列表char buffer[1024]; vsprintf(buffer, format, args);va_end(args);string ret [ filename ] [ line: to_string(line) ] [ levelname ] [ timename ] [ buffer ] \n;pthread_mutex_lock(log_mutex);if(IsSave){Save(ret);}else{cout ret;}pthread_mutex_unlock(log_mutex);
}