当前位置:首页>Linux>Arm Linux 日志 -— 手把手教 EasyLogger 开源库的移植、使用

Arm Linux 日志 -— 手把手教 EasyLogger 开源库的移植、使用

  • 2026-08-19 14:30:08
Arm Linux 日志 -— 手把手教 EasyLogger 开源库的移植、使用

本文基于本仓库 vendored 的 EasyLogger v2.2.0 源码(easylogger/EasyLogger-2.2.0/)以及官方文档撰写,所有 API 与示例均可在该版本上直接编译运行。官方链接见文末「官方资源汇总」。

引言

做 开发的朋友,几乎都踩过同一个坑:程序跑得越久越慢,最后发现是日志在拖后腿

原因是很多人图省事直接用 syslog(),而 syslog 走的是 Unix domain socket 同步 IPC,每次调用都要陷入内核、等守护进程处理。在低端 Cortex-A7 上,单条 syslog() 调用轻松 50~100µs,高频打印直接把业务线程堵死。

本文要讲的 EasyLogger,是 RT-Thread 社区大牛 armink 的作品。它的设计哲学和 syslog 完全相反:只负责把日志格式组装好,然后用一个环形缓冲区 + 后台线程(或自己的 sink)把"业务线程"和"真正的落盘"解耦。结果就是:业务线程打日志几乎零等待,慢速的文件/串口/Flash 写入交给后台线程慢慢做。


一、EasyLogger 介绍

维度
说明
作者 / 协议
armink / MIT(商用无忧)
定位
超轻量级、高性能 C/C++ 日志库
体积宣称
ROM < 1.6K、RAM < 0.3K(指 MCU 裸机最小配置;Linux 端开启异步+文件后会大些,但依然是"轻"量级)
支持平台
RT-Thread、UCOS、Linux、Windows、Nuttx,以及裸机
级别
6 级:Assert / Error / Warn / Info / Debug / Verbose(参考 Android Logcat)
核心能力
用户自定义输出(终端/文件/Flash/串口…)、线程安全、异步输出缓冲输出、RAW 与 hexdump、按标签/级别/关键词动态过滤、彩色、插件扩展

为什么在 Arm Linux 上值得用它?

  1. 异步非阻塞:业务线程只往内存环形缓冲写,后台 pthread 负责真正的输出。高频日志不再卡业务。
  2. 源码级集成,零第三方依赖:把几个 .c 加进工程编就行,交叉编译也只需要一个 -lpthread
  3. 一份代码多端复用:同时维护 RT-Thread 和 Arm Linux 应用时,日志接口完全一致,移植负担最低。
  4. 可裁剪:不需要的功能(异步、缓冲、颜色、文件、时间)关掉对应宏即可,体积可控。

⚠️ 适用边界:EasyLogger 是单例、全局配置的库(配置和输出后端全进程只有一套)。它不适合"同一进程里不同模块要输出到完全不同地方并各自独立配置"的复杂场景——那种需求看 zlog。它最适合"进程级统一日志 + 轻量高性能"的嵌入式 Linux 业务。


二、常用 API

2.1 六级日志

0  [A] 断言 Assert1  [E] 错误 Error2  [W] 警告 Warn3  [I] 信息 Info4  [D] 调试 Debug5  [V] 详细 Verbose

两级过滤机制(理解这个,调日志才不懵):

  • 静态(编译期)elog_cfg.h 里的 ELOG_OUTPUT_LVL。低于该级别的代码在编译时直接被替换成空。
  • 动态(运行期)elog_set_filter_lvl() 等 API。低于该级别的日志在运行时被丢弃。

优先级:ELOG_OUTPUT_ENABLE(总开关) > ELOG_OUTPUT_LVL(静态) > elog_set_filter_*(动态)。

2.2 打印日志的两种写法

EasyLogger 提供了两套宏,本质一样,只是一个要每次写 tag,一个靠文件顶部预先定义的 LOG_TAG

