summaryrefslogtreecommitdiff
path: root/deps
diff options
context:
space:
mode:
authoryangwei <[email protected]>2024-08-30 14:49:24 +0800
committeryangwei <[email protected]>2024-08-30 15:04:46 +0800
commitaa5c41e4d76cd999238c6a608a27453f88f51031 (patch)
treef4f9f502471d81a6f1db929fd11be9b29b481daa /deps
parent5ccd66db73efa4f49480a439510f421ab08067bc (diff)
🦄 refactor(infra/log & http test case): mv log to deps/logger
trim http test case cmake
Diffstat (limited to 'deps')
-rw-r--r--deps/CMakeLists.txt3
-rw-r--r--deps/logger/CMakeLists.txt7
-rw-r--r--deps/logger/log.c347
-rw-r--r--deps/logger/log_private.h18
-rw-r--r--deps/logger/test/CMakeLists.txt9
-rw-r--r--deps/logger/test/conf/log_file.toml4
-rw-r--r--deps/logger/test/conf/log_stderr.toml4
-rw-r--r--deps/logger/test/gtest_log.cpp104
8 files changed, 495 insertions, 1 deletions
diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt
index 58773e3..69ffeec 100644
--- a/deps/CMakeLists.txt
+++ b/deps/CMakeLists.txt
@@ -4,4 +4,5 @@ add_subdirectory(toml)
add_subdirectory(rbtree)
add_subdirectory(interval_tree)
add_subdirectory(bitmap)
-add_subdirectory(nmx_pool) \ No newline at end of file
+add_subdirectory(nmx_pool)
+add_subdirectory(logger) \ No newline at end of file
diff --git a/deps/logger/CMakeLists.txt b/deps/logger/CMakeLists.txt
new file mode 100644
index 0000000..b23f240
--- /dev/null
+++ b/deps/logger/CMakeLists.txt
@@ -0,0 +1,7 @@
+add_library(logger log.c)
+target_include_directories(logger PUBLIC ${CMAKE_CURRENT_LIST_DIR})
+target_include_directories(logger PUBLIC ${CMAKE_SOURCE_DIR}/include)
+
+target_link_libraries(logger toml)
+
+add_subdirectory(test) \ No newline at end of file
diff --git a/deps/logger/log.c b/deps/logger/log.c
new file mode 100644
index 0000000..3f961fe
--- /dev/null
+++ b/deps/logger/log.c
@@ -0,0 +1,347 @@
+#include <time.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+#include "toml.h"
+#include "log_private.h"
+
+enum log_output
+{
+ LOG_OUTPUT_STDERR,
+ LOG_OUTPUT_FILE,
+ LOG_OUTPUT_BOTH,
+};
+
+struct log_config
+{
+ enum log_output output;
+ enum log_level level;
+ char log_file[PATH_MAX];
+};
+
+struct logger
+{
+ char config_file[PATH_MAX];
+ struct log_config config;
+
+ int log_fd;
+ int log_file_opened_day;
+};
+
+static unsigned char level_str[6][6] = {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"};
+static unsigned char weekday_str[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+static unsigned char month_str[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+__thread struct logger *__thread_local_logger;
+
+/******************************************************************************
+ * Private API
+ ******************************************************************************/
+
+static void local_time(struct tm *local)
+{
+ time_t t;
+ time(&t);
+ localtime_r(&t, local);
+}
+
+static int str_to_level(const char *level)
+{
+ if (level == NULL)
+ {
+ return -1;
+ }
+ if (strcasecmp(level, "TRACE") == 0)
+ {
+ return LOG_TRACE;
+ }
+ else if (strcasecmp(level, "DEBUG") == 0)
+ {
+ return LOG_DEBUG;
+ }
+ else if (strcasecmp(level, "INFO") == 0)
+ {
+ return LOG_INFO;
+ }
+ else if (strcasecmp(level, "WARN") == 0)
+ {
+ return LOG_WARN;
+ }
+ else if (strcasecmp(level, "ERROR") == 0)
+ {
+ return LOG_ERROR;
+ }
+ else if (strcasecmp(level, "FATAL") == 0)
+ {
+ return LOG_FATAL;
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+static int config_parse(struct log_config *config, const char *config_file)
+{
+ int ret = -1;
+ FILE *fp = NULL;
+ char errbuf[200];
+ char *ptr_output = NULL;
+ char *ptr_file = NULL;
+ char *ptr_level = NULL;
+ const char *ptr;
+ toml_table_t *section = NULL;
+ toml_table_t *table = NULL;
+
+ fp = fopen(config_file, "r");
+ if (fp == NULL)
+ {
+ fprintf(stderr, "(logger) open config file %s failed, %s\n", config_file, strerror(errno));
+ goto error_out;
+ }
+
+ table = toml_parse_file(fp, errbuf, sizeof(errbuf));
+ if (table == NULL)
+ {
+ fprintf(stderr, "(logger) parse config file %s failed, %s\n", config_file, errbuf);
+ goto error_out;
+ }
+
+ section = toml_table_in(table, "log");
+ if (section == NULL)
+ {
+ fprintf(stderr, "(logger) config file %s missing log section\n", config_file);
+ goto error_out;
+ }
+
+ ptr = toml_raw_in(section, "output");
+ if (ptr == NULL || toml_rtos(ptr, &ptr_output) != 0)
+ {
+ fprintf(stderr, "(logger) config file %s missing log.output\n", config_file);
+ goto error_out;
+ }
+ if (strcasecmp(ptr_output, "stderr") == 0)
+ {
+ config->output = LOG_OUTPUT_STDERR;
+ }
+ else if (strcasecmp(ptr_output, "file") == 0)
+ {
+ config->output = LOG_OUTPUT_FILE;
+ }
+ else if (strcasecmp(ptr_output, "both") == 0)
+ {
+ config->output = LOG_OUTPUT_BOTH;
+ }
+ else
+ {
+ fprintf(stderr, "(logger) config file %s invalid log.output\n", config_file);
+ goto error_out;
+ }
+
+ if (config->output == LOG_OUTPUT_FILE || config->output == LOG_OUTPUT_BOTH)
+ {
+ ptr = toml_raw_in(section, "file");
+ if (ptr == NULL || toml_rtos(ptr, &ptr_file) != 0)
+ {
+ fprintf(stderr, "(logger) config file %s missing log.file\n", config_file);
+ goto error_out;
+ }
+ strcpy(config->log_file, ptr_file);
+ }
+
+ ptr = toml_raw_in(section, "level");
+ if (ptr == NULL || toml_rtos(ptr, &ptr_level) != 0)
+ {
+ fprintf(stderr, "(logger) config file %s missing log.level\n", config_file);
+ goto error_out;
+ }
+ config->level = (enum log_level)str_to_level(ptr_level);
+ if ((int)config->level == -1)
+ {
+ fprintf(stderr, "config file %s invalid log.level\n", config_file);
+ goto error_out;
+ }
+
+ ret = 0;
+
+error_out:
+ if (ptr_output)
+ {
+ free(ptr_output);
+ }
+ if (ptr_file)
+ {
+ free(ptr_file);
+ }
+ if (ptr_level)
+ {
+ free(ptr_level);
+ }
+ if (table)
+ {
+ toml_free(table);
+ }
+ if (fp)
+ {
+ fclose(fp);
+ }
+
+ return ret;
+}
+
+static int log_file_reopen(struct logger *logger)
+{
+ int new_fd;
+ int old_fd;
+ struct tm local;
+ char buff[PATH_MAX * 2] = {0};
+ local_time(&local);
+ snprintf(buff, sizeof(buff), "%s.%d-%02d-%02d", logger->config.log_file, local.tm_year + 1900, local.tm_mon + 1, local.tm_mday);
+
+ new_fd = open(buff, O_WRONLY | O_APPEND | O_CREAT, 0644);
+ if (new_fd == -1)
+ {
+ fprintf(stderr, "(logger) open() \"%s\" failed, %s\n", buff, strerror(errno));
+ return -1;
+ }
+
+ logger->log_file_opened_day = local.tm_mday;
+ old_fd = logger->log_fd;
+ logger->log_fd = new_fd;
+
+ if (old_fd > 0)
+ {
+ close(old_fd);
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ * Public API
+ ******************************************************************************/
+
+struct logger *log_new(const char *config_file)
+{
+ struct logger *logger = (struct logger *)calloc(1, sizeof(struct logger));
+ if (logger == NULL)
+ {
+ fprintf(stderr, "(logger) logger calloc() failed, %s\n", strerror(errno));
+ return NULL;
+ }
+
+ memcpy(&logger->config_file, config_file, strlen(config_file));
+ if (config_parse(&logger->config, config_file) != 0)
+ {
+ goto error_out;
+ }
+
+ if (logger->config.output == LOG_OUTPUT_FILE || logger->config.output == LOG_OUTPUT_BOTH)
+ {
+ if (log_file_reopen(logger) != 0)
+ {
+ goto error_out;
+ }
+ }
+
+ return logger;
+
+error_out:
+ log_free(logger);
+ return NULL;
+}
+
+void log_free(struct logger *logger)
+{
+ if (logger)
+ {
+ if (logger->config.output == LOG_OUTPUT_FILE || logger->config.output == LOG_OUTPUT_BOTH)
+ {
+ if (logger->log_fd > 0)
+ {
+ close(logger->log_fd);
+ logger->log_fd = -1;
+ }
+ }
+
+ free(logger);
+ logger = NULL;
+ }
+}
+
+int log_check_level(struct logger *logger, enum log_level level)
+{
+ if (logger)
+ {
+ return level >= logger->config.level;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+void log_reload_level(struct logger *logger)
+{
+ struct log_config config = {};
+ if (config_parse(&config, logger->config_file) == 0)
+ {
+ logger->config.level = config.level;
+ fprintf(stderr, "(logger) logger level reload to %s\n", level_str[config.level]);
+ }
+ else
+ {
+ fprintf(stderr, "(logger) logger level reload failed\n");
+ }
+}
+
+void log_print(struct logger *logger, enum log_level level, const char *module, const char *fmt, ...)
+{
+ int nwrite;
+ char buf[4096] = {0};
+ char *p = buf;
+ char *end = buf + sizeof(buf);
+ va_list args;
+ struct tm local;
+
+ local_time(&local);
+ // add time
+ p += snprintf(p, end - p, "%s %s %d %02d:%02d:%02d %d ",
+ weekday_str[local.tm_wday], month_str[local.tm_mon], local.tm_mday, local.tm_hour, local.tm_min, local.tm_sec, local.tm_year + 1900);
+ // add tid
+ p += snprintf(p, end - p, "%lu ", pthread_self());
+ // add level
+ p += snprintf(p, end - p, "%s ", level_str[level]);
+ // add module
+ p += snprintf(p, end - p, "(%s), ", module);
+ // add content
+ va_start(args, fmt);
+ p += vsnprintf(p, end - p, fmt, args);
+ va_end(args);
+ // add end of line
+ p += snprintf(p, end - p, "\n");
+
+ if (logger->config.output == LOG_OUTPUT_STDERR || logger->config.output == LOG_OUTPUT_BOTH)
+ {
+ fprintf(stderr, "%s", buf);
+ }
+
+ if (logger->config.output == LOG_OUTPUT_FILE || logger->config.output == LOG_OUTPUT_BOTH)
+ {
+ if (logger->log_file_opened_day != local.tm_mday)
+ {
+ log_file_reopen(logger);
+ }
+ do
+ {
+ nwrite = write(logger->log_fd, buf, p - buf);
+ } while (nwrite == -1 && errno == EINTR);
+ }
+} \ No newline at end of file
diff --git a/deps/logger/log_private.h b/deps/logger/log_private.h
new file mode 100644
index 0000000..844d86b
--- /dev/null
+++ b/deps/logger/log_private.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "stellar/log.h"
+
+extern __thread struct logger *__thread_local_logger;
+
+struct logger *log_new(const char *config_file);
+void log_free(struct logger *logger);
+void log_reload_level(struct logger *logger);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/deps/logger/test/CMakeLists.txt b/deps/logger/test/CMakeLists.txt
new file mode 100644
index 0000000..6d47222
--- /dev/null
+++ b/deps/logger/test/CMakeLists.txt
@@ -0,0 +1,9 @@
+add_executable(gtest_log gtest_log.cpp)
+target_link_libraries(gtest_log logger gtest)
+target_include_directories(gtest_log PUBLIC ${CMAKE_SOURCE_DIR}/deps/logger)
+
+include(GoogleTest)
+gtest_discover_tests(gtest_log)
+
+file(COPY ./conf/log_file.toml DESTINATION ./conf/)
+file(COPY ./conf/log_stderr.toml DESTINATION ./conf/) \ No newline at end of file
diff --git a/deps/logger/test/conf/log_file.toml b/deps/logger/test/conf/log_file.toml
new file mode 100644
index 0000000..43e3621
--- /dev/null
+++ b/deps/logger/test/conf/log_file.toml
@@ -0,0 +1,4 @@
+[log]
+output = "file" # stderr, file, both
+file = "stellar.log"
+level = "INFO" # TRACE, DEBUG, INFO, WARN, ERROR, FATAL
diff --git a/deps/logger/test/conf/log_stderr.toml b/deps/logger/test/conf/log_stderr.toml
new file mode 100644
index 0000000..a2febcc
--- /dev/null
+++ b/deps/logger/test/conf/log_stderr.toml
@@ -0,0 +1,4 @@
+[log]
+output = "stderr" # stderr, file, both
+file = "stellar.log"
+level = "INFO" # TRACE, DEBUG, INFO, WARN, ERROR, FATAL
diff --git a/deps/logger/test/gtest_log.cpp b/deps/logger/test/gtest_log.cpp
new file mode 100644
index 0000000..2183109
--- /dev/null
+++ b/deps/logger/test/gtest_log.cpp
@@ -0,0 +1,104 @@
+#include <gtest/gtest.h>
+
+#include "log_private.h"
+
+#if 1
+TEST(LOG, STDERR)
+{
+ char buffer[1024] = {0};
+ const char *config = "./conf/log_stderr.toml";
+ snprintf(buffer, sizeof(buffer), "sed -i 's/DEBUG/ERROR/g' %s", config);
+ struct logger *logger = log_new(config);
+ EXPECT_TRUE(logger);
+
+ STELLAR_LOG_TRACE(logger, "test", "test log 1");
+ STELLAR_LOG_DEBUG(logger, "test", "test log 1");
+ STELLAR_LOG_INFO(logger, "test", "test log 1");
+ STELLAR_LOG_WARN(logger, "test", "test log 1");
+ STELLAR_LOG_ERROR(logger, "test", "test log 1");
+ STELLAR_LOG_FATAL(logger, "test", "test log 1");
+
+ system(buffer);
+ log_reload_level(logger);
+
+ STELLAR_LOG_TRACE(logger, "test", "test log 2");
+ STELLAR_LOG_DEBUG(logger, "test", "test log 2");
+ STELLAR_LOG_INFO(logger, "test", "test log 2");
+ STELLAR_LOG_WARN(logger, "test", "test log 2");
+ STELLAR_LOG_ERROR(logger, "test", "test log 2");
+ STELLAR_LOG_FATAL(logger, "test", "test log 2");
+
+ log_free(logger);
+}
+#endif
+
+#if 1
+TEST(LOG, FILE)
+{
+ char buffer[1024] = {0};
+ const char *config = "./conf/log_file.toml";
+ snprintf(buffer, sizeof(buffer), "sed -i 's/DEBUG/ERROR/g' %s", config);
+ struct logger *logger = log_new(config);
+ EXPECT_TRUE(logger);
+
+ STELLAR_LOG_TRACE(logger, "test", "test log 1");
+ STELLAR_LOG_DEBUG(logger, "test", "test log 1");
+ STELLAR_LOG_INFO(logger, "test", "test log 1");
+ STELLAR_LOG_WARN(logger, "test", "test log 1");
+ STELLAR_LOG_ERROR(logger, "test", "test log 1");
+ STELLAR_LOG_FATAL(logger, "test", "test log 1");
+
+ system(buffer);
+ log_reload_level(logger);
+
+ STELLAR_LOG_TRACE(logger, "test", "test log 2");
+ STELLAR_LOG_DEBUG(logger, "test", "test log 2");
+ STELLAR_LOG_INFO(logger, "test", "test log 2");
+ STELLAR_LOG_WARN(logger, "test", "test log 2");
+ STELLAR_LOG_ERROR(logger, "test", "test log 2");
+ STELLAR_LOG_FATAL(logger, "test", "test log 2");
+
+ log_free(logger);
+}
+#endif
+
+// disable this test case because it will change the system date
+#if 0
+TEST(LOG, REOPEN)
+{
+ char buffer1[1024] = "date \"+%Y-%m-%d\" >> .date.txt";
+ char buffer2[1024] = "date -s 2099/01/01";
+ char buffer3[1024] = "cat .date.txt | xargs date -s && rm .date.txt";
+ const char *config = "./conf/log_file.toml";
+
+ system(buffer1); // record current date
+
+ EXPECT_TRUE(log_init(config) == 0);
+
+ for (int i = 0; i < 3; i++)
+ {
+ STELLAR_LOG_TRACE(logger, "test", "test log %d", i);
+ STELLAR_LOG_DEBUG(logger, "test", "test log %d", i);
+ STELLAR_LOG_INFO(logger, "test", "test log %d", i);
+ STELLAR_LOG_WARN(logger, "test", "test log %d", i);
+ STELLAR_LOG_ERROR(logger, "test", "test log %d", i);
+ STELLAR_LOG_FATAL(logger, "test", "test log %d", i);
+ if (i == 1)
+ {
+ system(buffer2); // set date to 2099/01/01
+ }
+ else if (i == 2)
+ {
+ system(buffer3); // recover date
+ }
+ }
+
+ log_free(logger);
+}
+#endif
+
+int main(int argc, char **argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}