[C语言实战]C语言文件操作实战:打造高效日志系统(六)

发布于:2025-05-30 ⋅ 阅读:(19) ⋅ 点赞:(0)

[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(&current_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 验证步骤

  1. 编译运行

    gcc -o logger_test logger.c -lpthread
    ./logger_test
    
  2. 检查生成文件

    ls -lh app.log*
    

    预期输出:

    -rw-r--r-- 1 user group 1.0M Jul 10 15:30 app.log
    
  3. 查看日志内容

    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);
    }
}

五、扩展功能建议

  1. 网络日志支持:通过UDP将日志发送到远程服务器
  2. 日志过滤:根据文件名、函数名动态过滤日志
  3. 性能统计:记录每秒日志写入量、平均延迟
  4. 崩溃日志:通过signal handler捕获段错误并记录堆栈

最佳实践:生产环境建议设置max_size=10MBbackup_num=10
性能对比:同步写入 vs 异步写入吞吐量测试数据

希望本教程对您有帮助,请点赞❤️收藏⭐关注支持!欢迎在评论区留言交流技术细节!