Skip to content

Commit 8937602

Browse files
authored
Add OpenTelemetry to TodosApi (#2014)
This adds the default OpenTelemetry configuration used by .NET Aspire ServiceDefaults - enable instrumentation for: * AspNetCore * HttpClient * Runtime and OTLP exporter.
1 parent 0ca291d commit 8937602

File tree

9 files changed

+107
-26
lines changed

9 files changed

+107
-26
lines changed

Diff for: build/dependencies.props

+1
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,6 @@
3333
<RazorSlicesVersion>0.7.0</RazorSlicesVersion>
3434
<SystemCommandLineVersion>2.0.0-beta4.22272.1</SystemCommandLineVersion>
3535
<MicrosoftCrankEventSourcesVersion>0.2.0-alpha.24114.2</MicrosoftCrankEventSourcesVersion>
36+
<OpenTelemetryVersion>1.9.0</OpenTelemetryVersion>
3637
</PropertyGroup>
3738
</Project>

Diff for: src/BenchmarksApps/TodosApi/AppSettings.cs

+2-7
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ internal class AppSettings
1111
public bool SuppressDbInitialization { get; set; }
1212
}
1313

14-
// Change to using ValidateDataAnnotations once https://github.com/dotnet/runtime/issues/77412 is complete
14+
// Changing this to use the Options Validation source generator increases the app size significantly.
15+
// See https://github.com/dotnet/runtime/issues/106366
1516
internal class AppSettingsValidator : IValidateOptions<AppSettings>
1617
{
1718
public ValidateOptionsResult Validate(string? name, AppSettings options)
@@ -42,12 +43,6 @@ public static IServiceCollection ConfigureAppSettings(this IServiceCollection se
4243
optionsBuilder.ValidateOnStart();
4344
}
4445

45-
// Change to using ValidateDataAnnotations once https://github.com/dotnet/runtime/issues/77412 is complete
46-
//services.AddOptions<AppSettings>()
47-
// .BindConfiguration(nameof(AppSettings))
48-
// .ValidateDataAnnotations()
49-
// .ValidateOnStart();
50-
5146
return services;
5247
}
5348
}

Diff for: src/BenchmarksApps/TodosApi/DatabaseHealthCheck.cs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Microsoft.Extensions.Diagnostics.HealthChecks;
2+
using Nanorm;
23
using Npgsql;
34

45
namespace TodosApi;

Diff for: src/BenchmarksApps/TodosApi/DatabaseInitializer.cs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Microsoft.Extensions.Options;
2+
using Nanorm;
23
using Npgsql;
34

45
namespace TodosApi;

Diff for: src/BenchmarksApps/TodosApi/Extensions.cs

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
2+
using Microsoft.Extensions.Diagnostics.HealthChecks;
3+
using OpenTelemetry;
4+
using OpenTelemetry.Metrics;
5+
using OpenTelemetry.Trace;
6+
using TodosApi;
7+
8+
namespace Microsoft.Extensions.Hosting;
9+
10+
public static class Extensions
11+
{
12+
public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder)
13+
{
14+
builder.ConfigureOpenTelemetry();
15+
16+
builder.AddDefaultHealthChecks();
17+
18+
return builder;
19+
}
20+
21+
public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder)
22+
{
23+
builder.Logging.AddOpenTelemetry(logging =>
24+
{
25+
logging.IncludeFormattedMessage = true;
26+
logging.IncludeScopes = true;
27+
});
28+
29+
builder.Services.AddOpenTelemetry()
30+
.WithMetrics(metrics =>
31+
{
32+
metrics.AddAspNetCoreInstrumentation()
33+
.AddHttpClientInstrumentation()
34+
.AddRuntimeInstrumentation();
35+
})
36+
.WithTracing(tracing =>
37+
{
38+
tracing.AddAspNetCoreInstrumentation()
39+
.AddHttpClientInstrumentation();
40+
});
41+
42+
builder.AddOpenTelemetryExporters();
43+
44+
return builder;
45+
}
46+
47+
private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder)
48+
{
49+
var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);
50+
51+
if (useOtlpExporter)
52+
{
53+
builder.Services.AddOpenTelemetry().UseOtlpExporter();
54+
}
55+
56+
return builder;
57+
}
58+
59+
public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder)
60+
{
61+
builder.Services.AddHealthChecks()
62+
// Add a default liveness check to ensure app is responsive
63+
.AddCheck("self", () => HealthCheckResult.Healthy(), ["live"])
64+
.AddCheck<DatabaseHealthCheck>("Database", timeout: TimeSpan.FromSeconds(2))
65+
.AddCheck<JwtHealthCheck>("JwtAuthentication");
66+
67+
return builder;
68+
}
69+
70+
public static WebApplication MapDefaultEndpoints(this WebApplication app)
71+
{
72+
// Adding health checks endpoints to applications in non-development environments has security implications.
73+
// See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments.
74+
if (app.Environment.IsDevelopment())
75+
{
76+
// All health checks must pass for app to be considered ready to accept traffic after starting
77+
app.MapHealthChecks("/health");
78+
79+
// Only health checks tagged with the "live" tag must pass for app to be considered alive
80+
app.MapHealthChecks("/alive", new HealthCheckOptions
81+
{
82+
Predicate = r => r.Tags.Contains("live")
83+
});
84+
}
85+
86+
return app;
87+
}
88+
}