/* 方式一:elog_x(tag, ...) —— 每次显式传标签 */elog_a("net""assert: %s", expr);   /* 注意第一个参数是 tag */elog_e("net""connect failed, errno=%d", errno);elog_w("net""retry later");elog_i("net""connected");elog_d("net""send seq=%u", seq);elog_v("net""heartbeat");/* 方式二:log_x(...) —— 文件顶部先 #define LOG_TAG "net",   之后无需再写 tag,LOG_TAG 自动填入 */#define LOG_TAG    "net"#include <elog.h>log_e("connect failed, errno=%d", errno);  /* 等价于 elog_e("net", ...) */log_i("connected");

实战经验:每个 .c 文件顶部按模块定义不同的 LOG_TAG(如 "wifi.proto""can.disp"),再统一用 log_x(),日志天然按模块分组,后期过滤/排查极方便。还可以配合 LOG_LVL 做文件级静态级别控制(详见官方 API 文档「技巧二」)。

2.3 生命周期与配置 API(常用)

下面这些都在 easylogger/inc/elog.h 声明,是最常用的几个:

API
作用
ElogErrCode elog_init(void)
库初始化,任何日志调用前必须先调用
void elog_start(void)
启动日志输出(初始化后必须调用,否则不输出)
void elog_set_fmt(uint8_t level, size_t set)
设置某级别的输出格式位(级别/标签/时间/进程/线程/文件/函数/行号)
void elog_set_filter_lvl(uint8_t level)
动态设置过滤级别(低于该级不输出)
void elog_set_filter_tag(const char *tag)
按标签过滤
void elog_set_filter_kw(const char *kw)
按关键词过滤
void elog_set_filter_tag_lvl(const char *tag, uint8_t lvl)按模块(标签)单独设级别
,非常实用
void elog_set_output_enabled(bool en)
运行时整体开/关输出
void elog_set_text_color_enabled(bool en)
开/关彩色(需 ELOG_COLOR_ENABLE
void elog_raw(const char *fmt, ...)
输出 RAW 原始日志(无格式化头)
void elog_hexdump(const char *name, uint8_t width, uint8_t *buf, uint16_t size)
十六进制 dump,调试协议报文神器
void elog_buf_enabled(bool en)
 / void elog_flush(void)
缓冲输出模式开关 / 把缓冲全部刷出
void elog_async_enabled(bool en)
异步输出模式开/关

格式位宏(ELOG_FMT_*):ELOG_FMT_LVLELOG_FMT_TAGELOG_FMT_TIMEELOG_FMT_P_INFO(进程)、ELOG_FMT_T_INFO(线程)、ELOG_FMT_DIRELOG_FMT_FUNCELOG_FMT_LINE,以及组合 ELOG_FMT_ALL

典型初始化:

setbuf(stdoutNULL);          /* 关掉 stdout 缓冲,避免日志看不到 */elog_init();/* 不同级别给不同格式:错误/警告带时间,调试给全量 */elog_set_fmt(ELOG_LVL_ERROR, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME);elog_set_fmt(ELOG_LVL_WARN,   ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME);elog_set_fmt(ELOG_LVL_INFO,   ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME);elog_set_fmt(ELOG_LVL_DEBUG,  ELOG_FMT_ALL & ~ELOG_FMT_FUNC);elog_set_fmt(ELOG_LVL_VERBOSE,ELOG_FMT_ALL & ~ELOG_FMT_FUNC);#ifdef ELOG_COLOR_ENABLEelog_set_text_color_enabled(true);#endifelog_start();

💡 想临时拉高某个模块的调试级别,不用重新编译:elog_set_filter_tag_lvl("wifi", ELOG_LVL_DEBUG); 打开 wifi 模块全部调试日志;elog_set_filter_tag_lvl("wifi", ELOG_FILTER_LVL_SILENT); 直接静默它。


三、 Arm Linux 移植的详细步骤

EasyLogger 的边界非常清晰:库本身只做"过滤 + 格式化",真正的输出(往哪打、怎么加锁、时间从哪来)全部由实现的一个 elog_port.c 决定。所以移植 = 改一个配置头 + 写一个 port 文件。

3.1 目录结构(从源码里取这些文件)

easylogger/├── inc/│   ├── elog.h          # 公共 API(不要改)│   └── elog_cfg.h      # ★ 唯一要改的配置文件├── src/│   ├── elog.c          # 核心│   ├── elog_utils.c    # 工具│   ├── elog_async.c    # 异步模式(按需加入编译)│   └── elog_buf.c      # 缓冲模式(按需加入编译)├── port/│   └── elog_port.c     # ★ 移植接口(Linux 下自己实现)└── plugins/    └── file/           # 文件输出插件(可选)        ├── elog_file.c        ├── elog_file.h        ├── elog_file_cfg.h  # 文件插件配置        └── elog_file_port.c

导入到工程的动作(官方移植文档第 2 章):

  1. 把 easylogger/ 整个目录拷进工程;
  2. 把 src/elog.csrc/elog_utils.cport/elog_port.c 加入编译(elog_async.c/elog_buf.c 视是否开启异步/缓冲决定);
  3. 把 easylogger/inc/ 加入头文件搜索路径;
  4. 启用文件插件时,再把 plugins/file/elog_file.celog_file_port.c 加入编译,并把 plugins/ 加入搜索路径。

3.2 配置 elog_cfg.h(关键宏)

下面是一份 Arm Linux 用户态推荐配置(在官方 Linux demo 配置基础上加了中文注释):

/* ===== 总开关与级别 ===== */#define ELOG_OUTPUT_ENABLE                 /* 总开关,关掉则所有日志变空 */#define ELOG_OUTPUT_LVL        ELOG_LVL_VERBOSE  /* 静态级别:编译期最低可见级 */#define ELOG_ASSERT_ENABLE                 /* 开启断言检查 *//* ===== 缓冲区与过滤长度 ===== */#define ELOG_LINE_BUF_SIZE      512        /* 单行日志最大字符数 */#define ELOG_LINE_NUM_MAX_LEN   5          /* 行号最大位数 */#define ELOG_FILTER_TAG_MAX_LEN 16         /* 标签最大长度 */#define ELOG_FILTER_KW_MAX_LEN  16         /* 关键词最大长度 */#define ELOG_FILTER_TAG_LVL_MAX_NUM 5      /* 支持多少组"标签+级别"过滤 */#define ELOG_NEWLINE_SIGN       "\n"/* 换行符,可改 "\r\n" *//* ===== 彩色 ===== */#define ELOG_COLOR_ENABLE                  /* 终端彩色(需配合 elog_set_text_color_enabled(true)) *//* ===== 异步输出(强烈推荐开启) ===== */#define ELOG_ASYNC_OUTPUT_ENABLE           /* 开启异步模式 */#define ELOG_ASYNC_OUTPUT_LVL    ELOG_LVL_DEBUG   /* 高于此级的日志走同步,保证高优先级实时性 */#define ELOG_ASYNC_OUTPUT_BUF_SIZE  (ELOG_LINE_BUF_SIZE * 100)  /* 环形缓冲 51KB */#define ELOG_ASYNC_OUTPUT_USING_PTHREAD    /* 用 pthread 实现后台刷出线程 *//* #define ELOG_ASYNC_LINE_OUTPUT *//* 可选:保证每次 port_output 都是完整的一行 *//* ===== 文件插件(可选) ===== *//* #define ELOG_FILE_ENABLE *//* 取消注释即启用文件输出插件 *//* #define ELOG_FILE_FLUSH_CAHCE_ENABLE *//* 注意:上游宏名就是 CAHCE(拼写如此),开启即每次 fflush+fsync */

注意 ELOG_FILE_FLUSH_CAHCE_ENABLE 的拼写——上游源码里就是 CAHCE,照抄即可,别自己"修正"成 CACHE,否则宏不生效。

3.3 实现 elog_port.c(Linux 版)

这是移植的核心。需要提供的接口(官方移植文档第 3 章):

接口
必须
说明
elog_port_init()
初始化移植所需资源(如锁、打开文件)
elog_port_output(log, size)
日志最终落点:往终端/文件/串口写
elog_port_output_lock()
输出加锁(多线程保护)
elog_port_output_unlock()
输出解锁
elog_port_get_time()
返回时间戳字符串(开 ELOG_TIME 时用)
elog_port_get_p_info()
进程信息(多进程排查很有用)
elog_port_get_t_info()
线程信息(多线程排查很有用)

下面是一份生产可用的基础 port(终端 + 进程/线程信息),也是示例 1、2 用的:

/* elog_port.c —— Arm Linux 用户态基础移植(终端输出) */#include <elog.h>#include <stdio.h>#include <pthread.h>#include <unistd.h>#include <time.h>/* 进程内线程安全锁(保护输出不被多线程交错撕碎) */static pthread_mutex_t s_lock = PTHREAD_MUTEX_INITIALIZER;ElogErrCode elog_port_init(void) {/* 基础移植无需额外资源,预留扩展点 */return ELOG_NO_ERR;}/* 日志最终出口:这里打到终端。要写文件/串口,在此扩展即可 */void elog_port_output(const char *logsize_t size) {printf("%.*s", (int)size, log);}void elog_port_output_lock(void)   { pthread_mutex_lock(&s_lock); }void elog_port_output_unlock(void) { pthread_mutex_unlock(&s_lock); }/* 时间戳:启用 ELOG_TIME_ENABLE 时会被调用 */const char *elog_port_get_time(void) {static char buf[24] = {0};time_t t = time(NULL);struct tm *tm = localtime(&t);if (tm == NULLreturn "";snprintf(buf, sizeof(buf), "%02d-%02d %02d:%02d:%02d",             tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);return buf;}/* 进程信息:多进程场景用它一眼看出日志来自哪个进程 */const char *elog_port_get_p_info(void) {static char buf[16] = {0};snprintf(buf, sizeof(buf), "pid:%04d", (int)getpid());return buf;}/* 线程信息:多线程场景区分日志来源 */const char *elog_port_get_t_info(void) {static char buf[16] = {0};snprintf(buf, sizeof(buf), "tid:%04ld", (long)pthread_self());return buf;}

把时间、进程、线程信息都打开后,一条日志会长这样:\033[32;22mI/var 04-18 11:20:01 pid:1234 tid:5678 main: hello\033[0m级别、时间、pid、tid、tag 一目了然,多进程多线程排障时省太多事。

3.4 文件输出插件(可选)

想让日志落盘,启用 ELOG_FILE_ENABLE 并补全两个文件:

elog_file_cfg.h(插件自己的配置):

#define ELOG_FILE_NAME        "/var/log/myapp.log"/* 日志文件路径 */#define ELOG_FILE_MAX_SIZE    (1024 * 1024)          /* 单文件最大 1MB */#define ELOG_FILE_MAX_ROTATE  5                       /* 超过后滚动 5 个备份 */

elog_file_port.c(插件移植,加锁即可):

#include "elog_file.h"ElogErrCode elog_file_port_init(void)     { return ELOG_NO_ERR; }void elog_file_port_lock(void)            { /* 进程内锁,需要可加 pthread_mutex */ }void elog_file_port_unlock(void)          { /* 与上面配对 */ }void elog_file_port_deinit(void)          { /* 清理 */ }

启用后,基础 port 的 elog_port_output 里顺便调一句 elog_file_write(log, size); 即可同时写文件(官方 Linux demo 的 elog_port.c 就是这样做的)。

3.5 编译(本机 & 交叉编译)

# 本机编译(示例 1/2 用到的文件)gcc main.c \    easylogger/src/elog.c easylogger/src/elog_utils.c \    easylogger/src/elog_async.c easylogger/src/elog_buf.c \    elog_port.c \    -I./easylogger/inc -lpthread -o app# 启用文件插件时再加:#    easylogger/plugins/file/elog_file.c easylogger/plugins/file/elog_file_port.c#    -I./easylogger/plugins -I./easylogger/plugins/file# Arm Linux 交叉编译(把 gcc 换成工具链前缀即可)arm-linux-gnueabihf-gcc main.c \    easylogger/src/elog.c easylogger/src/elog_utils.c \    easylogger/src/elog_async.c easylogger/src/elog_buf.c \    elog_port.c \    -I./easylogger/inc -lpthread -o app_arm

四、代码示例

下面三个示例都基于上面的 elog_port.c编译时请确保 elog_cfg.h 里 ELOG_ASYNC_OUTPUT_ENABLE 已开(否则异步相关行为不同)。

4.1 示例一:单线程(终端 + 文件)

最基础的用法:初始化 → 打各级别日志 → hexdump 一段报文 → 退出。

/* * 示例一:单线程(终端输出 + hexdump + 动态过滤) * 编译:make demo_single   运行:./demo_single */#define LOG_TAG    "main"#include <elog.h>#include <stdio.h>#include <unistd.h>int main(void) {/* 关掉 stdout 缓冲,避免日志“攒批”看不到 */    setbuf(stdoutNULL);    elog_init();/* 不同级别给不同格式:信息级带 级别/标签/时间/进程,调试级给全量 */    elog_set_fmt(ELOG_LVL_INFO,   ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME | ELOG_FMT_P_INFO);    elog_set_fmt(ELOG_LVL_DEBUG,  ELOG_FMT_ALL & ~ELOG_FMT_FUNC);#ifdef ELOG_COLOR_ENABLE    elog_set_text_color_enabled(true);#endif    elog_start();    log_a("assert demo");                              /* 一般只在真断言时出现 */    log_e("open %s failed, errno=%d""/dev/ttyS1"2);    log_w("cpu load high: %d%%"92);    log_i("system started, pid=%d", getpid());    log_d("rx buffer free=%u"1024u);    log_v("heartbeat tick");/* 协议调试:把一帧原始数据 dump 出来 */uint8_t frame[16];for (int i = 0; i < 16; i++) {        frame[i] = (uint8_t)(i * 16 + i);    }    elog_hexdump("uart_rx"16, frame, sizeof(frame));/* 动态调级演示:只保留 WARN 及以上 */    elog_set_filter_lvl(ELOG_LVL_WARN);    log_i("这条 INFO 不会被输出");    log_w("这条 WARN 仍可见");/* ★ 异步模式退出注意:EasyLogger 2.2.0 没有“等待异步刷完”的 API。       本程序会马上退出,若不稍等,分离的异步后台线程会被一起杀掉,       环形缓冲里最后的日志就丢了。长时间运行的服务无需担心(线程持续排空)。       演示/工具类程序可用下面两种方式之一:         1) 像这里留一点排空时间;  2) 调 elog_async_enabled(false) 切回同步。 */    usleep(200000);return 0;}

编译运行:

gcc demo_single.c easylogger/src/elog.c easylogger/src/elog_utils.c \    easylogger/src/elog_async.c easylogger/src/elog_buf.c elog_port.c \    -I./easylogger/inc -lpthread -o demo_single./demo_single

4.2 示例二:多线程(N 个线程并发打日志)

EasyLogger 在 Linux 下是线程安全的,前提是 elog_port_output_lock/unlock 用了互斥锁(上面 port 已经做了)。异步模式下,真正的写文件/终端是后台 pthread 在做,业务线程只是往环形缓冲塞数据,几乎无锁竞争。

下面起 3 个线程,每个循环打 50 条日志,验证不会"串味":

/* * 示例二:多线程(N 个线程并发打日志) * 编译:make demo_mt   运行:./demo_mt * * 结论:EasyLogger 在 Linux 下是线程安全的,前提是 elog_port_output_lock/unlock *      用了互斥锁(基础 port 已经做了)。异步模式下真正的写终端由后台 pthread *      完成,业务线程只往环形缓冲塞数据,几乎无锁竞争。 */#define LOG_TAG    "mt"#include <elog.h>#include <stdio.h>#include <pthread.h>#include <unistd.h>static void *worker(void *arg) {long id = (long)arg;for (int i = 0; i < 50; i++) {        log_i("thread-%ld loop-%d", id, i);        usleep(1000);    }return NULL;}int main(void) {    setbuf(stdoutNULL);    elog_init();    elog_set_fmt(ELOG_LVL_INFO, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_T_INFO | ELOG_FMT_P_INFO);#ifdef ELOG_COLOR_ENABLE    elog_set_text_color_enabled(true);#endif    elog_start();pthread_t t[3];for (long i = 0; i < 3; i++) {        pthread_create(&t[i], NULL, worker, (void *)i);    }for (int i = 0; i < 3; i++) {        pthread_join(t[i], NULL);    }    log_i("all threads done");/* 同 demo_single:退出前给异步后台线程留排空时间,避免最后几条日志丢失 */    usleep(200000);return 0;}

实战经验:

  • 多线程下不要在 elog_port_output 里做耗时极长的同步 IO 且不开异步——开了异步后重活都在后台线程,业务线程瞬间返回。
  • 环形缓冲写满时,EasyLogger 会丢弃超出部分(不会阻塞业务),所以极端高频场景要酌情调大 ELOG_ASYNC_OUTPUT_BUF_SIZE,或把最关键的日志级别设到 ELOG_ASYNC_OUTPUT_LVL 以下走同步,确保不丢。
  • 进程退出前,若担心最后几条还在缓冲里没刷完,可让主线程在 return 前 sleep 一小会儿,给后台线程排空时间(2.2.0 没有提供"等待异步刷完"的 API,这是最稳妥的做法)。

4.3 示例三:多进程(fork 后各自独立实例,写同一文件)

这是最容易踩坑的地方,先把原理讲清:

  1. EasyLogger 是"进程内"库。它的缓冲区、异步后台线程、配置都在进程私有内存里。所以 fork 之后,子进程必须重新 elog_init() + elog_start(),否则子进程既没初始化,也没有那个后台刷出线程(fork 只复制调用线程,异步线程不会被复制)。
  2. 多个进程写同一个文件必须跨进程加锁。基础 port 里的 pthread_mutex 只保护"进程内"的并发,挡不住另一个进程的写入交错。正确做法:打开文件时用 O_APPEND(Linux 对 write() 到普通文件保证原子追加),再叠加 flock() 做跨进程互斥。
  3. fork 后子进程应重新 open() 文件,得到独立的"文件描述/锁",否则父子共享同一个 flock 锁对象,反而失去互斥效果。

下面给一份多进程安全的 port + 示例(不依赖文件插件,自己用 open+flock 落盘,最直观):

/* elog_port_mp.c —— 多进程安全移植:共享文件 + flock */#include <elog.h>#include <stdio.h>#include <pthread.h>#include <unistd.h>#include <time.h>#include <fcntl.h>#include <sys/file.h>static pthread_mutex_t s_lock = PTHREAD_MUTEX_INITIALIZER;  /* 进程内 */static int g_fd = -1;                                       /* 各进程独立打开 */ElogErrCode elog_port_init(void) {/* 每个进程各自 open 一次,拿到独立的文件描述,flock 才能跨进程互斥 */    g_fd = open("/var/log/elog_mp.log", O_WRONLY | O_CREAT | O_APPEND, 0644);return ELOG_NO_ERR;}void elog_port_output(const char *logsize_t size) {/* 终端也打一份,方便现场看 */printf("%.*s", (int)size, log);/* 文件:flock 跨进程加锁 + O_APPEND 原子追加,保证不交错、不丢字节 */if (g_fd >= 0) {        flock(g_fd, LOCK_EX);        write(g_fd, log, size);        flock(g_fd, LOCK_UN);    }}void elog_port_output_lock(void)   { pthread_mutex_lock(&s_lock); }void elog_port_output_unlock(void) { pthread_mutex_unlock(&s_lock); }const char *elog_port_get_time(void) {static char buf[24] = {0};time_t t = time(NULL); struct tm *tm = localtime(&t);if (tm == NULLreturn "";snprintf(buf, sizeof(buf), "%02d-%02d %02d:%02d:%02d",             tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);return buf;}const char *elog_port_get_p_info(void) {static char buf[16] = {0};snprintf(buf, sizeof(buf), "pid:%04d", (int)getpid());return buf;}const char *elog_port_get_t_info(void) {static char buf[16] = {0};snprintf(buf, sizeof(buf), "tid:%04ld", (long)pthread_self());return buf;}
/* demo_mp.c —— 父进程 fork 出 2 个子进程,三者并发写同一日志文件 *//* * 示例三:多进程(fork 后各自独立实例,并发写同一日志文件) * 编译:make demo_mp   运行:./demo_mp ; 之后 cat /var/log/elog_mp.log * * ★★★ 多进程必读:fork 与异步模式的坑 ★★★ *   1) EasyLogger 是“进程内”库:缓冲区、异步后台线程、配置都在进程私有内存。 *      fork 之后子进程必须重新 elog_init() + elog_start(),否则子进程既没有 *      初始化,也没有后台刷出线程(fork 只复制调用线程)。 *   2) 更隐蔽的坑:elog_async.c 里的 init_ok 是 static,父进程初始化后置为 *      true,fork 把它复制到子进程;于是子进程 elog_init() 看到 init_ok==true *      会直接返回,不会创建异步线程 → 子进程的日志塞进环形缓冲却永远没人排空, *      最后全丢。所以本例在父、子进程里都显式 elog_async_enabled(false), *      改成同步输出(写文件这种慢操作交给端口里的 flock,本就不靠后台线程)。 *   3) 写同一文件必须跨进程互斥:用 open(O_APPEND) + flock(),且每个进程各自 *      open(见 elog_port_mp.c),不能继承父进程的 fd。 */#define LOG_TAG    "mp"#include <elog.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/wait.h>int main(void) {    setbuf(stdoutNULL);/* 父进程先初始化(会 open 自己的 fd) */    elog_init();    elog_set_fmt(ELOG_LVL_INFO, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME | ELOG_FMT_P_INFO);#ifdef ELOG_COLOR_ENABLE    elog_set_text_color_enabled(true);#endif    elog_start();/* 见上方说明:父进程也关闭异步,避免 fork 后子进程异步线程缺失导致丢日志 */    elog_async_enabled(false);    log_i("parent ready, going to fork");for (int c = 0; c < 2; c++) {pid_t pid = fork();if (pid == 0) {/* ===== 子进程:必须重新 init/start ===== */            elog_init();                       /* 内部再次 open,获得独立 fd/锁 */            elog_set_fmt(ELOG_LVL_INFO, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME | ELOG_FMT_P_INFO);#ifdef ELOG_COLOR_ENABLE            elog_set_text_color_enabled(true);#endif            elog_start();            elog_async_enabled(false);        /* 关键:子进程强制同步输出 */for (int i = 0; i < 30; i++) {                log_i("child-%d iter-%d", c, i);                usleep(5000);            }            log_i("child-%d exit", c);exit(0);        }    }/* 父进程自己也打一些 */for (int i = 0; i < 30; i++) {        log_i("parent iter-%d", i);        usleep(5000);    }/* 收尸,确保子进程日志都刷完 */int status;while (wait(&status) > 0) { /* 等待所有子进程 */ }    log_i("all children reaped, parent exit");return 0;}

编译运行:

gcc demo_mp.c easylogger/src/elog.c easylogger/src/elog_utils.c \    easylogger/src/elog_async.c easylogger/src/elog_buf.c elog_port_mp.c \    -I./easylogger/inc -lpthread -o demo_mp./demo_mpcat /var/log/elog_mp.log    # 父/子进程的日志按行完整、互不交错

多进程更简单的替代方案(推荐小项目用):每个进程写自己的文件,文件名带 PID(如 /var/log/myapp_%d.log)。完全不需要跨进程锁,排查时 grep 也清爽。上面的 flock 共享文件方案适合"希望所有进程日志汇聚到一个文件"的场景。


五、避坑与实践总结

  • ❌ 不要在高频循环里用同步日志(原生 syslog、printf 直接刷盘)。EasyLogger 开了异步后,业务线程只是内存拷贝,几乎不阻塞。
  • ✅ 异步模式写满会丢日志,不会卡业务。高频场景调大 ELOG_ASYNC_OUTPUT_BUF_SIZE;关键日志设到 ELOG_ASYNC_OUTPUT_LVL 以下走同步保不丢。
  • ✅ 多线程天然安全:只要 elog_port_output_lock/unlock 用互斥锁(Linux demo 已做)。
  • ⚠️ 多进程必须各 init 各的fork 后子进程一定要重新 elog_init()+elog_start(),否则既没有初始化也没有后台刷出线程。
  • ⚠️ 多进程写同一文件要跨进程锁pthread_mutex 管不了别的进程,用 open(O_APPEND)+flock,或干脆每进程一个文件。
  • 静态 vs 动态级别ELOG_OUTPUT_LVL 是编译期裁剪(更狠、零运行时开销),elog_set_filter_lvl() 是运行期过滤(可随时改)。线上想"临时拉满某模块日志",用 elog_set_filter_tag_lvl("模块名", ELOG_LVL_DEBUG)
  • 彩色只在终端有效:输出到文件时颜色转义符会污染日志,落盘场景可在 elog_cfg.h 关掉 ELOG_COLOR_ENABLE,或仅对终端后端启用。
  • 交叉编译记住三件事:工具链前缀替换 gcc、保留 -lpthread、头文件路径包含 easylogger/inc(以及插件目录)。

官方资源汇总(建议 Watch & Star)

  • 项目主页 / 源码:https://github.com/armink/EasyLogger
  • 国内镜像(下载慢时用):https://git.oschina.net/armink/EasyLogger
  • 核心 API 文档:https://github.com/armink/EasyLogger/blob/master/docs/zh/api/kernel.md
  • 移植文档:https://github.com/armink/EasyLogger/blob/master/docs/zh/port/kernel.md
  • 文件插件源码:https://github.com/armink/EasyLogger/tree/master/easylogger/plugins/file
  • 本仓库 vendored 版本:本地 easylogger/EasyLogger-2.2.0/(API 以本地源码为准)
  • 协议:MIT(Copyright armink.ztl@gmail.com)

总结

  1. 移植 EasyLogger = 改 elog_cfg.h + 写 elog_port.c,零第三方依赖。
  2. 生命周期:elog_init() → elog_set_fmt(...) → elog_start(),少一步都不输出。
  3. 打日志用 elog_x(tag,...) 或 log_x(...)(配合文件顶部 LOG_TAG)。
  4. 异步是多线程/高频场景的必选项;环形缓冲写满丢日志不阻塞。
  5. 多线程安全靠 port 里的互斥锁;多进程各自 init 且写共享文件要 flock
  6. 调级别:编译期 ELOG_OUTPUT_LVL,运行期 elog_set_filter_*,按模块用 elog_set_filter_tag_lvl

如果觉得不错,欢迎 点赞、收藏、转发 ,如果想第一时间收到推送,也可以给个星标⭐~~

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-08-21 22:53:49 HTTP/2.0 GET : https://f.mffb.com.cn/a/507615.html
  2. 运行时间 : 0.235902s [ 吞吐率:4.24req/s ] 内存消耗:4,681.56kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=c565173804747e19f5d208a598df6038
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000982s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001443s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000698s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000691s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001318s ]
  6. SELECT * FROM `set` [ RunTime:0.000538s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001566s ]
  8. SELECT * FROM `article` WHERE `id` = 507615 LIMIT 1 [ RunTime:0.001175s ]
  9. UPDATE `article` SET `lasttime` = 1787324029 WHERE `id` = 507615 [ RunTime:0.040051s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.010775s ]
  11. SELECT * FROM `article` WHERE `id` < 507615 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001192s ]
  12. SELECT * FROM `article` WHERE `id` > 507615 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.008301s ]
  13. SELECT * FROM `article` WHERE `id` < 507615 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.001675s ]
  14. SELECT * FROM `article` WHERE `id` < 507615 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.001483s ]
  15. SELECT * FROM `article` WHERE `id` < 507615 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.004058s ]
0.239463s