Bug: AoT does not work with metrics or logger
Opened this issue ยท 9 comments
Expected Behaviour
Logger should work with AoT trimming enabled.
Current Behaviour
The following exception is thrown
10-Oct-2024 9:34:07 AM 2024-10-10T13:34:07.448Z 9bc362e6-76bc-4f22-b004-8c5622ca8078 fail System.InvalidOperationException: Reflection-based serialization has been disabled for this application. Either use the source generator APIs or explicitly configure the 'JsonSerializerOptions.TypeInfoResolver' property.
at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_JsonSerializerIsReflectionDisabled()
at System.Text.Json.JsonSerializerOptions.ConfigureForJsonSerializer()
at System.Text.Json.JsonSerializer.GetTypeInfo(JsonSerializerOptions, Type)
at System.Text.Json.JsonSerializer.GetTypeInfo[T](JsonSerializerOptions)
at System.Text.Json.JsonSerializer.Serialize[TValue](TValue, JsonSerializerOptions )
at AWS.Lambda.Powertools.Logging.Serializers.PowertoolsLoggingSerializer.Serialize(Object, Type)
at AWS.Lambda.Powertools.Logging.Internal.PowertoolsLogger.Log[TState](LogLevel, EventId, TState, Exception, Func`3)
at Microsoft.Extensions.Logging.LoggerExtensions.Log(ILogger , LogLevel, EventId, Exception, String, Object[] )
at Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(ILogger, String , Object[] )
at submit_feedback.Function.FunctionHandlerAsync(SubmitFeedbackRequest request, ILambdaContext context) in ./Function.cs:line 63
at Amazon.Lambda.RuntimeSupport.HandlerWrapper.<>c__DisplayClass14_0`1.<<GetHandlerWrapper>b__0>d.MoveNext()
--- End of stack trace from previous location ---
at Amazon.Lambda.RuntimeSupport.LambdaBootstrap.InvokeOnceAsync(CancellationToken )
Code snippet
Here are the relevant lines of what I have in my csproj file (Let me know if you need more context):
<PropertyGroup>
<PublishReadyToRun>true</PublishReadyToRun>
<StripSymbols>true</StripSymbols>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>partial</TrimMode>
</PropertyGroup>
<ItemGroup>
<!-- Exclude EF Core assemblies from being trimmed (not supported) -->
<TrimmerRootAssembly Include="Microsoft.EntityFrameworkCore" />
<TrimmerRootAssembly Include="Microsoft.EntityFrameworkCore.Relational" />
<TrimmerRootAssembly Include="Npgsql.EntityFrameworkCore.PostgreSQL" />
<TrimmerRootAssembly Include="EFCore.NamingConventions" />
</ItemGroup>
Here's how I've been able to minimally reproduce this issue:
public class Function
{
private static async Task Main()
{
Console.WriteLine("Starting up");
Func<SubmitFeedbackRequest, ILambdaContext, Task> handler = FunctionHandlerAsync;
await LambdaBootstrapBuilder.Create(handler, new PowertoolsSourceGeneratorSerializer<LambdaFunctionJsonContext>())
.Build()
.RunAsync();
Console.WriteLine("Shutting down");
}
[Logging]
public static async Task FunctionHandlerAsync(SubmitFeedbackRequest request, ILambdaContext context)
{
Logger.LogInformation("Starting up!");
}
}
[JsonSerializable(typeof(SubmitFeedbackRequest))]
public partial class LambdaFunctionJsonContext : JsonSerializerContext { }
For reference, here are my package versions:
Possible Solution
No response
Steps to Reproduce
See code snippet.
Powertools for AWS Lambda (.NET) version
latest
AWS Lambda function runtime
dotnet8 (AOT)
Debugging logs
No response
@Euclidite I can reproduce the error you are getting when I have the PublishTrimmed
The correct configuration to run Lambda on AOT would be <PublishAot>true</PublishAot>
instead of <PublishTrimmed>true</PublishTrimmed>
:
<!-- Generate Native AOT image during publishing to improve cold start time. -->
<PublishAot>true</PublishAot>
<!-- StripSymbols tells the compiler to strip debugging symbols from the final executable if we're on Linux and put them into their own file.
This will greatly reduce the final executable's size.-->
<StripSymbols>true</StripSymbols>
<!-- TrimMode partial will only trim assemblies marked as trimmable. To reduce package size make all assemblies trimmable and set TrimMode to full.
If there are trim warnings during build, you can hit errors at runtime.-->
<TrimMode>partial</TrimMode>
One explanation for the differences between both those properties dotnet/runtime#97288 (comment)
Also you don't need the <PublishReadyToRun>true</PublishReadyToRun>
property
The config I was using was to slowly get to full AoT because using true forces it to use a docker build, so I wanted to chip away at a single problem at a time ๐
Regardless, I tested out with your suggested changes, but I still get the same error message.
Indeed local dev docker is required, I usually use a dev box ie: an ec2 AL2023 and develop/piblish from there, no docker needed ๐
Same error message? On the code you shared?
Oh! I might look into setting that up once we iron out some of our build pipelines - thanks for the tip!
And yes:
- I didn't change anything from my sample Function code
- I updated the .csproj with your suggested changes
- Published (it invoked docker, built, pushed)
- Invoked - same error as originally noted
I can't seem to replicate the issue, here is my code:
I even changed main to the initial code you had.
Function.cs
using Amazon.Lambda.Core;
using Amazon.Lambda.RuntimeSupport;
using AWS.Lambda.Powertools.Logging;
using AWS.Lambda.Powertools.Logging.Serializers;
using System.Text.Json.Serialization;
namespace pt_aot_test;
public class Function
{
private static async Task Main()
{
await LambdaBootstrapBuilder.Create<SubmitFeedbackRequest>(
FunctionHandlerAsync,
new PowertoolsSourceGeneratorSerializer<LambdaFunctionJsonContext>())
.Build()
.RunAsync();
}
[Logging]
public static async Task FunctionHandlerAsync(SubmitFeedbackRequest request, ILambdaContext context)
{
Logger.LogInformation("Starting up!");
}
}
public class SubmitFeedbackRequest
{
public int Id { get; set; }
}
[JsonSerializable(typeof(SubmitFeedbackRequest))]
public partial class LambdaFunctionJsonContext : JsonSerializerContext { }
csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AWSProjectType>Lambda</AWSProjectType>
<!-- This property makes the build directory similar to a publish directory and helps the AWS .NET Lambda Mock Test Tool find project dependencies. -->
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
<!-- Generate Native AOT image during publishing to improve cold start time. -->
<PublishAot>true</PublishAot>
<!-- StripSymbols tells the compiler to strip debugging symbols from the final executable if we're on Linux and put them into their own file.
This will greatly reduce the final executable's size.-->
<StripSymbols>true</StripSymbols>
<!-- TrimMode partial will only trim assemblies marked as trimmable. To reduce package size make all assemblies trimmable and set TrimMode to full.
If there are trim warnings during build, you can hit errors at runtime.-->
<TrimMode>partial</TrimMode>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Amazon.Lambda.APIGatewayEvents" Version="2.7.1" />
<PackageReference Include="Amazon.Lambda.RuntimeSupport" Version="1.11.0" />
<PackageReference Include="Amazon.Lambda.Core" Version="2.3.0" />
<PackageReference Include="Amazon.Lambda.Serialization.SystemTextJson" Version="2.4.3" />
<PackageReference Include="AWS.Lambda.Powertools.Logging" Version="1.6.1" />
</ItemGroup>
</Project>
aws-lambda-tools-defaults.json (don't think it matters)
"profile": "",
"region": "eu-west-1",
"configuration": "Release",
"function-runtime": "dotnet8",
"function-memory-size": 512,
"function-timeout": 5,
"function-handler": "pt-aot-test",
"function-architecture": "x86_64",
"framework": "net8.0",
"msbuild-parameters": "--self-contained true"
I've compared the values, and I have them set identically. I retested just in case and still get the same error (or well slightly different, but I think that's simply because of the AoT)
11-Oct-2024 1:19:49 AM 2024-10-11T05:19:49.133Z da531fee-6b61-46b3-bda9-ac09127aa30c fail System.InvalidOperationException: Reflection-based serialization has been disabled for this application. Either use the source generator APIs or explicitly configure the 'JsonSerializerOptions.TypeInfoResolver' property.
at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_JsonSerializerIsReflectionDisabled()
at System.Text.Json.JsonSerializerOptions.ConfigureForJsonSerializer()
at System.Text.Json.JsonSerializer.GetTypeInfo(JsonSerializerOptions, Type)
at System.Text.Json.JsonSerializer.GetTypeInfo[T](JsonSerializerOptions)
at System.Text.Json.JsonSerializer.Serialize[TValue](TValue, JsonSerializerOptions )
at AWS.Lambda.Powertools.Logging.Serializers.PowertoolsLoggingSerializer.Serialize(Object, Type)
at AWS.Lambda.Powertools.Logging.Internal.PowertoolsLogger.Log[TState](LogLevel, EventId, TState, Exception, Func`3)
at Microsoft.Extensions.Logging.LoggerExtensions.Log(ILogger , LogLevel, EventId, Exception, String, Object[] )
at Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(ILogger, String , Object[] )
at submit_feedback.Function.FunctionHandlerAsync(SubmitFeedbackRequest request, ILambdaContext context)
at Amazon.Lambda.RuntimeSupport.HandlerWrapper.<>c__DisplayClass14_0`1.<<GetHandlerWrapper>b__0>d.MoveNext()
--- End of stack trace from previous location ---
at Amazon.Lambda.RuntimeSupport.LambdaBootstrap.InvokeOnceAsync(CancellationToken )
For reference, here's the SubmitFeedbackRequest
class - but I don't see anything that would cause an issue
public class SubmitFeedbackRequest
{
[Required(AllowEmptyStrings = false)]
public required string SessionId { get; set; }
[MinLength(1)] // This logs an AOT warning, but its safe to ignore and will be fixed in a future release
public required IEnumerable<int> FeedbackType { get; set; }
[Range(1, int.MaxValue)]
public required int ReferenceId { get; set; }
[EmailAddress]
public required string UserEmail { get; set; }
}
I do see these warnings in the build:
... docker run: /tmp/dotnet/.nuget/packages/aws.lambda.powertools.logging/1.6.1/lib/net8.0/AWS.Lambda.Powertools.Logging.dll : warning IL3053: Assembly 'AWS.Lambda.Powertools.Logging' produced AOT analysis warnings. [/tmp/source/submitFeedback/submit-feedback.csproj]
... docker run: /tmp/dotnet/.nuget/packages/aws.lambda.powertools.logging/1.6.1/lib/net8.0/AWS.Lambda.Powertools.Logging.dll : warning IL2104: Assembly 'AWS.Lambda.Powertools.Logging' produced trim warnings. For more information see https://aka.ms/dotnet-illink/libraries [/tmp/source/submitFeedback/submit-feedback.csproj]
... docker run: ILC : Trim analysis warning IL2026: AWS.Lambda.Powertools.Logging.Serializers.PowertoolsLoggingSerializationContext.ExceptionSerializeHandler(Utf8JsonWriter,Exception): Using member 'System.Exception.TargetSite.get' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Metadata for the method might be incomplete or removed. [/tmp/source/submitFeedback/submit-feedback.csproj]
... docker run: ILC : Trim analysis warning IL2026: AWS.Lambda.Powertools.Logging.Serializers.PowertoolsLoggingSerializationContext.<>c.<ExceptionPropInit>b__70_10(Object): Using member 'System.Exception.TargetSite.get' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Metadata for the method might be incomplete or removed. [/tmp/source/submitFeedback/submit-feedback.csproj]
Thanks for providing more details @Euclidite .
I am having a hard time reproducing the error. Does the code work without Powertools?
I have updated my code to match your SubmitFeedbackRequest
with DataAnnotations and still works.
Function.cs
using Amazon.Lambda.Core;
using Amazon.Lambda.RuntimeSupport;
using AWS.Lambda.Powertools.Logging;
using AWS.Lambda.Powertools.Logging.Serializers;
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
namespace pt_aot_test;
public class Function
{
private static async Task Main()
{
await LambdaBootstrapBuilder.Create<SubmitFeedbackRequest>(
FunctionHandlerAsync,
new PowertoolsSourceGeneratorSerializer<LambdaFunctionJsonContext>())
.Build()
.RunAsync();
}
[Logging]
public static async Task FunctionHandlerAsync(SubmitFeedbackRequest request, ILambdaContext context)
{
Logger.LogInformation("Starting up!");
Logger.LogInformation(request);
}
}
public class SubmitFeedbackRequest
{
[Required(AllowEmptyStrings = false)]
public required string SessionId { get; set; }
[MinLength(1)] // This logs an AOT warning, but its safe to ignore and will be fixed in a future release
public required IEnumerable<int> FeedbackType { get; set; }
[Range(1, int.MaxValue)]
public required int ReferenceId { get; set; }
[EmailAddress]
public required string UserEmail { get; set; }
}
[JsonSerializable(typeof(SubmitFeedbackRequest))]
public partial class LambdaFunctionJsonContext : JsonSerializerContext { }
Request
{
"SessionId": "123",
"FeedbackType": [1,2],
"ReferenceId": 1,
"UserEmail": "mail@mail.com"
}
Response
{
"coldStart": true,
"xrayTraceId": "1-6708d42c-7547436656f7808d4abaafd9",
"functionName": "pt-aot-test",
"functionVersion": "$LATEST",
"functionMemorySize": 512,
"functionArn": "-",
"functionRequestId": "7ddfcf41-436e-45c7-bcec-f56cc530f469",
"timestamp": "2024-10-11T07:30:52.9777292Z",
"level": "Information",
"service": "henrique_service",
"name": "AWS.Lambda.Powertools.Logging.Logger",
"message": "Starting up!"
}
{
"coldStart": true,
"xrayTraceId": "1-6708d42c-7547436656f7808d4abaafd9",
"functionName": "pt-aot-test",
"functionVersion": "$LATEST",
"functionMemorySize": 512,
"functionArn": "-",
"functionRequestId": "7ddfcf41-436e-45c7-bcec-f56cc530f469",
"timestamp": "2024-10-11T07:30:52.9778246Z",
"level": "Information",
"service": "henrique_service",
"name": "AWS.Lambda.Powertools.Logging.Logger",
"message": {
"sessionId": "123",
"feedbackType": [
1,
2
],
"referenceId": 1,
"userEmail": "mail@mail.com"
}
}
If you want we can have a chat to go over this issue, just send me an email henrigra@amazon.com, or reach out on our discord