使用C语言设计编写程序日志,实现时间戳、 日志级别、日志内容、 日志输出、 日志轮转功能,同时结合程序日志的配置文件,并给出配置文件的内容,形成.c,.h和测试函数
时间: 2024-02-16 14:04:11 浏览: 149
好的,我可以为您提供一个简单的程序日志实现。首先,我们需要定义日志级别和输出方式:
```c
typedef enum {
LOG_DEBUG,
LOG_INFO,
LOG_WARN,
LOG_ERROR
} log_level_t;
typedef enum {
LOG_OUTPUT_CONSOLE,
LOG_OUTPUT_FILE
} log_output_t;
```
然后我们定义一个日志结构体,用于存储日志信息:
```c
typedef struct {
log_level_t level;
char *message;
time_t timestamp;
} log_entry_t;
```
接下来,我们定义一个日志模块,包含初始化、写入日志、切换日志文件等功能:
```c
typedef struct {
char *filename;
log_output_t output;
log_level_t level;
FILE *file;
int max_size;
int max_files;
int current_file_index;
log_entry_t *buffer;
int buffer_size;
int buffer_count;
} log_module_t;
void log_init(log_module_t *log, char *filename, log_output_t output, log_level_t level, int max_size, int max_files);
void log_write(log_module_t *log, log_level_t level, char *format, ...);
void log_switch_file(log_module_t *log);
void log_cleanup(log_module_t *log);
```
在初始化函数中,我们需要读取配置文件,配置文件的格式如下:
```ini
[log]
filename=log.txt
output=console
level=info
max_size=10240
max_files=5
```
我们可以使用`ini`库来解析配置文件。具体实现可以参考以下代码:
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
#include "ini.h"
#define LOG_MAX_BUFFER_SIZE 1024
#define LOG_MAX_FILE_NAME 256
#define LOG_MAX_TIMESTAMP_SIZE 32
static char log_level_names[][6] = {"DEBUG", "INFO", "WARN", "ERROR"};
static void log_write_to_file(log_module_t *log, log_entry_t *entry);
static void log_write_to_console(log_entry_t *entry);
static void log_rotate_files(log_module_t *log);
static int log_config_handler(void *user, const char *section, const char *name, const char *value) {
log_module_t *log = (log_module_t*) user;
if (strcasecmp(section, "log") == 0) {
if (strcasecmp(name, "filename") == 0) {
log->filename = strdup(value);
} else if (strcasecmp(name, "output") == 0) {
if (strcasecmp(value, "console") == 0) {
log->output = LOG_OUTPUT_CONSOLE;
} else if (strcasecmp(value, "file") == 0) {
log->output = LOG_OUTPUT_FILE;
}
} else if (strcasecmp(name, "level") == 0) {
if (strcasecmp(value, "debug") == 0) {
log->level = LOG_DEBUG;
} else if (strcasecmp(value, "info") == 0) {
log->level = LOG_INFO;
} else if (strcasecmp(value, "warn") == 0) {
log->level = LOG_WARN;
} else if (strcasecmp(value, "error") == 0) {
log->level = LOG_ERROR;
}
} else if (strcasecmp(name, "max_size") == 0) {
log->max_size = atoi(value);
} else if (strcasecmp(name, "max_files") == 0) {
log->max_files = atoi(value);
}
}
return 1;
}
void log_init(log_module_t *log, char *filename, log_output_t output, log_level_t level, int max_size, int max_files) {
log->filename = filename;
log->output = output;
log->level = level;
log->max_size = max_size;
log->max_files = max_files;
log->file = NULL;
log->buffer = (log_entry_t*) calloc(log->max_size, sizeof(log_entry_t));
log->buffer_size = log->max_size;
log->buffer_count = 0;
log->current_file_index = 0;
ini_parse("log.ini", log_config_handler, log);
}
void log_write(log_module_t *log, log_level_t level, char *format, ...) {
if (level < log->level) {
return;
}
log_entry_t entry;
entry.level = level;
entry.message = (char*) malloc(LOG_MAX_BUFFER_SIZE);
entry.timestamp = time(NULL);
va_list args;
va_start(args, format);
vsnprintf(entry.message, LOG_MAX_BUFFER_SIZE, format, args);
va_end(args);
if (log->output == LOG_OUTPUT_FILE) {
log_write_to_file(log, &entry);
} else {
log_write_to_console(&entry);
}
free(entry.message);
}
void log_switch_file(log_module_t *log) {
if (log->output != LOG_OUTPUT_FILE) {
return;
}
fclose(log->file);
char filename[LOG_MAX_FILE_NAME];
snprintf(filename, LOG_MAX_FILE_NAME, "%s.%d", log->filename, log->current_file_index);
rename(log->filename, filename);
log->file = fopen(log->filename, "a+");
if (log->file == NULL) {
log->file = fopen(log->filename, "w");
}
log_rotate_files(log);
}
void log_cleanup(log_module_t *log) {
if (log->output == LOG_OUTPUT_FILE) {
fclose(log->file);
}
free(log->filename);
free(log->buffer);
}
static void log_write_to_file(log_module_t *log, log_entry_t *entry) {
if (log->buffer_count >= log->buffer_size) {
log_rotate_files(log);
}
fwrite(entry->message, strlen(entry->message), 1, log->file);
fflush(log->file);
log->buffer[log->buffer_count++] = *entry;
}
static void log_write_to_console(log_entry_t *entry) {
char timestamp[LOG_MAX_TIMESTAMP_SIZE];
strftime(timestamp, LOG_MAX_TIMESTAMP_SIZE, "%Y-%m-%d %H:%M:%S", localtime(&entry->timestamp));
printf("[%s] %s: %s\n", timestamp, log_level_names[entry->level], entry->message);
}
static void log_rotate_files(log_module_t *log) {
if (log->max_files <= 1) {
return;
}
char filename[LOG_MAX_FILE_NAME];
for (int i = log->max_files - 2; i >= 0; i--) {
if (i == 0) {
snprintf(filename, LOG_MAX_FILE_NAME, "%s", log->filename);
} else {
snprintf(filename, LOG_MAX_FILE_NAME, "%s.%d", log->filename, i);
}
char new_filename[LOG_MAX_FILE_NAME];
snprintf(new_filename, LOG_MAX_FILE_NAME, "%s.%d", log->filename, i + 1);
rename(filename, new_filename);
}
log->current_file_index++;
if (log->current_file_index >= log->max_files) {
log->current_file_index = 0;
}
char new_filename[LOG_MAX_FILE_NAME];
snprintf(new_filename, LOG_MAX_FILE_NAME, "%s.%d", log->filename, log->current_file_index);
fclose(log->file);
log->file = fopen(new_filename, "a+");
if (log->file == NULL) {
log->file = fopen(new_filename, "w");
}
log->buffer_count = 0;
}
```
最后,我们可以编写一个测试函数来测试程序日志的功能:
```c
void test_log() {
log_module_t log;
log_init(&log, "log.txt", LOG_OUTPUT_CONSOLE, LOG_DEBUG, 10240, 5);
log_write(&log, LOG_DEBUG, "This is a debug message.");
log_write(&log, LOG_INFO, "This is an info message.");
log_write(&log, LOG_WARN, "This is a warning message.");
log_write(&log, LOG_ERROR, "This is an error message.");
for (int i = 0; i < 10000; i++) {
log_write(&log, LOG_INFO, "This is message #%d.", i);
}
log_cleanup(&log);
}
```
以上代码实现了一个简单的程序日志,包含时间戳、日志级别、日志内容、日志输出、日志轮转等功能。
阅读全文