diff options
| author | yangwei <[email protected]> | 2024-08-30 14:49:24 +0800 |
|---|---|---|
| committer | yangwei <[email protected]> | 2024-08-30 15:04:46 +0800 |
| commit | aa5c41e4d76cd999238c6a608a27453f88f51031 (patch) | |
| tree | f4f9f502471d81a6f1db929fd11be9b29b481daa /deps | |
| parent | 5ccd66db73efa4f49480a439510f421ab08067bc (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.txt | 3 | ||||
| -rw-r--r-- | deps/logger/CMakeLists.txt | 7 | ||||
| -rw-r--r-- | deps/logger/log.c | 347 | ||||
| -rw-r--r-- | deps/logger/log_private.h | 18 | ||||
| -rw-r--r-- | deps/logger/test/CMakeLists.txt | 9 | ||||
| -rw-r--r-- | deps/logger/test/conf/log_file.toml | 4 | ||||
| -rw-r--r-- | deps/logger/test/conf/log_stderr.toml | 4 | ||||
| -rw-r--r-- | deps/logger/test/gtest_log.cpp | 104 |
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(); +} |
