Skip to content

Logging & Diagnostics

Cocoar.Configuration uses Microsoft.Extensions.Logging with source-generated log messages ([LoggerMessage]) for structured, high-performance logging. This page covers how to configure log output and interpret the diagnostics the library emits.

Log Categories

Each internal class creates its log messages under its own category (the fully-qualified class name). The main categories you will see are:

CategoryArea
Cocoar.Configuration.Core.ConfigurationEngineRecompute lifecycle (start, finish, cancellation, errors)
Cocoar.Configuration.Core.ConfigurationStateSnapshot publication, deserialization failures
Cocoar.Configuration.Core.ConfigurationAccessorFallback deserialization during recompute phase
Cocoar.Configuration.Rules.RuleManagerRule evaluation, provider failures, transform caching
Cocoar.Configuration.Infrastructure.RecomputeCoalescerDebounce timer errors
Cocoar.Configuration.Infrastructure.ChangeSubscriptionManagerChange subscription errors
Cocoar.Configuration.Infrastructure.ExposureRegistryInterface-to-concrete type mapping
Cocoar.Configuration.Infrastructure.ProviderRegistryProvider creation, acquire/release, disposal
Cocoar.Configuration.Reactive.ReactiveConfigManagerReactive config wrapper creation
Cocoar.Configuration.Reactive.ReactiveConfigurationFactoryReactive priming, tuple element resolution
Cocoar.Configuration.Reactive.ReactiveTupleConfigTuple stream errors, tuple emission failures

Because all categories start with Cocoar.Configuration, you can configure them with a single filter prefix.

Log Levels

Debug

Debug messages trace the internal mechanics of the configuration engine. These are useful when troubleshooting why a recompute fired or why a provider was recreated.

Event IDSourceMessage
2002ConfigurationEngineRecompute started
2003ConfigurationEngineRecompute cancelled
2004ConfigurationEngineRecompute finished
5003RuleManagerQuery key hash failed for {QueryType}; falling back to JSON serialization
5004RuleManagerTransform key computation failed; falling back to empty key
3000ConfigurationAccessorFallback deserialization for {TypeName} during recompute phase
3000ExposureRegistryConfigureSpec does not have a valid primary type capability, skipping
3002ExposureRegistryExposed interface {InterfaceType} ->
3004ExposureRegistryInterface deserialization mapping: {InterfaceType} ->
1000-1004ProviderRegistryProvider creation, acquire, release, disposal (when diagnostics are enabled)
6006ReactiveConfigManagerCreated reactive config wrapper for type

Information

Information messages mark significant lifecycle events.

Event IDSourceMessage
2006ConfigurationEngineStartup phase complete - switching to resilient mode
4002ConfigurationStateConfiguration snapshot published: version={Version}, types=
3005ExposureRegistryBuilt exposure registry with {ExposureCount} DI mappings and {DeserializationCount} deserialization mappings
6000ReactiveConfigManagerRecreating dead observable for configuration type

Warning

Warnings indicate degraded conditions that the library recovers from automatically.

Event IDSourceMessage
4001ConfigurationStateRuntime deserialization failed for {FailureCount} types, keeping last good configuration
5000RuleManagerSelection path '{SelectPath}' failed; skipping optional rule
5002RuleManagerOptional rule failed and will be skipped: {Provider}->
3001ConfigurationAccessorFallback deserialization failed for {TypeName}:
3001ExposureRegistryInterface {InterfaceType} was already exposed by {ExistingConcreteType}, now overridden by
3003ExposureRegistryInterface {InterfaceType} deserialization was already mapped to {ExistingConcreteType}, now overridden by
6001ReactiveConfigManagerFailed to get initial config for type {Type}, using default value
6100ReactiveTupleConfigTuple reactive config stream error ignored to keep alive for
6101ReactiveTupleConfigFailed to build CurrentValue for tuple
6103ReactiveTupleConfigFailed building tuple emission for
6400ReactiveConfigurationFactoryFailed to locate GetReactiveConfig for type
6401ReactiveConfigurationFactoryFailed to locate GetConfig for type
6402ReactiveConfigurationFactoryFailed to prime reactive configuration for tuple element
6403ReactiveConfigurationFactoryType {Type} is not a class, skipping reactive priming

