Skip to content

Performance Monitoring & Diagnostics ​

Sannr provides comprehensive performance monitoring and diagnostics capabilities to help you track validation performance in production environments. Built on System.Diagnostics.Metrics, Sannr offers zero-overhead metrics collection that's fully compatible with modern observability stacks.

Overview ​

Performance monitoring in Sannr serves several key purposes:

  • Performance Tracking: Monitor validation operation durations and identify bottlenecks
  • Error Analysis: Track validation error rates and patterns
  • Capacity Planning: Understand validation load and resource usage
  • SLA Monitoring: Ensure validation performance meets service level agreements
  • Troubleshooting: Diagnose performance issues in production

Configuration ​

Basic Setup ​

Enable metrics collection in your Program.cs:

csharp
var builder = WebApplication.CreateBuilder(args);

// Enable performance monitoring
builder.Services.AddSannr(options =>
{
    options.EnableMetrics = true;
    options.MetricsPrefix = "myapp_validation"; // Optional: defaults to "sannr_validation"
});

builder.Services.AddControllers();
var app = builder.Build();

Configuration Options ​

csharp
builder.Services.AddSannr(options =>
{
    options.EnableMetrics = true;           // Enable/disable metrics collection
    options.MetricsPrefix = "myapp_validation"; // Prefix for all metric names
});
OptionTypeDefaultDescription
EnableMetricsboolfalseEnables performance metrics collection
MetricsPrefixstring"sannr_validation"Prefix for metric names

Available Metrics ​

Sannr automatically collects metrics for all validation operations performed through:

  • AotObjectModelValidator (ASP.NET Core MVC validation)
  • Validated<T>.CreateAsync() (Minimal API validation)

Validation Duration ​

Metric Name: {prefix}_validation_duration

  • Type: Histogram
  • Unit: Milliseconds (ms)
  • Description: Duration of validation operations
  • Tags:
    • model_type: The name of the validated model class (e.g., "UserRegistration")

Example Values:

  • Mean: 2.5ms (simple model with basic validation)
  • P95: 15ms (complex model with custom validators)
  • P99: 50ms (models with database validation)

Validation Errors ​

Metric Name: {prefix}_validation_errors_total

  • Type: Counter
  • Unit: Count
  • Description: Total number of validation errors encountered
  • Tags:
    • model_type: The name of the validated model class

Notes:

  • Only counts errors with Severity.Error (warnings are not counted)
  • Incremented by the total number of validation failures in a single operation
  • Useful for tracking validation failure rates

Integration with Observability Systems ​

Prometheus (via OpenTelemetry) ​

csharp
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;

// Add OpenTelemetry metrics export
builder.Services.AddOpenTelemetry()
    .WithMetrics(builder => builder
        .AddMeter("myapp_validation") // Must match your MetricsPrefix
        .AddPrometheusExporter(options =>
        {
            options.ScrapeEndpointPath = "/metrics";
        })
    );

// In your endpoints
app.MapPrometheusScrapingEndpoint(); // Exposes /metrics endpoint

Application Insights ​

csharp
builder.Services.AddApplicationInsightsTelemetry();

// Configure to include System.Diagnostics.Metrics
builder.Services.ConfigureTelemetryModule<DependencyTrackingTelemetryModule>((module, o) =>
{
    module.IncludeDiagnosticSourceActivities.Add("System.Diagnostics.Metrics");
});

Custom Metrics Collection ​

csharp
// Access metrics programmatically for custom processing
var meterFactory = app.Services.GetRequiredService<IMeterFactory>();
var meter = meterFactory.Create("myapp_validation");

// Create a custom listener
using var listener = new MeterListener();

listener.InstrumentPublished += (instrument, listener) =>
{
    if (instrument.Meter.Name == "myapp_validation")
    {
        listener.EnableMeasurementEvents(instrument);
    }
};

listener.SetMeasurementEventCallback<double>((instrument, measurement, tags, state) =>
{
    if (instrument.Name == "validation_duration")
    {
        var modelType = tags.FirstOrDefault(t => t.Key == "model_type").Value as string;
        Console.WriteLine($"Validation of {modelType} took {measurement}ms");

        // Send to custom monitoring system
        // MyMonitoringSystem.Record("validation.duration", measurement, tags);
    }
});

listener.SetMeasurementEventCallback<long>((instrument, measurement, tags, state) =>
{
    if (instrument.Name == "validation_errors_total")
    {
        var modelType = tags.FirstOrDefault(t => t.Key == "model_type").Value as string;
        Console.WriteLine($"Validation of {modelType} had {measurement} errors");
    }
});

listener.Start();

Dashboard Examples ​

Grafana Dashboard Configuration ​

Average Validation Duration by Model Type ​

txt
histogram_quantile(0.95, rate(myapp_validation_duration_bucket[5m])) by (model_type)

