一、介绍
日志是计算机程序中用于记录运行时事件和状态的重要工具。通过记录关键信息和错误情况,日志可以帮助程序开发人员和维护人员追踪程序的执行过程,排查问题和改进性能。 在软件开发中,日志通常记录如下类型的信息:
- 事件信息:记录程序执行过程中的重要事件,如启动和关闭,特定操作完成等。
- 错误信息:记录发生的错误和异常情况,包括错误类型、位置和可能的原因。
- 警告信息:记录潜在问题或需要注意的情况,但不是严重到导致程序崩溃的程度。
- 调试信息:记录用于调试程序的详细信息,方便追踪程序状态和流程。 日志的记录一般包括时间戳、事件类型、事件描述等信息。这些记录可以写入文件、数据库或发送至远程服务器,以供后续分析和监控。
通过分析日志,开发人员可以了解程序的运行情况,发现潜在问题并优化程序性能。因此,良好的日志记录和管理对于确保软件运行稳定、故障排除和改进具有重要意义。
二、日志级别
#define DEBUG 0
#define NORMAL 1
#define WARNING 2
#define ERROR 3
#define FATAL 4
const char* gLevelMap[]={
"DEBUG",
"NORMAL",
"WARNING",
"ERROR",
"FATAL"
};
日志级别是一种对日志信息进行分类和标记的方法,用于帮助程序员和系统管理员更好地理解和处理日志信息。日志级别包括DEBUG(调试)、NORMAL(正常)、WARNING(警告)、ERROR(错误)和FATAL(严重错误)
五种级别。通过宏定义将这五种级别编号化,可以在程序中方便地使用对应的编号来表示不同的日志级别。同时,通过使用一个数组 gLevelMap[],可以将编号与日志级别名对应起来,从而方便地在程序中根据编号找到对应的级别名,并进行相应的处理。这种分类和标记的方法有助于开发人员根据日志级别进行精细化的调试、监控和错误处理。
三、日志函数logMessage
// 日志打印 文件名
#define LOGFILE "./LOG.log"
void logMessage(int level, const char* format, ...)
{
char timebuffer[1024];
// 日志时间
time_t timestamp = time(nullptr);
tm* ltm = localtime(×tamp);
int year = ltm->tm_year+1900;
int month = ltm->tm_mon+1;
int day = ltm->tm_mday;
int hour = ltm->tm_hour;
int min = ltm->tm_min;
int second = ltm->tm_sec;
snprintf(timebuffer, sizeof timebuffer, "[%s][%04d-%02d-%02d %02d:%02d:%02d]",
gLevelMap[level], year, month, day, hour, min, second);
// 日志内容
char logBuffer[1024];
va_list args; // 初始化参数信息
va_start(args, format); // 初始化参数列表
vsnprintf(logBuffer, sizeof logBuffer, format, args); //日志内容存入logBuffe中
va_end(args);
// 显示器打印日志信息
printf("%s %s\n", timebuffer, logBuffer);
// //往文件打印
// FILE *fp = fopen(LOGFILE, "a");
// fprintf(fp, "%s %s\n", timebuffer, logBuffer);
// fclose(fp);
}
logMessage(int level, const char* format, ...)
该日志函数level
代表日志等级、format
表示日志内容、...
是可变参数列表。这样使日志的使用跟printf()
使用方法类似。
timebuffer:用于存储日志的等级和时间信息。
logBuffer:用于存储日志的内容。
内部解析
#define LOGFILE "./LOG.log"
定义的一个日志文件,可以设置把打印的日志显示到这个文件里面。
// 日志时间
time_t timestamp = time(nullptr);
tm* ltm = localtime(×tamp);
int year = ltm->tm_year+1900;
int month = ltm->tm_mon+1;
int day = ltm->tm_mday;
int hour = ltm->tm_hour;
int min = ltm->tm_min;
int second = ltm->tm_sec;
time_t timestamp = time(nullptr);
获取系统当前的时间戳。
tm* ltm = localtime(×tamp);
将时间戳转为用本地时区表示,比如Thu Aug 23 09:12:05 2012
localtime():原型(struct tm *localtime(const time_t *timer)
)localtime的函数声明。返回的是tm结构体,其结构如下:
struct tm {
int tm_sec; /* 秒,范围从 0 到 59*/
int tm_min; /* 分,范围从 0 到 59*/
int tm_hour; /* 小时,范围从 0 到 23*/
int tm_mday; /* 一月中的第几天,范围从 1 到 31 */
int tm_mon; /* 月份,范围从 0 到 11*/
int tm_year; /* 自 1900 起的年数 */
int tm_wday; /* 一周中的第几天,范围从 0 到 6 */
int tm_yday; /* 一年中的第几天,范围从 0 到 365 */
int tm_isdst; /* 夏令时*/
};
所以通过调用ltm
对象的内部变量,就可以获取想要的时间格式。例如int year = ltm->tm_year+1900;
可以获取当前时间的年份(+1900是因为获取的年份是从1900年至今的)。
snprintf(timebuffer, sizeof timebuffer, "[%s][%04d-%02d-%02d %02d:%02d:%02d]",
gLevelMap[level], year, month, day, hour, min, second);
使用格式化输出字符串snprintf
函数,我们可以将当前的日志级别、时间,按指定的格式输入到缓冲区timebuffer中。这样,timebuffer中的内容就如同这个样子:([日志级别][时间])
// 日志内容
char logBuffer[1024];
va_list args; // 初始化参数信息
va_start(args, format); // 初始化参数列表
vsnprintf(logBuffer, sizeof logBuffer, format, args); //日志内容存入logBuffe中
va_end(args);
format是传进来的日志内容,因为logMessge()
有一个可变的参数列表...
。所以需要va_list args;va_start(args, format);
va_list是C语言中的一个宏定义,用于表示一个变长参数列表。它是一个指向变长参数列表的指针,可以通过宏va_start、va_arg和va_end对变长参数列表进行访问和操作。在函数中需要接收不定数量的参数时,可以使用va_list来处理这些参数。
va_start:它的作用是初始化一个va_list类型的变量,使其指向可变参数列表的第一个参数。
vsnprintf()用于向一个字符串缓冲区打印格式化字符串,且可以限定打印的格式化字符串的最大长度。用它把日志的内容输入到logBuffer中。
va_end:是一个宏,用于结束使用 va_start 和 va_arg 宏定义的可变参数列表。它的作用是清理 va_list 类型变量,以便该变量可以被再次使用。
// 打印日志信息
printf("%s %s\n", timebuffer, logBuffer);
// //往文件打印
FILE *fp = fopen(LOGFILE, "a");
fprintf(fp, "%s %s\n", timebuffer, logBuffer);
fclose(fp);
打印日志内容有两种方式,一种是向显示器上打,一种是往文件里写。通过将timebuffer
和logBuffer
把时间和内容组合到一起,就是一条完整的日志信息了。
测试
简单的测试,将日志信息打印在显示器上:
#include "log.hpp"
int main()
{
printf("ssd\n");
int n=1;
logMessage(DEBUG, "测试日志信息输出%d", n);
return 0;
}
四、完整代码
#pragma once
#include <iostream>
#include <cstring>
#include <time.h>
#include <stdarg.h>
#include <cstdio>
//日志级别
#define DEBUG 0
#define NORMAL 1
#define WARNING 2
#define ERROR 3
#define FATAL 4
const char* gLevelMap[]={
"DEBUG",
"NORMAL",
"WARNING",
"ERROR",
"FATAL"
};
// 日志打印 文件名
#define LOGFILE "./LOG.log"
void logMessage(int level, const char* format, ...)
{
char timebuffer[1024];
// 日志时间
time_t timestamp = time(nullptr);
tm* ltm = localtime(×tamp);
int year = ltm->tm_year+1900;
int month = ltm->tm_mon+1;
int day = ltm->tm_mday;
int hour = ltm->tm_hour;
int min = ltm->tm_min;
int second = ltm->tm_sec;
snprintf(timebuffer, sizeof timebuffer, "[%s][%04d-%02d-%02d %02d:%02d:%02d]",
gLevelMap[level], year, month, day, hour, min, second);
// 日志内容
char logBuffer[1024];
va_list args; // 初始化参数信息
va_start(args, format); // 初始化参数列表
vsnprintf(logBuffer, sizeof logBuffer, format, args); //日志内容存入logBuffe中
va_end(args);
// 打印日志信息
printf("%s %s\n", timebuffer, logBuffer);
// //往文件打印
// FILE *fp = fopen(LOGFILE, "a");
// fprintf(fp, "%s %s\n", timebuffer, logBuffer);
// fclose(fp);
}