Skip to content

Commit 133c31c

Browse files
committed
Add OTEL support for Lambda projects in the Aspire Dashboard
1 parent fc8dfbb commit 133c31c

28 files changed

+539
-111
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"Projects": [
3+
{
4+
"Name": "Aspire.Hosting.AWS",
5+
"Type": "Patch",
6+
"ChangelogMessages": [
7+
"Add OpenTelemetry support for Lambda in the Aspire Dashboard"
8+
]
9+
}
10+
]
11+
}

Directory.Packages.props

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<PackageVersion Include="Aspire.Hosting" Version="$(AspireVersion)" />
99
<PackageVersion Include="Aspire.Hosting.AppHost" Version="$(AspireVersion)" />
1010
<PackageVersion Include="Aspire.Hosting.Testing" Version="$(AspireVersion)" />
11+
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery" Version="$(AspireVersion)" />
1112
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery.Dns" Version="$(AspireVersion)" />
1213
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="$(AspireVersion)" />
1314
<!-- AWS SDK for .NET dependencies -->
@@ -31,7 +32,8 @@
3132
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
3233
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
3334
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
34-
<PackageVersion Include="OpenTelemetry.Instrumentation.AWS" Version="1.1.0-beta.6" />
35+
<PackageVersion Include="OpenTelemetry.Instrumentation.AWS" Version="1.11.0" />
36+
<PackageVersion Include="OpenTelemetry.Instrumentation.AWSLambda" Version="1.11.0" />
3537
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
3638
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.9.0" />
3739
<!-- Test dependencies -->

integrations-on-dotnet-aspire-for-aws.sln

