c
#define _GNU_SOURCE
#include "./log.h"
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <time.h>
#include <unistd.h>
/* ========== 常量 ========== */
#define LOG_SLOT_SIZE 512
#define DEFAULT_RING_CAPACITY 8192 /* 必须是 2 的幂 */
#define MAX_BATCH_WRITE 256 /* writev 单次最大 iovec 数 */
#define DEFAULT_FLUSH_INTERVAL 512
#define DEFAULT_FLUSH_TIMEOUT_MS 100
#define PATH_BUF_SIZE 256
#define TIME_BUF_SIZE 20
/* slot 状态 */
enum slot_state { SLOT_FREE = 0, SLOT_WRITING = 1, SLOT_READY = 2 };
/* ========== Ring Buffer ========== */
struct log_slot {
atomic_uint state;
uint32_t len;
char data[LOG_SLOT_SIZE];
};
struct ring_buffer {
struct log_slot *slots;
uint32_t capacity; /* 2 的幂 */
uint32_t mask; /* capacity - 1 */
atomic_uint head; /* 生产者写入位置 */
atomic_uint tail; /* 消费者读取位置 */
};
/* ========== Logger 结构 ========== */
struct logger {
/* 配置 */
const char *path;
const char *name;
int fd;
enum LOG_LVL log_lvl;
bool only_current_output;
bool stdout_console;
/* 异步核心 */
struct ring_buffer rb;
pthread_t flush_thread;
atomic_bool running;
pthread_mutex_t wake_mutex;
pthread_cond_t wake_cond;
/* 时间戳缓存 - 生产者读,后台线程写 */
atomic_long cached_sec; /* time_t */
char cached_time[TIME_BUF_SIZE];
/* 轮转 */
uint32_t threshold;
uint32_t written;
int32_t n_log;
int32_t max_logs;
char **record;
uint32_t inner_index;
/* flush 策略 */
uint32_t flush_interval;
uint32_t flush_timeout_ms;
};
/* ========== 工具函数 ========== */
static int create_dir(const char *path, int mode) {
if (path == NULL || strlen(path) >= PATH_BUF_SIZE) {
return -1;
}
char tmp[PATH_BUF_SIZE], built[PATH_BUF_SIZE];
struct stat fst;
memset(built, 0, sizeof(built));
strncpy(tmp, path, sizeof(tmp) - 1);
tmp[sizeof(tmp) - 1] = '\0';
char *sp = tmp;
const char *tok = NULL;
int32_t pos = 0;
while ((tok = strsep(&sp, "/"))) {
if (tok[0] == '\0')
continue;
pos += snprintf(built + pos, sizeof(built) - pos, "/%s", tok);
if (stat(built, &fst) != 0) {
mkdir(built, mode);
} else if (!S_ISDIR(fst.st_mode)) {
if (remove(built) == 0)
mkdir(built, mode);
}
}
return 0;
}
/* 快速格式化时间戳到 buf,返回长度 */
static inline int format_time(char *buf, size_t len, const struct tm *t) {
/* 手动格式化比 snprintf 快 ~5x */
if (len < 15)
return 0;
int y = t->tm_year + 1900;
buf[0] = '0' + (y / 1000);
buf[1] = '0' + (y / 100 % 10);
buf[2] = '0' + (y / 10 % 10);
buf[3] = '0' + (y % 10);
int m = t->tm_mon + 1;
buf[4] = '0' + (m / 10);
buf[5] = '0' + (m % 10);
int d = t->tm_mday;
buf[6] = '0' + (d / 10);
buf[7] = '0' + (d % 10);
int h = t->tm_hour;
buf[8] = '0' + (h / 10);
buf[9] = '0' + (h % 10);
int mi = t->tm_min;
buf[10] = '0' + (mi / 10);
buf[11] = '0' + (mi % 10);
int s = t->tm_sec;
buf[12] = '0' + (s / 10);
buf[13] = '0' + (s % 10);
buf[14] = '\0';
return 14;
}
/* 更新时间戳缓存(后台线程调用) */
static void update_time_cache(struct logger *dev) {
time_t now = time(NULL);
time_t cached = atomic_load_explicit(&dev->cached_sec, memory_order_relaxed);
if (now != cached) {
struct tm tminfo;
localtime_r(&now, &tminfo);
format_time(dev->cached_time, TIME_BUF_SIZE, &tminfo);
atomic_store_explicit(&dev->cached_sec, now, memory_order_release);
}
}
/* 获取缓存时间戳(生产者调用,无锁) */
static inline const char *get_cached_time(struct logger *dev) {
return dev->cached_time;
}
/* ========== Ring Buffer 操作 ========== */
static int rb_init(struct ring_buffer *rb, uint32_t capacity) {
/* 确保 capacity 是 2 的幂 */
uint32_t cap = 1;
while (cap < capacity)
cap <<= 1;
rb->capacity = cap;
rb->mask = cap - 1;
rb->slots = (struct log_slot *)calloc(cap, sizeof(struct log_slot));
if (rb->slots == NULL)
return -1;
atomic_init(&rb->head, 0);
atomic_init(&rb->tail, 0);
for (uint32_t i = 0; i < cap; i++) {
atomic_init(&rb->slots[i].state, SLOT_FREE);
}
return 0;
}
static void rb_destroy(struct ring_buffer *rb) {
free(rb->slots);
rb->slots = NULL;
}
/* 生产者:获取一个 slot 用于写入,返回 NULL 表示队列满 */
static struct log_slot *rb_acquire(struct ring_buffer *rb) {
uint32_t pos =
atomic_fetch_add_explicit(&rb->head, 1, memory_order_relaxed);
uint32_t idx = pos & rb->mask;
struct log_slot *slot = &rb->slots[idx];
/* 自旋等待 slot 变为 FREE(极少触发,仅在队列满时) */
uint32_t spin = 0;
while (atomic_load_explicit(&slot->state, memory_order_acquire) !=
SLOT_FREE) {
if (++spin > 1000) {
sched_yield();
spin = 0;
}
}
atomic_store_explicit(&slot->state, SLOT_WRITING, memory_order_relaxed);
return slot;
}
/* 生产者:标记 slot 写入完成 */
static inline void rb_publish(struct log_slot *slot) {
atomic_store_explicit(&slot->state, SLOT_READY, memory_order_release);
}
/* ========== 日志轮转 ========== */
static void rotate_log(struct logger *dev) {
if (dev->fd <= 0)
return;
close(dev->fd);
dev->fd = -1;
if (dev->max_logs >= 1) {
if (dev->n_log >= dev->max_logs) {
for (int32_t i = 0; i < dev->max_logs; i++) {
if (dev->record[i][0] != '\0')
remove(dev->record[i]);
}
dev->n_log = 1;
dev->inner_index = 0;
}
}
char fn[PATH_BUF_SIZE], cur_fn[PATH_BUF_SIZE];
snprintf(cur_fn, sizeof(cur_fn), "%s/%s.txt", dev->path, dev->name);
snprintf(fn, sizeof(fn), "%s/%s.%s.txt", dev->path, dev->name,
dev->cached_time);
if (access(fn, F_OK) == 0) {
snprintf(fn, sizeof(fn), "%s/%s.%s(%d).txt", dev->path, dev->name,
dev->cached_time, dev->inner_index++);
}
if (dev->n_log >= 1 && dev->n_log <= dev->max_logs) {
strncpy(dev->record[dev->n_log - 1], fn, PATH_BUF_SIZE - 1);
}
rename(cur_fn, fn);
dev->n_log++;
/* 重新打开 */
int retry = 0;
while (retry < 5) {
dev->fd = open(cur_fn, O_WRONLY | O_CREAT | O_APPEND, 0644);
if (dev->fd >= 0)
break;
create_dir(dev->path, 0755);
usleep(100000);
retry++;
}
if (dev->fd < 0) {
dev->fd = STDERR_FILENO;
}
dev->written = 0;
}
/* ========== 后台 Flush 线程 ========== */
static void *flush_thread_func(void *arg) {
struct logger *dev = (struct logger *)arg;
struct ring_buffer *rb = &dev->rb;
struct iovec iovecs[MAX_BATCH_WRITE];
while (atomic_load_explicit(&dev->running, memory_order_relaxed)) {
/* 等待唤醒或超时 */
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_nsec += (long)dev->flush_timeout_ms * 1000000L;
if (ts.tv_nsec >= 1000000000L) {
ts.tv_sec += ts.tv_nsec / 1000000000L;
ts.tv_nsec %= 1000000000L;
}
pthread_mutex_lock(&dev->wake_mutex);
pthread_cond_timedwait(&dev->wake_cond, &dev->wake_mutex, &ts);
pthread_mutex_unlock(&dev->wake_mutex);
/* 更新时间戳缓存 */
update_time_cache(dev);
/* 批量消费 */
uint32_t tail = atomic_load_explicit(&rb->tail, memory_order_relaxed);
uint32_t head = atomic_load_explicit(&rb->head, memory_order_acquire);
uint32_t count = 0;
uint32_t batch_bytes = 0;
while (tail != head && count < MAX_BATCH_WRITE) {
uint32_t idx = tail & rb->mask;
struct log_slot *slot = &rb->slots[idx];
/* 等待 slot 变为 READY */
if (atomic_load_explicit(&slot->state, memory_order_acquire) !=
SLOT_READY) {
break; /* 还在写入中,下次再处理 */
}
iovecs[count].iov_base = slot->data;
iovecs[count].iov_len = slot->len;
batch_bytes += slot->len;
count++;
tail++;
}
if (count > 0) {
/* 检查是否需要轮转 */
if (dev->threshold > 0 && dev->fd != STDOUT_FILENO &&
dev->fd != STDERR_FILENO &&
dev->written + batch_bytes >= dev->threshold) {
rotate_log(dev);
}
/* writev 批量写入 */
ssize_t n = writev(dev->fd, iovecs, count);
(void)n;
/* 控制台输出 */
if (dev->stdout_console && dev->fd != STDOUT_FILENO &&
dev->fd != STDERR_FILENO) {
writev(STDOUT_FILENO, iovecs, count);
}
dev->written += batch_bytes;
/* 释放 slots */
uint32_t old_tail =
atomic_load_explicit(&rb->tail, memory_order_relaxed);
for (uint32_t i = 0; i < count; i++) {
uint32_t idx = (old_tail + i) & rb->mask;
rb->slots[idx].len = 0;
atomic_store_explicit(&rb->slots[idx].state, SLOT_FREE,
memory_order_release);
}
atomic_store_explicit(&rb->tail, old_tail + count,
memory_order_release);
}
}
/* shutdown: drain remaining */
uint32_t tail = atomic_load_explicit(&rb->tail, memory_order_relaxed);
uint32_t head = atomic_load_explicit(&rb->head, memory_order_acquire);
while (tail != head) {
uint32_t idx = tail & rb->mask;
struct log_slot *slot = &rb->slots[idx];
/* spin wait for READY */
while (atomic_load_explicit(&slot->state, memory_order_acquire) !=
SLOT_READY) {
sched_yield();
}
if (slot->len > 0) {
write(dev->fd, slot->data, slot->len);
if (dev->stdout_console && dev->fd != STDOUT_FILENO &&
dev->fd != STDERR_FILENO) {
write(STDOUT_FILENO, slot->data, slot->len);
}
}
atomic_store_explicit(&slot->state, SLOT_FREE, memory_order_release);
tail++;
}
atomic_store_explicit(&rb->tail, tail, memory_order_release);
if (dev->fd != STDOUT_FILENO && dev->fd != STDERR_FILENO) {
fdatasync(dev->fd);
}
return NULL;
}
/* ========== 生产者:格式化 + 入队 ========== */
static const char *lvl_strings[] = {"CRIT ", "ERROR", "WARN ", "INFO ", "DEBUG"};
static inline int32_t logger_emit(struct logger *dev, enum LOG_LVL lvl,
const char *file, uint32_t line,
const char *func, const char *fmt,
va_list ap) {
if (dev == NULL)
return -1;
/* 级别检查(无锁) */
if (dev->only_current_output) {
if (dev->log_lvl != lvl)
return 0;
} else {
if (dev->log_lvl < lvl)
return 0;
}
struct log_slot *slot = rb_acquire(&dev->rb);
/* 格式化 header */
const char *ts = get_cached_time(dev);
const char *lvl_str = lvl_strings[lvl];
int pos;
if (dev->log_lvl == DEBUG && file != NULL && func != NULL) {
pos = snprintf(slot->data, LOG_SLOT_SIZE,
"[ %-14s] [ %s] [ %s] [ %s:%u] ", ts, lvl_str, func, file,
line);
} else if (func != NULL) {
pos = snprintf(slot->data, LOG_SLOT_SIZE, "[ %-14s] [ %s] [ %s] ", ts,
lvl_str, func);
} else {
pos = snprintf(slot->data, LOG_SLOT_SIZE, "[ %-14s] [ %s] ", ts, lvl_str);
}
if (pos < 0)
pos = 0;
if (pos >= (int)LOG_SLOT_SIZE)
pos = LOG_SLOT_SIZE - 1;
/* 格式化用户消息 */
int msg_len =
vsnprintf(slot->data + pos, LOG_SLOT_SIZE - pos, fmt, ap);
if (msg_len < 0)
msg_len = 0;
pos += msg_len;
if (pos >= (int)LOG_SLOT_SIZE)
pos = LOG_SLOT_SIZE - 1;
slot->len = pos;
/* 发布 + 唤醒 */
rb_publish(slot);
/* 仅在累积一定数量后唤醒,减少 syscall */
uint32_t head = atomic_load_explicit(&dev->rb.head, memory_order_relaxed);
uint32_t tail = atomic_load_explicit(&dev->rb.tail, memory_order_relaxed);
if ((head - tail) >= dev->flush_interval) {
pthread_cond_signal(&dev->wake_cond);
}
return 0;
}
/* ========== 公开 API ========== */
struct logger *new_logger(const char *path, const char *name,
uint32_t threshold, int32_t max_logs) {
struct logger *dev = (struct logger *)calloc(1, sizeof(struct logger));
if (dev == NULL)
return NULL;
dev->path = path;
dev->name = name;
dev->threshold = threshold;
dev->log_lvl = INFO;
dev->only_current_output = false;
dev->stdout_console = false;
dev->flush_interval = DEFAULT_FLUSH_INTERVAL;
dev->flush_timeout_ms = DEFAULT_FLUSH_TIMEOUT_MS;
dev->inner_index = 1;
dev->n_log = 1;
dev->max_logs = max_logs;
dev->written = 0;
/* 轮转记录 */
if (max_logs > 0) {
dev->record = (char **)calloc(max_logs, sizeof(char *));
for (int32_t i = 0; i < max_logs; i++) {
dev->record[i] = (char *)calloc(PATH_BUF_SIZE, 1);
}
}
/* 打开文件 */
if (path == NULL) {
dev->fd = STDOUT_FILENO;
} else {
create_dir(path, 0755);
char fn[PATH_BUF_SIZE];
snprintf(fn, sizeof(fn), "%s/%s.txt", path, name);
dev->fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (dev->fd < 0) {
dev->fd = STDERR_FILENO;
}
}
/* 初始化 ring buffer */
if (rb_init(&dev->rb, DEFAULT_RING_CAPACITY) != 0) {
free(dev);
return NULL;
}
/* 初始化时间戳缓存 */
time_t now = time(NULL);
struct tm tminfo;
localtime_r(&now, &tminfo);
format_time(dev->cached_time, TIME_BUF_SIZE, &tminfo);
atomic_init(&dev->cached_sec, now);
/* 启动后台线程 */
pthread_mutex_init(&dev->wake_mutex, NULL);
pthread_cond_init(&dev->wake_cond, NULL);
atomic_init(&dev->running, true);
pthread_create(&dev->flush_thread, NULL, flush_thread_func, dev);
return dev;
}
void logger_free(struct logger *dev) {
if (dev == NULL)
return;
/* 停止后台线程 */
atomic_store_explicit(&dev->running, false, memory_order_release);
pthread_cond_signal(&dev->wake_cond);
pthread_join(dev->flush_thread, NULL);
pthread_mutex_destroy(&dev->wake_mutex);
pthread_cond_destroy(&dev->wake_cond);
/* 关闭文件 */
if (dev->fd >= 0 && dev->fd != STDOUT_FILENO && dev->fd != STDERR_FILENO) {
close(dev->fd);
}
/* 释放资源 */
rb_destroy(&dev->rb);
if (dev->record != NULL) {
for (int32_t i = 0; i < dev->max_logs; i++) {
free(dev->record[i]);
}
free(dev->record);
}
free(dev);
}
int32_t logger_flush(struct logger *dev) {
if (dev == NULL)
return -1;
pthread_cond_signal(&dev->wake_cond);
/* 自旋等待队列排空 */
uint32_t spin = 0;
while (atomic_load_explicit(&dev->rb.head, memory_order_acquire) !=
atomic_load_explicit(&dev->rb.tail, memory_order_acquire)) {
if (++spin > 10000) {
sched_yield();
spin = 0;
}
}
return 0;
}
int32_t logger_level(struct logger *dev, enum LOG_LVL lv) {
if (dev == NULL)
return -1;
dev->log_lvl = lv;
return 0;
}
int32_t logger_filter(struct logger *dev, enum LOG_LVL lv) {
if (dev == NULL)
return -1;
dev->log_lvl = lv;
dev->only_current_output = true;
return 0;
}
int32_t logger_cancel_filter(struct logger *dev) {
if (dev == NULL)
return -1;
dev->only_current_output = false;
return 0;
}
void logger_console(struct logger *dev, bool on) {
if (dev != NULL)
dev->stdout_console = on;
}
int32_t logger_set_flush_interval(struct logger *dev, uint32_t count) {
if (dev == NULL || count == 0)
return -1;
dev->flush_interval = count;
return 0;
}
int32_t logger_set_flush_timeout(struct logger *dev, uint32_t ms) {
if (dev == NULL || ms == 0)
return -1;
dev->flush_timeout_ms = ms;
return 0;
}
/* ========== 日志级别 API ========== */
int32_t logger_info(struct logger *dev, const char *file, uint32_t line,
const char *func, const char *fmt, ...) {
va_list arg;
va_start(arg, fmt);
int32_t ret = logger_emit(dev, INFO, file, line, func, fmt, arg);
va_end(arg);
return ret;
}
int32_t logger_warn(struct logger *dev, const char *file, uint32_t line,
const char *func, const char *fmt, ...) {
va_list arg;
va_start(arg, fmt);
int32_t ret = logger_emit(dev, WARN, file, line, func, fmt, arg);
va_end(arg);
return ret;
}
int32_t logger_error(struct logger *dev, const char *file, uint32_t line,
const char *func, const char *fmt, ...) {
va_list arg;
va_start(arg, fmt);
int32_t ret = logger_emit(dev, ERROR, file, line, func, fmt, arg);
va_end(arg);
return ret;
}
int32_t logger_debug(struct logger *dev, const char *file, uint32_t line,
const char *func, const char *fmt, ...) {
va_list arg;
va_start(arg, fmt);
int32_t ret = logger_emit(dev, DEBUG, file, line, func, fmt, arg);
va_end(arg);
return ret;
}
int32_t logger_critical(struct logger *dev, const char *file, uint32_t line,
const char *func, const char *fmt, ...) {
va_list arg;
va_start(arg, fmt);
int32_t ret = logger_emit(dev, CRITICAL, file, line, func, fmt, arg);
va_end(arg);
return ret;
}
int32_t logger_print(struct logger *dev, const char *fmt, ...) {
if (dev == NULL)
return -1;
struct log_slot *slot = rb_acquire(&dev->rb);
va_list ap;
va_start(ap, fmt);
int len = vsnprintf(slot->data, LOG_SLOT_SIZE, fmt, ap);
va_end(ap);
if (len < 0)
len = 0;
if (len >= (int)LOG_SLOT_SIZE)
len = LOG_SLOT_SIZE - 1;
slot->len = len;
rb_publish(slot);
return 0;
}
设计思路都很清楚,但是AI的切入点有些不同,而且质量要比人工高,以后人工写代码的时代要过去了,正如看到一个视频上讲,现在的AI 好比工业革命时的织布机,终将会替代人工去完成一些,重复的工作,刚开始的迎接的时候,就是工作经验为王了,技巧不是太重要了,人类获取的知识的途径,再一次的拉低了门槛
3年前之前花了1天写的,功能是正常的,但是在异常处理那里和指针释放没有那么仔细,写入效率偏低,现在AI-1分钟完成了代码的优化,和输出在加一个bench mark 😀
工业级软件会有一波质量级的更新
但是扎实的伦理基础关于编程还是很重要的,基础算法,编译原理,和计算机原理,都要懂才行,还是要多读书多练习,还有多掌握几门编程语言,还有各式各样的设计模式
使用原子变量,在嵌入式开发中很重要,可以构造很轻量级的线程锁,线程在嵌入式系统中有时会拖慢系统,协程和原子变量的使用,要比线程的开销小的多(这也是和AI学到的,AI联想到的更全面一些),但是又不能随意推到重来,交给AI后,重构的工作量就剩下debug了和补全AI的经验误差,Rust 这中语言,AI来写正合适,对人类来说不友好