Skip to content
Merged
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ All notable changes to this project will be documented in this file.
validation, and side-effect-free materialization contract for Configuration
Loading v1. JSON/properties parsing and logger instantiation remain separate
follow-up layers.
- Added side-effect-free built-in configuration validation and default
materialization for file, memory, MDBX, OTLP, and Prometheus plans.

### Fixed

Expand Down
1 change: 1 addition & 0 deletions include/logit_cpp/logit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "logit/config.hpp"
#include "logit/enums.hpp"
#include "logit/configuration.hpp"
#include "logit/configuration_builtins.hpp"
#include "logit/utils.hpp"
#include "logit/formatter.hpp"
#include "logit/loggers.hpp"
Expand Down
46 changes: 35 additions & 11 deletions include/logit_cpp/logit/configuration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,17 @@ namespace logit {
return false;
}

void append_rebased(const ConfigurationDiagnostics& source,
const std::string& prefix) {
for (std::vector<ConfigurationDiagnostic>::const_iterator it = source.m_items.begin();
it != source.m_items.end(); ++it) {
const std::string path = it->path.empty()
? prefix
: prefix + "." + it->path;
add(it->code, path, it->message);
}
}

private:
std::vector<ConfigurationDiagnostic> m_items;
};
Expand Down Expand Up @@ -242,6 +253,7 @@ namespace logit {
std::string type;
ConfigurationStrategySpec strategy;
ConfigurationFieldMap backend;
std::vector<std::string> required_bindings;
};

/// \struct ConfigurationPlan
Expand All @@ -261,11 +273,16 @@ namespace logit {
///
/// Returning true with a diagnostic is still a failed phase. Adapters
/// must add only error diagnostics; warnings are not part of this v1
/// contract.
/// contract. Diagnostic paths are relative to the current logger root
/// (for example `backend.foo`, `type`, or `binding`); the document
/// layer prefixes them with `loggers[N]`.
virtual bool validate_backend(const ConfigurationLoggerSpec& logger,
ConfigurationDiagnostics& diagnostics) const = 0;

/// \brief Materialize a DTO after all document validation has succeeded.
///
/// Diagnostic paths follow the same relative-to-logger-root rule as
/// `validate_backend()`; the document layer adds the logger index.
virtual bool materialize_backend(const ConfigurationLoggerSpec& logger,
ConfigurationMaterializedLogger& output,
ConfigurationDiagnostics& diagnostics) const = 0;
Expand Down Expand Up @@ -425,13 +442,17 @@ namespace logit {
}

for (std::size_t index = 0; index < document.loggers.size(); ++index) {
const std::size_t diagnostic_count = diagnostics.size();
const bool valid = materializer.validate_backend(document.loggers[index], diagnostics);
if (!valid || diagnostics.size() != diagnostic_count) {
if (diagnostics.size() == diagnostic_count) {
ConfigurationDiagnostics local_diagnostics;
const bool valid = materializer.validate_backend(document.loggers[index],
local_diagnostics);
if (!valid || !local_diagnostics.empty()) {
const std::string prefix = "loggers[" + std::to_string(index) + "]";
if (local_diagnostics.empty()) {
diagnostics.add(ConfigurationErrorCode::BackendValidationFailed,
"loggers[" + std::to_string(index) + "].backend",
prefix + ".backend",
"Backend materializer rejected the logger configuration");
} else {
diagnostics.append_rebased(local_diagnostics, prefix);
}
return false;
}
Expand All @@ -442,14 +463,17 @@ namespace logit {
candidate.loggers.reserve(document.loggers.size());
for (std::size_t index = 0; index < document.loggers.size(); ++index) {
ConfigurationMaterializedLogger output;
const std::size_t diagnostic_count = diagnostics.size();
ConfigurationDiagnostics local_diagnostics;
const bool materialized = materializer.materialize_backend(
document.loggers[index], output, diagnostics);
if (!materialized || diagnostics.size() != diagnostic_count) {
if (diagnostics.size() == diagnostic_count) {
document.loggers[index], output, local_diagnostics);
if (!materialized || !local_diagnostics.empty()) {
const std::string prefix = "loggers[" + std::to_string(index) + "]";
if (local_diagnostics.empty()) {
diagnostics.add(ConfigurationErrorCode::MaterializationFailed,
"loggers[" + std::to_string(index) + "]",
prefix,
"Backend materializer could not produce a logger plan");
} else {
diagnostics.append_rebased(local_diagnostics, prefix);
}
return false;
}
Expand Down
Loading
Loading