+25-6
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,20 @@ EndProject
3232
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebAddLambdaFunction", "playground\Lambda\WebAddLambdaFunction\WebAddLambdaFunction.csproj", "{4B38CE7D-C548-4106-AAC4-4216A1E7F62C}"
3333
EndProject
3434
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebMinusLambdaFunction", "playground\Lambda\WebMinusLambdaFunction\WebMinusLambdaFunction.csproj", "{F94B620B-7A26-4EC8-952C-12583063802A}"
35-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.Hosting.AWS.Integ.Tests", "tests\Aspire.Hosting.AWS.Integ.Tests\Aspire.Hosting.AWS.Integ.Tests.csproj", "{85E281A8-944D-4303-9D9A-28B4F5AEF69F}"
3635
EndProject
36+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.Hosting.AWS.Integ.Tests", "tests\Aspire.Hosting.AWS.Integ.Tests\Aspire.Hosting.AWS.Integ.Tests.csproj", "{85E281A8-944D-4303-9D9A-28B4F5AEF69F}"
3737
EndProject
3838
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebDefaultLambdaFunction", "playground\Lambda\WebDefaultLambdaFunction\WebDefaultLambdaFunction.csproj", "{FCAF3F64-BD16-4901-8D07-80049844B0A4}"
3939
EndProject
40+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lambda.ServiceDefaults", "playground\Lambda\Lambda.ServiceDefaults\Lambda.ServiceDefaults.csproj", "{A2750C2D-1F82-47CB-9EAB-B819E3EBDD74}"
41+
EndProject
42+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{100F1AB8-4765-4370-93BC-AC0AC0FBEADC}"
43+
ProjectSection(SolutionItems) = preProject
44+
Directory.Packages.props = Directory.Packages.props
45+
EndProjectSection
46+
EndProject
47+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebAWSCallsLambdaFunction", "playground\Lambda\WebAWSCallsLambdaFunction\WebAWSCallsLambdaFunction.csproj", "{96396A08-6FB9-49C2-A923-1AF1C97087EF}"
48+
EndProject
4049
Global
4150
GlobalSection(SolutionConfigurationPlatforms) = preSolution
4251
Debug|Any CPU = Debug|Any CPU
@@ -83,14 +92,22 @@ Global
8392
{F94B620B-7A26-4EC8-952C-12583063802A}.Debug|Any CPU.Build.0 = Debug|Any CPU
8493
{F94B620B-7A26-4EC8-952C-12583063802A}.Release|Any CPU.ActiveCfg = Release|Any CPU
8594
{F94B620B-7A26-4EC8-952C-12583063802A}.Release|Any CPU.Build.0 = Release|Any CPU
86-
{FCAF3F64-BD16-4901-8D07-80049844B0A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
87-
{FCAF3F64-BD16-4901-8D07-80049844B0A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
88-
{FCAF3F64-BD16-4901-8D07-80049844B0A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
89-
{FCAF3F64-BD16-4901-8D07-80049844B0A4}.Release|Any CPU.Build.0 = Release|Any CPU
9095
{85E281A8-944D-4303-9D9A-28B4F5AEF69F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
9196
{85E281A8-944D-4303-9D9A-28B4F5AEF69F}.Debug|Any CPU.Build.0 = Debug|Any CPU
9297
{85E281A8-944D-4303-9D9A-28B4F5AEF69F}.Release|Any CPU.ActiveCfg = Release|Any CPU
9398
{85E281A8-944D-4303-9D9A-28B4F5AEF69F}.Release|Any CPU.Build.0 = Release|Any CPU
99+
{FCAF3F64-BD16-4901-8D07-80049844B0A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
100+
{FCAF3F64-BD16-4901-8D07-80049844B0A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
101+
{FCAF3F64-BD16-4901-8D07-80049844B0A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
102+
{FCAF3F64-BD16-4901-8D07-80049844B0A4}.Release|Any CPU.Build.0 = Release|Any CPU
103+
{A2750C2D-1F82-47CB-9EAB-B819E3EBDD74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
104+
{A2750C2D-1F82-47CB-9EAB-B819E3EBDD74}.Debug|Any CPU.Build.0 = Debug|Any CPU
105+
{A2750C2D-1F82-47CB-9EAB-B819E3EBDD74}.Release|Any CPU.ActiveCfg = Release|Any CPU
106+
{A2750C2D-1F82-47CB-9EAB-B819E3EBDD74}.Release|Any CPU.Build.0 = Release|Any CPU
107+
{96396A08-6FB9-49C2-A923-1AF1C97087EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
108+
{96396A08-6FB9-49C2-A923-1AF1C97087EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
109+
{96396A08-6FB9-49C2-A923-1AF1C97087EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
110+
{96396A08-6FB9-49C2-A923-1AF1C97087EF}.Release|Any CPU.Build.0 = Release|Any CPU
94111
EndGlobalSection
95112
GlobalSection(SolutionProperties) = preSolution
96113
HideSolutionNode = FALSE
@@ -108,8 +125,10 @@ Global
108125
{B95CE2E4-BE17-4E29-ACE7-BDE309B34B26} = {C3C0B096-DB7C-4D1B-B8A0-91631B32B4DE}
109126
{4B38CE7D-C548-4106-AAC4-4216A1E7F62C} = {C3C0B096-DB7C-4D1B-B8A0-91631B32B4DE}
110127
{F94B620B-7A26-4EC8-952C-12583063802A} = {C3C0B096-DB7C-4D1B-B8A0-91631B32B4DE}
111-
{FCAF3F64-BD16-4901-8D07-80049844B0A4} = {C3C0B096-DB7C-4D1B-B8A0-91631B32B4DE}
112128
{85E281A8-944D-4303-9D9A-28B4F5AEF69F} = {7F5F26A8-F41A-4B16-83F0-8A11EE396FAC}
129+
{FCAF3F64-BD16-4901-8D07-80049844B0A4} = {C3C0B096-DB7C-4D1B-B8A0-91631B32B4DE}
130+
{A2750C2D-1F82-47CB-9EAB-B819E3EBDD74} = {C3C0B096-DB7C-4D1B-B8A0-91631B32B4DE}
131+
{96396A08-6FB9-49C2-A923-1AF1C97087EF} = {C3C0B096-DB7C-4D1B-B8A0-91631B32B4DE}
113132
EndGlobalSection
114133
GlobalSection(ExtensibilityGlobals) = postSolution
115134
SolutionGuid = {FBA55172-92F1-4495-A082-E0ABE4F4AF09}

playground/Lambda/Lambda.AppHost/Lambda.AppHost.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<PackageReference Include="Aspire.Hosting.AppHost" />
1414
<ProjectReference Include="..\ToUpperLambdaFunctionExecutable\ToUpperLambdaFunctionExecutable.csproj" />
1515
<ProjectReference Include="..\WebAddLambdaFunction\WebAddLambdaFunction.csproj" />
16+
<ProjectReference Include="..\WebAWSCallsLambdaFunction\WebAWSCallsLambdaFunction.csproj" />
1617
<ProjectReference Include="..\WebDefaultLambdaFunction\WebDefaultLambdaFunction.csproj" />
1718
<ProjectReference Include="..\WebMinusLambdaFunction\WebMinusLambdaFunction.csproj" />
1819
</ItemGroup>

playground/Lambda/Lambda.AppHost/Program.cs

+4-2
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@
1111
var defaultRouteLambda = builder.AddAWSLambdaFunction<Projects.WebDefaultLambdaFunction>("LambdaDefaultRoute", lambdaHandler: "WebDefaultLambdaFunction");
1212
var addRouteLambda = builder.AddAWSLambdaFunction<Projects.WebAddLambdaFunction>("AddDefaultRoute", lambdaHandler: "WebAddLambdaFunction");
1313
var minusRouteLambda = builder.AddAWSLambdaFunction<Projects.WebMinusLambdaFunction>("MinusDefaultRoute", lambdaHandler: "WebMinusLambdaFunction");
14-
14+
var listAwsResourcesRouteLambda = builder.AddAWSLambdaFunction<Projects.WebAWSCallsLambdaFunction>("ListAwsResourcesRoute", lambdaHandler: "WebAWSCallsLambdaFunction");
1515

1616
builder.AddAWSAPIGatewayEmulator("APIGatewayEmulator", Aspire.Hosting.AWS.Lambda.APIGatewayType.HttpV2)
1717
.WithReference(defaultRouteLambda, Method.Get, "/")
1818
.WithReference(addRouteLambda, Method.Get, "/add/{x}/{y}")
19-
.WithReference(minusRouteLambda, Method.Get, "/minus/{x}/{y}");
19+
.WithReference(minusRouteLambda, Method.Get, "/minus/{x}/{y}")
20+
.WithReference(listAwsResourcesRouteLambda, Method.Get, "/aws/{service}");
2021

2122
builder.Build().Run();
23+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
using Microsoft.AspNetCore.Builder;
2+
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
3+
using Microsoft.Extensions.DependencyInjection;
4+
using Microsoft.Extensions.Diagnostics.HealthChecks;
5+
using Microsoft.Extensions.Logging;
6+
using Microsoft.Extensions.ServiceDiscovery;
7+
using OpenTelemetry;
8+
using OpenTelemetry.Instrumentation.AWSLambda;
9+
using OpenTelemetry.Metrics;
10+
using OpenTelemetry.Trace;
11+
12+
namespace Microsoft.Extensions.Hosting
13+
{
14+
// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry.
15+
// This project should be referenced by each service project in your solution.
16+
// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults
17+
public static class Extensions
18+
{
19+
public static TBuilder AddServiceDefaults<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
20+
{
21+
builder.ConfigureOpenTelemetry();
22+
23+
builder.AddDefaultHealthChecks();
24+
25+
builder.Services.AddServiceDiscovery();
26+
27+
builder.Services.ConfigureHttpClientDefaults(http =>
28+
{
29+
// Turn on resilience by default
30+
http.AddStandardResilienceHandler();
31+
32+
// Turn on service discovery by default
33+
http.AddServiceDiscovery();
34+
});
35+
36+
// Uncomment the following to restrict the allowed schemes for service discovery.
37+
// builder.Services.Configure<ServiceDiscoveryOptions>(options =>
38+
// {
39+
// options.AllowedSchemes = ["https"];
40+
// });
41+
42+
return builder;
43+
}
44+
45+
public static TBuilder ConfigureOpenTelemetry<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
46+
{
47+
builder.Logging.AddOpenTelemetry(logging =>
48+
{
49+
logging.IncludeFormattedMessage = true;
50+
logging.IncludeScopes = true;
51+
});
52+
53+
builder.Services.AddOpenTelemetry()
54+
.WithMetrics(metrics =>
55+
{
56+
metrics.AddAspNetCoreInstrumentation()
57+
.AddHttpClientInstrumentation()
58+
.AddAWSInstrumentation()
59+
.AddRuntimeInstrumentation();
60+
})
61+
.WithTracing(tracing =>
62+
{
63+
tracing.AddSource(builder.Environment.ApplicationName)
64+
.AddAspNetCoreInstrumentation()
65+
.AddAWSInstrumentation()
66+
.AddAWSLambdaConfigurations(options => options.DisableAwsXRayContextExtraction = true)
67+
.AddHttpClientInstrumentation();
68+
});
69+
70+
builder.AddOpenTelemetryExporters();
71+
72+
return builder;
73+
}
74+
75+
private static TBuilder AddOpenTelemetryExporters<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
76+
{
77+
var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);
78+
79+
if (useOtlpExporter)
80+
{
81+
builder.Services.AddOpenTelemetry().UseOtlpExporter();
82+
}
83+
84+
return builder;
85+
}
86+
87+
public static TBuilder AddDefaultHealthChecks<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
88+
{
89+
builder.Services.AddHealthChecks()
90+
// Add a default liveness check to ensure app is responsive
91+
.AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);
92+
93+
return builder;
94+
}
95+
96+
public static WebApplication MapDefaultEndpoints(this WebApplication app)
97+
{
98+
// Adding health checks endpoints to applications in non-development environments has security implications.
99+
// See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments.
100+
if (app.Environment.IsDevelopment())
101+
{
102+
// All health checks must pass for app to be considered ready to accept traffic after starting
103+
app.MapHealthChecks("/health");
104+
105+
// Only health checks tagged with the "live" tag must pass for app to be considered alive
106+
app.MapHealthChecks("/alive", new HealthCheckOptions
107+
{
108+
Predicate = r => r.Tags.Contains("live")
109+
});
110+
}
111+
112+
return app;
113+
}
114+
}
115+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
<IsAspireSharedProject>true</IsAspireSharedProject>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<FrameworkReference Include="Microsoft.AspNetCore.App" />
12+
13+
<PackageReference Include="Microsoft.Extensions.Http.Resilience"/>
14+
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" />
15+
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" />
16+
<PackageReference Include="OpenTelemetry.Extensions.Hosting"/>
17+
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" />
18+
<PackageReference Include="OpenTelemetry.Instrumentation.Http"/>
19+
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" />
20+
<PackageReference Include="OpenTelemetry.Instrumentation.AWS" />
21+
<PackageReference Include="OpenTelemetry.Instrumentation.AWSLambda" />
22+
</ItemGroup>
23+
24+
</Project>

playground/Lambda/ToUpperLambdaFunctionExecutable/Function.cs

-18
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using Amazon.Lambda.APIGatewayEvents;
2+
using Amazon.Lambda.Core;
3+
using Amazon.Lambda.RuntimeSupport;
4+
using Amazon.Lambda.Serialization.SystemTextJson;
5+
using Microsoft.Extensions.Hosting;
6+
using OpenTelemetry.Instrumentation.AWSLambda;
7+
using OpenTelemetry.Trace;
8+
9+
namespace ToUpperLambdaFunctionExecutable;
10+
11+
internal class LambdaFunction(TracerProvider traceProvider) : BackgroundService
12+
{
13+
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
14+
{
15+
await LambdaBootstrapBuilder.Create<string, string>(TracingLambdaHandler, new DefaultLambdaJsonSerializer())
16+
.Build()
17+
.RunAsync(stoppingToken);
18+
}
19+
20+
private string TracingLambdaHandler(string input, ILambdaContext context)
21+
=> AWSLambdaWrapper.Trace(traceProvider, LambdaHandler, input, context);
22+
23+
private string LambdaHandler(string input, ILambdaContext context)
24+
{
25+
return input.ToUpper();
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using ToUpperLambdaFunctionExecutable;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using Microsoft.Extensions.Hosting;
4+
5+
var builder = new HostApplicationBuilder();
6+
7+
builder.AddServiceDefaults();
8+
builder.Services.AddHostedService<LambdaFunction>();
9+
10+
builder.Build().Run();

playground/Lambda/ToUpperLambdaFunctionExecutable/ToUpperLambdaFunctionExecutable.csproj

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<OutputType>Exe</OutputType>
44
<TargetFramework>net8.0</TargetFramework>
@@ -16,4 +16,7 @@
1616
<PackageReference Include="Amazon.Lambda.Core" />
1717
<PackageReference Include="Amazon.Lambda.Serialization.SystemTextJson" />
1818
</ItemGroup>
19+
<ItemGroup>
20+
<ProjectReference Include="..\Lambda.ServiceDefaults\Lambda.ServiceDefaults.csproj" />
21+
</ItemGroup>
1922
</Project>

0 commit comments

Comments
 (0)