diff --git a/AspNetCore.Examples.OpenTelemetry.Api/AspNetCore.Examples.OpenTelemetry.Api.csproj b/AspNetCore.Examples.OpenTelemetry.Api/AspNetCore.Examples.OpenTelemetry.Api.csproj
index 746ded4..68b458b 100644
--- a/AspNetCore.Examples.OpenTelemetry.Api/AspNetCore.Examples.OpenTelemetry.Api.csproj
+++ b/AspNetCore.Examples.OpenTelemetry.Api/AspNetCore.Examples.OpenTelemetry.Api.csproj
@@ -4,21 +4,16 @@
net9.0
enable
enable
- Linux
- ..\docker-compose.dcproj
-
-
-
-
-
-
+
+
+
diff --git a/AspNetCore.Examples.OpenTelemetry.Api/AspNetCore.Examples.OpenTelemetry.Api.http b/AspNetCore.Examples.OpenTelemetry.Api/AspNetCore.Examples.OpenTelemetry.Api.http
new file mode 100644
index 0000000..95bedb2
--- /dev/null
+++ b/AspNetCore.Examples.OpenTelemetry.Api/AspNetCore.Examples.OpenTelemetry.Api.http
@@ -0,0 +1,6 @@
+@AspNetCore.Examples.OpenTelemetry.Api_HostAddress = http://localhost:5250
+
+GET {{AspNetCore.Examples.OpenTelemetry.Api_HostAddress}}/weatherforecast/
+Accept: application/json
+
+###
diff --git a/AspNetCore.Examples.OpenTelemetry.Api/Extensions/OpenApiExtensions.cs b/AspNetCore.Examples.OpenTelemetry.Api/Extensions/OpenApiExtensions.cs
deleted file mode 100644
index 25adc8a..0000000
--- a/AspNetCore.Examples.OpenTelemetry.Api/Extensions/OpenApiExtensions.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using Microsoft.AspNetCore.OpenApi;
-using Microsoft.OpenApi.Any;
-using Microsoft.OpenApi.Models;
-using Swashbuckle.AspNetCore.SwaggerUI;
-
-namespace AspNetCore.Examples.OpenTelemetry.Api.Extensions;
-
-public static class OpenApiExtensions
-{
- private const string DocumentName = "v1";
- private const string Pattern = "/openapi/{documentName}.json";
- private const string Url = "/openapi/v1.json";
-
- public static IServiceCollection AddCustomOpenApi(this IServiceCollection services)
- {
- return services.AddOpenApi(DocumentName, options =>
- {
- options.AddSchemaTransformer();
- });
- }
-
- public static IEndpointRouteBuilder MapCustomOpenApi(this IEndpointRouteBuilder endpoints)
- {
- endpoints.MapOpenApi(Pattern);
- return endpoints;
- }
-
- public static IApplicationBuilder UseCustomOpenApiUI(this IApplicationBuilder app)
- {
- return app.UseSwaggerUI(options =>
- {
- var hostingEnv = app.ApplicationServices.GetRequiredService();
- options.ConfigObject.Urls = [
- new UrlDescriptor { Name = $"{hostingEnv.ApplicationName} v1", Url = Url },
- ];
- });
- }
-
- private class EnumAsStringSchemaTransformer : IOpenApiSchemaTransformer
- {
- public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext context, CancellationToken cancellationToken)
- {
- if (!context.JsonTypeInfo.Type.IsEnum)
- return Task.CompletedTask;
-
- var enumType = context.JsonTypeInfo.Type;
-
- schema.Type = "string";
-
- var defaultIntValue = (schema.Default as OpenApiInteger)?.Value;
- var defaultEnumValue = Enum.ToObject(enumType, defaultIntValue ?? 0);
- schema.Default = new OpenApiString(defaultEnumValue.ToString());
-
- schema.Enum = Enum.GetNames(enumType)
- .Select(name => (IOpenApiAny)new OpenApiString(name))
- .ToArray();
-
- return Task.CompletedTask;
- }
- }
-}
diff --git a/AspNetCore.Examples.OpenTelemetry.Api/Program.cs b/AspNetCore.Examples.OpenTelemetry.Api/Program.cs
index 5e80191..b6c6adc 100644
--- a/AspNetCore.Examples.OpenTelemetry.Api/Program.cs
+++ b/AspNetCore.Examples.OpenTelemetry.Api/Program.cs
@@ -1,11 +1,11 @@
-using AspNetCore.Examples.OpenTelemetry.Api.Extensions;
-using AspNetCore.Examples.OpenTelemetry.Api.SampleTelemetry;
+using AspNetCore.Examples.OpenTelemetry.Api.WeatherForecast;
+using Scalar.AspNetCore;
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
-
-builder.Services.AddCustomOpenApi();
+
+builder.Services.AddOpenApi();
if (builder.Environment.IsDevelopment())
{
@@ -18,18 +18,17 @@
}));
}
-builder.Services.AddSampleTelemetry();
-
+builder.Services.AddWeatherForecast();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseCors();
- app.MapCustomOpenApi();
- app.UseCustomOpenApiUI();
+ app.MapOpenApi();
+ app.MapScalarApiReference();
}
-app.MapSampleTelemetryEndpoints();
+app.MapWeatherForecast();
app.Run();
diff --git a/AspNetCore.Examples.OpenTelemetry.Api/Properties/launchSettings.json b/AspNetCore.Examples.OpenTelemetry.Api/Properties/launchSettings.json
index 922f6c7..5ff3d16 100644
--- a/AspNetCore.Examples.OpenTelemetry.Api/Properties/launchSettings.json
+++ b/AspNetCore.Examples.OpenTelemetry.Api/Properties/launchSettings.json
@@ -1,21 +1,15 @@
-{
+{
+ "$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
+ "dotnetRunMessages": true,
"launchBrowser": true,
- "launchUrl": "swagger",
+ "launchUrl": "scalar/v1",
+ "applicationUrl": "http://localhost:5250",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
- },
- "dotnetRunMessages": true,
- "applicationUrl": "http://localhost:8080"
- },
- "Docker": {
- "commandName": "Docker",
- "launchBrowser": true,
- "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
- "publishAllPorts": true
+ }
}
- },
- "$schema": "https://json.schemastore.org/launchsettings.json"
-}
\ No newline at end of file
+ }
+}
diff --git a/AspNetCore.Examples.OpenTelemetry.Api/SampleTelemetry/SampleTelemetryEndpoints.cs b/AspNetCore.Examples.OpenTelemetry.Api/SampleTelemetry/SampleTelemetryEndpoints.cs
deleted file mode 100644
index 7a522a9..0000000
--- a/AspNetCore.Examples.OpenTelemetry.Api/SampleTelemetry/SampleTelemetryEndpoints.cs
+++ /dev/null
@@ -1,83 +0,0 @@
-using System.Diagnostics;
-using System.Diagnostics.Metrics;
-
-namespace AspNetCore.Examples.OpenTelemetry.Api.SampleTelemetry;
-
-public static class SampleTelemetryEndpoints
-{
- public static IServiceCollection AddSampleTelemetry(this IServiceCollection services)
- {
- services
- .AddSingleton()
- .AddOpenTelemetry()
- .WithTracing(t => t.AddSource(Telemetry.ActivitySourceName))
- .WithMetrics(m => m.AddMeter(Telemetry.MeterName));
- return services;
- }
-
- public static IEndpointRouteBuilder MapSampleTelemetryEndpoints(this IEndpointRouteBuilder builder)
- {
- builder.MapGet("telemetry/traces", async (Telemetry telemetry, int durationMs = 0, string? tag = null) =>
- {
- var tags = new Dictionary() { { "tag", tag } };
- using var trace = telemetry.ActivitySource.StartActivity(name: "sample_activity", kind: ActivityKind.Internal, tags: tags);
-
- await Task.Delay(durationMs);
-
- return Results.Ok();
- });
-
- builder.MapGet("telemetry/metrics", (Telemetry telemetry, int value = 0, string? tag = null) =>
- {
- var tags = new Dictionary() { { "tag", tag } }.ToArray();
-
- telemetry.Histogram.Record(value, tags);
- telemetry.Counter.Add(value, tags);
- telemetry.Gauge.Record(value, tags);
- telemetry.UpDownCounter.Add(value, tags);
-
- return Results.Ok();
- });
-
-
- builder.MapGet("telemetry/logs", (Telemetry telemetry, LogLevel level = LogLevel.Information, string? attributeValue = null) =>
- {
- telemetry.Logger.Log(level, "Sample log message with attribute value: \"{attribute}\"", attributeValue);
-
- return Results.Ok();
- });
-
- return builder;
- }
-
- private sealed class Telemetry : IDisposable
- {
- public const string LoggerName = "sample_logger";
- public const string MeterName = "sample_meter";
- public const string ActivitySourceName = "sample_source";
- public readonly ILogger Logger;
- public readonly ActivitySource ActivitySource;
- public readonly Meter Meter;
- public readonly Histogram Histogram;
- public readonly Counter Counter;
- public readonly Gauge Gauge;
- public readonly UpDownCounter UpDownCounter;
-
- public Telemetry(ILoggerFactory loggerFactory, IMeterFactory meterFactory)
- {
- Logger = loggerFactory.CreateLogger(LoggerName);
- ActivitySource = new ActivitySource(ActivitySourceName, version: "1.0");
- Meter = meterFactory.Create(MeterName);
- Histogram = Meter.CreateHistogram(name: "sample_histogram", unit: "Units", description: "Sample Histogram description");
- Counter = Meter.CreateCounter(name: "sample_counter", unit: "Units", description: "Sample Counter description");
- Gauge = Meter.CreateGauge(name: "sample_gauge", unit: "Units", description: "Sample Gauge description");
- UpDownCounter = Meter.CreateUpDownCounter(name: "sample_up_down_counter", unit: "Units", description: "Sample Up Down Counter description");
- }
-
- public void Dispose()
- {
- ActivitySource.Dispose();
- Meter.Dispose();
- }
- }
-}
\ No newline at end of file
diff --git a/AspNetCore.Examples.OpenTelemetry.Api/WeatherForecast/WeatherForecast.cs b/AspNetCore.Examples.OpenTelemetry.Api/WeatherForecast/WeatherForecast.cs
new file mode 100644
index 0000000..38feabb
--- /dev/null
+++ b/AspNetCore.Examples.OpenTelemetry.Api/WeatherForecast/WeatherForecast.cs
@@ -0,0 +1,6 @@
+namespace AspNetCore.Examples.OpenTelemetry.Api.WeatherForecast;
+
+internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
+{
+ public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
+}
diff --git a/AspNetCore.Examples.OpenTelemetry.Api/WeatherForecast/WeatherForecastExtensions.cs b/AspNetCore.Examples.OpenTelemetry.Api/WeatherForecast/WeatherForecastExtensions.cs
new file mode 100644
index 0000000..296a1ac
--- /dev/null
+++ b/AspNetCore.Examples.OpenTelemetry.Api/WeatherForecast/WeatherForecastExtensions.cs
@@ -0,0 +1,46 @@
+using System.Diagnostics;
+
+namespace AspNetCore.Examples.OpenTelemetry.Api.WeatherForecast;
+
+internal static class WeatherForecastExtensions
+{
+ public static IServiceCollection AddWeatherForecast(this IServiceCollection services)
+ {
+ services.AddTelemetry();
+ return services;
+ }
+
+ public static IEndpointRouteBuilder MapWeatherForecast(this IEndpointRouteBuilder endpoints)
+ {
+ var summaries = new[]
+ {
+ "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
+ };
+
+ endpoints.MapGet("/weatherforecast", (IWeatherForecastTelemetry telemetry) =>
+ {
+ using var _ = telemetry.ActivitySource.StartActivity(name: "sample_activity", kind: ActivityKind.Internal);
+ telemetry.Logger.LogInformation("Requesting weather forecast");
+
+ var forecast = Enumerable.Range(1, 5).Select(index =>
+ new WeatherForecast
+ (
+ DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
+ Random.Shared.Next(-20, 55),
+ summaries[Random.Shared.Next(summaries.Length)]
+ ))
+ .ToArray();
+
+ telemetry.Logger.LogInformation("Weather forecast calculated: {@forecast}", forecast);
+ foreach (var item in forecast)
+ {
+ telemetry.TemperatureC.Record(item.TemperatureC);
+ }
+
+ return forecast;
+ })
+ .WithName("GetWeatherForecast");
+
+ return endpoints;
+ }
+}
diff --git a/AspNetCore.Examples.OpenTelemetry.Api/WeatherForecast/WeatherForecastTelemetry.cs b/AspNetCore.Examples.OpenTelemetry.Api/WeatherForecast/WeatherForecastTelemetry.cs
new file mode 100644
index 0000000..7415eec
--- /dev/null
+++ b/AspNetCore.Examples.OpenTelemetry.Api/WeatherForecast/WeatherForecastTelemetry.cs
@@ -0,0 +1,20 @@
+using System.Diagnostics;
+using System.Diagnostics.Metrics;
+
+namespace AspNetCore.Examples.OpenTelemetry.Api.WeatherForecast;
+
+internal interface IWeatherForecastTelemetry : ITelemetry
+{
+ Histogram TemperatureC { get; }
+}
+
+internal class WeatherForecastTelemetry : Telemetry, IWeatherForecastTelemetry
+{
+ public WeatherForecastTelemetry(ILoggerFactory loggerFactory, IMeterFactory meterFactory)
+ : base(loggerFactory, meterFactory)
+ {
+ TemperatureC = Meter.CreateHistogram("temperature", unit: "ºC");
+ }
+
+ public Histogram TemperatureC { get; }
+}
diff --git a/AspNetCore.Examples.OpenTelemetry.Api/appsettings.Development.json b/AspNetCore.Examples.OpenTelemetry.Api/appsettings.Development.json
new file mode 100644
index 0000000..0c208ae
--- /dev/null
+++ b/AspNetCore.Examples.OpenTelemetry.Api/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/AspNetCore.Examples.OpenTelemetry.Api/appsettings.json b/AspNetCore.Examples.OpenTelemetry.Api/appsettings.json
index cc7acd2..10f68b8 100644
--- a/AspNetCore.Examples.OpenTelemetry.Api/appsettings.json
+++ b/AspNetCore.Examples.OpenTelemetry.Api/appsettings.json
@@ -2,8 +2,7 @@
"Logging": {
"LogLevel": {
"Default": "Information",
- "Microsoft.AspNetCore": "Warning",
- "AspNetCore.Examples.OpenTelemetry.Api.Controllers.TelemetryController": "Trace"
+ "Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
diff --git a/AspNetCore.Examples.OpenTelemetry.AspireHost/AspNetCore.Examples.OpenTelemetry.AspireHost.csproj b/AspNetCore.Examples.OpenTelemetry.AspireHost/AspNetCore.Examples.OpenTelemetry.AspireHost.csproj
index 984dcb2..65b1d67 100644
--- a/AspNetCore.Examples.OpenTelemetry.AspireHost/AspNetCore.Examples.OpenTelemetry.AspireHost.csproj
+++ b/AspNetCore.Examples.OpenTelemetry.AspireHost/AspNetCore.Examples.OpenTelemetry.AspireHost.csproj
@@ -10,8 +10,8 @@
-
-
+
+
diff --git a/AspNetCore.Examples.OpenTelemetry.ServiceDefaults/AspNetCore.Examples.OpenTelemetry.ServiceDefaults.csproj b/AspNetCore.Examples.OpenTelemetry.ServiceDefaults/AspNetCore.Examples.OpenTelemetry.ServiceDefaults.csproj
index 08c916f..81b8b2b 100644
--- a/AspNetCore.Examples.OpenTelemetry.ServiceDefaults/AspNetCore.Examples.OpenTelemetry.ServiceDefaults.csproj
+++ b/AspNetCore.Examples.OpenTelemetry.ServiceDefaults/AspNetCore.Examples.OpenTelemetry.ServiceDefaults.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net9.0
enable
enable
true
@@ -10,8 +10,8 @@
-
-
+
+
diff --git a/AspNetCore.Examples.OpenTelemetry.ServiceDefaults/Extensions.cs b/AspNetCore.Examples.OpenTelemetry.ServiceDefaults/Extensions.cs
index 4bac5e2..5d5b872 100644
--- a/AspNetCore.Examples.OpenTelemetry.ServiceDefaults/Extensions.cs
+++ b/AspNetCore.Examples.OpenTelemetry.ServiceDefaults/Extensions.cs
@@ -7,112 +7,111 @@
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;
-namespace Microsoft.Extensions.Hosting
+namespace Microsoft.Extensions.Hosting;
+
+// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry.
+// This project should be referenced by each service project in your solution.
+// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults
+public static class Extensions
{
- // Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry.
- // This project should be referenced by each service project in your solution.
- // To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults
- public static class Extensions
+ public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder)
{
- public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder)
- {
- builder.ConfigureOpenTelemetry();
+ builder.ConfigureOpenTelemetry();
- builder.AddDefaultHealthChecks();
+ builder.AddDefaultHealthChecks();
- builder.Services.AddServiceDiscovery();
+ builder.Services.AddServiceDiscovery();
- builder.Services.ConfigureHttpClientDefaults(http =>
- {
- // Turn on resilience by default
- http.AddStandardResilienceHandler();
+ builder.Services.ConfigureHttpClientDefaults(http =>
+ {
+ // Turn on resilience by default
+ http.AddStandardResilienceHandler();
- // Turn on service discovery by default
- http.AddServiceDiscovery();
- });
+ // Turn on service discovery by default
+ http.AddServiceDiscovery();
+ });
- // Uncomment the following to restrict the allowed schemes for service discovery.
- // builder.Services.Configure(options =>
- // {
- // options.AllowedSchemes = ["https"];
- // });
+ // Uncomment the following to restrict the allowed schemes for service discovery.
+ // builder.Services.Configure(options =>
+ // {
+ // options.AllowedSchemes = ["https"];
+ // });
- return builder;
- }
+ return builder;
+ }
- public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder)
+ public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder)
+ {
+ builder.Logging.AddOpenTelemetry(logging =>
{
- builder.Logging.AddOpenTelemetry(logging =>
+ logging.IncludeFormattedMessage = true;
+ logging.IncludeScopes = true;
+ });
+
+ builder.Services.AddOpenTelemetry()
+ .WithMetrics(metrics =>
+ {
+ metrics.AddAspNetCoreInstrumentation()
+ .AddHttpClientInstrumentation()
+ .AddRuntimeInstrumentation();
+ })
+ .WithTracing(tracing =>
{
- logging.IncludeFormattedMessage = true;
- logging.IncludeScopes = true;
+ tracing.AddAspNetCoreInstrumentation()
+ // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package)
+ //.AddGrpcClientInstrumentation()
+ .AddHttpClientInstrumentation();
});
- builder.Services.AddOpenTelemetry()
- .WithMetrics(metrics =>
- {
- metrics.AddAspNetCoreInstrumentation()
- .AddHttpClientInstrumentation()
- .AddRuntimeInstrumentation();
- })
- .WithTracing(tracing =>
- {
- tracing.AddAspNetCoreInstrumentation()
- // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package)
- //.AddGrpcClientInstrumentation()
- .AddHttpClientInstrumentation();
- });
-
- builder.AddOpenTelemetryExporters();
-
- return builder;
- }
+ builder.AddOpenTelemetryExporters();
- private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder)
- {
- var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);
-
- if (useOtlpExporter)
- {
- builder.Services.AddOpenTelemetry().UseOtlpExporter();
- }
+ return builder;
+ }
- // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package)
- //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]))
- //{
- // builder.Services.AddOpenTelemetry()
- // .UseAzureMonitor();
- //}
+ private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder)
+ {
+ var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);
- return builder;
+ if (useOtlpExporter)
+ {
+ builder.Services.AddOpenTelemetry().UseOtlpExporter();
}
- public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder)
- {
- builder.Services.AddHealthChecks()
- // Add a default liveness check to ensure app is responsive
- .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);
+ // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package)
+ //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]))
+ //{
+ // builder.Services.AddOpenTelemetry()
+ // .UseAzureMonitor();
+ //}
- return builder;
- }
+ return builder;
+ }
- public static WebApplication MapDefaultEndpoints(this WebApplication app)
- {
- // Adding health checks endpoints to applications in non-development environments has security implications.
- // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments.
- if (app.Environment.IsDevelopment())
- {
- // All health checks must pass for app to be considered ready to accept traffic after starting
- app.MapHealthChecks("/health");
+ public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder)
+ {
+ builder.Services.AddHealthChecks()
+ // Add a default liveness check to ensure app is responsive
+ .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);
- // Only health checks tagged with the "live" tag must pass for app to be considered alive
- app.MapHealthChecks("/alive", new HealthCheckOptions
- {
- Predicate = r => r.Tags.Contains("live")
- });
- }
+ return builder;
+ }
+
+ public static WebApplication MapDefaultEndpoints(this WebApplication app)
+ {
+ // Adding health checks endpoints to applications in non-development environments has security implications.
+ // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments.
+ if (app.Environment.IsDevelopment())
+ {
+ // All health checks must pass for app to be considered ready to accept traffic after starting
+ app.MapHealthChecks("/health");
- return app;
+ // Only health checks tagged with the "live" tag must pass for app to be considered alive
+ app.MapHealthChecks("/alive", new HealthCheckOptions
+ {
+ Predicate = r => r.Tags.Contains("live")
+ });
}
+
+ return app;
}
}
diff --git a/AspNetCore.Examples.OpenTelemetry.TelemetryExtensions/AspNetCore.Examples.OpenTelemetry.TelemetryExtensions.csproj b/AspNetCore.Examples.OpenTelemetry.TelemetryExtensions/AspNetCore.Examples.OpenTelemetry.TelemetryExtensions.csproj
new file mode 100644
index 0000000..32c49c6
--- /dev/null
+++ b/AspNetCore.Examples.OpenTelemetry.TelemetryExtensions/AspNetCore.Examples.OpenTelemetry.TelemetryExtensions.csproj
@@ -0,0 +1,14 @@
+
+
+
+ net9.0
+ enable
+ enable
+
+
+
+
+
+
+
+
diff --git a/AspNetCore.Examples.OpenTelemetry.TelemetryExtensions/Extensions.cs b/AspNetCore.Examples.OpenTelemetry.TelemetryExtensions/Extensions.cs
new file mode 100644
index 0000000..24506f8
--- /dev/null
+++ b/AspNetCore.Examples.OpenTelemetry.TelemetryExtensions/Extensions.cs
@@ -0,0 +1,78 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
+
+namespace Microsoft.Extensions.Hosting;
+
+public static class Extensions
+{
+ private static readonly Type TelemetryType = typeof(Telemetry<>);
+ private static readonly Type ITelemetryType = typeof(ITelemetry<>);
+
+ public static IServiceCollection AddTelemetry(this IServiceCollection services)
+ where TService : class
+ where TImplementation : class, TService
+ {
+ services.AddSingleton();
+ services.AddTelemetryInternal(services => services.GetRequiredService());
+ return services;
+ }
+
+ public static IServiceCollection AddTelemetry(this IServiceCollection services)
+ where TService : class
+ {
+ services.AddSingleton();
+ services.AddTelemetryInternal(services => services.GetRequiredService());
+ return services;
+ }
+
+ private static IServiceCollection AddTelemetryInternal(this IServiceCollection services, Func implementationFactory)
+ where TService : class
+ {
+ var implementationType = typeof(TService);
+ if (implementationType.TryGetBaseTelemetryType(out var baseTelemetryType))
+ {
+ services.AddSingleton(baseTelemetryType, implementationFactory);
+ var categoryName = baseTelemetryType
+ .GetField(nameof(Telemetry