| ArtifactType | Language | Tags | 
|---|---|---|
| nupkg. | csharp. | SLI, OpenTelemetry, Metrics. | 
Service level indicators (SLIs) are metrics used to measure the performance of a service. They are typically used in the context of service level agreements (SLAs), which are contracts between a service provider and its customers that define the expected level of service. SLIs are used to track the actual performance of the service against the agreed upon SLA.
There are many different types of SLIs that can be used to measure the performance of a service. Some common examples include:
- Availability: This measures the percentage of time that a service is available and functioning properly.
- Response time: This measures the amount of time it takes for a service to respond to a request.
- Throughput: This measures the amount of work that a service can handle in a given period of time.
- Error rate: This measures the percentage of requests that result in errors.
SLIs are important because they provide a way to objectively measure the performance of a service. By tracking SLIs over time, service providers can identify trends and make improvements to the service to ensure that it meets the needs of its customers.
ServiceLevelIndicators library will help emit latency metrics for each API operation to help monitor the service performance over time. The metrics is emitted via standard .NET Meter Class.
By default, a meter named ServiceLevelIndicator with instrument name operation.duration is added to the service metrics. The metrics are emitted with the following attributes.
- CustomerResourceId - A value that helps identity the customer, customer group or calling service.
- LocationId - The location where the service running. eg. Public cloud, West US 3 region. Azure Core
- Operation - The name of the operation.
- activity.status.code - The activity status code is set based on the success or failure of the operation. ActivityStatusCode.
ServiceLevelIndicators.Asp adds the following dimensions.
- Operation - In ASP.NET the operation name defaults to AttributeRouteInfo.Templateinformation likeGET Weatherforecast.
- The activity status code will be "Ok" when the http response status code is in the 2xx range, "Error" when the http response status code is in the 5xx range, "Unset" for any other status code.
- http.response.status_code - The http status code.
- http.request.method (Optional)- The http request method (GET, POST, etc) is added.
Difference between ServiceLevelIndicator and http.server.request.duration
| ServiceLevelIndicator | http.server.request.duration | |
|---|---|---|
| Resolution | milliseconds | seconds | 
| Customer | CustomerResourceId | N/A | 
| Error check | Activity or HTTP status.code | HTTP status code | 
ServiceLevelIndicators.Asp.Versioning adds the following dimensions.
- http.api.version - The API Version when used in conjunction with API Versioning package.
- 
ServiceLevelIndicators This library can be used to emit SLI for all .net core applications, where each operation is measured. 
- 
ServiceLevelIndicators.Asp For measuring SLI for ASP.NET Core applications use this library that will automatically measure each API operation. 
- 
ServiceLevelIndicators.Asp.ApiVersioning If API Versioning package is used, this library will add the API version as a metric dimension. 
- 
Register SLI with open telemetry by calling AddServiceLevelIndicatorInstrumentation.Example. builder.Services.AddOpenTelemetry() .ConfigureResource(configureResource) .WithMetrics(builder => { builder.AddServiceLevelIndicatorInstrumentation(); builder.AddOtlpExporter(); }); 
- 
Add ServiceLevelIndicator, into the dependency injection. AddMvc() is required for overrides present in SLI attributes to take effect. Example. builder.Services.AddServiceLevelIndicator(options => { options.LocationId = ServiceLevelIndicator.CreateLocationId("public", AzureLocation.WestUS3.Name); }) .AddMvc(); 
- 
Add the middleware to the pipeline. app.UseServiceLevelIndicator(); 
- 
Register SLI with open telemetry by calling AddServiceLevelIndicatorInstrumentation.Example. builder.Services.AddOpenTelemetry() .ConfigureResource(configureResource) .WithMetrics(builder => { builder.AddServiceLevelIndicatorInstrumentation(); builder.AddOtlpExporter(); }); 
- 
Add ServiceLevelIndicator into the dependency injection. Example. builder.Services.AddServiceLevelIndicator(options => { options.LocationId = ServiceLevelIndicator.CreateLocationId("public", AzureLocation.WestUS3.Name); }); 
- 
Add the middleware to the ASP.NET core pipeline. Example. app.UseServiceLevelIndicator(); 
- 
To each API route mapping, add AddServiceLevelIndicator()Example. app.MapGet("/hello", () => "Hello World!") .AddServiceLevelIndicator(); 
You can measure a block of code by boxing it in a using clause of MeasuredOperation. Example.
async Task MeasureCodeBlock(ServiceLevelIndicator serviceLevelIndicator)
{
    using var measuredOperation = serviceLevelIndicator.StartMeasuring("OperationName");
    // Do Work.
    measuredOperation.SetActivityStatusCode(System.Diagnostics.ActivityStatusCode.Ok);
}Once the Prerequisites are done, all controllers will emit SLI information. The default operation name is in the format <HTTP Method> <Controller>/<Action>. eg GET WeatherForecast/Action1
- 
To add API versioning as a dimension use package ServiceLevelIndicators.Asp.ApiVersioningand enrich the metrics withAddApiVersion.Example. builder.Services.AddServiceLevelIndicator(options => { /// Options }) .AddMvc() .AddApiVersion(); 
- 
To add HTTP method as a dimension, add AddHttpMethodto Service Level Indicator.Example. builder.Services.AddServiceLevelIndicator(options => { /// Options }) .AddMvc() .AddHttpMethod(); 
- 
Enrich SLI with Enrichcallback. The callback receives aMeasuredOperationas context that can be used to set toCustomerResourceIdor additional attributes. An async versionEnrichAsyncis also available.Example. builder.Services.AddServiceLevelIndicator(options => { options.LocationId = ServiceLevelIndicator.CreateLocationId(Cloud, Region); }) .AddMvc() .Enrich(context => { var upn = context.HttpContext.User.Claims .FirstOrDefault(c => c.Type == "upn")?.Value ?? "Unknown"; context.SetCustomerResourceId(upn); // Set CustomerResourceId context.AddAttribute("UserPrincipalName", upn); // Add custom attribute }); 
- 
To override the default operation name add the attribute [ServiceLevelIndicator]and specify the operation name.Example. [HttpGet("MyAction2")] [ServiceLevelIndicator(Operation = "MyNewOperationName")] public IEnumerable<WeatherForecast> GetOperation() => GetWeather(); 
- 
To set the CustomerResourceIdwithin an API method, mark the parameter with the attribute[CustomerResourceId][HttpGet("get-by-zip-code/{zipCode}")] public IEnumerable<WeatherForecast> GetByZipcode([CustomerResourceId] string zipCode) => GetWeather(); Or use GetMeasuredOperationextension method.[HttpGet("{customerResourceId}")] public IEnumerable<WeatherForecast> Get(string customerResourceId) { HttpContext.GetMeasuredOperation().CustomerResourceId = customerResourceId; return GetWeather(); } 
- 
To add custom Open Telemetry attributes. HttpContext.GetMeasuredOperation().AddAttribute(attribute, value); GetMeasuredOperation will throw if the route is not configured to emit SLI. When used in a middleware or scenarios where a route may not be configured to emit SLI. if (HttpContext.TryGetMeasuredOperation(out var measuredOperation)) measuredOperation.AddAttribute("CustomAttribute", value); You can add additional dimensions to the SLI data by using the Measureattribute.[HttpGet("name/{first}/{surname}")] public IActionResult GetCustomerResourceId( [Measure] string first, [CustomerResourceId] string surname) => Ok(first + " " + surname); 
- 
To prevent automatically emitting SLI information on all controllers, set the option, ServiceLevelIndicatorOptions.AutomaticallyEmitted = false; In this case, add the attribute [ServiceLevelIndicator]on the controllers that should emit SLI.
- 
To measure a process, run it within a using StartMeasuringblock.Example. public void StoreItem(MyDomainEvent domainEvent) { var attribute = new KeyValuePair<string, object?>("Event", domainEvent.GetType().Name); using var measuredOperation = _serviceLevelIndicator.StartMeasuring("StoreItem", attribute); DoTheWork(); ) 
Try out the sample weather forecast Web API.
To view the metrics locally.
- Run Docker Desktop
- Run sample\DockerOpenTelemetry\run.cmd to download and run zipkin and prometheus.
- Run the sample web API project and call the GET WeatherForecastusing the Open API UI.
- You should see the SLI metrics in prometheus under the meter operation_duration_milliseconds_bucketwhere theOperation = "GET WeatherForeCase",http.response.status_code = 200,LocationId = "ms-loc://az/public/westus2",activity.status_code = Ok 
- If you run the sample with API Versioning, you will see something similar to the following.
 
