ITPub博客

首页 > 应用开发 > IT综合 > Linux环境下的日志系统从设计到实现

Linux环境下的日志系统从设计到实现

投稿 IT综合 作者:bhluo 时间:2025-12-25 15:03:23 0 删除 编辑

# Linux环境下的日志系统从设计到实现


日志系统是软件开发中至关重要的基础设施,它记录了程序运行时的关键信息,为调试、监控和问题排查提供支持。本文将完整展示一个轻量级日志系统的设计思路与实现过程,包含线程安全、日志分级、文件回滚等核心功能。


## 日志系统架构设计


一个完整的日志系统应具备以下特性:

1. 多级别日志输出

2. 线程安全支持

3. 日志文件管理

4. 灵活的输出配置

5. 性能开销可控


## 核心数据结构定义


```c

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <time.h>

#include <pthread.h>

#include <stdarg.h>

#include <unistd.h>

#include <sys/stat.h>


// 日志级别枚举

typedef enum {

    LOG_LEVEL_DEBUG = 0,

    LOG_LEVEL_INFO,

    LOG_LEVEL_WARN,

    LOG_LEVEL_ERROR,

    LOG_LEVEL_FATAL

} LogLevel;


// 日志系统配置

typedef struct {

    char log_dir[256];      // 日志目录

    char log_name[128];     // 日志文件名

    LogLevel level;         // 当前日志级别

    size_t max_file_size;   // 单个文件最大大小

    int max_backup_files;   // 最大备份文件数

    int enable_console;     // 是否输出到控制台

} LogConfig;


// 日志条目

typedef struct {

    char timestamp[32];     // 时间戳

    LogLevel level;         // 日志级别

    char filename[64];      // 源文件名

    int line;               // 行号

    char message[1024];     // 日志消息

} LogEntry;


// 日志系统主体

typedef struct {

    LogConfig config;       // 配置信息

    FILE* log_file;         // 当前日志文件指针

    pthread_mutex_t lock;   // 互斥锁

    char current_filepath[512]; // 当前日志文件路径

    size_t current_size;    // 当前文件大小

} Logger;

```


## 日志系统初始化


```c

// 全局日志实例

static Logger* g_logger = NULL;

static pthread_once_t g_once = PTHREAD_ONCE_INIT;


// 默认配置

static LogConfig default_config = {

    .log_dir = "./logs",

    .log_name = "app",

    .level = LOG_LEVEL_INFO,

    .max_file_size = 10 * 1024 * 1024,  // 10MB

    .max_backup_files = 10,

    .enable_console = 1

};


// 获取日志级别字符串

const char* get_level_string(LogLevel level) {

    switch (level) {

        case LOG_LEVEL_DEBUG: return "DEBUG";

        case LOG_LEVEL_INFO:  return "INFO";

        case LOG_LEVEL_WARN:  return "WARN";

        case LOG_LEVEL_ERROR: return "ERROR";

        case LOG_LEVEL_FATAL: return "FATAL";

        default: return "UNKNOWN";

    }

}


// 获取当前时间字符串

void get_current_time(char* buffer, size_t size) {

    time_t now = time(NULL);

    struct tm* tm_info = localtime(&now);

    strftime(buffer, size, "%Y-%m-%d %H:%M:%S", tm_info);

}


// 创建日志目录

int create_log_dir(const char* dir) {

    struct stat st = {0};

    if (stat(dir, &st) == -1) {

        if (mkdir(dir, 0755) == -1) {

            return -1;

        }

    }

    return 0;

}


// 生成日志文件路径

void generate_log_filepath(Logger* logger) {

    time_t now = time(NULL);

    struct tm* tm_info = localtime(&now);

    

    snprintf(logger->current_filepath, sizeof(logger->current_filepath),

             "%s/%s_%04d%02d%02d.log",

             logger->config.log_dir,

             logger->config.log_name,

             tm_info->tm_year + 1900,

             tm_info->tm_mon + 1,

             tm_info->tm_mday);

}


// 打开日志文件

int open_log_file(Logger* logger) {

    if (create_log_dir(logger->config.log_dir) != 0) {

        return -1;

    }

    

    generate_log_filepath(logger);

    

    // 打开文件,追加模式

    logger->log_file = fopen(logger->current_filepath, "a");

    if (logger->log_file == NULL) {

        return -1;

    }

    

    // 获取当前文件大小

    fseek(logger->log_file, 0, SEEK_END);

    logger->current_size = ftell(logger->log_file);

    

    return 0;

}

```


## 日志系统核心实现


