refactor(logging): implement get_logger and set_logger functions

- Add get_logger and set_logger functions to manage global logger
- Update Logger class to use unique_ptr for child loggers
- Modify get_child method to use template and transfer ownership
- Update logging initialization and shutdown processes
- Adjust test cases to use new logger management functions
This commit is contained in:
ovizro 2024-12-10 15:53:48 +08:00
parent 610a7e4008
commit ccaa4908b5
12 changed files with 129 additions and 95 deletions

View File

@ -31,38 +31,17 @@ if (CMAKE_CROSSCOMPILING)
message(STATUS "CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}") message(STATUS "CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}")
endif() endif()
include_directories(./include) include_directories(${CMAKE_SOURCE_DIR}/include)
aux_source_directory(${CMAKE_SOURCE_DIR}/src SRC_LIST)
aux_source_directory(./src SRC_LIST)
add_library(exint_static STATIC ${SRC_LIST})
add_library(exint SHARED ${SRC_LIST}) add_library(exint SHARED ${SRC_LIST})
add_library(exint_static STATIC ${SRC_LIST})
set_target_properties(exint_static PROPERTIES OUTPUT_NAME exint)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(exint PRIVATE -Wno-nonnull -fvisibility=hidden) target_compile_options(exint PRIVATE -Wno-nonnull -fvisibility=hidden)
endif() endif()
# link_libraries(pthread)
set(TESTS_DIR "${CMAKE_SOURCE_DIR}/tests")
set(TEST_COMMON_SOURCE "${TESTS_DIR}/c_testcase.cpp")
# test_ subdirs(tests)
file(GLOB_RECURSE TEST_FILES "${TESTS_DIR}/test_*.cpp")
#
foreach(TEST_FILE ${TEST_FILES})
#
get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE)
#
add_executable(${TEST_NAME} ${TEST_FILE} ${TEST_COMMON_SOURCE} ${SRC_LIST})
#
list(APPEND TEST_EXECUTABLES "${EXECUTABLE_OUTPUT_PATH}/${TEST_NAME}")
endforeach()
enable_testing()
add_test(NAME test
COMMAND ${CMAKE_SOURCE_DIR}/scripts/unittest.py ${TEST_EXECUTABLES}
)

View File

@ -12,7 +12,7 @@ cmake . -B build
cmake --build build -j6 cmake --build build -j6
``` ```
使用cmake及ctest进行测试: 使用cmake调用测试脚本进行测试:
```bash ```bash
cmake . -B build cmake . -B build

View File

@ -158,11 +158,11 @@ extern "C"{
if (memcmp((expr1), (expr2), (size)) != 0) { \ if (memcmp((expr1), (expr2), (size)) != 0) { \
printf("assertion failed: %s == %s\n", #expr1, #expr2); \ printf("assertion failed: %s == %s\n", #expr1, #expr2); \
printf("\t#0: "); \ printf("\t#0: "); \
for (int i = 0; i < (size); i++) { \ for (size_t i = 0; i < (size); i++) { \
printf("%02X", ((uint8_t*)(expr1))[i]); \ printf("%02X", ((uint8_t*)(expr1))[i]); \
} \ } \
printf("\n\t#1: "); \ printf("\n\t#1: "); \
for (int i = 0; i < (size); i++) { \ for (size_t i = 0; i < (size); i++) { \
printf("%02X", ((uint8_t*)(expr2))[i]); \ printf("%02X", ((uint8_t*)(expr2))[i]); \
} \ } \
printf("\nfile: \"%s\", line %d, in %s\n", __FILE__, __LINE__, __ASSERT_FUNCTION);\ printf("\nfile: \"%s\", line %d, in %s\n", __FILE__, __LINE__, __ASSERT_FUNCTION);\

View File

@ -74,7 +74,38 @@ public:
LoggerOStream operator[](Level level); LoggerOStream operator[](Level level);
LoggerOStream operator[](int level); LoggerOStream operator[](int level);
Logger* get_child(const std::string& name, Level level = Level::UNKNOWN); void move_children_to(Logger& other);
template<class T_Logger = Logger>
Logger* get_child(const std::string& name, Level level)
{
Logger* logger;
auto sep_pos = name.find(namesep);
do {
if (name.empty() || sep_pos == 0) {
logger = this;
break;
}
std::string base_name;
if (sep_pos == std::string::npos) {
base_name = name;
} else {
base_name = name.substr(0, sep_pos);
}
auto iter = logger_cache.find(base_name);
if (iter != logger_cache.end()) {
logger = iter->second.get();
break;
}
logger = new T_Logger(base_name, level, this);
logger_cache[base_name] = std::unique_ptr<Logger>(logger);
} while (0);
auto seqlen = strlen(namesep);
if (sep_pos == std::string::npos || sep_pos + seqlen >= name.size())
return logger;
return logger->get_child(name.substr(sep_pos + seqlen), level);
}
protected: protected:
explicit Logger(const std::string& name, Level level = Level::INFO, Logger* parent = nullptr); explicit Logger(const std::string& name, Level level = Level::INFO, Logger* parent = nullptr);
@ -132,16 +163,18 @@ struct Record
Record(const std::string& name, Logger::Level level, const std::string& msg); Record(const std::string& name, Logger::Level level, const std::string& msg);
}; };
std::ostream& operator<<(std::ostream& os, const Logger::Level level);
Logger::Level str2level(const char* level);
extern std::unique_ptr<Logger> global_logger;
using LogLevel = Logger::Level; using LogLevel = Logger::Level;
std::ostream& operator<<(std::ostream& os, const Logger::Level level);
Logger::Level str2level(const char* level);
std::unique_ptr<Logger>& _get_global_logger();
Logger& get_global_logger();
void set_global_logger(std::unique_ptr<Logger>&& logger);
template<class T_Logger = Logger>
static inline Logger* get_logger(const std::string& name, LogLevel level = LogLevel::UNKNOWN) static inline Logger* get_logger(const std::string& name, LogLevel level = LogLevel::UNKNOWN)
{ {
return global_logger->get_child(name, level); return _get_global_logger()->get_child<T_Logger>(name, level);
} }
} }

