Browse Source

feat: 使用多线程日志

SongZihuan 3 năm trước cách đây
mục cha
commit
dd5da06287

+ 2 - 0
include/core/core_init.h

@@ -10,6 +10,7 @@ typedef struct aFunCoreInitInfo aFunCoreInitInfo;
 struct aFunCoreInitInfo {
     char *base_dir;
 
+    bool log_asyn;
     jmp_buf *buf;
     LogLevel level;
 };
@@ -21,6 +22,7 @@ struct aFunRunInfo {
 
 AFUN_CORE_EXPORT extern Logger *aFunCoreLogger;
 AFUN_CORE_EXPORT bool aFunCoreInit(aFunCoreInitInfo *info);
+AFUN_CORE_EXPORT bool aFunCoreDestruct(void);
 
 AFUN_CORE_EXPORT void defineRunEnvCore(aFunRunInfo *run_env);
 AFUN_CORE_EXPORT void undefRunEnvCore(aFunRunInfo *run_env);

+ 3 - 0
include/runtime/aFunlang.h

@@ -8,11 +8,14 @@ typedef struct aFunInitInfo aFunInitInfo;
 struct aFunInitInfo {
     char *base_dir;
 
+    bool log_asyn;
     jmp_buf *buf;
     LogLevel level;
 };
 
 AFUN_LANG_EXPORT bool aFunInit(aFunInitInfo *info);
+AFUN_LANG_EXPORT bool aFunDestruct(void);
+
 AFUN_LANG_EXPORT void defineRunEnv(aFunRunInfo *run_env);
 AFUN_LANG_EXPORT void redefRunEnvSignal(void);
 AFUN_LANG_EXPORT void undefRunEnv(aFunRunInfo *run_env);

+ 2 - 1
include/tool/log.h

@@ -31,7 +31,8 @@ struct Logger {
 };
 typedef struct Logger Logger;
 
-AFUN_TOOL_EXPORT int initLogSystem(FilePath path);
+AFUN_TOOL_EXPORT int initLogSystem(FilePath path, bool asyn);
+AFUN_TOOL_EXPORT int destructLogSystem(void);
 
 AFUN_TOOL_EXPORT void initLogger(Logger *logger, char *id, LogLevel level);
 AFUN_TOOL_EXPORT void destructLogger(Logger *logger);

+ 7 - 1
src/core/core_init.c

@@ -25,6 +25,7 @@ bool aFunCoreInit(aFunCoreInitInfo *info) {
     if (info == NULL) {
         static aFunCoreInitInfo info_default = {.base_dir=".",
                                                 .buf=NULL,
+                                                .log_asyn=true,
                                                 .level=log_info};
         info = &info_default;
     }
@@ -41,7 +42,7 @@ bool aFunCoreInit(aFunCoreInitInfo *info) {
     atexit(destructCoreExit);
 
     char *log = strJoin(log_path, "aFunlang", false, false);
-    bool re = initLogSystem(log);
+    bool re = initLogSystem(log, info->log_asyn);
     free(log);
     if (re == 0)
         return false;
@@ -77,6 +78,11 @@ bool aFunCoreInit(aFunCoreInitInfo *info) {
     return true;
 }
 
+bool aFunCoreDestruct(void) {
+    destructLogSystem();
+    return true;
+}
+
 void defineRunEnvCore(aFunRunInfo *run_env) {
     memset(&run_env->si, 0, sizeof(af_SignalInfo));
     aFunSignalInit(&run_env->si);

+ 6 - 3
src/main.c

@@ -68,9 +68,14 @@ int main(int argc, char **argv) {
 #else
                          .level=log_info,
 #endif
+                         .log_asyn=true,
                          .buf=&main_buf,
     };
 
+    ff_FFlags *ff = ff_initFFlags(argc, argv, true, false, stderr, aFunlang_exe);
+    if (ff == NULL)
+        return EXIT_FAILURE;
+
     if (!aFunInit(&info)) {
 INIT_ERROR:
         printf_stderr(0, "aFunlang init error.");
@@ -82,9 +87,6 @@ INIT_ERROR:
     writeDebugLog(aFunlangLogger, "aFunlang-exe init success: %s", (tty_stdin ? "tty" : "no-tty"));
 
     int exit_code = EXIT_SUCCESS;
-    ff_FFlags *ff = ff_initFFlags(argc, argv, true, false, stderr, aFunlang_exe);
-    if (ff == NULL)
-        return EXIT_FAILURE;
     char *child = ff_getChild(ff);
     name = *argv;  // 获取第一个参数为name
 
@@ -98,6 +100,7 @@ INIT_ERROR:
         exit_code = mainHelp(ff);
 
     ff_freeFFlags(ff);
+    aFunDestruct();
     return exit_code;
 }
 

+ 6 - 0
src/runtime/aFunlang.c

@@ -21,6 +21,7 @@ bool aFunInit(aFunInitInfo *info) {
         static aFunInitInfo info_default = {
                 .base_dir=".",
                 .buf=NULL,
+                .log_asyn=true,
                 .level=log_info
         };
         info = &info_default;
@@ -28,6 +29,7 @@ bool aFunInit(aFunInitInfo *info) {
 
     aFunCoreInitInfo core_info = {.base_dir=info->base_dir,
                                   .buf=info->buf,
+                                  .log_asyn=info->log_asyn,
                                   .level=info->level};
 
     aFunInit_mark = aFunCoreInit(&core_info);
@@ -36,6 +38,10 @@ bool aFunInit(aFunInitInfo *info) {
     return aFunInit_mark;
 }
 
+bool aFunDestruct(void) {
+    return aFunCoreDestruct();
+}
+
 /*
  * 函数名: defineRunEnv
  * 目标: 运行前的初始化

+ 126 - 32
src/tool/log.c

@@ -38,6 +38,21 @@
 
 #undef calloc
 
+typedef struct LogNode LogNode;
+struct LogNode {
+    LogLevel level;
+    char *id;
+    pid_t tid;
+    char *date;  // 需要释放
+    time_t time;
+    char *file;
+    int line;
+    char *func;
+    char *info;  // 需要释放
+
+    LogNode *next;
+};
+
 static struct LogFactory {
     bool init;  // 是否已经初始化
     pid_t pid;
@@ -46,10 +61,17 @@ static struct LogFactory {
     FILE *csv;
 
     Logger sys_log;
+
+    bool asyn;  // 异步
+    pthread_t pt;
+    pthread_cond_t cond;  // 有日志
+    LogNode *log_buf;
+    LogNode **plog_buf;  // 指向 log_buf的末端
 } log_factory = {.init=false};
-static pthread_mutex_t LogFactoryMutext = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t log_factory_mutex = PTHREAD_MUTEX_INITIALIZER;
+#define MUTEX (&log_factory_mutex)
 
-static void destructLogSystem_at_exit(void);
+static void *ansyWritrLog_(void *_);
 
 /*
  * 函数名: initLogSystem
@@ -58,13 +80,20 @@ static void destructLogSystem_at_exit(void);
  * 1 表示初始化成功
  * 2 表示已经初始化
  * 0 表示初始化失败
+ *
+ * 该程序线程不安全
  */
-int initLogSystem(FilePath path) {
-    if (log_factory.init)
-        return 2;
+int initLogSystem(FilePath path, bool asyn){
     if (strlen(path) >= 218)  // 路径过长
         return 0;
 
+    int re = 1;
+    pthread_mutex_lock(MUTEX);
+    if (log_factory.init) {
+        pthread_mutex_unlock(MUTEX);
+        return 2;
+    }
+
     char log_path[218] = {0};
     char csv_path[218] = {0};
     log_factory.pid = getpid();  // 获取进程ID
@@ -81,12 +110,15 @@ int initLogSystem(FilePath path) {
     log_factory.log = fopen(log_path, "a");
     if (log_factory.log == NULL) {
         printf("log_path = %s\n", log_path);
+        pthread_mutex_unlock(MUTEX);
         return 0;
     }
 
     log_factory.csv = fopen(csv_path, "a");
-    if (log_factory.csv == NULL)
+    if (log_factory.csv == NULL) {
+        pthread_mutex_unlock(MUTEX);
         return 0;
+    }
 
 #define CSV_FORMAT "%s,%s,%d,%d,%s,%ld,%s,%d,%s,%s\n"
 #define CSV_TITLE  "Level,Logger,PID,TID,Data,Timestamp,File,Line,Function,Log\n"
@@ -97,39 +129,53 @@ int initLogSystem(FilePath path) {
 #undef CSV_TITLE
 
     log_factory.init = true;
-    atexit(destructLogSystem_at_exit);
-
-    initLogger(&(log_factory.sys_log), "SYSTEM", log_debug);  // 设置为 debug, 记录 success 信息
-    writeDebugLog(NULL, "Log system init success");
-    writeDebugLog(NULL, "Log .log size %lld", log_size);
-    writeDebugLog(NULL, "Log .csv size %lld", csv_size);
-    log_factory.sys_log.level = log_error;
-    return 1;
-}
+    log_factory.asyn = asyn;
+    if (log_factory.asyn) {
+        pthread_cond_init(&log_factory.cond, NULL);
+        pthread_create(&log_factory.pt, NULL, ansyWritrLog_, NULL);
+    }
 
-static void destructLogSystem_at_exit(void) {
-    if (!log_factory.init)
-        return;
-    log_factory.sys_log.level = log_debug;
-    writeDebugLog(NULL, "Log system destruct by exit.");
-    fclose(log_factory.log);
-    fclose(log_factory.csv);
-    log_factory.log = NULL;
-    log_factory.csv = NULL;
-    log_factory.init = false;
+    initLogger(&(log_factory.sys_log), "SYSTEM", log_info);  // 设置为 debug, 记录 success 信息
+    pthread_mutex_unlock(MUTEX);
+    writeInfoLog(NULL, "Log system init success");
+    writeInfoLog(NULL, "Log .log size %lld", log_size);
+    writeInfoLog(NULL, "Log .csv size %lld", csv_size);
+    return re;
 }
 
 int destructLogSystem(void) {
-    if (!log_factory.init)
-        return 2;
-    log_factory.sys_log.level = log_debug;
-    writeDebugLog(NULL, "Log system destruct by user.");
+    int re = 1;
+    pthread_mutex_lock(MUTEX);
+    if (!log_factory.init) {
+        re = 2;
+        goto RETURN;
+    }
+    pthread_mutex_unlock(MUTEX);
+
+    writeInfoLog(NULL, "Log system destruct by exit.");  // 需要用锁
+
+    pthread_mutex_lock(MUTEX);
+    log_factory.init = false;
+    if (log_factory.asyn) {
+        pthread_mutex_unlock(MUTEX);
+        pthread_cond_signal(&log_factory.cond);
+
+        pthread_join(log_factory.pt, NULL);
+
+        pthread_mutex_lock(MUTEX);
+        pthread_cond_destroy(&log_factory.cond);
+        if (log_factory.log_buf != NULL)
+            printf_stderr(0, "Logsystem destruct error.");
+    }
+
     fclose(log_factory.log);
     fclose(log_factory.csv);
     log_factory.log = NULL;
     log_factory.csv = NULL;
     log_factory.init = false;
-    return 1;
+RETURN:
+    pthread_mutex_unlock(MUTEX);
+    return re;
 }
 
 void initLogger(Logger *logger, char *id, LogLevel level) {
@@ -190,11 +236,54 @@ static void writeLogToConsole_(LogLevel level, char *id, pid_t tid, char *ti, ti
 #undef STD_BUF_SIZE
 }
 
+static void writeLogToAsyn_(LogLevel level, char *id, pid_t tid, char *ti, time_t t, char *file, int line, char *func, char *info) {
+#define D(i) (*(log_factory.plog_buf))->i
+    *(log_factory.plog_buf) = calloc(1, sizeof(LogNode));
+    D(level) = level;
+    D(id) = id;
+    D(tid) = tid;
+    D(date) = strCopy(ti);
+    D(time) = t;
+    D(file) = file;
+    D(line) = line;
+    D(func) = func;
+    D(info) = strCopy(info);
+    log_factory.plog_buf = &(D(next));
+    pthread_cond_signal(&log_factory.cond);
+#undef D
+}
+
+static void *ansyWritrLog_(void *_) {
+    pthread_mutex_lock(MUTEX);
+    while (1) {
+        while (log_factory.init && log_factory.log_buf == NULL)
+            pthread_cond_wait(&log_factory.cond, MUTEX);
+        if (!log_factory.init && log_factory.log_buf == NULL)
+            break;
+#define D(i) log_factory.log_buf->i
+        writeLogToFactory_(D(level), D(id), D(tid), D(date), D(time), D(file), D(line), D(func), D(info));
+#undef D
+        LogNode *tmp = log_factory.log_buf->next;
+        free(log_factory.log_buf->date);
+        free(log_factory.log_buf->info);
+        free(log_factory.log_buf);
+        log_factory.log_buf = tmp;
+        if (tmp == NULL)
+            log_factory.plog_buf = &log_factory.log_buf;
+    }
+    pthread_mutex_unlock(MUTEX);
+    return NULL;
+}
+
 static int writeLog_(Logger *logger, bool pc, LogLevel level, char *file, int line, char *func, char *format, va_list ap){
     if (logger->level > level)
         return 2;
-    if (!log_factory.init || log_factory.log == NULL)
+
+    pthread_mutex_lock(MUTEX);
+    if (!log_factory.init || log_factory.log == NULL) {
+        pthread_mutex_unlock(MUTEX);
         return 1;
+    }
     CLEAR_FERROR(log_factory.log);
 
     // 输出 head 信息
@@ -206,10 +295,15 @@ static int writeLog_(Logger *logger, bool pc, LogLevel level, char *file, int li
     vsnprintf(tmp, 1024, format, ap);  // ap只使用一次
     va_end(ap);
 
-    writeLogToFactory_(level, logger->id, tid, ti, t, file, line, func, tmp);
+    if (!log_factory.asyn)
+        writeLogToAsyn_(level, logger->id, tid, ti, t, file, line, func, tmp);
+    else
+        writeLogToFactory_(level, logger->id, tid, ti, t, file, line, func, tmp);
+
     if (pc)
         writeLogToConsole_(level, logger->id, tid, ti, t, file, line, func, tmp);
 
+    pthread_mutex_unlock(MUTEX);
     free(ti);
     return 0;
 }

+ 4 - 0
test/src/run_code.c

@@ -400,6 +400,7 @@ int main(int argc, char **argv) {
     aFunInitInfo info = {
             .base_dir=base_path,
             .level=log_debug,
+            .log_asyn=true,
             .buf=&main_buf
     };
 
@@ -1305,6 +1306,7 @@ INIT_ERROR:
 #ifndef IN_CTEST
     getc(stdin);
 #endif
+    aFunCoreDestruct();
     return 0;
 
 RETURN_1:
@@ -1313,6 +1315,7 @@ RETURN_1:
 #ifndef IN_CTEST
     getc(stdin);
 #endif
+    aFunCoreDestruct();
     return 1;
 
 RETURN_2:
@@ -1321,5 +1324,6 @@ RETURN_2:
 #ifndef IN_CTEST
     getc(stdin);
 #endif
+    aFunCoreDestruct();
     return 2;
 }