[C语言实战]C语言文件操作实战:打造高效日志系统(六)
摘要:本文基于C语言标准文件I/O函数,实现支持多级日志、文件轮转、线程安全的轻量级日志系统。包含核心模块源码解析、性能优化技巧及完整测试方案。
一、日志系统核心设计
1.1 功能需求
功能模块 | 实现要点 |
---|---|
日志分级 | DEBUG/INFO/WARN/ERROR等级别 |
文件写入 | 追加模式+缓冲优化 |
线程安全 | 互斥锁保护文件操作 |
日志轮转 | 按大小分割文件 |
时间戳 | 精确到毫秒的本地时间 |
1.2 架构设计图
二、核心代码实现
2.1 头文件定义(logger.h)
#include <stdio.h>
#include <pthread.h>
#include <time.h>
// 日志级别枚举
typedef enum {
LOG_DEBUG,
LOG_INFO,
LOG_WARNING,
LOG_ERROR
} LogLevel;
// 日志配置结构体
typedef struct {
LogLevel level; // 当前日志级别
char path[256]; // 日志文件路径
size_t max_size; // 单个文件最大大小(MB)
int backup_num; // 保留旧日志数量
} LoggerConfig;
// 初始化日志系统
int logger_init(const LoggerConfig *config);
// 写入日志接口
void log_write(LogLevel level, const char *file, int line, const char *fmt, ...);
// 宏定义简化调用
#define LOG_DEBUG(...) log_write(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
#define LOG_INFO(...) log_write(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__)
#define LOG_WARN(...) log_write(LOG_WARNING, __FILE__, __LINE__, __VA_ARGS__)
#define LOG_ERROR(...) log_write(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__)
2.2 日志写入模块(logger.c)
#include <stdarg.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <string.h>
#include <stdio.h>
#include <pthread.h>
#include "logger.h"
static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
static LoggerConfig current_config;
static FILE *log_fp = NULL;
static unsigned long current_size = 0;
// 检查并执行日志轮转
static void rotate_log() {
if (current_size < current_config.max_size * 1024 * 1024) return;
fclose(log_fp);
// 重命名旧日志文件
for (int i = current_config.backup_num-1; i > 0; --i) {
char old_name[256], new_name[256];
snprintf(old_name, sizeof(old_name), "%s.%d", current_config.path, i);
snprintf(new_name, sizeof(new_name), "%s.%d", current_config.path, i+1);
rename(old_name, new_name);
}
// 修复文件名拼接
char rotated_name[256];
snprintf(rotated_name, sizeof(rotated_name), "%s.1", current_config.path);
rename(current_config.path, rotated_name);
// 创建新日志文件
log_fp = fopen(current_config.path, "a");
current_size = 0;
}
// 获取当前时间字符串
static void get_timestamp(char *buffer, size_t len) {
struct timeval tv;
gettimeofday(&tv, NULL);
struct tm *tm = localtime(&tv.tv_sec);
strftime(buffer, len, "%Y-%m-%d %H:%M:%S", tm);
sprintf(buffer + strlen(buffer), ".%03ld", tv.tv_usec / 1000);
}
// 初始化日志系统
int logger_init(const LoggerConfig *config) {
pthread_mutex_lock(&log_mutex);
memcpy(¤t_config, config, sizeof(LoggerConfig));
log_fp = fopen(config->path, "a");
if (!log_fp) {
pthread_mutex_unlock(&log_mutex);
return -1;
}
// 获取当前文件大小
struct stat st;
stat(config->path, &st);
current_size = st.st_size;
pthread_mutex_unlock(&log_mutex);
return 0;
}
// 核心写入函数
void log_write(LogLevel level, const char *file, int line, const char *fmt, ...) {
if (level < current_config.level) return;
pthread_mutex_lock(&log_mutex);
// 格式化日志头
char timestamp[32], log_header[256];
get_timestamp(timestamp, sizeof(timestamp));
const char *level_str[] = {"DEBUG", "INFO", "WARN", "ERROR"};
int header_len = snprintf(log_header, sizeof(log_header),
"[%s] [%s] [%s:%d] ",
timestamp, level_str[level], file, line);
// 格式化可变参数
char log_body[1024];
va_list args;
va_start(args, fmt);
vsnprintf(log_body, sizeof(log_body), fmt, args);
va_end(args);
// 组合完整日志并写入
fprintf(log_fp, "%s%s\n", log_header, log_body);
current_size += header_len + strlen(log_body) + 1;
// 刷新缓冲区并检查轮转
fflush(log_fp);
rotate_log();
pthread_mutex_unlock(&log_mutex);
}
int main() {
LoggerConfig config = {
.level = LOG_DEBUG,
.path = "app.log",
.max_size = 1, // 1MB
.backup_num = 3
};
// 修复条件判断
if (logger_init(&config) != 0) {
fprintf(stderr, "Failed to initialize logger\n");
return 1;
}
// 生成测试日志
for (int i = 0; i < 1000; ++i) {
LOG_DEBUG("User %d login, session_id: %s", i, "ABCDEFG123456");
LOG_INFO("Processed %d requests", i*10);
if (i % 100 == 0) {
LOG_WARN("High latency detected: %dms", rand()%500);
}
}
LOG_ERROR("Connection timeout");
return 0;
} // 确保大括号正确闭合
三、测试验证方案
3.1 基础功能测试
int main() {
LoggerConfig config = {
.level = LOG_DEBUG,
.path = "app.log",
.max_size = 1, // 1MB
.backup_num = 3
};
// 修复条件判断
if (logger_init(&config) != 0) {
fprintf(stderr, "Failed to initialize logger\n");
return 1;
}
// 生成测试日志
for (int i = 0; i < 1000; ++i) {
LOG_DEBUG("User %d login, session_id: %s", i, "ABCDEFG123456");
LOG_INFO("Processed %d requests", i*10);
if (i % 100 == 0) {
LOG_WARN("High latency detected: %dms", rand()%500);
}
}
LOG_ERROR("Connection timeout");
return 0;
} // 确保大括号正确闭合
3.2 验证步骤
编译运行
gcc -o logger_test logger.c -lpthread ./logger_test
检查生成文件
ls -lh app.log*
预期输出:
-rw-r--r-- 1 user group 1.0M Jul 10 15:30 app.log
查看日志内容
tail -n 5 app.log
预期输出:
[2023-07-10 15:30:23.456] [DEBUG] [test.c:25] User 999 login... [2023-07-10 15:30:23.457] [INFO] [test.c:26] Processed 9990 requests [2023-07-10 15:30:23.458] [ERROR] [test.c:30] Connection timeout
四、性能优化技巧
4.1 缓冲策略优化
// 修改fprintf为块写入
char full_log[2048];
int len = snprintf(full_log, sizeof(full_log), "%s%s\n", log_header, log_body);
fwrite(full_log, 1, len, log_fp);
current_size += len;
4.2 异步日志写入
// 添加任务队列
void async_log(const char *msg) {
pthread_mutex_lock(&queue_mutex);
enqueue(log_queue, msg);
pthread_cond_signal(&queue_cond);
pthread_mutex_unlock(&queue_mutex);
}
// 独立写线程
static void *write_thread(void *arg) {
while (1) {
pthread_mutex_lock(&queue_mutex);
while (queue_empty(log_queue)) {
pthread_cond_wait(&queue_cond, &queue_mutex);
}
char *msg = dequeue(log_queue);
pthread_mutex_unlock(&queue_mutex);
fputs(msg, log_fp);
free(msg);
}
}
五、扩展功能建议
- 网络日志支持:通过UDP将日志发送到远程服务器
- 日志过滤:根据文件名、函数名动态过滤日志
- 性能统计:记录每秒日志写入量、平均延迟
- 崩溃日志:通过signal handler捕获段错误并记录堆栈
最佳实践:生产环境建议设置max_size=10MB
和backup_num=10
性能对比:同步写入 vs 异步写入吞吐量测试数据
希望本教程对您有帮助,请点赞❤️收藏⭐关注支持!欢迎在评论区留言交流技术细节!