Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,51 @@ jobs:
run: cmake --install build --prefix install
- name: Test
run: ctest --test-dir build --output-on-failure
- name: Install optional JSON configuration frontend dependency
if: matrix.std == 11
run: sudo apt-get update && sudo apt-get install -y nlohmann-json3-dev
- name: Configure optional JSON configuration frontend
if: matrix.std == 11
run: >-
cmake -S . -B build-json-config
-DLOGIT_CPP_BUILD_TESTS=ON
-DLOGIT_WITH_JSON_CONFIG=ON
-DLOGIT_WITH_SYSLOG=OFF
-DLOGIT_WITH_WIN_EVENT_LOG=OFF
-DCMAKE_CXX_STANDARD=11
- name: Build optional JSON configuration frontend test
if: matrix.std == 11
run: cmake --build build-json-config --target configuration_json_test
- name: Test optional JSON configuration frontend
if: matrix.std == 11
run: ctest --test-dir build-json-config --output-on-failure -R '^configuration_json_test$'
- name: Install optional JSON configuration package
if: matrix.std == 11
run: cmake --install build-json-config --prefix install-json-config
- name: Configure optional JSON installed-package consumer
if: matrix.std == 11
run: >-
cmake -S tests/install_consumer -B build-json-consumer
-DCMAKE_PREFIX_PATH=${{ github.workspace }}/install-json-config
-DLOGIT_CONSUMER_REQUIRE_JSON_CONFIG=ON
-DCMAKE_CXX_STANDARD=11
- name: Build and run optional JSON installed-package consumer
if: matrix.std == 11
run: |
cmake --build build-json-consumer
./build-json-consumer/install_consumer
- name: Configure optional JSON pkg-config consumer
if: matrix.std == 11
env:
PKG_CONFIG_PATH: ${{ github.workspace }}/install-json-config/share/pkgconfig:${{ github.workspace }}/install-json-config/lib/pkgconfig
run: >-
cmake -S tests/pkgconfig_consumer -B build-json-pkgconfig
-DLOGIT_CONSUMER_REQUIRE_JSON_CONFIG=ON
- name: Build and run optional JSON pkg-config consumer
if: matrix.std == 11
run: |
cmake --build build-json-pkgconfig
./build-json-pkgconfig/pkgconfig_consumer
- name: Configure benchmarks
# if: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/stable') }}
run: cmake -S . -B build-bench -DLOGIT_BENCH_ENABLE=ON -DLOGIT_BENCH_WITH_SPDLOG=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD=${{ matrix.std }} -DLOGIT_WITH_SYSLOG=ON -DLOGIT_WITH_WIN_EVENT_LOG=OFF
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ All notable changes to this project will be documented in this file.
- Added MDBX runtime factory support when the optional MDBX backend is enabled.
- Added optional typed MDBX `on_error` binding support and build-feature-aware
validation for MDBX payload compression.
- Added an optional nlohmann::json frontend that maps JSON text or values into
the parser-neutral configuration model without performing runtime side effects.

### Fixed

