目录
- 一、日志文件的重要性
- 二、日志文件的简单实现
- 1、comm.hpp
- 2、log.hpp
- 三、测试用例
- 总结
一、日志文件的重要性
- 故障排查与问题定位
快速发现问题:日志能够实时记录系统运行过程中的各种事件和状态信息,当系统出现故障或异常时,通过查看日志可以快速察觉到问题的发生,例如,服务器突然崩溃,日志中可能会记录下崩溃前的错误信息、异常堆栈,帮助运维人员第一时间得知系统出现了故障
精准定位根源:详细的日志可以提供问题发生时的上下文信息,如函数调用顺序、变量值等,以数据库连接失败为例,日志可能会记录下数据库的连接地址、端口、用户名、密码验证情况等,帮助开发人员精准定位是配置问题、网络问题还是数据库本身的问题
- 系统监控与性能分析
监控系统状态:通过对日志的实时分析,可以监控系统的运行状态,例如,记录系统的 CPU 使用率、内存占用情况、网络流量等信息,一旦这些指标超出正常范围,日志会及时反映出来,以便管理员采取相应的措施,如增加服务器资源、优化代码等
性能瓶颈分析:日志可以记录每个操作的执行时间,通过对这些时间数据的分析,可以找出系统的性能瓶颈,比如,在一个 Web 应用中,通过日志可以发现某个 API 接口的响应时间过长,进而对该接口的代码进行优化,提高系统的整体性能
- 安全审计与合规性
安全事件追踪:日志能够记录用户的操作行为,包括登录、数据访问、权限变更等,当发生安全事件,如数据泄露、非法登录时,可以通过查看日志追踪事件的源头和过程,确定是否存在内部人员违规操作或外部攻击
满足合规要求:许多行业和地区都有相关的法规和标准要求企业对系统操作和数据进行记录和审计,例如,金融行业的 PCI-DSS 标准、医疗行业的 HIPAA 法规等,详细的日志记录可以帮助企业满足这些合规要求,避免因违规而面临的法律风险
二、日志文件的简单实现
1、comm.hpp
comm.hpp用来建立信道,Init类用于建立命名管道和退出时自动销毁命名管道
#pragma once #include <IOStream> #include <string> #include <cerrno> #include <cstrinwww.devze.comg> #include <cstdlib> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> //命名管道文件路径 #define FIFO_FILE "./myfifo" //文件权限 #define MODE 0664 //枚举命名管道错误 enum { FIFO_CREATE_ERR = 1, FIFO_DELETE_ERR, FIFO_OPEN_ERR }; class Init { public: Init() { // 创建命名管道 int n = mkfifo(FIFO_FILE, MODE); if (n == -1) { perror("mkfifo"); exit(FIFO_CREATE_ERR); } } ~Init() { //销毁命名管道 int m = unlink(FIFO_FILE); if (m == -1) { perror("unlink"); exit(FIFO_DELETE_ERR); } } };
2、log.hpp
log.hpp
中存放的是日志真正的实现,将来我们可以在写代码的过程中使用这个文件来打印错误信息到日志上
#pragma once #include <iostream> #include <time.h> #include <stdarg.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #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 LogFile "log.txt" class Log { public: Log() { //初始状态下我们把日志打印到屏幕上 printMethod = Screen; path = "./log/"; } //可以通过该函数对日志打印模式改变 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"; http://www.devze.com case Fatal: return "Fatal"; default: return "None"; } } //日志打印函数 void printLog(int level, const std::string &logtxt) { //选择打印模式 switch (printMethod) { //选择屏幕就把信息打印出来 case Screen: std::cout << logtxt << std::endl; break; //选择一个文件就把日志全部打印到一个文件当中 case Onefile: printOneFile(LogFile, logtxt); break; //按照等级打印到不同文件中,每个文件对应一个等级 case Classfile: printClassFile(level, logtxt); break; default: break; } } //打印到一个文件中 void printOneFile(const std::string &logname, const std::string &logtxt) { //将路径与文件名称结合起来就可以用open打开文件 std::string _logname = path + logname; int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); if (fd < 0) return; //将日志信息logtxt写到文件当中 write(fd, logtxt.c_str(), logtxt.size()); close(fd); } //打印到多个文件中 void printClassFile(int level, const std::string &logtxt) { //将文件按照错误等级分出来再复用一个文件的方法写入每个文件 std::string filename = LogFile; filename += "."; filename += levelToString(level); printOneFile(filename, logtxt); } //析构函数,全在栈上就不用写 ~Log() { } //运算符重载,可变参数列表 void operator()(int level, const char *format, ...) { //获取本地时间 time_t t = time(nullptr); struct tm *ctime = localtime(&t); //生成日志的时间戳和日志级别 char leftbuffer[SIZE]; snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(), ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday, www.devze.com ctime->tm_hour, ctime->tm_min, ctime->tm_sec); //va_list用于处理可变参数列表 va_list s; //初始化s,使其指向可变参数列表第一个参数,format是可变参数列表前面那个参数 va_start(s, format); char righjstbuffer[SIZE]; //将可变参数按照format字符串的格式写入rightbuffer中 vsnprintf(rightbuffer, sizeof(rightbuffpythoner), format, s); //结束对可变参数列表的访问 va_end(s); // 格式:默认部分+自定义部分 //定义一个数组logtxt,用于存储完整的日志信息 char logtxt[SIZE * 2]; //将日志左侧部分也就是时间戳与等级部分(默认部分) //和右侧部分也就是自定义部分组合成一条完整的日志信息 snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer); //将日志信息输出 printLog(level, logtxt); } private: int printMethod;//打印模式 std::string path;//路径 };
三、测试用例
#include "log.hpp" #include <cstdlib> #include <unistd.h> int main() { Log log; int cnt = 10; log.Enable(Onefile); while (cnt--) { log.printOneFile("log","i am super little monster\n"); log.printClassFile(0,"this is 1\n"); log.printClassFile(1,"this is 2\n"); log.printClassFile(2,"this is 3\n"); log.printClassFile(3,"this is 4\n"); log.printClassFile(4,"this is 5\n"); } return 0; }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。
精彩评论