```c

// 初始化日志系统

void logger_init_once() {

    g_logger = (Logger*)malloc(sizeof(Logger));

    if (g_logger == NULL) {

        return;

    }

    

    // 初始化配置

    memcpy(&g_logger->config, &default_config, sizeof(LogConfig));

    

    // 初始化互斥锁

    pthread_mutex_init(&g_logger->lock, NULL);

    

    // 打开日志文件

    if (open_log_file(g_logger) != 0) {

        free(g_logger);

        g_logger = NULL;

        return;

    }

    

    g_logger->current_size = 0;

}


// 获取日志实例

Logger* get_logger() {

    pthread_once(&g_once, logger_init_once);

    return g_logger;

}


// 检查是否需要轮转日志文件

int need_rotate(Logger* logger) {

    return logger->current_size >= logger->config.max_file_size;

<"tz.xingjungy.cn"><"n1.xingjungy.cn"><"wap.xingjungy.cn">

}


// 执行日志文件轮转

void rotate_log_file(Logger* logger) {

    fclose(logger->log_file);

    

    // 备份旧文件

    for (int i = logger->config.max_backup_files - 1; i > 0; i--) {

        char old_path[512];

        char new_path[512];

        

        snprintf(old_path, sizeof(old_path), "%s.%d", logger->current_filepath, i);

        snprintf(new_path, sizeof(new_path), "%s.%d", logger->current_filepath, i + 1);

        

        if (access(old_path, F_OK) == 0) {

            rename(old_path, new_path);

        }

    }

    

    // 备份当前文件

    char backup_path[512];

    snprintf(backup_path, sizeof(backup_path), "%s.1", logger->current_filepath);

    rename(logger->current_filepath, backup_path);

    

    // 创建新日志文件

    open_log_file(logger);

}

```


## 日志输出函数


```c

// 核心日志记录函数

void log_message(LogLevel level, const char* filename, int line, 

                 const char* format, ...) {

    Logger* logger = get_logger();

    if (logger == NULL || level < logger->config.level) {

        return;

    }

    

    pthread_mutex_lock(&logger->lock);

    

    // 检查文件轮转

    if (need_rotate(logger)) {

        rotate_log_file(logger);

    }

    

    // 准备日志条目

    LogEntry entry;

    memset(&entry, 0, sizeof(LogEntry));

    

    // 设置时间戳

    get_current_time(entry.timestamp, sizeof(entry.timestamp));

    

    // 设置日志级别

    entry.level = level;

    

    // 设置文件名和行号

    const char* base_name = strrchr(filename, '/');

    if (base_name != NULL) {

        strncpy(entry.filename, base_name + 1, sizeof(entry.filename) - 1);

    } else {

        strncpy(entry.filename, filename, sizeof(entry.filename) - 1);

    }

    entry.line = line;

    

    // 格式化消息

    va_list args;

    va_start(args, format);

    vsnprintf(entry.message, sizeof(entry.message), format, args);

    va_end(args);

    

    // 构建日志行

    char log_line[2048];

    int len = snprintf(log_line, sizeof(log_line),

                       "[%s] [%s] [%s:%d] %s\n",

                       entry.timestamp,

                       get_level_string(entry.level),

                       entry.filename,

                       entry.line,

                       entry.message);

    

    // 输出到文件

    if (logger->log_file != NULL) {

        fputs(log_line, logger->log_file);

        fflush(logger->log_file);

        logger->current_size += len;

    }

    

    // 输出到控制台

    if (logger->config.enable_console) {

        // 为不同级别设置颜色

        const char* color_code = "";

        const char* reset_code = "";

        

        if (isatty(fileno(stdout))) {

            switch (level) {

                case LOG_LEVEL_ERROR:

                case LOG_LEVEL_FATAL:

                    color_code = "\033[1;31m";  // 红色

                    reset_code = "\033[0m";

                    break;

                case LOG_LEVEL_WARN:

                    color_code = "\033[1;33m";  // 黄色

                    reset_code = "\033[0m";

                    break;

                case LOG_LEVEL_INFO:

                    color_code = "\033[1;32m";  // 绿色

                    reset_code = "\033[0m";

                    break;

                default:

                    break;

            }

        }

        

        fprintf(stdout, "%s%s%s", color_code, log_line, reset_code);

    }

    

    pthread_mutex_unlock(&logger->lock);

}

```


## 便捷的日志宏定义


```c

// 日志宏定义

#define LOG_DEBUG(format, ...) \

    log_message(LOG_LEVEL_DEBUG, __FILE__, __LINE__, format, ##__VA_ARGS__)


#define LOG_INFO(format, ...) \

    log_message(LOG_LEVEL_INFO, __FILE__, __LINE__, format, ##__VA_ARGS__)


#define LOG_WARN(format, ...) \

    log_message(LOG_LEVEL_WARN, __FILE__, __LINE__, format, ##__VA_ARGS__)


#define LOG_ERROR(format, ...) \

    log_message(LOG_LEVEL_ERROR, __FILE__, __LINE__, format, ##__VA_ARGS__)


#define LOG_FATAL(format, ...) \

    log_message(LOG_LEVEL_FATAL, __FILE__, __LINE__, format, ##__VA_ARGS__)


// 带条件的断言日志

#define LOG_ASSERT(condition, format, ...) \

    do { \

        if (!(condition)) { \

            LOG_ERROR("Assertion failed: " format, ##__VA_ARGS__); \

        } \

    } while (0)

```