Error

Errors indicate failures that may require attention. Required rule failures prevent the application from starting (during startup) or roll back the recompute (at runtime).

Event IDSourceMessage
2000ConfigurationEngineConfigManager initialization failed
2001ConfigurationEngineRuntime recompute failed - preserving current configuration
2005ConfigurationEngineRecompute failed from change trigger
4000ConfigurationStateDeserialization failed for {TypeName}:
5001RuleManagerRequired rule failed: {Provider}->
4000RecomputeCoalescerRecompute failed from initial debounce trigger
4001RecomputeCoalescerRecompute failed from trailing trigger
4100ChangeSubscriptionManagerRecompute failed from change trigger

Configuring Log Levels

Filter by Prefix

Because every log category starts with Cocoar.Configuration, you can control verbosity with a single filter:

csharp
// In code
builder.Logging.AddFilter("Cocoar.Configuration", LogLevel.Debug);
json
// In appsettings.json
{
  "Logging": {
    "LogLevel": {
      "Cocoar.Configuration": "Debug"
    }
  }
}

Fine-Grained Control

You can also filter individual subsystems:

json
{
  "Logging": {
    "LogLevel": {
      "Cocoar.Configuration": "Warning",
      "Cocoar.Configuration.Core.ConfigurationEngine": "Debug",
      "Cocoar.Configuration.Rules.RuleManager": "Debug"
    }
  }
}

Passing the Logger

When using ConfigManager directly (without DI), pass a logger via the builder:

csharp
var loggerFactory = LoggerFactory.Create(b => b
    .AddConsole()
    .AddFilter("Cocoar.Configuration", LogLevel.Debug));

var manager = ConfigManager.Create(c => c
    .UseLogger(loggerFactory.CreateLogger<ConfigManager>())
    .UseConfiguration(rules => [
        rules.For<AppSettings>().FromFile("appsettings.json")
    ]));

When using DI via AddCocoarConfiguration, the logger is resolved from the service container automatically.

OpenTelemetry Integration

Metrics

The library exposes metrics via System.Diagnostics.Metrics under the meter name Cocoar.Configuration. See the Health Overview for the full list of counters and histograms.

csharp
builder.Services.AddOpenTelemetry()
    .WithMetrics(m => m.AddMeter("Cocoar.Configuration"));

Distributed Tracing

The library emits traces via a System.Diagnostics.ActivitySource named Cocoar.Configuration. To collect these traces, add the source to your OpenTelemetry configuration:

csharp
builder.Services.AddOpenTelemetry()
    .WithTracing(t => t.AddSource("Cocoar.Configuration"));

The following activities are emitted:

Activity NameDescriptionTags
cocoar.config.recomputeFull recompute cyclerule_count, start_index, status
cocoar.config.ruleIndividual rule evaluation within a recomputerule_type, rule_index, required
cocoar.feature_flag.evaluateFeature flag evaluationflag.key, flag.kind
cocoar.entitlement.evaluateEntitlement evaluationflag.key, flag.kind

The status tag on cocoar.config.recompute is one of success, cancelled, or failure.

Debounce Timing

The debounce interval controls how rapidly the engine reacts to configuration source changes. See Debouncing for configuration details.

The RecomputeCoalescer logs errors at Error level if the debounce timer callback or trailing pass fails. The recompute lifecycle itself (start, cancel, finish) is logged at Debug level by ConfigurationEngine, so enabling Debug logging lets you observe when debounce windows close and recomputes fire.

csharp
var manager = ConfigManager.Create(c => c
    .UseDebounce(500) // 500ms debounce
    .UseLogger(logger)
    .UseConfiguration(rules => [ /* ... */ ]));

Released under the Apache-2.0 License.