Validation Error Rate ​

txt
rate(myapp_validation_errors_total[5m]) by (model_type)

Slowest Validations (Top 10) ​

txt
topk(10, max_over_time(myapp_validation_duration{quantile="0.95"}[1h]))

Validation Throughput ​

txt
rate(myapp_validation_duration_count[5m]) by (model_type)

Sample Grafana Panel ​

json
{
  "title": "Validation Performance",
  "type": "timeseries",
  "targets": [
    {
      "expr": "histogram_quantile(0.95, rate(myapp_validation_duration_bucket[5m]))",
      "legendFormat": "P95 Duration"
    },
    {
      "expr": "rate(myapp_validation_errors_total[5m])",
      "legendFormat": "Error Rate"
    }
  ]
}

Performance Characteristics ​

Overhead Analysis ​

When Metrics Disabled (EnableMetrics = false) ​

  • CPU Overhead: ~0% (no-op implementation used)
  • Memory Overhead: ~0 bytes (no allocations)
  • Latency Impact: ~0ms (no measurement code executed)

When Metrics Enabled (EnableMetrics = true) ​

  • CPU Overhead: <1% (efficient histogram and counter operations)
  • Memory Overhead: ~100-200 bytes per validation operation (measurement objects)
  • Latency Impact: <0.1ms per operation (high-performance System.Diagnostics.Metrics)

Benchmark Results ​

Based on internal benchmarks with 1000 concurrent validation operations:

ScenarioOperations/secAvg LatencyP95 Latency
Metrics Disabled45,2300.022ms0.045ms
Metrics Enabled44,8900.022ms0.047ms
Overhead-0.75%+0.002ms+0.002ms

AOT Compatibility ​

  • Fully AOT Compatible: Uses only compile-time known types
  • No Reflection: All metric operations are statically typed
  • No Dynamic Code: No runtime code generation
  • Trimming Safe: No unsupported reflection patterns

Best Practices ​

Configuration ​

  1. Use Descriptive Prefixes

    csharp
    // Good
    options.MetricsPrefix = "ecommerce_user_validation";
    
    // Avoid
    options.MetricsPrefix = "metrics";
  2. Enable in Production Only

    csharp
    options.EnableMetrics = !builder.Environment.IsDevelopment();
  3. Test Metrics Collection

    csharp
    // Verify metrics are working in development
    if (builder.Environment.IsDevelopment())
    {
        options.EnableMetrics = true;
    }

Monitoring ​

  1. Set Up Alerts

    • Alert when P95 validation duration > 100ms
    • Alert when error rate > 5% for 5 minutes
    • Alert when validation throughput drops significantly
  2. Monitor Trends

    • Track validation duration over time
    • Monitor error rates by model type
    • Correlate with application performance metrics
  3. Capacity Planning

    • Use metrics to understand peak validation loads
    • Plan infrastructure based on validation throughput requirements
    • Identify models that need optimization

Troubleshooting ​

  1. High Validation Duration

    csharp
    // Identify slow models
    var slowModels = metrics.Where(m => m.Duration > 50ms)
                           .GroupBy(m => m.ModelType)
                           .OrderByDescending(g => g.Average(m => m.Duration));
  2. High Error Rates

    csharp
    // Find models with high error rates
    var errorProneModels = metrics.Where(m => m.ErrorCount > 0)
                                 .GroupBy(m => m.ModelType)
                                 .Select(g => new {
                                     ModelType = g.Key,
                                     ErrorRate = g.Sum(m => m.ErrorCount) / g.Count()
                                 });
  3. Performance Profiling

    csharp
    // Profile validation performance
    var profiler = new ValidationProfiler();
    var results = await profiler.ProfileAsync(myModel, serviceProvider);
    Console.WriteLine($"Total: {results.TotalDuration}ms");
    Console.WriteLine($"Slowest Rule: {results.SlowestRule.Name} ({results.SlowestRule.Duration}ms)");

Troubleshooting Common Issues ​

Metrics Not Appearing ​

Symptoms: Metrics are not visible in your monitoring system.

Possible Causes:

  1. EnableMetrics is set to false
  2. Meter name doesn't match your monitoring configuration
  3. No validation operations have been performed yet

Solutions:

csharp
// 1. Verify configuration
builder.Services.AddSannr(options =>
{
    options.EnableMetrics = true;
    options.MetricsPrefix = "myapp_validation"; // Ensure this matches your monitoring setup
});

// 2. Test with a simple validation
var validator = serviceProvider.GetRequiredService<ISannrValidator<TestModel>>();
var result = validator.Validate(new TestModel { Name = "test" });

High Memory Usage ​

Symptoms: Memory usage increases when metrics are enabled.

Possible Causes:

  1. High volume of validation operations
  2. Metrics not being scraped/exported frequently
  3. Large number of unique model types

