[注释规范] 头文件全部函数声明加详细注释 + myLog.c 全面注释 + 代码风格记忆更新

- myLog.h: 所有函数/macro 加 @brief/@param/@return 注释
- myFunc.h: 65 个函数全部加详细注释,结构体成员逐行注释
- myLog.c: 每个内部函数加功能注释、关键逻辑行间注释、}后空行、return前空行
- 代码风格规范已写入 /memories/code-style.md
This commit is contained in:
ypc 2026-07-07 13:47:24 +08:00
parent b5e4a361b4
commit 33252501c3
5 changed files with 719 additions and 154 deletions

View File

@ -1,6 +1,8 @@
/** /**
* @file myFunc.h * @file myFunc.h
* @brief //////IPC * @brief 65 7
* @details / / / /
* / / IPC
*/ */
#ifndef _MY_FUNC_H_ #ifndef _MY_FUNC_H_
@ -13,141 +15,306 @@ extern "C"
{ {
#endif #endif
/* ---- 系统时间 ---- */ /**
* @brief
* @note year: 7bit(0-127+1900), month:4bit(1-12), day:5bit(1-31)
* hour:5bit(0-23), min:6bit(0-59), ms:16bit(0-59999)
*/
typedef struct typedef struct
{ {
uint32_t ms : 16; uint32_t ms : 16; /* 毫秒(实际存储秒*1000+毫秒,范围 0-59999 */
uint32_t min : 6; uint32_t min : 6; /* 分钟 0-59 */
uint32_t : 2; uint32_t : 2; /* 保留 */
uint32_t hour: 5; uint32_t hour: 5; /* 小时 0-23 */
uint32_t : 3; uint32_t : 3; /* 保留 */
uint32_t day : 5; uint32_t day : 5; /* 日期 1-31 */
uint32_t week: 3; uint32_t week: 3; /* 星期 */
uint32_t month:4; uint32_t month:4; /* 月份 1-12 */
uint32_t : 4; uint32_t : 4; /* 保留 */
uint32_t year: 7; uint32_t year: 7; /* 年份偏移(实际值-1900),范围 0-127 */
uint32_t : 1; uint32_t : 1; /* 保留 */
} stru_time_sys; } stru_time_sys;
/* ---- 日历时间 ---- */ /**
* @brief Unix +
*/
typedef struct typedef struct
{ {
time_t sec; time_t sec; /* Unix 时间戳(秒) */
time_t usec; time_t usec; /* 微秒部分 */
} stru_time_cal; } stru_time_cal;
/* ---- IPC 消息文本 ---- */ /**
* @brief IPC
*/
typedef struct typedef struct
{ {
int qid; int qid; /* 响应队列 ID */
int type; int type; /* 响应类型enum_ipc_resp */
int len; int len; /* 有效数据长度 */
char text[4096]; char text[4096]; /* 消息内容 */
} stru_ipc_text; } stru_ipc_text;
/* ---- IPC 响应类型 ---- */ /**
* @brief IPC
*/
typedef enum typedef enum
{ {
IPC_RESP_NONE = 1, IPC_RESP_NONE = 1, /* 无需响应 */
IPC_RESP_ONCE, IPC_RESP_ONCE, /* 单次响应 */
IPC_RESP_ALWAYS IPC_RESP_ALWAYS /* 持续响应 */
} enum_ipc_resp; } enum_ipc_resp;
/* ---- IPC 消息 ---- */ /**
* @brief IPC System V
*/
typedef struct typedef struct
{ {
long id; long id; /* 消息类型 ID */
stru_ipc_text t; stru_ipc_text t; /* 消息载荷 */
} stru_ipc_msg; } stru_ipc_msg;
/* ---- 文件描述头(256字节) ---- */ /**
* @brief 256 CRC
* @note flag 0xA55AA55AA55AA55A
*/
#pragma pack(1) #pragma pack(1)
typedef struct typedef struct
{ {
uint64_t flag; uint64_t flag; /* 魔数标识 0xA55AA55AA55AA55A */
char ver[16]; char ver[16]; /* 版本号 */
char author[16]; char author[16]; /* 作者 */
char modify[32]; char modify[32]; /* 修改时间 YYYY-MM-DD HH-MM-SS */
uint16_t crc; uint16_t crc; /* CRC16 校验 */
char desc[128]; char desc[128]; /* 附加描述 */
char bak[54]; char bak[54]; /* 对齐填充(总计 256 字节) */
} stru_file_info; } stru_file_info;
#pragma pack() #pragma pack()
/* ===== 校验 ===== */ /* ================================================================
* CRC16 使 Modbus 0xA001
* ================================================================ */
/** 8 位累加校验和 */
uint8_t func_cal_sum_byte(const uint8_t *d, uint32_t n); uint8_t func_cal_sum_byte(const uint8_t *d, uint32_t n);
/** 16 位累加校验和 */
uint16_t func_cal_sum_word(const uint8_t *d, uint32_t n); uint16_t func_cal_sum_word(const uint8_t *d, uint32_t n);
/** CRC16Modbus 多项式,初始值 0xFFFF */
uint16_t func_cal_crc16(const uint8_t *d, uint32_t n); uint16_t func_cal_crc16(const uint8_t *d, uint32_t n);
/** CRC32标准多项式初始值 0xFFFFFFFF */
uint32_t func_cal_crc32(const uint8_t *d, uint32_t n); uint32_t func_cal_crc32(const uint8_t *d, uint32_t n);
/** 文件 CRC164KB 缓冲区流式计算) */
uint16_t func_cal_file_crc16(const char *path); uint16_t func_cal_file_crc16(const char *path);
/** 文件 CRC324KB 缓冲区流式计算) */
uint32_t func_cal_file_crc32(const char *path); uint32_t func_cal_file_crc32(const char *path);
/* ===== 字符串转换 ===== */ /* ================================================================
*
* ================================================================ */
/** 单个十六进制字符 → 数值('F'→15 */
uint8_t func_hex_ch(char c); uint8_t func_hex_ch(char c);
/** 单个十进制字符 → 数值('9'→9 */
uint8_t func_dec_ch(char c); uint8_t func_dec_ch(char c);
/** 十六进制字符串 "FF" → int 255 */
int func_hex2dec(char *s); int func_hex2dec(char *s);
/** 十进制字符串 "255" → int 255支持负号 */
int func_dec2dec(char *s); int func_dec2dec(char *s);
/**
* @brief
* @param s "0102FF"
* @param dst
* @param len [out]
* @return 0 -1
*/
int func_hex2buf(const char *s, char *dst, int *len); int func_hex2buf(const char *s, char *dst, int *len);
/**
* @brief int
* @param width 0 width=4 val=255 "0255"
*/
char *func_dec2str(char *buf, int sz, int val, int width); char *func_dec2str(char *buf, int sz, int val, int width);
/** int → 十六进制字符串width=0 时不补零) */
char *func_hex2str(char *buf, int sz, int val, int width); char *func_hex2str(char *buf, int sz, int val, int width);
/** float → 字符串 */
char *func_float2str(char *buf, int sz, float f, int width); char *func_float2str(char *buf, int sz, float f, int width);
/** 字节数组 → 十六进制字符串(如 {0x01,0xFF} → "01FF" */
char *func_buf2str(char *buf, int sz, const char *src, int n); char *func_buf2str(char *buf, int sz, const char *src, int n);
/** 字符串按空格分割为 argc/argv */
int func_str2argv(char *s, uint8_t *argc, char *argv[], uint8_t max); int func_str2argv(char *s, uint8_t *argc, char *argv[], uint8_t max);
/* ===== 时间 ===== */ /* ================================================================
*
* ================================================================ */
/** 系统时间 → "YYYY-MM-DD HH:MM:SS.mmm" 格式字符串 */
char *func_time_sys2str(char *buf, int sz, stru_time_sys *t); char *func_time_sys2str(char *buf, int sz, stru_time_sys *t);
/** 字符串 → 系统时间,解析失败返回 -1 */
int func_str2time_sys(const char *s, stru_time_sys *t); int func_str2time_sys(const char *s, stru_time_sys *t);
/** 获取当前时间type: 1=stru_time_sys, 2=stru_time_cal */
int func_get_time(void *t, int type); int func_get_time(void *t, int type);
/** 设置系统时间(需要 root 权限) */
int func_set_time(void *t, int type); int func_set_time(void *t, int type);
/** 系统时间 → 日历时间 */
void func_time_sys2cal(stru_time_sys *src, stru_time_cal *dst); void func_time_sys2cal(stru_time_sys *src, stru_time_cal *dst);
/** 日历时间 → 系统时间 */
void func_time_cal2sys(stru_time_cal *src, stru_time_sys *dst); void func_time_cal2sys(stru_time_cal *src, stru_time_sys *dst);
/* ===== 文件目录 ===== */ /* ================================================================
*
* ================================================================ */
/** 判断目录是否存在(返回 1=存在, 0=不存在) */
int func_dir_exist(const char *p); int func_dir_exist(const char *p);
/** 递归创建目录 */
int func_make_dirs(const char *p); int func_make_dirs(const char *p);
/** 递归删除目录(含所有内容) */
int func_del_dirs(const char *p); int func_del_dirs(const char *p);
/** 通过系统命令删除目录rm -rf */
int func_del_dirs_cmd(const char *p); int func_del_dirs_cmd(const char *p);
/** 判断文件是否存在(返回 1=存在, 0=不存在) */
int func_file_exist(const char *p); int func_file_exist(const char *p);
/** 获取文件大小(字节),失败返回 0 */
uint32_t func_file_size(const char *p); uint32_t func_file_size(const char *p);
/** 读取文件到预分配缓冲区out 返回实际读取字节数 */
int func_read_file(const char *p, char *buf, uint32_t cap, uint32_t *out); int func_read_file(const char *p, char *buf, uint32_t cap, uint32_t *out);
/** 读取文件到动态分配缓冲区(调用者负责 free */
uint8_t *func_read_file_alloc(const char *p, uint32_t *out); uint8_t *func_read_file_alloc(const char *p, uint32_t *out);
/** 写缓冲区到文件(覆盖模式) */
int func_write_file(const char *p, const char *buf, uint32_t n); int func_write_file(const char *p, const char *buf, uint32_t n);
/**
* @brief
* @param check 1= flag CRC0=
*/
int func_read_file_info(const char *p, stru_file_info *info, int check); int func_read_file_info(const char *p, stru_file_info *info, int check);
/* ===== 进程 ===== */ /* ================================================================
* /proc
* ================================================================ */
/** 获取当前进程名称 */
int func_proc_self_name(char *buf, uint32_t sz); int func_proc_self_name(char *buf, uint32_t sz);
/** 通过 PID 获取进程名称 */
int func_proc_name_by_pid(int pid, char *buf, uint32_t sz); int func_proc_name_by_pid(int pid, char *buf, uint32_t sz);
/** 获取当前进程 PID */
int func_proc_pid(void); int func_proc_pid(void);
/** 通过进程名称获取 PID返回 -1 表示未找到) */
int func_proc_pid_by_name(const char *name); int func_proc_pid_by_name(const char *name);
/** 通过 PID 判断进程是否存在 */
int func_proc_exist_by_pid(int pid); int func_proc_exist_by_pid(int pid);
/** 通过名称判断进程是否存在 */
int func_proc_exist_by_name(const char *name); int func_proc_exist_by_name(const char *name);
/** 通过 PID 获取进程可执行文件路径 */
int func_proc_path_by_pid(int pid, char *buf, uint32_t sz); int func_proc_path_by_pid(int pid, char *buf, uint32_t sz);
/** 获取当前进程可执行文件路径 */
int func_proc_self_path(char *buf, uint32_t sz); int func_proc_self_path(char *buf, uint32_t sz);
/** 获取当前进程所在目录 */
int func_proc_self_dir(char *buf, uint32_t sz); int func_proc_self_dir(char *buf, uint32_t sz);
/* ===== 环境变量路径 ===== */ /* ================================================================
*
* ================================================================ */
/** 获取工作目录路径(环境变量 WORK_PATH */
int func_get_work_path(char *buf, uint32_t sz); int func_get_work_path(char *buf, uint32_t sz);
/** 获取系统配置路径SYS_CFG_PATH */
int func_get_syscfg_path(char *buf, uint32_t sz); int func_get_syscfg_path(char *buf, uint32_t sz);
/** 获取应用根路径APP_ROOT_PATH */
int func_get_app_root(char *buf, uint32_t sz); int func_get_app_root(char *buf, uint32_t sz);
/** 获取历史数据根路径HIS_ROOT_PATH */
int func_get_his_root(char *buf, uint32_t sz); int func_get_his_root(char *buf, uint32_t sz);
/** 获取日志根路径LOG_ROOT_PATH */
int func_get_log_root(char *buf, uint32_t sz); int func_get_log_root(char *buf, uint32_t sz);
/** 获取数据库根路径DBC_ROOT_PATH */
int func_get_dbc_root(char *buf, uint32_t sz); int func_get_dbc_root(char *buf, uint32_t sz);
/** 获取脚本路径SHELL_PATH */
int func_get_shell_path(char *buf, uint32_t sz); int func_get_shell_path(char *buf, uint32_t sz);
/** 获取更新包路径UPDATE_PATH */
int func_get_update_path(char *buf, uint32_t sz); int func_get_update_path(char *buf, uint32_t sz);
/** 拼接应用路径APP_ROOT_PATH/app_name */
int func_get_app_path(const char *app, char *buf, uint32_t sz); int func_get_app_path(const char *app, char *buf, uint32_t sz);
/** 拼接应用配置路径APP_ROOT_PATH/app_name/ext */
int func_get_app_cfg(const char *app, const char *ext, char *buf, uint32_t sz); int func_get_app_cfg(const char *app, const char *ext, char *buf, uint32_t sz);
/** 拼接应用历史路径HIS_ROOT_PATH/app_name */
int func_get_app_his(const char *app, char *buf, uint32_t sz); int func_get_app_his(const char *app, char *buf, uint32_t sz);
/** 拼接应用日志路径LOG_ROOT_PATH/app_name */
int func_get_app_log(const char *app, char *buf, uint32_t sz); int func_get_app_log(const char *app, char *buf, uint32_t sz);
/* ===== IPC 消息队列 ===== */ /* ================================================================
* IPC System V
* ================================================================ */
/** 按进程名创建消息队列 */
int func_ipc_create_by_name(char *name, int *qid); int func_ipc_create_by_name(char *name, int *qid);
/** 按 PID 创建消息队列 */
int func_ipc_create_by_pid(int pid, int *qid); int func_ipc_create_by_pid(int pid, int *qid);
/** 按进程名获取消息队列 ID不存在则创建 */
int func_ipc_get_by_name(const char *name, int *qid); int func_ipc_get_by_name(const char *name, int *qid);
/** 按 PID 获取消息队列 ID不存在则创建 */
int func_ipc_get_by_pid(int pid, int *qid); int func_ipc_get_by_pid(int pid, int *qid);
/** 发送原始字节到消息队列 */
int func_ipc_send_buf(int qid, uint8_t *tx, uint32_t n); int func_ipc_send_buf(int qid, uint8_t *tx, uint32_t n);
/** 从消息队列接收原始字节 */
int func_ipc_recv_buf(int qid, uint8_t *rx, uint32_t n); int func_ipc_recv_buf(int qid, uint8_t *rx, uint32_t n);
/** 发送结构体消息 */
int func_ipc_send_msg(int qid, stru_ipc_msg *m); int func_ipc_send_msg(int qid, stru_ipc_msg *m);
/** 接收结构体消息 */
int func_ipc_recv_msg(int qid, stru_ipc_msg *m); int func_ipc_recv_msg(int qid, stru_ipc_msg *m);
/** 删除消息队列 */
int func_ipc_delete(int qid); int func_ipc_delete(int qid);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -1,6 +1,9 @@
/** /**
* @file myLog.h * @file myLog.h
* @brief C * @brief C
* @details INFO/ERROR
*
* log_prt init
*/ */
#ifndef _MY_LOG_H_ #ifndef _MY_LOG_H_
@ -11,24 +14,52 @@ extern "C"
{ {
#endif #endif
/** 日志级别INFO普通信息 */
#define LOG_INFO 0 #define LOG_INFO 0
/** 日志级别ERROR错误信息控制台红色输出 */
#define LOG_ERROR 1 #define LOG_ERROR 1
/** 初始化(首次调用 log_prt 时自动触发,也可手动调用) */ /**
* @brief log_prt
* @param dir NULL /tmp/logs
* @return 0
*/
int log_init(const char *dir); int log_init(const char *dir);
/** 核心日志函数(非阻塞,消息入队后立即返回) */ /**
* @brief
* @param level LOG_INFO LOG_ERROR
* @param file __FILE__
* @param func __FUNCTION__
* @param line __LINE__
* @param fmt printf
* @param ...
*/
void log_prt(uint32_t level, const char *file, const char *func, void log_prt(uint32_t level, const char *file, const char *func,
uint32_t line, const char *fmt, ...); uint32_t line, const char *fmt, ...);
void log_file_on(void); /* 开启文件写入 */ /** 开启日志文件写入 */
void log_file_off(void); /* 关闭文件写入 */ void log_file_on(void);
void log_console_on(void); /* 开启控制台输出 */
void log_console_off(void); /* 关闭控制台输出 */
void log_flush(void); /* 等待队列清空 */
void log_cleanup(void); /* 停止线程、释放资源 */
/** 关闭日志文件写入 */
void log_file_off(void);
/** 开启控制台彩色输出 */
void log_console_on(void);
/** 关闭控制台输出 */
void log_console_off(void);
/** 等待异步队列清空(阻塞直到所有消息写入完成) */
void log_flush(void);
/** 停止工作线程、关闭文件、释放消息池内存 */
void log_cleanup(void);
/** INFO 级别日志宏 */
#define LOG_I(fmt, ...) log_prt(LOG_INFO, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__) #define LOG_I(fmt, ...) log_prt(LOG_INFO, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
/** ERROR 级别日志宏 */
#define LOG_E(fmt, ...) log_prt(LOG_ERROR, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__) #define LOG_E(fmt, ...) log_prt(LOG_ERROR, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -1,6 +1,8 @@
/** /**
* @file myFunc.h * @file myFunc.h
* @brief //////IPC * @brief 65 7
* @details / / / /
* / / IPC
*/ */
#ifndef _MY_FUNC_H_ #ifndef _MY_FUNC_H_
@ -13,141 +15,306 @@ extern "C"
{ {
#endif #endif
/* ---- 系统时间 ---- */ /**
* @brief
* @note year: 7bit(0-127+1900), month:4bit(1-12), day:5bit(1-31)
* hour:5bit(0-23), min:6bit(0-59), ms:16bit(0-59999)
*/
typedef struct typedef struct
{ {
uint32_t ms : 16; uint32_t ms : 16; /* 毫秒(实际存储秒*1000+毫秒,范围 0-59999 */
uint32_t min : 6; uint32_t min : 6; /* 分钟 0-59 */
uint32_t : 2; uint32_t : 2; /* 保留 */
uint32_t hour: 5; uint32_t hour: 5; /* 小时 0-23 */
uint32_t : 3; uint32_t : 3; /* 保留 */
uint32_t day : 5; uint32_t day : 5; /* 日期 1-31 */
uint32_t week: 3; uint32_t week: 3; /* 星期 */
uint32_t month:4; uint32_t month:4; /* 月份 1-12 */
uint32_t : 4; uint32_t : 4; /* 保留 */
uint32_t year: 7; uint32_t year: 7; /* 年份偏移(实际值-1900),范围 0-127 */
uint32_t : 1; uint32_t : 1; /* 保留 */
} stru_time_sys; } stru_time_sys;
/* ---- 日历时间 ---- */ /**
* @brief Unix +
*/
typedef struct typedef struct
{ {
time_t sec; time_t sec; /* Unix 时间戳(秒) */
time_t usec; time_t usec; /* 微秒部分 */
} stru_time_cal; } stru_time_cal;
/* ---- IPC 消息文本 ---- */ /**
* @brief IPC
*/
typedef struct typedef struct
{ {
int qid; int qid; /* 响应队列 ID */
int type; int type; /* 响应类型enum_ipc_resp */
int len; int len; /* 有效数据长度 */
char text[4096]; char text[4096]; /* 消息内容 */
} stru_ipc_text; } stru_ipc_text;
/* ---- IPC 响应类型 ---- */ /**
* @brief IPC
*/
typedef enum typedef enum
{ {
IPC_RESP_NONE = 1, IPC_RESP_NONE = 1, /* 无需响应 */
IPC_RESP_ONCE, IPC_RESP_ONCE, /* 单次响应 */
IPC_RESP_ALWAYS IPC_RESP_ALWAYS /* 持续响应 */
} enum_ipc_resp; } enum_ipc_resp;
/* ---- IPC 消息 ---- */ /**
* @brief IPC System V
*/
typedef struct typedef struct
{ {
long id; long id; /* 消息类型 ID */
stru_ipc_text t; stru_ipc_text t; /* 消息载荷 */
} stru_ipc_msg; } stru_ipc_msg;
/* ---- 文件描述头(256字节) ---- */ /**
* @brief 256 CRC
* @note flag 0xA55AA55AA55AA55A
*/
#pragma pack(1) #pragma pack(1)
typedef struct typedef struct
{ {
uint64_t flag; uint64_t flag; /* 魔数标识 0xA55AA55AA55AA55A */
char ver[16]; char ver[16]; /* 版本号 */
char author[16]; char author[16]; /* 作者 */
char modify[32]; char modify[32]; /* 修改时间 YYYY-MM-DD HH-MM-SS */
uint16_t crc; uint16_t crc; /* CRC16 校验 */
char desc[128]; char desc[128]; /* 附加描述 */
char bak[54]; char bak[54]; /* 对齐填充(总计 256 字节) */
} stru_file_info; } stru_file_info;
#pragma pack() #pragma pack()
/* ===== 校验 ===== */ /* ================================================================
* CRC16 使 Modbus 0xA001
* ================================================================ */
/** 8 位累加校验和 */
uint8_t func_cal_sum_byte(const uint8_t *d, uint32_t n); uint8_t func_cal_sum_byte(const uint8_t *d, uint32_t n);
/** 16 位累加校验和 */
uint16_t func_cal_sum_word(const uint8_t *d, uint32_t n); uint16_t func_cal_sum_word(const uint8_t *d, uint32_t n);
/** CRC16Modbus 多项式,初始值 0xFFFF */
uint16_t func_cal_crc16(const uint8_t *d, uint32_t n); uint16_t func_cal_crc16(const uint8_t *d, uint32_t n);
/** CRC32标准多项式初始值 0xFFFFFFFF */
uint32_t func_cal_crc32(const uint8_t *d, uint32_t n); uint32_t func_cal_crc32(const uint8_t *d, uint32_t n);
/** 文件 CRC164KB 缓冲区流式计算) */
uint16_t func_cal_file_crc16(const char *path); uint16_t func_cal_file_crc16(const char *path);
/** 文件 CRC324KB 缓冲区流式计算) */
uint32_t func_cal_file_crc32(const char *path); uint32_t func_cal_file_crc32(const char *path);
/* ===== 字符串转换 ===== */ /* ================================================================
*
* ================================================================ */
/** 单个十六进制字符 → 数值('F'→15 */
uint8_t func_hex_ch(char c); uint8_t func_hex_ch(char c);
/** 单个十进制字符 → 数值('9'→9 */
uint8_t func_dec_ch(char c); uint8_t func_dec_ch(char c);
/** 十六进制字符串 "FF" → int 255 */
int func_hex2dec(char *s); int func_hex2dec(char *s);
/** 十进制字符串 "255" → int 255支持负号 */
int func_dec2dec(char *s); int func_dec2dec(char *s);
/**
* @brief
* @param s "0102FF"
* @param dst
* @param len [out]
* @return 0 -1
*/
int func_hex2buf(const char *s, char *dst, int *len); int func_hex2buf(const char *s, char *dst, int *len);
/**
* @brief int
* @param width 0 width=4 val=255 "0255"
*/
char *func_dec2str(char *buf, int sz, int val, int width); char *func_dec2str(char *buf, int sz, int val, int width);
/** int → 十六进制字符串width=0 时不补零) */
char *func_hex2str(char *buf, int sz, int val, int width); char *func_hex2str(char *buf, int sz, int val, int width);
/** float → 字符串 */
char *func_float2str(char *buf, int sz, float f, int width); char *func_float2str(char *buf, int sz, float f, int width);
/** 字节数组 → 十六进制字符串(如 {0x01,0xFF} → "01FF" */
char *func_buf2str(char *buf, int sz, const char *src, int n); char *func_buf2str(char *buf, int sz, const char *src, int n);
/** 字符串按空格分割为 argc/argv */
int func_str2argv(char *s, uint8_t *argc, char *argv[], uint8_t max); int func_str2argv(char *s, uint8_t *argc, char *argv[], uint8_t max);
/* ===== 时间 ===== */ /* ================================================================
*
* ================================================================ */
/** 系统时间 → "YYYY-MM-DD HH:MM:SS.mmm" 格式字符串 */
char *func_time_sys2str(char *buf, int sz, stru_time_sys *t); char *func_time_sys2str(char *buf, int sz, stru_time_sys *t);
/** 字符串 → 系统时间,解析失败返回 -1 */
int func_str2time_sys(const char *s, stru_time_sys *t); int func_str2time_sys(const char *s, stru_time_sys *t);
/** 获取当前时间type: 1=stru_time_sys, 2=stru_time_cal */
int func_get_time(void *t, int type); int func_get_time(void *t, int type);
/** 设置系统时间(需要 root 权限) */
int func_set_time(void *t, int type); int func_set_time(void *t, int type);
/** 系统时间 → 日历时间 */
void func_time_sys2cal(stru_time_sys *src, stru_time_cal *dst); void func_time_sys2cal(stru_time_sys *src, stru_time_cal *dst);
/** 日历时间 → 系统时间 */
void func_time_cal2sys(stru_time_cal *src, stru_time_sys *dst); void func_time_cal2sys(stru_time_cal *src, stru_time_sys *dst);
/* ===== 文件目录 ===== */ /* ================================================================
*
* ================================================================ */
/** 判断目录是否存在(返回 1=存在, 0=不存在) */
int func_dir_exist(const char *p); int func_dir_exist(const char *p);
/** 递归创建目录 */
int func_make_dirs(const char *p); int func_make_dirs(const char *p);
/** 递归删除目录(含所有内容) */
int func_del_dirs(const char *p); int func_del_dirs(const char *p);
/** 通过系统命令删除目录rm -rf */
int func_del_dirs_cmd(const char *p); int func_del_dirs_cmd(const char *p);
/** 判断文件是否存在(返回 1=存在, 0=不存在) */
int func_file_exist(const char *p); int func_file_exist(const char *p);
/** 获取文件大小(字节),失败返回 0 */
uint32_t func_file_size(const char *p); uint32_t func_file_size(const char *p);
/** 读取文件到预分配缓冲区out 返回实际读取字节数 */
int func_read_file(const char *p, char *buf, uint32_t cap, uint32_t *out); int func_read_file(const char *p, char *buf, uint32_t cap, uint32_t *out);
/** 读取文件到动态分配缓冲区(调用者负责 free */
uint8_t *func_read_file_alloc(const char *p, uint32_t *out); uint8_t *func_read_file_alloc(const char *p, uint32_t *out);
/** 写缓冲区到文件(覆盖模式) */
int func_write_file(const char *p, const char *buf, uint32_t n); int func_write_file(const char *p, const char *buf, uint32_t n);
/**
* @brief
* @param check 1= flag CRC0=
*/
int func_read_file_info(const char *p, stru_file_info *info, int check); int func_read_file_info(const char *p, stru_file_info *info, int check);
/* ===== 进程 ===== */ /* ================================================================
* /proc
* ================================================================ */
/** 获取当前进程名称 */
int func_proc_self_name(char *buf, uint32_t sz); int func_proc_self_name(char *buf, uint32_t sz);
/** 通过 PID 获取进程名称 */
int func_proc_name_by_pid(int pid, char *buf, uint32_t sz); int func_proc_name_by_pid(int pid, char *buf, uint32_t sz);
/** 获取当前进程 PID */
int func_proc_pid(void); int func_proc_pid(void);
/** 通过进程名称获取 PID返回 -1 表示未找到) */
int func_proc_pid_by_name(const char *name); int func_proc_pid_by_name(const char *name);
/** 通过 PID 判断进程是否存在 */
int func_proc_exist_by_pid(int pid); int func_proc_exist_by_pid(int pid);
/** 通过名称判断进程是否存在 */
int func_proc_exist_by_name(const char *name); int func_proc_exist_by_name(const char *name);
/** 通过 PID 获取进程可执行文件路径 */
int func_proc_path_by_pid(int pid, char *buf, uint32_t sz); int func_proc_path_by_pid(int pid, char *buf, uint32_t sz);
/** 获取当前进程可执行文件路径 */
int func_proc_self_path(char *buf, uint32_t sz); int func_proc_self_path(char *buf, uint32_t sz);
/** 获取当前进程所在目录 */
int func_proc_self_dir(char *buf, uint32_t sz); int func_proc_self_dir(char *buf, uint32_t sz);
/* ===== 环境变量路径 ===== */ /* ================================================================
*
* ================================================================ */
/** 获取工作目录路径(环境变量 WORK_PATH */
int func_get_work_path(char *buf, uint32_t sz); int func_get_work_path(char *buf, uint32_t sz);
/** 获取系统配置路径SYS_CFG_PATH */
int func_get_syscfg_path(char *buf, uint32_t sz); int func_get_syscfg_path(char *buf, uint32_t sz);
/** 获取应用根路径APP_ROOT_PATH */
int func_get_app_root(char *buf, uint32_t sz); int func_get_app_root(char *buf, uint32_t sz);
/** 获取历史数据根路径HIS_ROOT_PATH */
int func_get_his_root(char *buf, uint32_t sz); int func_get_his_root(char *buf, uint32_t sz);
/** 获取日志根路径LOG_ROOT_PATH */
int func_get_log_root(char *buf, uint32_t sz); int func_get_log_root(char *buf, uint32_t sz);
/** 获取数据库根路径DBC_ROOT_PATH */
int func_get_dbc_root(char *buf, uint32_t sz); int func_get_dbc_root(char *buf, uint32_t sz);
/** 获取脚本路径SHELL_PATH */
int func_get_shell_path(char *buf, uint32_t sz); int func_get_shell_path(char *buf, uint32_t sz);
/** 获取更新包路径UPDATE_PATH */
int func_get_update_path(char *buf, uint32_t sz); int func_get_update_path(char *buf, uint32_t sz);
/** 拼接应用路径APP_ROOT_PATH/app_name */
int func_get_app_path(const char *app, char *buf, uint32_t sz); int func_get_app_path(const char *app, char *buf, uint32_t sz);
/** 拼接应用配置路径APP_ROOT_PATH/app_name/ext */
int func_get_app_cfg(const char *app, const char *ext, char *buf, uint32_t sz); int func_get_app_cfg(const char *app, const char *ext, char *buf, uint32_t sz);
/** 拼接应用历史路径HIS_ROOT_PATH/app_name */
int func_get_app_his(const char *app, char *buf, uint32_t sz); int func_get_app_his(const char *app, char *buf, uint32_t sz);
/** 拼接应用日志路径LOG_ROOT_PATH/app_name */
int func_get_app_log(const char *app, char *buf, uint32_t sz); int func_get_app_log(const char *app, char *buf, uint32_t sz);
/* ===== IPC 消息队列 ===== */ /* ================================================================
* IPC System V
* ================================================================ */
/** 按进程名创建消息队列 */
int func_ipc_create_by_name(char *name, int *qid); int func_ipc_create_by_name(char *name, int *qid);
/** 按 PID 创建消息队列 */
int func_ipc_create_by_pid(int pid, int *qid); int func_ipc_create_by_pid(int pid, int *qid);
/** 按进程名获取消息队列 ID不存在则创建 */
int func_ipc_get_by_name(const char *name, int *qid); int func_ipc_get_by_name(const char *name, int *qid);
/** 按 PID 获取消息队列 ID不存在则创建 */
int func_ipc_get_by_pid(int pid, int *qid); int func_ipc_get_by_pid(int pid, int *qid);
/** 发送原始字节到消息队列 */
int func_ipc_send_buf(int qid, uint8_t *tx, uint32_t n); int func_ipc_send_buf(int qid, uint8_t *tx, uint32_t n);
/** 从消息队列接收原始字节 */
int func_ipc_recv_buf(int qid, uint8_t *rx, uint32_t n); int func_ipc_recv_buf(int qid, uint8_t *rx, uint32_t n);
/** 发送结构体消息 */
int func_ipc_send_msg(int qid, stru_ipc_msg *m); int func_ipc_send_msg(int qid, stru_ipc_msg *m);
/** 接收结构体消息 */
int func_ipc_recv_msg(int qid, stru_ipc_msg *m); int func_ipc_recv_msg(int qid, stru_ipc_msg *m);
/** 删除消息队列 */
int func_ipc_delete(int qid); int func_ipc_delete(int qid);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -1,6 +1,9 @@
/** /**
* @file myLog.h * @file myLog.h
* @brief C * @brief C
* @details INFO/ERROR
*
* log_prt init
*/ */
#ifndef _MY_LOG_H_ #ifndef _MY_LOG_H_
@ -11,24 +14,52 @@ extern "C"
{ {
#endif #endif
/** 日志级别INFO普通信息 */
#define LOG_INFO 0 #define LOG_INFO 0
/** 日志级别ERROR错误信息控制台红色输出 */
#define LOG_ERROR 1 #define LOG_ERROR 1
/** 初始化(首次调用 log_prt 时自动触发,也可手动调用) */ /**
* @brief log_prt
* @param dir NULL /tmp/logs
* @return 0
*/
int log_init(const char *dir); int log_init(const char *dir);
/** 核心日志函数(非阻塞,消息入队后立即返回) */ /**
* @brief
* @param level LOG_INFO LOG_ERROR
* @param file __FILE__
* @param func __FUNCTION__
* @param line __LINE__
* @param fmt printf
* @param ...
*/
void log_prt(uint32_t level, const char *file, const char *func, void log_prt(uint32_t level, const char *file, const char *func,
uint32_t line, const char *fmt, ...); uint32_t line, const char *fmt, ...);
void log_file_on(void); /* 开启文件写入 */ /** 开启日志文件写入 */
void log_file_off(void); /* 关闭文件写入 */ void log_file_on(void);
void log_console_on(void); /* 开启控制台输出 */
void log_console_off(void); /* 关闭控制台输出 */
void log_flush(void); /* 等待队列清空 */
void log_cleanup(void); /* 停止线程、释放资源 */
/** 关闭日志文件写入 */
void log_file_off(void);
/** 开启控制台彩色输出 */
void log_console_on(void);
/** 关闭控制台输出 */
void log_console_off(void);
/** 等待异步队列清空(阻塞直到所有消息写入完成) */
void log_flush(void);
/** 停止工作线程、关闭文件、释放消息池内存 */
void log_cleanup(void);
/** INFO 级别日志宏 */
#define LOG_I(fmt, ...) log_prt(LOG_INFO, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__) #define LOG_I(fmt, ...) log_prt(LOG_INFO, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
/** ERROR 级别日志宏 */
#define LOG_E(fmt, ...) log_prt(LOG_ERROR, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__) #define LOG_E(fmt, ...) log_prt(LOG_ERROR, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -22,112 +22,155 @@
#include <stdlib.h> #include <stdlib.h>
/* ========== 可配置参数 ========== */ /* ========== 可配置参数 ========== */
/** 消息池预分配大小 */
#define POOL_SIZE 10000 #define POOL_SIZE 10000
/** 单次批量处理消息数上限 */
#define BATCH_MAX 100 #define BATCH_MAX 100
/** 空闲等待间隔(毫秒) */
#define FLUSH_MS 100 #define FLUSH_MS 100
/** 单日志文件最大 10MB */
#define FILE_MAX_SIZE (10 * 1024 * 1024) #define FILE_MAX_SIZE (10 * 1024 * 1024)
/** 保留历史文件数 */
#define FILE_MAX_KEEP 5 #define FILE_MAX_KEEP 5
/** 单条日志内容上限 4KB */
#define CONTENT_MAX 4096 #define CONTENT_MAX 4096
/* ========== 数据结构 ========== */ /* ========== 数据结构 ========== */
/** 单条日志消息 */ /**
* @brief +
*/
typedef struct typedef struct
{ {
uint32_t level; uint32_t level; /* 日志级别 LOG_INFO/LOG_ERROR */
char time_str[64]; char time_str[64]; /* 时间戳 YYYY-MM-DD HH:MM:SS.mmm */
char src_info[128]; /* "文件名/函数:行号" */ char src_info[128]; /* "文件名/函数:行号" */
char content[CONTENT_MAX]; char content[CONTENT_MAX]; /* 格式化后的日志内容 */
struct log_msg *next; /* 空闲链表 / 队列链表 */ struct log_msg *next; /* 空闲链表/队列链表指针 */
} stru_log_msg; } stru_log_msg;
/** 线程安全队列 */ /**
* @brief 线-
*/
typedef struct typedef struct
{ {
stru_log_msg *head; stru_log_msg *head; /* 队头 */
stru_log_msg *tail; stru_log_msg *tail; /* 队尾 */
int count; int count; /* 当前消息数 */
pthread_mutex_t lock; pthread_mutex_t lock; /* 互斥锁 */
pthread_cond_t cond_pop; pthread_cond_t cond_pop; /* 消费者等待条件 */
pthread_cond_t cond_push; pthread_cond_t cond_push; /* 生产者等待条件(队列满) */
} stru_msg_queue; } stru_msg_queue;
/** 日志系统全局状态 */ /**
* @brief
*/
typedef struct typedef struct
{ {
stru_msg_queue q; stru_msg_queue q; /* 消息队列 */
pthread_t worker; pthread_t worker; /* 工作线程 */
volatile int running; volatile int running; /* 线程运行标志 */
FILE *fp; FILE *fp; /* 当前日志文件句柄 */
long file_bytes; long file_bytes; /* 当前文件已写入字节数 */
int to_file; int to_file; /* 文件输出开关 */
int to_console; int to_console; /* 控制台输出开关 */
char dir[256]; char dir[256]; /* 日志目录路径 */
} stru_log_state; } stru_log_state;
LOCAL stru_log_state g_log; LOCAL stru_log_state g_log;
/* ========== 消息池(预分配,无运行时 malloc) ========== */ /* ========== 消息池(预分配,无运行时 malloc) ========== */
LOCAL stru_log_msg *g_pool_free;
LOCAL stru_log_msg g_pool_buf[POOL_SIZE]; LOCAL stru_log_msg *g_pool_free; /* 空闲链表头 */
LOCAL stru_log_msg g_pool_buf[POOL_SIZE]; /* 预分配数组 */
LOCAL pthread_mutex_t g_pool_lock = PTHREAD_MUTEX_INITIALIZER; LOCAL pthread_mutex_t g_pool_lock = PTHREAD_MUTEX_INITIALIZER;
LOCAL pthread_once_t g_init_once = PTHREAD_ONCE_INIT; LOCAL pthread_once_t g_init_once = PTHREAD_ONCE_INIT;
/** 初始化消息池:把数组元素链成空闲链表 */ /**
* @brief
* @detail global_init
*/
LOCAL void pool_init(void) LOCAL void pool_init(void)
{ {
pthread_mutex_lock(&g_pool_lock); pthread_mutex_lock(&g_pool_lock);
if (g_pool_free != NULL) if (g_pool_free != NULL)
{ {
pthread_mutex_unlock(&g_pool_lock); pthread_mutex_unlock(&g_pool_lock);
return; return;
} }
/* 链成空闲链表 */
for (int i = 0; i < POOL_SIZE - 1; i++) for (int i = 0; i < POOL_SIZE - 1; i++)
{ {
g_pool_buf[i].next = &g_pool_buf[i + 1]; g_pool_buf[i].next = &g_pool_buf[i + 1];
} }
g_pool_buf[POOL_SIZE - 1].next = NULL; g_pool_buf[POOL_SIZE - 1].next = NULL;
g_pool_free = &g_pool_buf[0]; g_pool_free = &g_pool_buf[0];
pthread_mutex_unlock(&g_pool_lock); pthread_mutex_unlock(&g_pool_lock);
} }
/** 从池中取一个空闲消息块(失败返回 NULL) */ /**
* @brief
* @return NULL
*/
LOCAL stru_log_msg *pool_alloc(void) LOCAL stru_log_msg *pool_alloc(void)
{ {
pthread_mutex_lock(&g_pool_lock); pthread_mutex_lock(&g_pool_lock);
stru_log_msg *m = g_pool_free; stru_log_msg *m = g_pool_free;
if (m) if (m)
{ {
g_pool_free = m->next; g_pool_free = m->next;
} }
pthread_mutex_unlock(&g_pool_lock); pthread_mutex_unlock(&g_pool_lock);
return m; return m;
} }
/** 归还消息块到池 */ /**
* @brief
*/
LOCAL void pool_free(stru_log_msg *m) LOCAL void pool_free(stru_log_msg *m)
{ {
if (!m) if (!m)
{ {
return; return;
} }
pthread_mutex_lock(&g_pool_lock); pthread_mutex_lock(&g_pool_lock);
m->next = g_pool_free; m->next = g_pool_free;
g_pool_free = m; g_pool_free = m;
pthread_mutex_unlock(&g_pool_lock); pthread_mutex_unlock(&g_pool_lock);
} }
/* ========== 队列操作 ========== */ /* ========== 队列操作 ========== */
/** 入队(满则阻塞) */ /**
* @brief
* @detail
*/
LOCAL void q_push(stru_log_msg *m) LOCAL void q_push(stru_log_msg *m)
{ {
pthread_mutex_lock(&g_log.q.lock); pthread_mutex_lock(&g_log.q.lock);
/* 队列满则等待 */
while (g_log.q.count >= POOL_SIZE) while (g_log.q.count >= POOL_SIZE)
{ {
pthread_cond_wait(&g_log.q.cond_push, &g_log.q.lock); pthread_cond_wait(&g_log.q.cond_push, &g_log.q.lock);
} }
m->next = NULL; m->next = NULL;
/* 尾插 */
if (!g_log.q.head) if (!g_log.q.head)
{ {
g_log.q.head = m; g_log.q.head = m;
@ -136,40 +179,59 @@ LOCAL void q_push(stru_log_msg *m)
{ {
g_log.q.tail->next = m; g_log.q.tail->next = m;
} }
g_log.q.tail = m; g_log.q.tail = m;
g_log.q.count++; g_log.q.count++;
pthread_cond_signal(&g_log.q.cond_pop); pthread_cond_signal(&g_log.q.cond_pop);
pthread_mutex_unlock(&g_log.q.lock); pthread_mutex_unlock(&g_log.q.lock);
} }
/** 出队(空则等待或返回 NULL */ /**
* @brief
* @detail 线
* @return 线 NULL
*/
LOCAL stru_log_msg *q_pop(void) LOCAL stru_log_msg *q_pop(void)
{ {
pthread_mutex_lock(&g_log.q.lock); pthread_mutex_lock(&g_log.q.lock);
while (g_log.q.count == 0 && g_log.running) while (g_log.q.count == 0 && g_log.running)
{ {
pthread_cond_wait(&g_log.q.cond_pop, &g_log.q.lock); pthread_cond_wait(&g_log.q.cond_pop, &g_log.q.lock);
} }
if (g_log.q.count == 0) if (g_log.q.count == 0)
{ {
pthread_mutex_unlock(&g_log.q.lock); pthread_mutex_unlock(&g_log.q.lock);
return NULL; return NULL;
} }
/* 头摘 */
stru_log_msg *m = g_log.q.head; stru_log_msg *m = g_log.q.head;
g_log.q.head = m->next; g_log.q.head = m->next;
if (!g_log.q.head) if (!g_log.q.head)
{ {
g_log.q.tail = NULL; g_log.q.tail = NULL;
} }
g_log.q.count--; g_log.q.count--;
pthread_cond_signal(&g_log.q.cond_push); pthread_cond_signal(&g_log.q.cond_push);
pthread_mutex_unlock(&g_log.q.lock); pthread_mutex_unlock(&g_log.q.lock);
return m; return m;
} }
/* ========== 文件管理 ========== */ /* ========== 文件管理 ========== */
/** 拼接日志文件路径dir/app.log 或 dir/app.log.N */ /**
* @brief
* @param idx 0=(dir/app.log), N>=1=(dir/app.log.N)
*/
LOCAL void file_path(char *buf, size_t sz, int idx) LOCAL void file_path(char *buf, size_t sz, int idx)
{ {
if (idx == 0) if (idx == 0)
@ -182,38 +244,49 @@ LOCAL void file_path(char *buf, size_t sz, int idx)
} }
} }
/** 创建日志目录 */ /**
* @brief
* @return 0
*/
LOCAL int ensure_dir(void) LOCAL int ensure_dir(void)
{ {
if (!g_log.dir[0]) if (!g_log.dir[0])
{ {
/* 默认放到 /tmp/logs */
snprintf(g_log.dir, sizeof(g_log.dir), "/tmp/logs"); snprintf(g_log.dir, sizeof(g_log.dir), "/tmp/logs");
} }
struct stat st; struct stat st;
if (stat(g_log.dir, &st) != 0) if (stat(g_log.dir, &st) != 0)
{ {
return mkdir(g_log.dir, 0755); return mkdir(g_log.dir, 0755);
} }
return 0; return 0;
} }
/** 轮转日志文件app.log→.1→.2→...→.N(max),删除最旧的 */ /**
* @brief
* @detail app.logapp.log.1app.log.2...app.log.N(max)
*/
LOCAL void rotate_files(void) LOCAL void rotate_files(void)
{ {
char old[512]; char old[512];
char neo[512]; char neo[512];
/* 关闭当前文件 */
if (g_log.fp) if (g_log.fp)
{ {
fclose(g_log.fp); fclose(g_log.fp);
g_log.fp = NULL; g_log.fp = NULL;
} }
/* 删除最旧 */ /* 删除最旧的历史文件 */
file_path(old, sizeof(old), FILE_MAX_KEEP); file_path(old, sizeof(old), FILE_MAX_KEEP);
remove(old); remove(old);
/* 依次后移 */ /* 历史文件依次后移一位 */
for (int i = FILE_MAX_KEEP - 1; i >= 1; i--) for (int i = FILE_MAX_KEEP - 1; i >= 1; i--)
{ {
file_path(old, sizeof(old), i); file_path(old, sizeof(old), i);
@ -221,25 +294,32 @@ LOCAL void rotate_files(void)
rename(old, neo); rename(old, neo);
} }
/* 当前文件 .1 */ /* 当前文件重命名为 .1 */
file_path(old, sizeof(old), 0); file_path(old, sizeof(old), 0);
file_path(neo, sizeof(neo), 1); file_path(neo, sizeof(neo), 1);
rename(old, neo); rename(old, neo);
g_log.file_bytes = 0; g_log.file_bytes = 0;
} }
/** 打开当前日志文件 */ /**
* @brief
* @return FILE*, NULL
*/
LOCAL FILE *open_file(void) LOCAL FILE *open_file(void)
{ {
if (ensure_dir() != 0) if (ensure_dir() != 0)
{ {
return NULL; return NULL;
} }
char path[512]; char path[512];
file_path(path, sizeof(path), 0); file_path(path, sizeof(path), 0);
/* 检查已有文件大小 */ /* 检查已有文件大小,超出则先轮转 */
struct stat st; struct stat st;
if (stat(path, &st) == 0) if (stat(path, &st) == 0)
{ {
if (st.st_size > FILE_MAX_SIZE) if (st.st_size > FILE_MAX_SIZE)
@ -253,21 +333,28 @@ LOCAL FILE *open_file(void)
} }
FILE *fp = fopen(path, "a"); FILE *fp = fopen(path, "a");
if (fp) if (fp)
{ {
setlinebuf(fp); setlinebuf(fp); /* 行缓冲模式 */
} }
return fp; return fp;
} }
/* ========== 格式化 ========== */ /* ========== 格式化 ========== */
/** 当前时间字符串YYYY-MM-DD HH:MM:SS.mmm */ /**
* @brief "YYYY-MM-DD HH:MM:SS.mmm"
*/
LOCAL void fmt_time(char *buf, size_t sz) LOCAL void fmt_time(char *buf, size_t sz)
{ {
struct timeval tv; struct timeval tv;
gettimeofday(&tv, NULL); gettimeofday(&tv, NULL);
struct tm *t = localtime(&tv.tv_sec); struct tm *t = localtime(&tv.tv_sec);
if (t) if (t)
{ {
snprintf(buf, sz, "%04d-%02d-%02d %02d:%02d:%02d.%03d", snprintf(buf, sz, "%04d-%02d-%02d %02d:%02d:%02d.%03d",
@ -281,70 +368,91 @@ LOCAL void fmt_time(char *buf, size_t sz)
} }
} }
/** 提取文件名(去掉路径) 并拼接成 "file/func:line" */ /**
* @brief "文件名/函数名:行号"
* @detail
*/
LOCAL void fmt_src(char *buf, size_t sz, LOCAL void fmt_src(char *buf, size_t sz,
const char *file, const char *func, uint32_t line) const char *file, const char *func, uint32_t line)
{ {
if (!file || !func) if (!file || !func)
{ {
snprintf(buf, sz, "?:?:0"); snprintf(buf, sz, "?:?:0");
return; return;
} }
/* 提取纯文件名(去掉路径前缀) */
const char *p = strrchr(file, '/'); const char *p = strrchr(file, '/');
if (!p) if (!p)
{ {
p = strrchr(file, '\\'); p = strrchr(file, '\\');
} }
const char *name = p ? p + 1 : file; const char *name = p ? p + 1 : file;
snprintf(buf, sz, "%s/%s:%u", name, func, line); snprintf(buf, sz, "%s/%s:%u", name, func, line);
} }
/* ========== 输出 ========== */ /* ========== 输出 ========== */
/** 批量写控制台 + 文件 */ /**
* @brief +
* @detail
*/
LOCAL void output_batch(stru_log_msg **batch, int cnt) LOCAL void output_batch(stru_log_msg **batch, int cnt)
{ {
if (cnt == 0) if (cnt == 0)
{ {
return; return;
} }
static const char *titles[] = { "INFO", "ERROR" }; static const char *titles[] = { "INFO", "ERROR" };
static const char *colors[] = { COLOR_WHITE, COLOR_RED }; static const char *colors[] = { COLOR_WHITE, COLOR_RED };
/* 控制台(带颜色) */ /* 控制台彩色输出 */
if (g_log.to_console) if (g_log.to_console)
{ {
for (int i = 0; i < cnt; i++) for (int i = 0; i < cnt; i++)
{ {
stru_log_msg *m = batch[i]; stru_log_msg *m = batch[i];
printf("%s[%s] [%s%s%s] %s\n", printf("%s[%s] [%s%s%s] %s\n",
colors[m->level], m->time_str, colors[m->level], m->time_str,
colors[m->level], titles[m->level], colors[m->level], titles[m->level],
COLOR_RESET, m->content); COLOR_RESET, m->content);
} }
fflush(stdout); fflush(stdout);
} }
/* 文件 */ /* 文件输出 */
if (g_log.to_file) if (g_log.to_file)
{ {
if (!g_log.fp) if (!g_log.fp)
{ {
g_log.fp = open_file(); g_log.fp = open_file();
} }
if (g_log.fp) if (g_log.fp)
{ {
for (int i = 0; i < cnt; i++) for (int i = 0; i < cnt; i++)
{ {
stru_log_msg *m = batch[i]; stru_log_msg *m = batch[i];
int n = fprintf(g_log.fp, "[%s] [%s] %s\n", int n = fprintf(g_log.fp, "[%s] [%s] %s\n",
titles[m->level], m->time_str, m->content); titles[m->level],
m->time_str, m->content);
if (n > 0) if (n > 0)
{ {
g_log.file_bytes += n; g_log.file_bytes += n;
} }
} }
fflush(g_log.fp); fflush(g_log.fp);
/* 超出限制则轮转 */
if (g_log.file_bytes > FILE_MAX_SIZE) if (g_log.file_bytes > FILE_MAX_SIZE)
{ {
rotate_files(); rotate_files();
@ -355,9 +463,14 @@ LOCAL void output_batch(stru_log_msg **batch, int cnt)
/* ========== 工作线程 ========== */ /* ========== 工作线程 ========== */
/**
* @brief 线
* @detail
*/
LOCAL void *worker_thread(void *arg) LOCAL void *worker_thread(void *arg)
{ {
(void)arg; (void)arg;
while (g_log.running) while (g_log.running)
{ {
stru_log_msg *batch[BATCH_MAX]; stru_log_msg *batch[BATCH_MAX];
@ -367,16 +480,20 @@ LOCAL void *worker_thread(void *arg)
for (int i = 0; i < BATCH_MAX; i++) for (int i = 0; i < BATCH_MAX; i++)
{ {
stru_log_msg *m = q_pop(); stru_log_msg *m = q_pop();
if (!m) if (!m)
{ {
break; break;
} }
batch[cnt++] = m; batch[cnt++] = m;
} }
if (cnt > 0) if (cnt > 0)
{ {
output_batch(batch, cnt); output_batch(batch, cnt);
/* 归还消息块到池 */
for (int i = 0; i < cnt; i++) for (int i = 0; i < cnt; i++)
{ {
pool_free(batch[i]); pool_free(batch[i]);
@ -384,45 +501,67 @@ LOCAL void *worker_thread(void *arg)
} }
else else
{ {
/* 队列空,休眠等待 */
usleep(FLUSH_MS * 1000); usleep(FLUSH_MS * 1000);
} }
} }
return NULL; return NULL;
} }
/* ========== 全局初始化(pthread_once) ========== */ /* ========== 全局初始化(pthread_once) ========== */
/**
* @brief pthread_once
* @detail + 线
*/
LOCAL void global_init(void) LOCAL void global_init(void)
{ {
pool_init(); pool_init();
/* g_log.q 已在声明时用 {0} 初始化 */
/* 状态字段已在声明时用 {0} 初始化 */
g_log.q.head = NULL; g_log.q.head = NULL;
g_log.q.tail = NULL; g_log.q.tail = NULL;
g_log.q.count = 0; g_log.q.count = 0;
g_log.to_console = 1; g_log.to_console = 1;
g_log.to_file = 0; g_log.to_file = 0;
g_log.running = 1; g_log.running = 1;
pthread_create(&g_log.worker, NULL, worker_thread, NULL); pthread_create(&g_log.worker, NULL, worker_thread, NULL);
} }
/* ========== 公开 API ========== */ /* ========== 公开 API ========== */
/**
* @brief
* @param dir NULL /tmp/logs
* @return 0
*/
int log_init(const char *dir) int log_init(const char *dir)
{ {
if (dir && dir[0]) if (dir && dir[0])
{ {
snprintf(g_log.dir, sizeof(g_log.dir), "%s", dir); snprintf(g_log.dir, sizeof(g_log.dir), "%s", dir);
} }
pool_init(); pool_init();
g_log.to_file = 1; g_log.to_file = 1;
return 0; return 0;
} }
/**
* @brief
* @detail pthread_once
* //
*
*/
void log_prt(uint32_t level, const char *file, const char *func, void log_prt(uint32_t level, const char *file, const char *func,
uint32_t line, const char *fmt, ...) uint32_t line, const char *fmt, ...)
{ {
/* 懒初始化 */ /* 懒初始化:仅首次调用执行 */
pthread_once(&g_init_once, global_init); pthread_once(&g_init_once, global_init);
if (level > LOG_ERROR || !fmt || !g_log.running) if (level > LOG_ERROR || !fmt || !g_log.running)
{ {
return; return;
@ -430,24 +569,33 @@ void log_prt(uint32_t level, const char *file, const char *func,
/* 从消息池取空闲块 */ /* 从消息池取空闲块 */
stru_log_msg *m = pool_alloc(); stru_log_msg *m = pool_alloc();
if (!m) if (!m)
{ {
return; return; /* 池耗尽,静默丢弃 */
} }
/* 填充消息字段 */
m->level = level; m->level = level;
fmt_time(m->time_str, sizeof(m->time_str)); fmt_time(m->time_str, sizeof(m->time_str));
fmt_src(m->src_info, sizeof(m->src_info), file, func, line); fmt_src(m->src_info, sizeof(m->src_info), file, func, line);
/* 格式化日志内容 */
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
int pos = snprintf(m->content, sizeof(m->content), "[%s] ", m->src_info);
int pos = snprintf(m->content, sizeof(m->content),
"[%s] ", m->src_info);
if (pos < (int)sizeof(m->content)) if (pos < (int)sizeof(m->content))
{ {
vsnprintf(m->content + pos, sizeof(m->content) - pos, fmt, ap); vsnprintf(m->content + pos, sizeof(m->content) - pos, fmt, ap);
} }
va_end(ap); va_end(ap);
/* 入队(生产者端) */
q_push(m); q_push(m);
} }
@ -471,6 +619,9 @@ void log_console_off(void)
g_log.to_console = 0; g_log.to_console = 0;
} }
/**
* @brief
*/
void log_flush(void) void log_flush(void)
{ {
for (;;) for (;;)
@ -478,39 +629,57 @@ void log_flush(void)
pthread_mutex_lock(&g_log.q.lock); pthread_mutex_lock(&g_log.q.lock);
int c = g_log.q.count; int c = g_log.q.count;
pthread_mutex_unlock(&g_log.q.lock); pthread_mutex_unlock(&g_log.q.lock);
if (c == 0) if (c == 0)
{ {
break; break;
} }
usleep(10000);
usleep(10000); /* 10ms 轮询 */
} }
} }
/**
* @brief
* @detail 线
*/
void log_cleanup(void) void log_cleanup(void)
{ {
if (!g_log.running) if (!g_log.running)
{ {
return; return;
} }
/* 通知线程停止 */
g_log.running = 0; g_log.running = 0;
pthread_cond_signal(&g_log.q.cond_pop); pthread_cond_signal(&g_log.q.cond_pop);
/* 等待线程退出 */
pthread_join(g_log.worker, NULL); pthread_join(g_log.worker, NULL);
/* 关闭文件 */
if (g_log.fp) if (g_log.fp)
{ {
fclose(g_log.fp); fclose(g_log.fp);
g_log.fp = NULL; g_log.fp = NULL;
} }
/* 释放池中残留消息 */
/* 释放队列中残留消息 */
pthread_mutex_lock(&g_log.q.lock); pthread_mutex_lock(&g_log.q.lock);
stru_log_msg *cur = g_log.q.head; stru_log_msg *cur = g_log.q.head;
while (cur) while (cur)
{ {
stru_log_msg *tmp = cur; stru_log_msg *tmp = cur;
cur = cur->next; cur = cur->next;
pool_free(tmp); pool_free(tmp);
} }
g_log.q.head = NULL; g_log.q.head = NULL;
g_log.q.tail = NULL; g_log.q.tail = NULL;
g_log.q.count = 0; g_log.q.count = 0;
pthread_mutex_unlock(&g_log.q.lock); pthread_mutex_unlock(&g_log.q.lock);
} }