Install the NuGet package into your project.
dotnet add package Wemogy.AspNet
When spinning up the ASP.NET application, you configure the library, which is usually done in the Startup.cs
file.
First, create a StartupOptions
instance and configure it for your project.
private readonly StartupOptions _options;
public Startup(IConfiguration configuration)
{
Configuration = configuration;
_options = new StartupOptions();
// Middleware
_options
.AddMiddleware<ApiExceptionFilter>();
// Swagger
_options
.AddOpenApi("v1")
.WithApiGroup("public", "Fancy API", "This is my fancy API.")
.WithSecurityScheme(SecuritySchemeDefaults.JwtBearer);
// Monitoring
_options
.AddMonitoring()
.WithMeter(Observability.Meter.Name)
.WithApplicationInsights(Configuration["AzureApplicationInsightsConnectionString"])
.WithPrometheus();
// Health Checks
_options.ConfigureHealthChecks(builder =>
builder.AddCheck("MyHealthCheck", () => HealthCheckResult.Healthy("Everything is fine."));
}
Add the StartupOptions
and register the default setup using the AddDefaultSetup
extension method. Alternatively, you can also register our defaults manually in case you need to tweak in some of your own adjustments.
public void ConfigureServices(IServiceCollection services)
{
// Register all options automatically
services.AddDefaultSetup(_options);
// or
// Register the options manually
services.AddDefaultCors();
services.AddDefaultSwagger(_options);
services.AddDefaultMonitoring(_options);
services.AddDefaultControllers(_options, _options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes);
services.AddDefaultHealthChecks(_options);
services.AddDefaultRouting();
}
Make sure, the default setup is getting used. Again, you can either use the UseDefaultSetup
extension method or use the options manually, in case you need to tweak in some of your own adjustments.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseDefaultSetup(env, _options);
// or
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseDefaultCors();
app.UseDefaultSwagger(_options);
app.UseDefaultMonitoring(_options);
app.UseDefaultRouting();
app.UseCloudEvents(); // when using Dapr
app.UseAuthentication();
app.UseAuthorization();
app.UseDefaultMiddleware(_options);
app.UseDefaultEndpoints(_options);
}
For modern or Minimal API services (introduced in .NET 6), this would look similar to this. In the Program.cs
, add the StartupOptions
and register the default setup.
var options = new StartupOptions();
// Middleware
options
.AddMiddleware<ApiExceptionFilter>();
// Swagger
options
.AddOpenApi("v1")
.WithApiGroup("public", "Fancy API", "This is my fancy API.")
.WithSecurityScheme(SecuritySchemeDefaults.JwtBearer);
// Monitoring
options
.AddMonitoring()
.WithMeter(Observability.Meter.Name)
.WithApplicationInsights(Configuration["AzureApplicationInsightsConnectionString"])
.WithPrometheus();
// Health Checks
options.ConfigureHealthChecks(builder =>
builder.AddCheck("MyHealthCheck", () => HealthCheckResult.Healthy("Everything is fine."));
app.AddDefaultSetup(options);
app.UseDefaultSetup(app.Environment, options);
We use Open Telemetry to provide monitoring for your application. The library provides a default setup for you, which you can use as is or extend to your needs. Open Telemetry interacts with the native Metrics and Tracing capabilities of ASP.NET.
Every project should have a Observability.cs
class, which is responsible for defining the Meters.
using System.Diagnostics;
using System.Diagnostics.Metrics;
using System.Reflection;
using OpenTelemetry.Metrics;
public class Observability
{
// Define a default ActivitySource
public static readonly ActivitySource DefaultActivities = new ActivitySource("ServiceName");
// Define a default Meter with name and version
public static readonly Meter Meter = new("ServiceName", Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "0.0.0");
// Create Counters, Histograms, etc. from that default Meter
public static readonly Counter<long> Pings = Meter.CreateCounter<long>("service_countername", description: "Total number of pings");
public static readonly Histogram<int> PingDelay = Meter.CreateHistogram<int>("service_histgramname", "ms", "Think time in ms for a ping");
public static void ConfigureStandardLogger(this ILoggingBuilder builder)
{
builder.ClearProviders();
builder.AddConsole();
}
}
Make sure to register the Meter at Startup.
var options = new StartupOptions();
options
.AddMonitoring()
.WithActivitySource(Observability.DefaultActivities.Name)
.WithMeter(Observability.Meter.Name)
// ...
Use the Meter in your code.
Observability.Pings.Add(1);
Observability.PingDelay.Record(new Random().Next(50, 100));
Use Activities in your Code to create sections of code that can be traced.
using (var fistActivity = Observability.DefaultActivities.StartActivity("First section"))
{
fistActivity?.AddTag("date", DateTime.UtcNow.ToString()); // Add optional tags
Thread.Sleep(100); // Do work
fistActivity?.Stop(); // Stop activity
};
using (var secondActivity = Observability.DefaultActivities.StartActivity("Second section"))
{
secondActivity?.AddTag("foo", "far"); // Add optional tags
Thread.Sleep(200); // Do work
secondActivity?.Stop(); // Stop activity
};
The library automatically includes a health check endpoint at /healthz
, which checks the basic health of the service.
You can add additional health checks to the default setup.
- Inline Health Checks
- Custom Health Checks, that implement the IHealthCheck interface
- Database Health Checks
- Entity Framework Core DbContext probes
_options.ConfigureHealthChecks(builder => {
builder
.AddCheck("MyHealthCheck", () => HealthCheckResult.Healthy("Everything is fine.")
.AddCheck("MyOtherHealthCheck", MyHealthChecker) // Implement IHealthCheck
.AddSqlServer("<MY_CONNECTION_STRING>")
.AddDbContextCheck<SampleDbContext>();
});