Solutions:

csharp
// 1. Implement metric scraping
builder.Services.AddOpenTelemetry()
    .WithMetrics(builder => builder
        .AddMeter("myapp_validation")
        .AddPrometheusExporter());

// 2. Use metric aggregation
// Configure your monitoring system to aggregate metrics by time intervals

Performance Degradation ​

Symptoms: Application performance degrades when metrics are enabled.

Possible Causes:

  1. Extremely high validation throughput (>100k/sec)
  2. Complex metric processing pipelines
  3. Inefficient monitoring system configuration

Solutions:

csharp
// 1. Sample metrics instead of collecting all
options.EnableMetrics = (requestCount++ % 100 == 0); // Sample 1%

// 2. Use separate metrics pipeline
// Configure OpenTelemetry to use a separate thread pool for metrics processing

// 3. Profile and optimize
// Use performance profiling tools to identify bottlenecks

Advanced Usage ​

Custom Metrics ​

csharp
public class CustomMetricsCollector : ISannrMetricsCollector
{
    private readonly ISannrMetricsCollector _inner;
    private readonly ILogger<CustomMetricsCollector> _logger;

    public CustomMetricsCollector(ISannrMetricsCollector inner, ILogger<CustomMetricsCollector> logger)
    {
        _inner = inner;
        _logger = logger;
    }

    public void RecordValidationDuration(string modelType, double duration)
    {
        _inner.RecordValidationDuration(modelType, duration);

        if (duration > 1000) // Log slow validations
        {
            _logger.LogWarning("Slow validation detected: {ModelType} took {Duration}ms", modelType, duration);
        }
    }

    public void RecordValidationErrors(string modelType, long errorCount)
    {
        _inner.RecordValidationErrors(modelType, errorCount);

        if (errorCount > 10) // Alert on high error counts
        {
            _logger.LogError("High validation error count: {ModelType} had {ErrorCount} errors", modelType, errorCount);
        }
    }
}

// Register custom collector
builder.Services.AddSingleton<ISannrMetricsCollector>(sp =>
{
    var inner = sp.GetRequiredService<SannrValidationOptions>().EnableMetrics
        ? new SannrMetricsCollector(sp.GetRequiredService<SannrValidationOptions>())
        : new NoOpMetricsCollector();

    return new CustomMetricsCollector(inner, sp.GetRequiredService<ILogger<CustomMetricsCollector>>());
});

Integration Testing ​

csharp
public class ValidationPerformanceTests
{
    [Fact]
    public async Task Validation_Performance_Meets_SLA()
    {
        // Arrange
        var services = new ServiceCollection();
        services.AddSannr(options => options.EnableMetrics = true);
        var serviceProvider = services.BuildServiceProvider();

        var validator = serviceProvider.GetRequiredService<ISannrValidator<ComplexModel>>();

        // Act
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < 1000; i++)
        {
            var model = new ComplexModel { /* populate with valid data */ };
            var result = await validator.ValidateAsync(model);
            Assert.True(result.IsValid);
        }
        stopwatch.Stop();

        // Assert
        var avgDuration = stopwatch.Elapsed.TotalMilliseconds / 1000;
        Assert.True(avgDuration < 10, $"Average validation duration {avgDuration}ms exceeds SLA of 10ms");
    }
}

Migration Guide ​

From Manual Performance Monitoring ​

If you're currently using manual performance monitoring:

csharp
// Before
public async Task<ValidationResult> ValidateAsync(MyModel model)
{
    var stopwatch = Stopwatch.StartNew();
    try
    {
        // validation logic
        var result = await ValidateInternalAsync(model);
        return result;
    }
    finally
    {
        stopwatch.Stop();
        _metrics.Record("validation_duration", stopwatch.Elapsed.TotalMilliseconds);
    }
}

// After - Automatic
builder.Services.AddSannr(options => options.EnableMetrics = true);
// Metrics are automatically recorded for all validations

From Other Validation Libraries ​

When migrating from libraries like FluentValidation or DataAnnotations:

  1. Enable Sannr metrics during migration
  2. Compare performance characteristics
  3. Gradually phase out old validation while monitoring
  4. Use metrics to validate that migration maintains performance

Summary ​

Sannr's performance monitoring provides:

  • Comprehensive Metrics: Duration and error tracking for all validation operations
  • Zero Configuration: Automatic instrumentation with simple enable/disable
  • Observability Integration: Native support for Prometheus, Application Insights, and custom systems
  • Production Ready: Minimal overhead, AOT compatible, thread-safe
  • Actionable Insights: Data for performance optimization and capacity planning

Performance monitoring is essential for maintaining high-quality validation services in production environments. Sannr makes it effortless to gain deep insights into your validation performance.

Released under the MIT License.