## 配置管理接口


```c

// 配置日志系统

int logger_configure(const LogConfig* config) {

<"rw.xingjungy.cn"><"qm.xingjungy.cn"><"ghb.xingjungy.cn">

    Logger* logger = get_logger();

    if (logger == NULL || config == NULL) {

        return -1;

    }

    

    pthread_mutex_lock(&logger->lock);

    

    // 更新配置

    if (strlen(config->log_dir) > 0) {

        strncpy(logger->config.log_dir, config->log_dir, 

                sizeof(logger->config.log_dir) - 1);

    }

    

    if (strlen(config->log_name) > 0) {

        strncpy(logger->config.log_name, config->log_name,

<"eqw.xingjungy.cn"><"egv.xingjungy.cn"><"nkl.xingjungy.cn">

                sizeof(logger->config.log_name) - 1);

    }

    

    logger->config.level = config->level;

    logger->config.max_file_size = config->max_file_size;

    logger->config.max_backup_files = config->max_backup_files;

    logger->config.enable_console = config->enable_console;

    

    // 重新打开日志文件

    if (logger->log_file != NULL) {

        fclose(logger->log_file);

    }

    

    if (open_log_file(logger) != 0) {

        pthread_mutex_unlock(&logger->lock);

        return -1;

    }

    

    pthread_mutex_unlock(&logger->lock);

    return 0;

}


// 获取当前配置

int logger_get_config(LogConfig* config) {

    Logger* logger = get_logger();

    if (logger == NULL || config == NULL) {

        return -1;

    }

    

    pthread_mutex_lock(&logger->lock);

    memcpy(config, &logger->config, sizeof(LogConfig));

    pthread_mutex_unlock(&logger->lock);

    

    return 0;

}

```


## 完整使用示例


```c

// 示例程序

#include "logger.h"


// 模拟业务函数

void process_data(int id) {

    LOG_DEBUG("开始处理数据,ID: %d", id);

    

    // 模拟处理逻辑

    for (int i = 0; i < 3; i++) {

        LOG_INFO("处理进度: %d/%d", i + 1, 3);

        sleep(1);

    }

    

    if (id % 5 == 0) {

        LOG_WARN("数据ID能被5整除,可能需要特殊处理");

    }

    

    if (id < 0) {

        LOG_ERROR("无效的数据ID: %d", id);

        return;

    }

    

    LOG_DEBUG("数据处理完成,ID: %d", id);

}


int main() {

    // 自定义配置

    LogConfig config = {

        .log_dir = "/var/log/myapp",

        .log_name = "application",

        .level = LOG_LEVEL_DEBUG,

        .max_file_size = 5 * 1024 * 1024,  // 5MB

        .max_backup_files = 5,

        .enable_console = 1

    };

    

    // 配置日志系统

    if (logger_configure(&config) != 0) {

        fprintf(stderr, "日志系统配置失败\n");

        return 1;

    }

    

    // 记录启动信息

    LOG_INFO("应用程序启动");

    LOG_INFO("日志级别: %s", get_level_string(config.level));

    LOG_INFO("日志目录: %s", config.log_dir);

    

    // 模拟业务处理

    for (int i = 1; i <= 10; i++) {

        process_data(i);

    }

    

    // 测试错误情况

    process_data(-1);

    

    // 记录关闭信息

    LOG_INFO("应用程序正常退出");

    

    return 0;

}

```


## 性能优化建议


1. **异步日志记录**:可添加队列机制,将日志写入与业务逻辑分离

2. **批量写入**:积累多条日志后一次性写入文件

3. **内存缓存**:使用内存缓冲区减少磁盘I/O次数

4. **格式优化**:减少不必要的字符串格式化操作

5. **条件编译**:在发布版本中移除调试日志


## 系统清理与关闭


```c

// 关闭日志系统

void logger_shutdown() {

    Logger* logger = get_logger();

    if (logger == NULL) {

        return;

    }

    

    pthread_mutex_lock(&logger->lock);


    

    LOG_INFO("日志系统关闭");

    

    if (logger->log_file != NULL) {

        fclose(logger->log_file);

        logger->log_file = NULL;

    }

    

    pthread_mutex_unlock(&logger->lock);

    pthread_mutex_destroy(&logger->lock);

    

    free(logger);

    g_logger = NULL;

}

```


## 总结


本文实现了一个功能完整的Linux日志系统,具备线程安全、多级别日志、文件轮转等核心特性。通过合理的架构设计和代码实现,该系统既保证了功能完整性,又维持了良好的性能表现。


日志系统的设计需要在功能丰富性和性能开销之间找到平衡点。实际项目中可根据具体需求进行调整,例如添加网络日志传输、数据库存储、实时监控等高级功能。良好的日志系统能够显著提升软件的可维护性和可靠性,是高质量软件开发不可或缺的组成部分。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/19545/viewspace-3106272/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2015-11-01

  • 博文量
    895
  • 访问量
    933620