Esta biblioteca en C ofrece un sistema de registro de logs con múltiples características para aplicaciones. Las funcionalidades principales incluyen gestión de niveles de log, configuración flexible meidante archivo JSON y rotación automática de archivos.
Características Implementadas
- Soporte para múltiples niveles de registro: ERROR, WARNING, SYSTEM, INFO y DEBUG.
- Configuración de la ruta de almacenamiento de los archivos de log.
- Personalización del nombre base de los arcihvos de log.
- Control del número máximo de archivos de log conservados.
- Definición del tamaño máximo por archivo de log en megabytes.
- Opción para habilitar o deshabilitar la escritura en archivo.
- Configuración por módulos diferentes, permitiendo compartir un mismo archivo de configuración.
Archivo de Cabecera: log_utils.h
#ifndef LOG_UTILS_H
#define LOG_UTILS_H
#define MAX_LOG_MSG_LEN 10240
// Macros para capturar información de depuración
#define LOG_DEBUG __FILE__, __LINE__, 4
#define LOG_INFO __FILE__, __LINE__, 3
#define LOG_SYS __FILE__, __LINE__, 2
#define LOG_WARN __FILE__, __LINE__, 1
#define LOG_ERR __FILE__, __LINE__, 0
#define LOG_RAW __FILE__, __LINE__, -1
// Inicialización del sistema de logging
int log_init(const char *cfg_path, const char *module);
// Función principal de registro
void log_print(const char *src_file, int src_line, int lvl, const char *fmt, ...);
#endif
Implementación Principal: log_utils.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include "cJSON.h"
#include "log_utils.h"
typedef struct {
char dir_path[100];
char file_prefix[100];
int max_log_files;
int max_file_size_mb;
int log_threshold;
int file_output;
} LogSettings;
static LogSettings settings;
static int initialized = 0;
static char cfg_path_global[256];
static const char *default_cfg = "{"
"\"default\": {"
"\"dir_path\":\"./\","
"\"file_prefix\":\"app\","
"\"max_log_files\":1,"
"\"max_file_size_mb\":1,"
"\"log_threshold\":4,"
"\"file_output\":1"
"}"
"}";
static char *load_text_file(const char *path) {
FILE *fp = fopen(path, "rb");
if (!fp) {
perror("Error al abrir archivo de configuración");
return NULL;
}
fseek(fp, 0, SEEK_END);
long size = ftell(fp);
rewind(fp);
char *buffer = (char *)malloc(size + 1);
if (!buffer) {
fclose(fp);
return NULL;
}
fread(buffer, 1, size, fp);
buffer[size] = '\0';
fclose(fp);
return buffer;
}
int log_init(const char *cfg_path, const char *module) {
memset(&settings, 0, sizeof(LogSettings));
initialized = 1;
strncpy(cfg_path_global, cfg_path, sizeof(cfg_path_global) - 1);
// Establecer valores predeterminados
strcpy(settings.dir_path, "./");
strcpy(settings.file_prefix, "app");
settings.max_log_files = 1;
settings.max_file_size_mb = 1;
settings.log_threshold = 4;
settings.file_output = 1;
const char *target_module = module ? module : "default";
// Verificar existencia del archivo de configuración
if (access(cfg_path, F_OK) != 0) {
FILE *file = fopen(cfg_path, "w");
if (file) {
fputs(default_cfg, file);
fclose(file);
}
}
char *content = load_text_file(cfg_path);
if (!content) return -1;
cJSON *json = cJSON_Parse(content);
free(content);
if (!json) {
fprintf(stderr, "Error en formato JSON\n");
return -1;
}
cJSON *module_node = cJSON_GetObjectItem(json, target_module);
if (!module_node) module_node = cJSON_GetObjectItem(json, "default");
cJSON *field;
field = cJSON_GetObjectItem(module_node, "dir_path");
if (field) strncpy(settings.dir_path, field->valuestring, sizeof(settings.dir_path) - 1);
field = cJSON_GetObjectItem(module_node, "file_prefix");
if (field) strncpy(settings.file_prefix, field->valuestring, sizeof(settings.file_prefix) - 1);
field = cJSON_GetObjectItem(module_node, "max_log_files");
if (field) settings.max_log_files = field->valueint;
field = cJSON_GetObjectItem(module_node, "max_file_size_mb");
if (field) settings.max_file_size_mb = field->valueint;
field = cJSON_GetObjectItem(module_node, "log_threshold");
if (field) settings.log_threshold = field->valueint;
field = cJSON_GetObjectItem(module_node, "file_output");
if (field) settings.file_output = field->valueint;
cJSON_Delete(json);
return 0;
}
void log_print(const char *src_file, int src_line, int lvl, const char *fmt, ...) {
if (!initialized || lvl > settings.log_threshold) return;
static const char *level_tags[] = {"ERR", "WRN", "SYS", "INF", "DBG"};
const char *tag = (lvl >= 0 && lvl <= 4) ? level_tags[lvl] : "";
char log_path[256];
FILE *log_fp = NULL;
if (settings.file_output) {
snprintf(log_path, sizeof(log_path), "%s/%s.log", settings.dir_path, settings.file_prefix);
// Crear directorio si no existe
struct stat st;
if (stat(settings.dir_path, &st) != 0) {
mkdir(settings.dir_path, 0755);
}
// Verificar necesidad de rotación
if (access(log_path, F_OK) == 0) {
if (stat(log_path, &st) == 0) {
if (st.st_size >= (off_t)settings.max_file_size_mb * 1024 * 1024) {
char old_path[256], new_path[256];
for (int i = settings.max_log_files; i > 0; i--) {
snprintf(old_path, sizeof(old_path), "%s/%s_%d.log", settings.dir_path, settings.file_prefix, i);
if (access(old_path, F_OK) == 0) {
if (i == settings.max_log_files) {
unlink(old_path);
} else {
snprintf(new_path, sizeof(new_path), "%s/%s_%d.log", settings.dir_path, settings.file_prefix, i + 1);
rename(old_path, new_path);
}
}
}
snprintf(old_path, sizeof(old_path), "%s/%s_1.log", settings.dir_path, settings.file_prefix);
rename(log_path, old_path);
}
}
}
log_fp = fopen(log_path, "a");
if (!log_fp) {
perror("No se pudo abrir archivo de log");
return;
}
}
char msg_buffer[MAX_LOG_MSG_LEN];
va_list ap;
va_start(ap, fmt);
vsnprintf(msg_buffer, sizeof(msg_buffer), fmt, ap);
va_end(ap);
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
char time_buf[20];
strftime(time_buf, sizeof(time_buf), "%Y%m%d %H:%M:%S", tm_info);
if (lvl == -1) {
printf("%s", msg_buffer);
if (log_fp) fprintf(log_fp, "%s", msg_buffer);
} else {
printf("[%s] [%s] [%s:%d] %s\n", time_buf, tag, src_file, src_line, msg_buffer);
if (log_fp) fprintf(log_fp, "[%s] [%s] [%s:%d] %s\n", time_buf, tag, src_file, src_line, msg_buffer);
}
if (log_fp) fclose(log_fp);
}
Makefile para Compilación
CC = gcc
CFLAGS = -Wall -O2 -fPIC
SHARED_FLAGS = -shared
OBJETIVO = liblog.so
OBJS = log_utils.o cJSON.o
all: $(OBJETIVO)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
$(OBJETIVO): $(OBJS)
$(CC) $(SHARED_FLAGS) -o $@ $^
clean:
rm -f $(OBJS) $(OBJETIVO)
Archivo de Configuración de Ejemplo (log_config.json)
{
"default": {
"dir_path": "./logs",
"file_prefix": "main",
"max_log_files": 3,
"max_file_size_mb": 5,
"log_threshold": 4,
"file_output": 1
},
"network": {
"dir_path": "./net_logs",
"file_prefix": "net",
"max_log_files": 2,
"max_file_size_mb": 10,
"log_threshold": 3,
"file_output": 1
}
}
El análisis del archivo JSON se realiza meidante la biblioteca cJSON, cuyo código fuente está disponible en el repositorio oficial: cJSON en GitHub.