Update .gitignore, CMakeLists.txt, CMakePresets.json, and 10 more files
This commit is contained in:
1 parent
88ff181bfd
commit
1f58c33bb7
13 files changed
+3380
No files matched your search
@@ -0,0 +1,4 @@
|
|||||||
|
/build*
|
||||||
|
/cpm_modules
|
||||||
|
.DS_Store
|
||||||
|
.cache*
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
|
||||||
|
|
||||||
|
# Standalone configuration of the spdlog logger from a JSON file.
|
||||||
|
project(
|
||||||
|
SPDLOGConfig
|
||||||
|
VERSION 1.0.0
|
||||||
|
LANGUAGES CXX
|
||||||
|
)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
|
||||||
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
|
||||||
|
|
||||||
|
if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
|
||||||
|
message(
|
||||||
|
FATAL_ERROR
|
||||||
|
"In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there."
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# ---- Dependencies via CPM ----
|
||||||
|
set(CPM_SOURCE_CACHE "${PROJECT_SOURCE_DIR}/../.cache-CPM-packages")
|
||||||
|
include(cmake/CPM.cmake)
|
||||||
|
|
||||||
|
CPMAddPackage("gh:TheLartians/PackageProject.cmake@1.13.0")
|
||||||
|
|
||||||
|
# ---- spdlog (shared, std::format backend like the main project) ----
|
||||||
|
CPMAddPackage(
|
||||||
|
NAME spdlog
|
||||||
|
GITHUB_REPOSITORY gabime/spdlog
|
||||||
|
GIT_TAG "v1.17.0"
|
||||||
|
OPTIONS
|
||||||
|
"SPDLOG_BUILD_EXAMPLE OFF"
|
||||||
|
"SPDLOG_BUILD_SHARED ON"
|
||||||
|
"SPDLOG_USE_STD_FORMAT ON"
|
||||||
|
"SPDLOG_INSTALL ON"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ---- reflect-cpp (JSON deserialization of the config file) ----
|
||||||
|
CPMAddPackage(
|
||||||
|
NAME reflectcpp
|
||||||
|
GITHUB_REPOSITORY getml/reflect-cpp
|
||||||
|
GIT_TAG "v0.24.0"
|
||||||
|
OPTIONS
|
||||||
|
"REFLECTCPP_BUILD_SHARED ON"
|
||||||
|
"CMAKE_POSITION_INDEPENDENT_CODE ON"
|
||||||
|
"REFLECTCPP_INSTALL ON"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(${PROJECT_NAME} INTERFACE)
|
||||||
|
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
|
||||||
|
|
||||||
|
target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_20)
|
||||||
|
|
||||||
|
target_link_libraries(${PROJECT_NAME} INTERFACE spdlog::spdlog reflectcpp::reflectcpp)
|
||||||
|
|
||||||
|
target_include_directories(
|
||||||
|
${PROJECT_NAME}
|
||||||
|
INTERFACE
|
||||||
|
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
||||||
|
$<INSTALL_INTERFACE:include/${PROJECT_NAME}-${PROJECT_VERSION}>
|
||||||
|
)
|
||||||
|
|
||||||
|
# ---- Tests (only when top-level and not disabled by consumers) ----
|
||||||
|
option(SPDLOGCONFIG_BUILD_TESTS "Build SPDLOGConfig's own test suite" ON)
|
||||||
|
if(PROJECT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR AND SPDLOGCONFIG_BUILD_TESTS)
|
||||||
|
enable_testing()
|
||||||
|
add_subdirectory(test)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# ---- Installable / CPM-compatible package ----
|
||||||
|
install(TARGETS ${PROJECT_NAME}
|
||||||
|
INCLUDES DESTINATION include/${PROJECT_NAME}-${PROJECT_VERSION}
|
||||||
|
)
|
||||||
|
|
||||||
|
packageProject(
|
||||||
|
NAME ${PROJECT_NAME}
|
||||||
|
VERSION ${PROJECT_VERSION}
|
||||||
|
NAMESPACE ${PROJECT_NAME}
|
||||||
|
BINARY_DIR ${PROJECT_BINARY_DIR}
|
||||||
|
INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include
|
||||||
|
INCLUDE_DESTINATION include/${PROJECT_NAME}-${PROJECT_VERSION}
|
||||||
|
VERSION_HEADER ""
|
||||||
|
COMPATIBILITY SameMajorVersion
|
||||||
|
DEPENDENCIES "spdlog 1.17.0;reflectcpp 0.24.0"
|
||||||
|
)
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"cmakeMinimumRequired": {
|
||||||
|
"major": 3,
|
||||||
|
"minor": 21,
|
||||||
|
"patch": 0
|
||||||
|
},
|
||||||
|
"configurePresets": [
|
||||||
|
{
|
||||||
|
"name": "linux-clang-21-debug",
|
||||||
|
"displayName": "Linux Clang 21 Debug",
|
||||||
|
"description": "Debug build on Linux using Clang 21",
|
||||||
|
"generator": "Unix Makefiles",
|
||||||
|
"binaryDir": "${sourceDir}/../build/spdlogConfig-linux-clang-21-debug",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Debug",
|
||||||
|
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
|
||||||
|
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../cmake-toolchain-files/clang_21_x86_64-pc-linux-gnu.cmake"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "linux-clang-21-release",
|
||||||
|
"displayName": "Linux Clang 21 Release",
|
||||||
|
"description": "Release build on Linux using Clang 21",
|
||||||
|
"generator": "Unix Makefiles",
|
||||||
|
"binaryDir": "${sourceDir}/../build/spdlogConfig-linux-clang-21-release",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Release",
|
||||||
|
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
|
||||||
|
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../cmake-toolchain-files/clang_21_x86_64-pc-linux-gnu.cmake"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "linux-clang-21-debug-asan",
|
||||||
|
"displayName": "Linux Clang 21 Debug (ASan)",
|
||||||
|
"description": "Debug build with AddressSanitizer on Linux using Clang 21",
|
||||||
|
"generator": "Unix Makefiles",
|
||||||
|
"binaryDir": "${sourceDir}/../build/spdlogConfig-linux-clang-21-debug-asan",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Debug",
|
||||||
|
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
|
||||||
|
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../cmake-toolchain-files/clang_21_x86_64-pc-linux-gnu.cmake",
|
||||||
|
"USE_SANITIZER": "Address"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buildPresets": [
|
||||||
|
{
|
||||||
|
"name": "linux-clang-21-debug",
|
||||||
|
"displayName": "Build Linux Clang 21 Debug",
|
||||||
|
"configurePreset": "linux-clang-21-debug",
|
||||||
|
"jobs": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "linux-clang-21-release",
|
||||||
|
"displayName": "Build Linux Clang 21 Release",
|
||||||
|
"configurePreset": "linux-clang-21-release",
|
||||||
|
"jobs": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "linux-clang-21-debug-asan",
|
||||||
|
"displayName": "Build Linux Clang 21 Debug (ASan)",
|
||||||
|
"configurePreset": "linux-clang-21-debug-asan",
|
||||||
|
"jobs": 8
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"testPresets": [
|
||||||
|
{
|
||||||
|
"name": "linux-clang-21-debug",
|
||||||
|
"displayName": "Run Linux Clang 21 Debug tests",
|
||||||
|
"configurePreset": "linux-clang-21-debug",
|
||||||
|
"output": {
|
||||||
|
"outputOnFailure": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "linux-clang-21-release",
|
||||||
|
"displayName": "Run Linux Clang 21 Release tests",
|
||||||
|
"configurePreset": "linux-clang-21-release",
|
||||||
|
"output": {
|
||||||
|
"outputOnFailure": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
distribute this software, either in source code form or as a compiled
|
||||||
|
binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
means.
|
||||||
|
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
of this software dedicate any and all copyright interest in the
|
||||||
|
software to the public domain. We make this dedication for the benefit
|
||||||
|
of the public at large and to the detriment of our heirs and
|
||||||
|
successors. We intend this dedication to be an overt act of
|
||||||
|
relinquishment in perpetuity of all present and future rights to this
|
||||||
|
software under copyright law.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
For more information, please refer to <https://unlicense.org>
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
# SPDLOGConfig
|
||||||
|
|
||||||
|
Eigenständiges, header-only CMake-Projekt zur Konfiguration von [spdlog](https://github.com/gabime/spdlog)
|
||||||
|
aus einer JSON-Datei. Es ist die aus [RelixTransferAI](../README.md) extrahierte Logging-Konfiguration
|
||||||
|
(Namespace `spdlogconfig`) und wird dort sowie in anderen Projekten
|
||||||
|
über [CPM.cmake](https://github.com/cpm-cmake/CPM.cmake) eingebunden.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Komplette Logger-Definition über eine JSON-Datei: globale und logger-spezifische Patterns,
|
||||||
|
Level, Sinks, async Logger (globaler und lokaler Thread-Pool, Overflow-Policies).
|
||||||
|
- Sinks: `console_st/mt`, `color_console_st/mt`, `basic_file_st/mt`, `daily_file_st/mt`,
|
||||||
|
`rotating_file_st/mt`, `null_sink_st/mt`, `syslog_st/mt` (POSIX), `msvc_sink_st/mt` (Windows),
|
||||||
|
optional `kafka_*`/`mongo_*` falls `librdkafka`/`mongocxx` im Include-Pfad liegen.
|
||||||
|
- Reload-Unterstützung: Level zur Laufzeit ändern, ohne Neustart (`setupFromReload()`).
|
||||||
|
- `LevelManager` als Singleton zur Laufzeit-Verwaltung einzelner Logger-Level.
|
||||||
|
- Installierbare CMake-Package (`find_package(SPDLOGConfig)`), CPM- und FetchContent-tauglich.
|
||||||
|
|
||||||
|
## Verwendung
|
||||||
|
|
||||||
|
### Einbindung über CPM
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
include(cmake/CPM.cmake)
|
||||||
|
|
||||||
|
CPMAddPackage(
|
||||||
|
NAME SPDLOGConfig
|
||||||
|
GITHUB_REPOSITORY <user>/SPDLOGConfig
|
||||||
|
GIT_TAG v1.0.0
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(myTarget PRIVATE SPDLOGConfig::SPDLOGConfig)
|
||||||
|
```
|
||||||
|
|
||||||
|
Lokal entwickeltes Checkout statt GitHub-Release (Source-Override vor dem `CPMAddPackage`-Aufruf):
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
set(CPM_SPDLOGConfig_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/../spdlogConfig")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verwendung im Code
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <spdlogconfig/SPDLOGConfig.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
spdlogconfig::setupFrom("config/logging.json");
|
||||||
|
// spdlogconfig::setupFromReload(); // später: Konfiguration neu einlesen
|
||||||
|
|
||||||
|
auto logger = spdlog::get("root");
|
||||||
|
logger->info("hello from SPDLOGConfig");
|
||||||
|
|
||||||
|
// Laufzeit-Level-Verwaltung:
|
||||||
|
spdlogconfig::LevelManager::instance().setLoggerLevel("root", "debug");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Fehler beim Öffnen/Parsen der Konfiguration werden von `setupFrom()` aktuell
|
||||||
|
**intern behandelt** (Ausgabe auf stdout/stderr, keine Propagation) — das ist
|
||||||
|
Legacy-Verhalten aus der ursprünglichen RelixTransfer-Headerdatei. Der Typ
|
||||||
|
`spdlogconfig::spdlogconfig_error` existiert trotzdem und wird intern für
|
||||||
|
teilweise Fehlerkontexte verwendet; auf Propagation sollte sich in neuen
|
||||||
|
Programmen nicht verlassen werden.
|
||||||
|
|
||||||
|
## Konfigurationsformat
|
||||||
|
|
||||||
|
Ein vollständiges Beispiel liegt unter [`test/full.json`](test/full.json). Das Schema
|
||||||
|
(`spdlog_conf_t`) wird mit [reflect-cpp](https://github.com/getml/reflect-cpp) deserialisiert;
|
||||||
|
unbekannte Felder, fehlende optionale Felder und Default-Werte werden vom Framework behandelt.
|
||||||
|
|
||||||
|
## Abhängigkeiten
|
||||||
|
|
||||||
|
| Paket | Version | Zweck |
|
||||||
|
|----------------|---------|------------------------------|
|
||||||
|
| spdlog | 1.17.0 | Logging (std::format-Modus) |
|
||||||
|
| reflect-cpp | 0.24.0 | JSON-Deserialisierung |
|
||||||
|
| doctest | 2.5.2 | Tests (nur test/) |
|
||||||
|
|
||||||
|
## Bauen und Testen
|
||||||
|
|
||||||
|
Voraussetzung: CMake ≥ 3.21, Clang 20/21 (oder MSVC/MSYS2-Clang), Ninja oder Make.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cmake --preset linux-clang-21-release
|
||||||
|
cmake --build --preset linux-clang-21-release
|
||||||
|
ctest --preset linux-clang-21-release
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lizenz
|
||||||
|
|
||||||
|
Public Domain (Unlicense) — siehe [LICENSE](LICENSE).
|
||||||
+1363
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,20 @@
|
|||||||
|
# tools.cmake — on-demand tool activation via `-DUSE_<TOOL>=<VALUE>`.
|
||||||
|
|
||||||
|
# only activate tools for top level project
|
||||||
|
if(NOT PROJECT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(${CMAKE_CURRENT_LIST_DIR}/CPM.cmake)
|
||||||
|
|
||||||
|
# enables sanitizers support using the `USE_SANITIZER` flag; available values are: Address,
|
||||||
|
# Memory, MemoryWithOrigins, Undefined, Thread, Leak, 'Address;Undefined'
|
||||||
|
if(USE_SANITIZER)
|
||||||
|
CPMAddPackage("gh:StableCoder/cmake-scripts#25.08")
|
||||||
|
include(${cmake-scripts_SOURCE_DIR}/sanitizers.cmake)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# enables CCACHE support through the USE_CCACHE flag; possible values are: YES, NO or equivalent
|
||||||
|
if(USE_CCACHE)
|
||||||
|
CPMAddPackage("gh:TheLartians/Ccache.cmake@1.2.4")
|
||||||
|
endif()
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,56 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace spdlogconfig {
|
||||||
|
// declaration section
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup error with a textual description.
|
||||||
|
*
|
||||||
|
* This exception type is used to report configuration and initialization
|
||||||
|
* failures within the spdlog configuration module.
|
||||||
|
*/
|
||||||
|
class spdlogconfig_error : public std::exception {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Construct an error from a C-string message.
|
||||||
|
*
|
||||||
|
* @param msg Error message to store.
|
||||||
|
* @throw std::bad_alloc If memory allocation for the message fails.
|
||||||
|
*/
|
||||||
|
spdlogconfig_error(const char *const msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an error from a std::string message.
|
||||||
|
*
|
||||||
|
* @param msg Error message to store (moved into this object).
|
||||||
|
* @throw std::bad_alloc If memory allocation for the message fails.
|
||||||
|
*/
|
||||||
|
spdlogconfig_error(std::string msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the stored error message.
|
||||||
|
*
|
||||||
|
* @return Null-terminated error message string.
|
||||||
|
*/
|
||||||
|
auto what() const noexcept -> const char * override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** Stored error message text. */
|
||||||
|
std::string msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
// implementation section
|
||||||
|
|
||||||
|
inline spdlogconfig_error::spdlogconfig_error(const char *const msg)
|
||||||
|
: msg(msg) {}
|
||||||
|
|
||||||
|
inline spdlogconfig_error::spdlogconfig_error(std::string msg)
|
||||||
|
: msg(std::move(msg)) {}
|
||||||
|
|
||||||
|
inline auto spdlogconfig_error::what() const noexcept -> const char * {
|
||||||
|
return msg.c_str();
|
||||||
|
}
|
||||||
|
} // namespace spdlogconfig
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
|
||||||
|
|
||||||
|
project(SPDLOGConfigTests LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
|
||||||
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
|
||||||
|
|
||||||
|
include(CTest)
|
||||||
|
|
||||||
|
# ---- Dependencies ----
|
||||||
|
CPMAddPackage("gh:doctest/doctest@2.5.2")
|
||||||
|
|
||||||
|
file(GLOB sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp)
|
||||||
|
|
||||||
|
add_executable(${PROJECT_NAME} ${sources})
|
||||||
|
set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 20)
|
||||||
|
|
||||||
|
target_link_libraries(${PROJECT_NAME} PRIVATE doctest::doctest SPDLOGConfig::SPDLOGConfig)
|
||||||
|
|
||||||
|
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||||
|
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wpedantic -Wextra)
|
||||||
|
elseif(MSVC)
|
||||||
|
target_compile_options(${PROJECT_NAME} PRIVATE /W4)
|
||||||
|
target_compile_definitions(${PROJECT_NAME} PRIVATE DOCTEST_CONFIG_USE_STD_HEADERS)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# ---- Register doctest test cases with CTest ----
|
||||||
|
include(${doctest_SOURCE_DIR}/scripts/cmake/doctest.cmake)
|
||||||
|
doctest_discover_tests(${PROJECT_NAME})
|
||||||
+159
@@ -0,0 +1,159 @@
|
|||||||
|
{
|
||||||
|
"global_pattern": "[%Y-%m-%dT%T%z] [%L] <%n>: %v",
|
||||||
|
"logger": [
|
||||||
|
{
|
||||||
|
"level": "trace",
|
||||||
|
"name": "root",
|
||||||
|
"sinks": [
|
||||||
|
"console_st",
|
||||||
|
"console_mt",
|
||||||
|
"color_console_st",
|
||||||
|
"color_console_mt",
|
||||||
|
"daily_out",
|
||||||
|
"daily_err",
|
||||||
|
"file_out",
|
||||||
|
"file_err",
|
||||||
|
"rotate_out",
|
||||||
|
"rotate_err",
|
||||||
|
"null_sink_st",
|
||||||
|
"null_sink_mt",
|
||||||
|
"syslog_st",
|
||||||
|
"syslog_mt",
|
||||||
|
"msvc_sink_st",
|
||||||
|
"msvc_sink_mt"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": "trace",
|
||||||
|
"name": "console",
|
||||||
|
"pattern": "succient",
|
||||||
|
"sinks": [
|
||||||
|
"console_st",
|
||||||
|
"console_mt"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": "trace",
|
||||||
|
"name": "global_async",
|
||||||
|
"pattern": "succient",
|
||||||
|
"sinks": [
|
||||||
|
"console_mt"
|
||||||
|
],
|
||||||
|
"type": "async"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": "trace",
|
||||||
|
"name": "local_async",
|
||||||
|
"overflow_policy": "overrun_oldest",
|
||||||
|
"pattern": "succient",
|
||||||
|
"sinks": [
|
||||||
|
"console_mt"
|
||||||
|
],
|
||||||
|
"thread_pool": "tp",
|
||||||
|
"type": "async"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pattern": [
|
||||||
|
{
|
||||||
|
"name": "succient",
|
||||||
|
"value": "%c-%L: %v"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sink": [
|
||||||
|
{
|
||||||
|
"name": "console_st",
|
||||||
|
"type": "stdout_sink_st"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "console_mt",
|
||||||
|
"type": "stdout_sink_mt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "color_console_st",
|
||||||
|
"type": "stdout_color_sink_st"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "color_console_mt",
|
||||||
|
"type": "stdout_color_sink_mt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"create_parent_dir": true,
|
||||||
|
"filename": "log/spdlog_setup.log",
|
||||||
|
"level": "info",
|
||||||
|
"name": "file_out",
|
||||||
|
"type": "basic_file_sink_st"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "log/spdlog_setup_err.log",
|
||||||
|
"level": "err",
|
||||||
|
"name": "file_err",
|
||||||
|
"truncate": true,
|
||||||
|
"type": "basic_file_sink_mt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"base_filename": "log/rotate_spdlog_setup.log",
|
||||||
|
"level": "info",
|
||||||
|
"max_files": 10,
|
||||||
|
"max_size": "1M",
|
||||||
|
"name": "rotate_out",
|
||||||
|
"type": "rotating_file_sink_st"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"base_filename": "log/rotate_spdlog_setup_err.log",
|
||||||
|
"level": "err",
|
||||||
|
"max_files": 10,
|
||||||
|
"max_size": "1M",
|
||||||
|
"name": "rotate_err",
|
||||||
|
"type": "rotating_file_sink_mt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"base_filename": "log/daily_spdlog_setup.log",
|
||||||
|
"level": "info",
|
||||||
|
"name": "daily_out",
|
||||||
|
"rotation_time": "17:30",
|
||||||
|
"type": "daily_file_sink_st"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"base_filename": "log/daily_spdlog_setup_err.log",
|
||||||
|
"level": "err",
|
||||||
|
"name": "daily_err",
|
||||||
|
"rotation_time": "17:30",
|
||||||
|
"type": "daily_file_sink_mt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "null_sink_st",
|
||||||
|
"type": "null_sink_st"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "null_sink_mt",
|
||||||
|
"type": "null_sink_mt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "syslog_st",
|
||||||
|
"type": "syslog_sink_st"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "syslog_mt",
|
||||||
|
"type": "syslog_sink_mt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "msvc_sink_st",
|
||||||
|
"type": "msvc_sink_st"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "msvc_sink_mt",
|
||||||
|
"type": "msvc_sink_mt"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thread_pools": [
|
||||||
|
{
|
||||||
|
"name": "tp",
|
||||||
|
"num_threads": 2,
|
||||||
|
"queue_size": 4096
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"global_thread_pool": {
|
||||||
|
"num_threads": 1,
|
||||||
|
"queue_size": 8192
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||||
|
|
||||||
|
#include <doctest/doctest.h>
|
||||||
@@ -0,0 +1,344 @@
|
|||||||
|
#include "spdlogconfig/SPDLOGConfig.h"
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
|
#include <doctest/doctest.h>
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Test helpers
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the path to `full.json` relative to this source file.
|
||||||
|
*
|
||||||
|
* Layout assumption:
|
||||||
|
* - This file lives in: .../test/source/spdlogconfig.cpp
|
||||||
|
* - `full.json` lives in: .../test/full.json
|
||||||
|
*
|
||||||
|
* @return Absolute/relative path string to `full.json`.
|
||||||
|
*/
|
||||||
|
inline std::string full_json_path() {
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
static const fs::path base =
|
||||||
|
fs::path(__FILE__).parent_path().parent_path(); // .../test
|
||||||
|
return (base / "full.json").string();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common setup for tests that require a fully initialized spdlog configuration.
|
||||||
|
*
|
||||||
|
* Drops all existing loggers first to keep test cases independent.
|
||||||
|
*/
|
||||||
|
inline void setup_spdlog_from_full_json() {
|
||||||
|
spdlog::drop_all();
|
||||||
|
spdlogconfig::setupFrom(full_json_path());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Tests for full.json
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEST_SUITE("Tests for full.json") {
|
||||||
|
|
||||||
|
TEST_CASE("full Setup") {
|
||||||
|
// Ensure this test is independent from others.
|
||||||
|
spdlog::drop_all();
|
||||||
|
|
||||||
|
SUBCASE("No loggers should be created yet.") {
|
||||||
|
CHECK(spdlog::get("root") == nullptr);
|
||||||
|
CHECK(spdlog::get("console") == nullptr);
|
||||||
|
CHECK(spdlog::get("global_async") == nullptr);
|
||||||
|
CHECK(spdlog::get("local_async") == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now load configuration.
|
||||||
|
spdlogconfig::setupFrom(full_json_path());
|
||||||
|
|
||||||
|
SUBCASE("All loggers should be created.") {
|
||||||
|
auto root = spdlog::get("root");
|
||||||
|
auto console = spdlog::get("console");
|
||||||
|
auto global_async = spdlog::get("global_async");
|
||||||
|
auto local_async = spdlog::get("local_async");
|
||||||
|
|
||||||
|
CHECK(root != nullptr);
|
||||||
|
CHECK(console != nullptr);
|
||||||
|
CHECK(global_async != nullptr);
|
||||||
|
CHECK(local_async != nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("full Root logger") {
|
||||||
|
setup_spdlog_from_full_json();
|
||||||
|
|
||||||
|
auto logger = spdlog::get("root");
|
||||||
|
REQUIRE(logger != nullptr); // avoid segfault
|
||||||
|
|
||||||
|
SUBCASE("Root logger should have expected sinks") {
|
||||||
|
// JSON defines 16 sinks, but 2 are platform-specific (syslog or msvc)
|
||||||
|
// and may not be created -> effectively 14 sinks.
|
||||||
|
CHECK(logger->sinks().size() == 14);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("Root logger's level should be trace.") {
|
||||||
|
CHECK(logger->level() == spdlog::level::level_enum::trace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Console logger") {
|
||||||
|
setup_spdlog_from_full_json();
|
||||||
|
|
||||||
|
auto logger_console = spdlog::get("console");
|
||||||
|
REQUIRE(logger_console != nullptr);
|
||||||
|
|
||||||
|
SUBCASE("Console logger should have two sinks.") {
|
||||||
|
CHECK(logger_console->sinks().size() == 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Global async logger") {
|
||||||
|
setup_spdlog_from_full_json();
|
||||||
|
|
||||||
|
auto logger_global_async = spdlog::get("global_async");
|
||||||
|
REQUIRE(logger_global_async != nullptr);
|
||||||
|
|
||||||
|
SUBCASE("Global async logger should have only one sink.") {
|
||||||
|
CHECK(logger_global_async->sinks().size() == 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Local async logger") {
|
||||||
|
setup_spdlog_from_full_json();
|
||||||
|
|
||||||
|
auto logger_local_async = spdlog::get("local_async");
|
||||||
|
REQUIRE(logger_local_async != nullptr);
|
||||||
|
|
||||||
|
SUBCASE("Local async logger should have only one sink.") {
|
||||||
|
CHECK(logger_local_async->sinks().size() == 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Additional tests: root logger sinks / file sinks / platform sinks
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEST_CASE("Root logger sink configuration") {
|
||||||
|
setup_spdlog_from_full_json();
|
||||||
|
|
||||||
|
auto logger = spdlog::get("root");
|
||||||
|
REQUIRE(logger != nullptr);
|
||||||
|
|
||||||
|
auto &sinks = logger->sinks();
|
||||||
|
|
||||||
|
SUBCASE("basic_file_sinks have correct filenames and levels") {
|
||||||
|
std::shared_ptr<spdlog::sinks::basic_file_sink_st> file_out_sink;
|
||||||
|
std::shared_ptr<spdlog::sinks::basic_file_sink_mt> file_err_sink;
|
||||||
|
|
||||||
|
for (auto &s : sinks) {
|
||||||
|
auto as_st =
|
||||||
|
std::dynamic_pointer_cast<spdlog::sinks::basic_file_sink_st>(s);
|
||||||
|
if (as_st &&
|
||||||
|
as_st->filename() == SPDLOG_FILENAME_T("log/spdlog_setup.log")) {
|
||||||
|
file_out_sink = as_st;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto as_mt =
|
||||||
|
std::dynamic_pointer_cast<spdlog::sinks::basic_file_sink_mt>(s);
|
||||||
|
if (as_mt && as_mt->filename() ==
|
||||||
|
SPDLOG_FILENAME_T("log/spdlog_setup_err.log")) {
|
||||||
|
file_err_sink = as_mt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(file_out_sink != nullptr);
|
||||||
|
CHECK(file_err_sink != nullptr);
|
||||||
|
|
||||||
|
if (file_out_sink) {
|
||||||
|
CHECK(file_out_sink->level() == spdlog::level::info);
|
||||||
|
}
|
||||||
|
if (file_err_sink) {
|
||||||
|
CHECK(file_err_sink->level() == spdlog::level::err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create_parent_dir=true -> "log" should exist
|
||||||
|
CHECK(std::filesystem::exists("log"));
|
||||||
|
CHECK(std::filesystem::is_directory("log"));
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("rotating_file_sinks have correct configuration") {
|
||||||
|
std::shared_ptr<spdlog::sinks::rotating_file_sink_st> rotate_out_sink;
|
||||||
|
std::shared_ptr<spdlog::sinks::rotating_file_sink_mt> rotate_err_sink;
|
||||||
|
|
||||||
|
for (auto &s : sinks) {
|
||||||
|
auto as_st =
|
||||||
|
std::dynamic_pointer_cast<spdlog::sinks::rotating_file_sink_st>(s);
|
||||||
|
if (as_st && as_st->filename() ==
|
||||||
|
SPDLOG_FILENAME_T("log/rotate_spdlog_setup.log")) {
|
||||||
|
rotate_out_sink = as_st;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto as_mt =
|
||||||
|
std::dynamic_pointer_cast<spdlog::sinks::rotating_file_sink_mt>(s);
|
||||||
|
if (as_mt && as_mt->filename() ==
|
||||||
|
SPDLOG_FILENAME_T("log/rotate_spdlog_setup_err.log")) {
|
||||||
|
rotate_err_sink = as_mt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(rotate_out_sink != nullptr);
|
||||||
|
CHECK(rotate_err_sink != nullptr);
|
||||||
|
|
||||||
|
const auto expected_size =
|
||||||
|
spdlogconfig::utils::GetMaxSizeInt("1M"); // from JSON
|
||||||
|
const std::size_t expected_files = 10;
|
||||||
|
|
||||||
|
if (rotate_out_sink) {
|
||||||
|
CHECK(rotate_out_sink->get_max_size() == expected_size);
|
||||||
|
CHECK(rotate_out_sink->get_max_files() == expected_files);
|
||||||
|
CHECK(rotate_out_sink->level() == spdlog::level::info);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rotate_err_sink) {
|
||||||
|
CHECK(rotate_err_sink->get_max_size() == expected_size);
|
||||||
|
CHECK(rotate_err_sink->get_max_files() == expected_files);
|
||||||
|
CHECK(rotate_err_sink->level() == spdlog::level::err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("Root logger has daily-file sinks") {
|
||||||
|
bool has_daily_st = false;
|
||||||
|
bool has_daily_mt = false;
|
||||||
|
|
||||||
|
for (auto &s : sinks) {
|
||||||
|
if (std::dynamic_pointer_cast<spdlog::sinks::daily_file_sink_st>(s)) {
|
||||||
|
has_daily_st = true;
|
||||||
|
}
|
||||||
|
if (std::dynamic_pointer_cast<spdlog::sinks::daily_file_sink_mt>(s)) {
|
||||||
|
has_daily_mt = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(has_daily_st);
|
||||||
|
CHECK(has_daily_mt);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("Null sinks exist") {
|
||||||
|
bool has_null_st = false;
|
||||||
|
bool has_null_mt = false;
|
||||||
|
|
||||||
|
for (auto &s : sinks) {
|
||||||
|
if (std::dynamic_pointer_cast<spdlog::sinks::null_sink_st>(s)) {
|
||||||
|
has_null_st = true;
|
||||||
|
}
|
||||||
|
if (std::dynamic_pointer_cast<spdlog::sinks::null_sink_mt>(s)) {
|
||||||
|
has_null_mt = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(has_null_st);
|
||||||
|
CHECK(has_null_mt);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("Platform-specific sinks exist") {
|
||||||
|
#ifndef _WIN32
|
||||||
|
bool has_syslog_st = false;
|
||||||
|
bool has_syslog_mt = false;
|
||||||
|
|
||||||
|
for (auto &s : sinks) {
|
||||||
|
if (dynamic_cast<spdlog::sinks::syslog_sink_st *>(s.get())) {
|
||||||
|
has_syslog_st = true;
|
||||||
|
}
|
||||||
|
if (dynamic_cast<spdlog::sinks::syslog_sink_mt *>(s.get())) {
|
||||||
|
has_syslog_mt = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(has_syslog_st);
|
||||||
|
CHECK(has_syslog_mt);
|
||||||
|
#else
|
||||||
|
bool has_msvc_mt = false;
|
||||||
|
|
||||||
|
for (auto &s : sinks) {
|
||||||
|
if (dynamic_cast<spdlog::sinks::msvc_sink_mt *>(s.get())) {
|
||||||
|
has_msvc_mt = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(has_msvc_mt);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("Console sinks inherit the logger level trace") {
|
||||||
|
for (auto &s : sinks) {
|
||||||
|
if (std::dynamic_pointer_cast<spdlog::sinks::stdout_sink_st>(s) ||
|
||||||
|
std::dynamic_pointer_cast<spdlog::sinks::stdout_sink_mt>(s) ||
|
||||||
|
std::dynamic_pointer_cast<spdlog::sinks::stdout_color_sink_st>(s) ||
|
||||||
|
std::dynamic_pointer_cast<spdlog::sinks::stdout_color_sink_mt>(s)) {
|
||||||
|
CHECK(s->level() == spdlog::level::trace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Additional tests: async logger & thread pool
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEST_CASE("Async logger configuration and thread pool") {
|
||||||
|
setup_spdlog_from_full_json();
|
||||||
|
|
||||||
|
SUBCASE("Global thread pool is initialized") {
|
||||||
|
auto tp = spdlog::thread_pool();
|
||||||
|
CHECK(tp != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("global_async and local_async are async_logger") {
|
||||||
|
auto global_async = spdlog::get("global_async");
|
||||||
|
auto local_async = spdlog::get("local_async");
|
||||||
|
|
||||||
|
REQUIRE(global_async != nullptr);
|
||||||
|
REQUIRE(local_async != nullptr);
|
||||||
|
|
||||||
|
auto as_global_async =
|
||||||
|
std::dynamic_pointer_cast<spdlog::async_logger>(global_async);
|
||||||
|
auto as_local_async =
|
||||||
|
std::dynamic_pointer_cast<spdlog::async_logger>(local_async);
|
||||||
|
|
||||||
|
CHECK(as_global_async != nullptr);
|
||||||
|
CHECK(as_local_async != nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Additional tests: utility functions
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEST_SUITE("spdlogconfig::utils") {
|
||||||
|
using spdlogconfig::utils::GetMaxSizeInt;
|
||||||
|
|
||||||
|
TEST_CASE("GetMaxSizeInt without suffix") {
|
||||||
|
CHECK(GetMaxSizeInt("0") == static_cast<std::size_t>(0));
|
||||||
|
CHECK(GetMaxSizeInt("42") == static_cast<std::size_t>(42));
|
||||||
|
CHECK(GetMaxSizeInt("10485760") == static_cast<std::size_t>(10485760));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("GetMaxSizeInt with K/M/G/T suffix") {
|
||||||
|
CHECK(GetMaxSizeInt("1K") == static_cast<std::size_t>(1024));
|
||||||
|
CHECK(GetMaxSizeInt("2M") == static_cast<std::size_t>(2 * 1024 * 1024));
|
||||||
|
CHECK(GetMaxSizeInt("3G") ==
|
||||||
|
static_cast<std::size_t>(3ull * 1024 * 1024 * 1024));
|
||||||
|
CHECK(GetMaxSizeInt("1T") ==
|
||||||
|
static_cast<std::size_t>(1ull * 1024 * 1024 * 1024 * 1024));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("GetMaxSizeInt ignores whitespace around the number") {
|
||||||
|
CHECK(GetMaxSizeInt(" 1K") == static_cast<std::size_t>(1024));
|
||||||
|
CHECK(GetMaxSizeInt("2M ") == static_cast<std::size_t>(2 * 1024 * 1024));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user