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