Diff for: src/BenchmarksApps/TodosApi/Program.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
builder.Logging.ClearProviders();
77
#endif
88

9+
// Add service defaults
10+
builder.AddServiceDefaults();
11+
912
// Bind app settings from configuration & validate
1013
builder.Services.ConfigureAppSettings(builder.Configuration, builder.Environment);
1114

@@ -24,9 +27,6 @@
2427
});
2528

2629
// Configure health checks
27-
builder.Services.AddHealthChecks()
28-
.AddCheck<DatabaseHealthCheck>("Database", timeout: TimeSpan.FromSeconds(2))
29-
.AddCheck<JwtHealthCheck>("JwtAuthentication");
3030

3131
// Problem details
3232
builder.Services.AddProblemDetails();
@@ -50,7 +50,7 @@
5050

5151
app.MapShortCircuit(StatusCodes.Status404NotFound, "/favicon.ico");
5252

53-
app.MapHealthChecks("/health");
53+
app.MapDefaultEndpoints();
5454

5555
// Enables testing request exception handling behavior
5656
app.MapGet("/throw", void () => throw new InvalidOperationException("You hit the throw endpoint"));

Diff for: src/BenchmarksApps/TodosApi/Todo.cs

+3-13
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
using System.ComponentModel.DataAnnotations;
2-
using Nanorm.Npgsql;
3-
using Npgsql;
2+
using Nanorm;
43

54
namespace TodosApi;
65

7-
internal sealed class Todo : IDataReaderMapper<Todo>, IValidatable
6+
[DataRecordMapper]
7+
internal sealed partial class Todo : IValidatable
88
{
99
public int Id { get; set; }
1010

@@ -14,16 +14,6 @@ internal sealed class Todo : IDataReaderMapper<Todo>, IValidatable
1414

1515
public bool IsComplete { get; set; }
1616

17-
public static Todo Map(NpgsqlDataReader dataReader)
18-
{
19-
return !dataReader.HasRows ? new() : new()
20-
{
21-
Id = dataReader.GetInt32(dataReader.GetOrdinal(nameof(Id))),
22-
Title = dataReader.GetString(dataReader.GetOrdinal(nameof(Title))),
23-
IsComplete = dataReader.GetBoolean(dataReader.GetOrdinal(nameof(IsComplete)))
24-
};
25-
}
26-
2717
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
2818
{
2919
if (string.IsNullOrEmpty(Title))

Diff for: src/BenchmarksApps/TodosApi/TodoApi.cs

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Text.Json.Serialization;
22
using Microsoft.AspNetCore.Http.HttpResults;
33
using Microsoft.AspNetCore.Mvc;
4+
using Nanorm;
45
using Npgsql;
56
using TodosApi;
67

Diff for: src/BenchmarksApps/TodosApi/TodosApi.csproj

+6-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
<Nullable>enable</Nullable>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<InvariantGlobalization>true</InvariantGlobalization>
8-
<LangVersion>preview</LangVersion>
98
<UserSecretsId>b8ffb8d3-b768-460b-ac1f-ef267c954c85</UserSecretsId>
109
<PublishAot>true</PublishAot>
1110
<OpenApiDocumentsDirectory>.\</OpenApiDocumentsDirectory>
@@ -16,12 +15,17 @@
1615
<ItemGroup>
1716
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="$(MicrosoftAspNetCoreAppPackageVersion)" />
1817
<PackageReference Include="Npgsql" Version="$(NpgsqlVersion80)" />
19-
<PackageReference Include="Nanorm.Npgsql" Version="0.0.5" />
18+
<PackageReference Include="Nanorm.Npgsql" Version="0.1.2" />
2019
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="$(MicrosoftAspNetCoreAppPackageVersion)" />
2120
<PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="$(MicrosoftAspNetCoreAppPackageVersion)">
2221
<PrivateAssets>all</PrivateAssets>
2322
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2423
</PackageReference>
24+
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="$(OpenTelemetryVersion)" />
25+
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="$(OpenTelemetryVersion)" />
26+
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="$(OpenTelemetryVersion)" />
27+
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="$(OpenTelemetryVersion)" />
28+
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="$(OpenTelemetryVersion)" />
2529
</ItemGroup>
2630

2731
<ItemGroup>

0 commit comments

Comments
 (0)