View File

@ -2,4 +2,4 @@
mkdir -p dist mkdir -p dist
zip -r "dist/exint-build-$(date +%Y%m%d-%H%M%S).zip" bin/ lib/ config/\ zip -r "dist/exint-build-$(date +%Y%m%d-%H%M%S).zip" bin/ lib/ config/\
scripts/ README.md scripts/ README.md include/external_interface.h

View File

@ -2,4 +2,4 @@
mkdir -p dist mkdir -p dist
tar -czvf "dist/exint-$(date +%Y%m%d-%H%M%S).tar.gz" src/ include/ tests/ config/\ tar -czvf "dist/exint-$(date +%Y%m%d-%H%M%S).tar.gz" src/ include/ tests/ config/\
scripts/ toolchains/ tools/ README.md CMakeLists.txt mak .gitignore scripts/ toolchains/ README.md CMakeLists.txt mak .gitignore

View File

@ -70,7 +70,7 @@ void exint_send(uint32_t type, size_t len, void* data) {
event_name = "send_audio"; event_name = "send_audio";
break; break;
default: default:
break; return;
} }
exint_event(event_name, len, data); exint_event(event_name, len, data);
} }

View File

@ -10,8 +10,23 @@
using namespace logging; using namespace logging;
__attribute__((__init_priority__(8192))) std::unique_ptr<Logger>& logging::_get_global_logger()
std::unique_ptr<Logger> logging::global_logger(new Logger(LogLevel::WARN)); {
static std::unique_ptr<Logger> global_logger(new Logger(LogLevel::WARN));
return global_logger;
}
Logger& logging::get_global_logger()
{
return *_get_global_logger();
}
void logging::set_global_logger(std::unique_ptr<Logger>&& logger)
{
auto& global_logger = _get_global_logger();
global_logger->move_children_to(*logger);
global_logger = std::move(logger);
}
Logger::Logger(Level level) : _parent(nullptr), _level(level) Logger::Logger(Level level) : _parent(nullptr), _level(level)
{} {}
@ -186,35 +201,15 @@ LoggerOStream Logger::operator[](int level)
return (*this)[static_cast<Logger::Level>(level)]; return (*this)[static_cast<Logger::Level>(level)];
} }
Logger* Logger::get_child(const std::string& name, Level level) void Logger::move_children_to(Logger& target_logger)
{ {
Logger* logger; if (logger_cache.empty()) return;
auto sep_pos = name.find(namesep); for (auto it = logger_cache.begin(); it != logger_cache.end(); ) {
do { // Transfer ownership of the child logger to the target_logger
if (name.empty() || sep_pos == 0) { it->second->_parent = &target_logger;
logger = this; target_logger.logger_cache.insert(std::move(*it));
break; it = logger_cache.erase(it); // Remove from current logger's cache
} }
std::string base_name;
if (sep_pos == std::string::npos) {
base_name = name;
} else {
base_name = name.substr(0, sep_pos);
}
auto iter = logger_cache.find(base_name);
if (iter != logger_cache.end()) {
logger = iter->second.get();
break;
}
logger = new Logger(base_name, level, this);
(*global_logger)[Level::DEBUG] << "created logger" << logger->_name << std::endl;
logger_cache[base_name] = std::unique_ptr<Logger>(logger);
} while (0);
auto seqlen = strlen(namesep);
if (sep_pos == std::string::npos || sep_pos + seqlen >= name.size())
return logger;
return logger->get_child(name.substr(sep_pos + seqlen), level);
} }
LoggerOStream::LoggerOStream(Logger& logger, Logger::Level level) LoggerOStream::LoggerOStream(Logger& logger, Logger::Level level)
@ -311,20 +306,17 @@ void log_init(const char* level)
} else if (strcasecmp(level, "env") == 0 || strcasecmp(level, "auto") == 0) { } else if (strcasecmp(level, "env") == 0 || strcasecmp(level, "auto") == 0) {
level = getenv("LOG_LEVEL"); level = getenv("LOG_LEVEL");
} }
if (global_logger->children_count()) { set_global_logger(std::unique_ptr<Logger>(new Logger(str2level(level))));
global_logger->warn("reseting logger with children");
}
global_logger = std::unique_ptr<Logger>(new Logger(str2level(level)));
} }
int log_level() int log_level()
{ {
return static_cast<int>(global_logger->level()); return static_cast<int>(_get_global_logger()->level());
} }
void log_set_level(int level) void log_set_level(int level)
{ {
global_logger->set_level(static_cast<Logger::Level>(level)); _get_global_logger()->set_level(static_cast<Logger::Level>(level));
} }
void log_log(int level, const char* fmt, ...) void log_log(int level, const char* fmt, ...)
@ -337,10 +329,5 @@ void log_log(int level, const char* fmt, ...)
void log_vlog(int level, const char* fmt, va_list args) void log_vlog(int level, const char* fmt, va_list args)
{ {
if (!global_logger) { _get_global_logger()->vlog(static_cast<Logger::Level>(level), fmt, args);
vfprintf(stderr, fmt, args);
fputc('\n', stderr);
return;
}
global_logger->vlog(static_cast<Logger::Level>(level), fmt, args);
} }

