Sstv.DomainExceptions.Extensions.DependencyInjection 2.2.0

dotnet add package Sstv.DomainExceptions.Extensions.DependencyInjection --version 2.2.0                
NuGet\Install-Package Sstv.DomainExceptions.Extensions.DependencyInjection -Version 2.2.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Sstv.DomainExceptions.Extensions.DependencyInjection" Version="2.2.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Sstv.DomainExceptions.Extensions.DependencyInjection --version 2.2.0                
#r "nuget: Sstv.DomainExceptions.Extensions.DependencyInjection, 2.2.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Sstv.DomainExceptions.Extensions.DependencyInjection as a Cake Addin
#addin nuget:?package=Sstv.DomainExceptions.Extensions.DependencyInjection&version=2.2.0

// Install Sstv.DomainExceptions.Extensions.DependencyInjection as a Cake Tool
#tool nuget:?package=Sstv.DomainExceptions.Extensions.DependencyInjection&version=2.2.0                

Sstv.DomainExceptions.Extensions.DependencyInjection

<- root readme

<- changelog

This library brings to Sstv.DomainExceptions additional capabilities to register some cool services to dependency injection container.

Install

You can install using Nuget Package Manager:

Install-Package Sstv.DomainExceptions.Extensions.DependencyInjection -Version 2.2.0

via the .NET CLI:

dotnet add package Sstv.DomainExceptions.Extensions.DependencyInjection --version 2.2.0

or you can add package reference manually:

<PackageReference Include="Sstv.DomainExceptions.Extensions.DependencyInjection" Version="2.2.0" />

How to use?

Register to Dependency injection:

Call AddDomainExceptions extension method on IServiceCollection, and optionally configure it's behaviour:

services.AddDomainExceptions();

Configure settings:

services.AddDomainExceptions(builder =>
{
    builder.ConfigureSettings = (sp, settings) =>
    {
        settings.GenerateExceptionIdAutomatically = true;      // true by default
        settings.CollectErrorCodesMetricAutomatically = true;  // true by default
        settings.ThrowIfHasNoErrorCodeDescription = true;      // true by default

        // manually provide you own singleton implementation of IErrorCodesDescriptionSource
        settings.ErrorCodesDescriptionSource = new MyAwesomeSource();

        // override default error description
        settings.DefaultErrorDescriptionProvider = 
            errorCode => new ErrorDescription(errorCode, "N/A");
    };
});

Choose IErrorCodesDescriptionSource:

If you use constants over enums, you can choose how to provide error codes description:

services.AddDomainExceptions(bulder =>
{
    // register your own implementation of IErrorCodesDescriptionSource as Singleton
    bulder.WithErrorCodesDescriptionSource<MyAwesomeSource>();
    
    // or you can pass in memory dictionary
    bulder.WithErrorCodesDescriptionFromMemory(new Dictionary<string, ErrorDescription>
    {
        ["SSTV.10004"] = new("SSTV.10004", "Some error description", "https://help.myproject.ru/error-codes/not-enough-money"),
        ["SSTV.10005"] = new("SSTV.10005", "Another error", "https://help.myproject.ru/error-codes/SSTV.10005"),
        ["SSTV.10006"] = new("SSTV.10006", "One more error", "https://help.myproject.ru/error-codes/SSTV.10006"),
    });

    // or load from appsettings.json, and optionally set configuration section name.
    bulder.WithErrorCodesDescriptionFromConfiguration();
});

All this sources internally merged and can be used by any DomainException class

Below example of appsettings.json, if you choose WithErrorCodesDescriptionFromConfiguration with default configuration section DomainExceptionSettings:ErrorCodes:

{
  "DomainExceptionSettings": {
    "ErrorCodes": {
      "SSTV.10004": {
        "Description": "You have not enough money",
        "HelpLink": "https://help.myproject.ru/error-codes/not-enough-money"
      },
      "SSTV.10005": {
        "Description": "This is an obsolete error code from appsettings.json",
        "IsObsolete": true
      }
    }
  }
}

if you don't choose any of ErrorCodesDescription, WithErrorCodesDescriptionFromConfiguration is would be set by default, so you can just add to DI and go and fill the appsettings! 😃

services.AddDomainExceptions();

Metric collection using OpenTelemetry

Sstv.DomainException expose public class ErrorCodesMeter with method Measure, which called every time, when DomainException instantiated, and counts errors occured with OpenTelemetry counter metric:

error_codes_total { code="SSTV.10004", message="You have not enough money" }

This library have an extension method AddDomainExceptionInstrumentation, that can be called on MeterProviderBuilder to start collecting this metric.

builder.Services.AddOpenTelemetry()
    .ConfigureResource(b => b.AddService(serviceName: "MyService"))
    .WithMetrics(mp =>
    {
+       mp.AddDomainExceptionInstrumentation();
        mp.AddAspNetCoreInstrumentation();
        mp.AddPrometheusExporter();
    });

this metrics can be visualized in Grafana dashboard like this:

Error rate and top ten error codes last 24 h

Prometheus queries:

// errors by code and message
sum(increase(error_codes_total[1m])) by (code, message)

// total
sum(increase(error_codes_total[1m]))

Error stats

Prometheus queries:

// TOP 10 error codes last 24 hours
topk(10, sum(increase(error_codes_total[24h])) by (code, message))

// Errors stats last 5 minutes
sum_over_time(sum(increase(error_codes_total[1m]))[5m:])
avg_over_time(sum(increase(error_codes_total[1m]))[5m:])
max_over_time(sum(increase(error_codes_total[1m]))[5m:])

// Errors stats last day
sum_over_time(sum(increase(error_codes_total[1m]))[1d:])
avg_over_time(sum(increase(error_codes_total[1m]))[1d:])
max_over_time(sum(increase(error_codes_total[1m]))[1d:])

Debug error codes middleware

Sometimes we want to see all the error codes in application, how they are configured, where they came from, is all description was correctly pulled from configuration on enum attribute and so on. For this purpose, you can call UseErrorCodesDebugView on DomainExceptionBuilder

services.AddDomainExceptions(builder =>
{
    // in this example used default values
    builder.UseErrorCodesDebugView("/error-codes", port: 8082);
});
// you can also add to middleware pipeline via IApplicationBuilder
app.UseErrorCodesDebugView();

output example:

// http://localhost:5115/error-codes
{
  "errorCodes": [
    {
      "code": "SSTV.10001",
      "helpLink": "https://help.myproject.ru/error-codes/not-enough-money",
      "message": "You have not enough money"
    },
    {
      "code": "DIF.10003",
      "helpLink": "https://help.myproject.ru/error-codes/DIF.10003",
      "message": "Obsolete error code in enum",
      "isObsolete": true
    },
    {
      "code": "SSTV.10004",
      "helpLink": "https://help.myproject.ru/error-codes/not-enough-money",
      "message": "You have not enough money"
    },
    {
      "code": "SSTV.10005",
      "message": "This is an obsolete error code from appsettings.json",
      "isObsolete": true
    }
  ]
}

also, if you want to enrich this output, you should implement IDomainExceptionDebugEnricher and register it to dependency injection container and extend AdditionalData dictionary property with your data.

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Sstv.DomainExceptions.Extensions.DependencyInjection:

Package Downloads
Sstv.DomainExceptions.Extensions.ProblemDetails

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.2.0 166 2/18/2024
2.1.1 112 2/14/2024
2.1.0 124 2/12/2024
1.0.0 128 10/4/2023