Logger日志
头文件
cpp
// log.h
#ifndef __LOG_H__
#define __LOG_H__
#include <stdio.h>
// 日志级别
typedef enum
{
LOG_DEBUG, // 调试
LOG_INFO, // 信息
LOG_WARN, // 警告
LOG_ERROR // 错误
} LogLevel;
// 日志输出目标
typedef enum
{
LOG_TO_CONSOLE, // 控制台
LOG_TO_FILE, // 文件
LOG_TO_BOTH // 两者都输出
} LogTarget;
// 日志结构体
typedef struct
{
FILE *log_file; // 日志文件指针
char file_path[256]; // 日志文件路径
LogTarget target; // 输出目标
LogLevel min_level; // 最小日志级别(低于此级别的不输出)
} Logger;
Logger *logger_init(const char *file_path, LogTarget target, LogLevel min_level);
void logger_destroy(Logger *logger);
void logger_log(Logger *logger, LogLevel level, const char *format, ...);
#endif
实现
cpp
// log.c
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include "log.h"
// 初始化日志器
Logger *logger_init(const char *file_path, LogTarget target, LogLevel min_level)
{
Logger *logger = (Logger *)malloc(sizeof(Logger));
if (logger == NULL)
{
perror("Failed to allocate logger");
return NULL;
}
// 初始化成员
logger->target = target;
logger->min_level = min_level;
strncpy(logger->file_path, file_path, 255);
logger->file_path[255] = '\0'; // 确保字符串结束
// 如果需要输出到文件,打开文件(追加模式)
if (target == LOG_TO_FILE || target == LOG_TO_BOTH)
{
logger->log_file = fopen(file_path, "a");
if (logger->log_file == NULL)
{
perror("Failed to open log file");
free(logger);
return NULL;
}
}
else
{
logger->log_file = NULL;
}
return logger;
}
// 销毁日志器
void logger_destroy(Logger *logger)
{
if (logger == NULL)
return;
// 关闭文件
if (logger->log_file != NULL)
{
fclose(logger->log_file);
}
free(logger);
logger = NULL; // 避免悬空指针
}
// 转换日志级别为字符串
static const char *level_to_string(LogLevel level)
{
switch (level)
{
case LOG_DEBUG:
return "DEBUG";
case LOG_INFO:
return "INFO";
case LOG_WARN:
return "WARN";
case LOG_ERROR:
return "ERROR";
default:
return "UNKNOWN";
}
}
// 获取当前时间(格式:YYYY-MM-DD HH:MM:SS)
static void get_current_time(char *time_str, size_t max_len)
{
time_t now = time(NULL);
struct tm tm_info;
localtime_r(&now, &tm_info);
strftime(time_str, max_len, "%Y-%m-%d %H:%M:%S", &tm_info);
}
// 打印日志
void logger_log(Logger *logger, LogLevel level, const char *format, ...)
{
// 检查日志器有效性和级别过滤
if (logger == NULL || level < logger->min_level)
{
return;
}
// 获取时间和级别字符串
char time_str[32];
get_current_time(time_str, sizeof(time_str));
const char *level_str = level_to_string(level);
// 构建日志前缀(时间 + 级别)
char log_prefix[128];
snprintf(log_prefix, sizeof(log_prefix),
"[%s] [%s] ", time_str, level_str);
// 处理可变参数(格式化日志内容)
va_list console_args, file_args;
va_start(console_args, format);
va_copy(file_args, console_args); // 复制参数以供多次使用
// 根据目标输出日志
if (logger->target == LOG_TO_CONSOLE || logger->target == LOG_TO_BOTH)
{
// 输出到控制台
printf("%s", log_prefix);
vprintf(format, console_args); // 处理可变参数
printf("\n");
}
va_end(console_args);
if (logger->target == LOG_TO_FILE || logger->target == LOG_TO_BOTH)
{
// 输出到文件(需检查文件是否打开)
if (logger->log_file != NULL)
{
fprintf(logger->log_file, "%s", log_prefix);
vfprintf(logger->log_file, format, file_args); // 写入文件
fprintf(logger->log_file, "\n");
fflush(logger->log_file); // 立即刷新到文件
}
}
va_end(file_args);
}
使用示例
cpp
// main.c
#include "log.h"
int main()
{
// 初始化日志器:输出到文件和控制台,最小级别为INFO
Logger *logger = logger_init("./app.log", LOG_TO_BOTH, LOG_DEBUG);
if (logger == NULL)
{
return 1;
}
// 打印不同级别的日志
logger_log(logger, LOG_DEBUG, "debug log");
logger_log(logger, LOG_INFO, "Program started. Version: %s", "1.0.0");
logger_log(logger, LOG_WARN, "Apple: %d remaining", 100);
logger_log(logger, LOG_ERROR, "Failed to connect to database: %s", "timeout");
// 销毁日志器
logger_destroy(logger);
return 0;
}
参考文章
用 C 语言实现日志打印结构体:从设计到实战