18
tests/CMakeLists.txt Normal file
View File

@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 3.15)
project(test)
set(TESTS_DIR .)
set(TEST_COMMON_SOURCE "${TESTS_DIR}/c_testcase.cpp")
file(GLOB_RECURSE TEST_FILES "${TESTS_DIR}/test_*.cpp")
foreach(TEST_FILE ${TEST_FILES})
get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE)
add_executable(${TEST_NAME} ${TEST_FILE} ${TEST_COMMON_SOURCE} ${SRC_LIST})
list(APPEND TEST_EXECUTABLES "${EXECUTABLE_OUTPUT_PATH}/${TEST_NAME}")
endforeach()
add_custom_target(test
COMMAND ${CMAKE_SOURCE_DIR}/scripts/unittest.py ${TEST_EXECUTABLES}
DEPENDS ${TEST_EXECUTABLES}
)

View File

@ -132,9 +132,7 @@ static __inline void print_separator_ex(char lc, const char* str, const char* co
static int collect_testcase() { static int collect_testcase() {
for (int i = 0; i < test_case_total; ++i) { for (int i = 0; i < test_case_total; ++i) {
puts(test_cases[i].name); puts(test_cases[i].name);
// putchar(' ');
} }
putchar('\n');
return 0; return 0;
} }
@ -197,6 +195,15 @@ static int unittest_testcase(TestCase* tc) {
int main(int argc, const char** argv) { int main(int argc, const char** argv) {
for (int i = 1; i < argc; ++i) { for (int i = 1; i < argc; ++i) {
const char* arg = argv[i]; const char* arg = argv[i];
if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
printf("usage: %s [-c] [-u NAME] [-h]\n", argv[0]);
printf("\nOptions:\n");
printf(" -i, --interactive: run in interactive mode.\n");
printf(" -c, --collect: list all test cases.\n");
printf(" -u, --unittest: run a single test case.\n");
printf(" -h, --help: show the help text.\n");
return 0;
}
if (strcmp(arg, "-i") == 0 || strcmp(arg, "--interactive") == 0) { if (strcmp(arg, "-i") == 0 || strcmp(arg, "--interactive") == 0) {
if (interactive) if (interactive)
return interactive(argc, argv); return interactive(argc, argv);

View File

@ -5,6 +5,8 @@
using namespace logging; using namespace logging;
static auto& module_logger = *get_logger("test_logging");
TEST_CASE(test_init) { TEST_CASE(test_init) {
log_info("test_init before"); log_info("test_init before");
log_init(NULL); log_init(NULL);
@ -23,10 +25,18 @@ TEST_CASE(test_init) {
END_TEST; END_TEST;
} }
TEST_CASE(test_module_logger)
{
module_logger.info("test_module_logger1");
log_init("debug");
module_logger.info("test_module_logger2");
END_TEST;
}
TEST_CASE(test_add_stream) { TEST_CASE(test_add_stream) {
std::stringstream ss; std::stringstream ss;
log_init(NULL); log_init(NULL);
logging::global_logger->add_stream(ss); get_global_logger().add_stream(ss);
log_info("test_stream"); log_info("test_stream");
auto s = ss.str(); auto s = ss.str();
@ -60,7 +70,7 @@ TEST_CASE(test_add_stream) {
assert_str_eq(s.c_str() + (s.length() - 26), "[FATAL] test_stream fatal\n"); assert_str_eq(s.c_str() + (s.length() - 26), "[FATAL] test_stream fatal\n");
ss.str(""); ss.str("");
logging::global_logger->log<logging::Logger::Level::DEBUG>("test_stream debug"); get_global_logger().log<logging::Logger::Level::DEBUG>("test_stream debug");
s = ss.str(); s = ss.str();
assert_str_eq(s.c_str() + (s.length() - 26), "[DEBUG] test_stream debug\n"); assert_str_eq(s.c_str() + (s.length() - 26), "[DEBUG] test_stream debug\n");
@ -69,7 +79,7 @@ TEST_CASE(test_add_stream) {
TEST_CASE(test_log_stream) { TEST_CASE(test_log_stream) {
log_init(NULL); log_init(NULL);
auto& logger = *logging::global_logger; auto& logger = get_global_logger();
std::stringstream ss; std::stringstream ss;
logger.add_stream(ss); logger.add_stream(ss);
@ -91,7 +101,7 @@ TEST_CASE(test_sub_logger) {
auto sublogger = get_logger("sublogger"); auto sublogger = get_logger("sublogger");
assert_eq(sublogger->level(), LogLevel::INFO); assert_eq(sublogger->level(), LogLevel::INFO);
assert_eq(sublogger->name(), "sublogger"); assert_eq(sublogger->name(), "sublogger");
assert_eq(sublogger->parent(), logging::global_logger.get()); assert_eq(sublogger->parent(), &get_global_logger());
auto subsublogger = get_logger("sublogger::subsublogger"); auto subsublogger = get_logger("sublogger::subsublogger");
assert_eq(subsublogger->level(), LogLevel::INFO); assert_eq(subsublogger->level(), LogLevel::INFO);
@ -104,11 +114,11 @@ TEST_CASE(test_sub_logger) {
auto sub1logger = get_logger("sublogger1"); auto sub1logger = get_logger("sublogger1");
assert_eq(sub1logger->level(), LogLevel::INFO); assert_eq(sub1logger->level(), LogLevel::INFO);
assert_eq(sub1logger->name(), "sublogger1"); assert_eq(sub1logger->name(), "sublogger1");
assert_eq(sub1logger->parent(), logging::global_logger.get()); assert_eq(sub1logger->parent(), &get_global_logger());
assert_eq(sub1sub1logger->parent(), sub1logger); assert_eq(sub1sub1logger->parent(), sub1logger);
auto root_logger = get_logger(""); auto root_logger = get_logger("");
assert_eq(root_logger, logging::global_logger.get()); assert_eq(root_logger, &get_global_logger());
auto root_logger1 = get_logger("::"); auto root_logger1 = get_logger("::");
assert_eq(root_logger1, root_logger); assert_eq(root_logger1, root_logger);
auto subsublogger1 = get_logger("sublogger::subsublogger"); auto subsublogger1 = get_logger("sublogger::subsublogger");
@ -120,12 +130,12 @@ TEST_CASE(test_sub_logger) {
auto subsublogger4 = get_logger("sublogger::::::subsublogger"); auto subsublogger4 = get_logger("sublogger::::::subsublogger");
assert_eq(subsublogger4, subsublogger); assert_eq(subsublogger4, subsublogger);
global_logger->set_level(LogLevel::DEBUG); get_global_logger().set_level(LogLevel::DEBUG);
assert_eq(global_logger->level(), LogLevel::DEBUG); assert_eq(get_global_logger().level(), LogLevel::DEBUG);
assert_eq(sublogger->level(), LogLevel::DEBUG); assert_eq(sublogger->level(), LogLevel::DEBUG);
assert_eq(subsublogger->level(), LogLevel::DEBUG); assert_eq(subsublogger->level(), LogLevel::DEBUG);
sublogger->set_level(LogLevel::WARN); sublogger->set_level(LogLevel::WARN);
assert_eq(global_logger->level(), LogLevel::DEBUG); assert_eq(get_global_logger().level(), LogLevel::DEBUG);
assert_eq(sublogger->level(), LogLevel::WARN); assert_eq(sublogger->level(), LogLevel::WARN);
assert_eq(subsublogger->level(), LogLevel::WARN); assert_eq(subsublogger->level(), LogLevel::WARN);
END_TEST; END_TEST;

View File

@ -1,5 +1,5 @@
set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm) set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc) set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc-9)
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++) set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++-9)