Expand Down
39 changes: 39 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ option(LOGIT_WITH_OTLP "Enable OTLP/HTTP log export via optional kurlyk dependen
option(LOGIT_WITH_PROMETHEUS "Enable Prometheus text payload support" OFF)
option(LOGIT_WITH_PROMETHEUS_SERVER "Enable Prometheus HTTP server backend" OFF)
option(LOGIT_WITH_MDBX "Enable MDBX structured log storage backend" OFF)
option(LOGIT_WITH_JSON_CONFIG "Enable the optional nlohmann::json configuration frontend" OFF)
option(LOGIT_USE_SUBMODULES "Allow bundled optional dependency fallback" OFF)
option(LOGIT_WITH_SYSLOG "Enable POSIX syslog backend" ON)
option(LOGIT_WITH_WIN_EVENT_LOG "Enable Windows Event Log backend" ON)
Expand Down Expand Up @@ -103,6 +104,25 @@ if(LOGIT_WITH_FMT)
target_link_libraries(log-it-cpp INTERFACE fmt::fmt)
endif()

# ---------- JSON configuration frontend ----------
if(LOGIT_WITH_JSON_CONFIG)
if(NOT TARGET nlohmann_json::nlohmann_json)
find_package(nlohmann_json 3.11 QUIET CONFIG)
endif()
if(NOT TARGET nlohmann_json::nlohmann_json AND LOGIT_USE_SUBMODULES)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/external/kurlyk/external/json/CMakeLists.txt")
set(JSON_BuildTests OFF CACHE BOOL "Disable bundled nlohmann/json tests" FORCE)
add_subdirectory(external/kurlyk/external/json EXCLUDE_FROM_ALL)
endif()
endif()
if(NOT TARGET nlohmann_json::nlohmann_json)
message(FATAL_ERROR
"nlohmann_json not found. Install nlohmann_json or enable LOGIT_USE_SUBMODULES with the kurlyk submodule present.")
endif()
target_compile_definitions(log-it-cpp INTERFACE LOGIT_WITH_JSON_CONFIG=1)
target_link_libraries(log-it-cpp INTERFACE nlohmann_json::nlohmann_json)
endif()

# ---------- OTLP/HTTP (kurlyk) ----------
if(LOGIT_WITH_OTLP)
if(EMSCRIPTEN)
Expand Down Expand Up @@ -329,6 +349,18 @@ if(LOGIT_WITH_FMT AND TARGET fmt::fmt)
endif()
endif()

if(LOGIT_WITH_JSON_CONFIG AND TARGET nlohmann_json::nlohmann_json)
get_target_property(_nlohmann_json_imported nlohmann_json::nlohmann_json IMPORTED)
if(NOT _nlohmann_json_imported)
set(_logit_install_export_supported OFF)
install(CODE [[
message(FATAL_ERROR
"log-it-cpp: Installing with bundled nlohmann_json is not supported. "
"Install nlohmann_json separately and use find_package(nlohmann_json), or disable LOGIT_WITH_JSON_CONFIG.")
]])
endif()
endif()

if(LOGIT_WITH_PROMETHEUS_SERVER)
set(_logit_install_export_supported OFF)
install(CODE [[
Expand Down Expand Up @@ -393,6 +425,13 @@ write_basic_package_version_file(
INSTALL_DESTINATION lib/cmake/log-it-cpp
)

set(LOGIT_PC_REQUIRES "time-shield >= 2.0.0, time-shield < 2.1.0")
set(LOGIT_PC_CFLAGS "")
if(LOGIT_WITH_JSON_CONFIG)
string(APPEND LOGIT_PC_REQUIRES ", nlohmann_json >= 3.11")
string(APPEND LOGIT_PC_CFLAGS " -DLOGIT_WITH_JSON_CONFIG=1")
endif()

configure_file(
cmake/log-it-cpp.pc.in
"${CMAKE_CURRENT_BINARY_DIR}/log-it-cpp.pc"
Expand Down
1 change: 1 addition & 0 deletions README-RU.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ scope-замер.
- [`docs/quickstart.md`](docs/quickstart.md) — краткий старт и карта документации.
- [`docs/installation.md`](docs/installation.md) — установка через CMake, vendored и installed package сценарии.
- [`docs/backends.md`](docs/backends.md) — матрица бэкендов, платформ, зависимостей и packaging.
- [`docs/configuration-loading-RU.md`](docs/configuration-loading-RU.md) — parser-neutral pipeline конфигурации и optional JSON frontend.
- [`docs/benchmarks.md`](docs/benchmarks.md) — методика benchmark и исторический snapshot.
- [`docs/comparison-RU.md`](docs/comparison-RU.md) — сравнение со spdlog, Quill, Boost.Log, glog и IceCream-Cpp.

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ Detailed guides and documentation map:
- [`docs/quickstart.md`](docs/quickstart.md) — quick start and documentation map.
- [`docs/installation.md`](docs/installation.md) — CMake, vendored, installed-package, and package-manager setup.
- [`docs/backends.md`](docs/backends.md) — backend, platform, dependency, and packaging matrix.
- [`docs/configuration-loading.md`](docs/configuration-loading.md) — parser-neutral configuration pipeline and optional JSON frontend.
- [`docs/benchmarks.md`](docs/benchmarks.md) — benchmark methodology and historical snapshot.
- [`docs/comparison.md`](docs/comparison.md) — comparison with spdlog, Quill, Boost.Log, glog, and IceCream-Cpp.

Expand Down
4 changes: 2 additions & 2 deletions cmake/log-it-cpp.pc.in
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ includedir=${prefix}/include
Name: log-it-cpp
Description: LogIt C++ logging library
Version: @PROJECT_VERSION@
Requires: time-shield >= 2.0.0, time-shield < 2.1.0
Cflags: -I${includedir}/logit_cpp
Requires: @LOGIT_PC_REQUIRES@
Cflags: -I${includedir}/logit_cpp@LOGIT_PC_CFLAGS@
Libs:
4 changes: 4 additions & 0 deletions cmake/log-it-cppConfig.cmake.in
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ if(@LOGIT_WITH_FMT@ AND NOT TARGET fmt::fmt)
find_dependency(fmt CONFIG)
endif()

if(@LOGIT_WITH_JSON_CONFIG@ AND NOT TARGET nlohmann_json::nlohmann_json)
find_dependency(nlohmann_json 3.11 CONFIG)
endif()

if(@LOGIT_WITH_OTLP@ AND NOT TARGET kurlyk)
find_dependency(kurlyk CONFIG)
endif()
Expand Down
74 changes: 74 additions & 0 deletions docs/configuration-loading-RU.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Загрузка конфигурации

Конфигурация LogIt++ разделена на явные этапы:

```text
JSON value или text
-> ConfigurationDocument
-> validation и materialization
-> разрешение programmatic bindings
-> создание runtime logger'ов
```

Основная semantic model и runtime-слои не зависят от JSON-библиотеки. JSON
frontend является optional и использует `nlohmann::json` только при включённой
опции `LOGIT_WITH_JSON_CONFIG=ON`.

## Включение JSON frontend

Установите `nlohmann_json` либо разрешите fallback из дерева исходников:

```bash
cmake -S . -B build \
-DLOGIT_WITH_JSON_CONFIG=ON \
-DLOGIT_USE_SUBMODULES=ON
```

Установленный пакет с этой опцией использует
`find_dependency(nlohmann_json 3.11 CONFIG)`. Установка сборки с bundled
fallback намеренно запрещена: для экспортируемого пакета установите
`nlohmann_json` отдельно.

## Разбор JSON

При включённой опции `<logit.hpp>` предоставляет overload'ы для JSON-текста и
уже разобранного значения `nlohmann::json`:

```cpp
#include <logit.hpp>

const std::string text = R"json({
"schema_version": 1,
"loggers": [{
"id": "application-file",
"type": "file",
"backend": {
"directory": "logs",
"async": true,
"use_dedicated_executor": true,
"queue_capacity": 8192,
"queue_policy": "block"
}
}]
})json";

logit::ConfigurationDocument document;
logit::ConfigurationDiagnostics diagnostics;
if (!logit::parse_configuration_json(text, document, diagnostics)) {
// Обработайте diagnostics.items().
}
```

Разбор не создаёт logger'ы и заменяет `document` только после полного успеха.
Frontend возвращает path-aware diagnostics для JSON syntax и shape/conversion
ошибок; синтаксические ошибки используют категорию
`configuration_parse_failed`. Backend schema validation выполняется отдельной
core-фазой: unknown fields, backend types, enum/named values, defaults и
feature availability могут быть диагностированы `materialize_configuration()`,
а не JSON conversion. Floating-point значения JSON отклоняются, поскольку в
schema v1 предусмотрены только signed и unsigned integer значения.

Следующие этапы вызываются явно через `materialize_configuration()` и
`instantiate_configuration()`. Frontend не читает файлы, не вводит собственные
defaults, не разрешает callback'и и не создаёт logger'ы. Properties input и hot
reload пока не реализованы.
79 changes: 79 additions & 0 deletions docs/configuration-loading.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Configuration loading

LogIt++ configuration is split into explicit phases:

```text
JSON value or text
-> ConfigurationDocument
-> validation and materialization
-> programmatic binding resolution
-> runtime logger construction
```

The core semantic model and runtime layers do not depend on a JSON library.
The JSON frontend is optional and uses `nlohmann::json` only when
`LOGIT_WITH_JSON_CONFIG=ON` is selected.

## Enabling the JSON frontend

Install `nlohmann_json` or allow the source-tree fallback:

```bash
cmake -S . -B build \
-DLOGIT_WITH_JSON_CONFIG=ON \
-DLOGIT_USE_SUBMODULES=ON
```

Installed packages configured with this option use
`find_dependency(nlohmann_json 3.11 CONFIG)`. Installing a build that uses the
bundled fallback is intentionally rejected; install `nlohmann_json` separately
for an exportable package.

## Parsing

With the option enabled, `<logit.hpp>` exposes overloads for JSON text and an
already parsed `nlohmann::json` value:

```cpp
#include <logit.hpp>

const std::string text = R"json({
"schema_version": 1,
"loggers": [{
"id": "application-file",
"type": "file",
"strategy": {
"level": "info",
"formatter": {"pattern": "%v", "json": false}
},
"backend": {
"directory": "logs",
"async": true,
"use_dedicated_executor": true,
"queue_capacity": 8192,
"queue_policy": "block"
}
}]
})json";

logit::ConfigurationDocument document;
logit::ConfigurationDiagnostics diagnostics;
if (!logit::parse_configuration_json(text, document, diagnostics)) {
// Inspect diagnostics.items().
}
```

Parsing has no logger side effects and replaces `document` only after a
complete successful conversion. The frontend reports JSON syntax and
shape/conversion errors with path-aware diagnostics and uses
`configuration_parse_failed` for syntax errors. Backend schema validation is a
later core phase: backend unknown fields, backend types, enum/named values,
defaults, and feature availability may be diagnosed by
`materialize_configuration()` rather than by the JSON conversion itself. JSON
floating-point values are rejected because schema v1 has only signed and
unsigned integer numeric values.

Call `materialize_configuration()` and `instantiate_configuration()` explicitly
for the later phases. The frontend does not read files, apply frontend-specific
defaults, resolve callbacks, or construct loggers. Properties input and hot
reload remain outside the current implementation.
6 changes: 4 additions & 2 deletions docs/future-plans.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,10 @@ Legend:
failures and are covered by tests.
- [ ] **Configuration loading** — design a versioned JSON/properties mapping to
the existing backend configuration. The proposed contract is recorded in
[ADR 0009](adr/0009-configuration-loading-contract.md). Treat file
watching/hot reload as a follow-up, not part of the first configuration API.
[ADR 0009](adr/0009-configuration-loading-contract.md). The parser-neutral
model, built-in materialization/runtime layers, and optional JSON text/value
frontend are delivered; file-loading convenience and a properties frontend
remain follow-ups. File watching/hot reload is not part of this API.
- [ ] **Extended filtering** — evaluate source/file, message, tag/MDC, and
range filters; define their cost and ordering before adding public API.
- [x] **Benchmark follow-up** — formatted and passthrough public-macro scenarios,
Expand Down
3 changes: 3 additions & 0 deletions include/logit_cpp/logit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
#include "logit/loggers.hpp"
#include "logit/configuration_runtime.hpp"
#include "logit/configuration_runtime_builtins.hpp"
#ifdef LOGIT_WITH_JSON_CONFIG
#include "logit/configuration_json.hpp"
#endif
#include "logit/Logger.hpp"
#include "logit/log_macros.hpp"

Expand Down
28 changes: 27 additions & 1 deletion include/logit_cpp/logit/configuration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ namespace logit {
BackendValidationFailed,
MaterializationFailed,
BindingResolutionFailed,
InstantiationFailed
InstantiationFailed,
ConfigurationParseFailed
};

inline const char* configuration_error_code_name(ConfigurationErrorCode code) {
Expand All @@ -51,6 +52,7 @@ namespace logit {
case ConfigurationErrorCode::MaterializationFailed: return "materialization_failed";
case ConfigurationErrorCode::BindingResolutionFailed: return "binding_resolution_failed";
case ConfigurationErrorCode::InstantiationFailed: return "instantiation_failed";
case ConfigurationErrorCode::ConfigurationParseFailed: return "configuration_parse_failed";
}
return "unknown_configuration_error";
}
Expand Down Expand Up @@ -309,6 +311,30 @@ namespace logit {
type == "prometheus_payload";
}

/// \brief Return the schema-v1 integer representation for a backend field.
///
/// Text frontends use this shared mapping because JSON/properties integers
/// do not carry the signed/unsigned C++ type required by ConfigurationValue.
inline bool configuration_backend_field_uses_signed_integer(
const std::string& type, const std::string& field) {
if (type == "file") {
return field == "auto_delete_days" || field == "compress_level";
}
if (type == "unique_file") return field == "auto_delete_days";
if (type == "memory") return field == "max_age_ms";
if (type == "mdbx") {
return field == "flush_interval_ms" ||
field == "payload_compression_level";
}
if (type == "otlp_http") {
return field == "export_interval_ms" ||
field == "request_timeout_sec" ||
field == "retry_attempts" || field == "retry_delay_ms" ||
field == "compression_level";
}
return false;
}

inline bool configuration_log_level_is_valid(LogLevel level) {
switch (level) {
case LogLevel::LOG_LVL_TRACE:
Expand Down
Loading
Loading