Skip to content

Add suppression for IL3050 when serializing objects for structured logging. #1795

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
<WarningsAsErrors>IL2026,IL2067,IL2075</WarningsAsErrors>
<IsTrimmable>true</IsTrimmable>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ private void WriteException(Utf8JsonWriter writer, MessageState state)
/// <param name="directive"></param>
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026",
Justification = "If formatting an object using JSON serialization this will do its best attempt. If the object has trim errors formatting will fall back to ToString for the object.")]
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("ReflectionAnalysis", "IL3050",
Justification = "If formatting an object using JSON serialization this will do its best attempt. If the object has trim errors formatting will fall back to ToString for the object.")]
private void FormatJsonValue(Utf8JsonWriter writer, object value, string formatArguments, MessageProperty.Directive directive)
{
if(value == null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using System;
using System.Diagnostics;
using System.Text;
using System.Reflection;

using Xunit;
using Xunit.Abstractions;
using System.IO;

#if NET8_0_OR_GREATER
namespace Amazon.Lambda.RuntimeSupport.UnitTests
{
public class NativeAOTTests
{
private ITestOutputHelper _output;

public NativeAOTTests(ITestOutputHelper output)
{
_output = output;
}

[Fact]
public void EnsureNoTrimWarningsDuringPublish()
{
var projectDirectory = FindProject("NativeAOTFunction");

_output.WriteLine("dotnet publish " + projectDirectory);
var output = ExecutePublish(projectDirectory);
_output.WriteLine(output.Log);

Assert.True(output.ExitCode == 0);
Assert.DoesNotContain("AOT analysis warning", output.Log);
}

private (int ExitCode, string Log) ExecutePublish(string projectDirectory)
{
var buffer = new StringBuilder();
var handler = (DataReceivedEventHandler)((o, e) =>
{
if (string.IsNullOrEmpty(e.Data))
return;

buffer.AppendLine(e.Data);
});

var startInfo = new ProcessStartInfo
{
FileName = "dotnet",
Arguments = $"publish",
WorkingDirectory = projectDirectory,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};

int exitCode;
using (var proc = new Process())
{
proc.StartInfo = startInfo;
proc.Start();

if (startInfo.RedirectStandardOutput)
{
proc.ErrorDataReceived += handler;
proc.OutputDataReceived += handler;
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();

proc.EnableRaisingEvents = true;
}

proc.WaitForExit();


exitCode = proc.ExitCode;
}

return (exitCode, buffer.ToString());
}

private string FindProject(string projectName)
{
var directory = Assembly.GetExecutingAssembly().Location;
while (directory != null)
{
var fullpath = Path.Combine(directory, projectName);
if (Directory.Exists(fullpath))
{
return fullpath;
}

directory = Directory.GetParent(directory)?.FullName;
}

throw new Exception("Failed to find project directory " + projectName);
}
}
}
#endif
60 changes: 60 additions & 0 deletions Libraries/test/NativeAOTFunction/Function.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using Amazon.Lambda.Core;
using Amazon.Lambda.RuntimeSupport;
using Amazon.Lambda.Serialization.SystemTextJson;
using System.Text.Json.Serialization;

namespace StructureAOTTest;

public class Function
{
/// <summary>
/// The main entry point for the Lambda function. The main function is called once during the Lambda init phase. It
/// initializes the .NET Lambda runtime client passing in the function handler to invoke for each Lambda event and
/// the JSON serializer to use for converting Lambda JSON format to the .NET types.
/// </summary>
private static async Task Main()
{
Func<string, ILambdaContext, string> handler = FunctionHandler;
await LambdaBootstrapBuilder.Create(handler, new SourceGeneratorLambdaJsonSerializer<LambdaFunctionJsonSerializerContext>())
.Build()
.RunAsync();
}

/// <summary>
/// A simple function that takes a string and does a ToUpper.
///
/// To use this handler to respond to an AWS event, reference the appropriate package from
/// https://github.com/aws/aws-lambda-dotnet#events
/// and change the string input parameter to the desired event type. When the event type
/// is changed, the handler type registered in the main method needs to be updated and the LambdaFunctionJsonSerializerContext
/// defined below will need the JsonSerializable updated. If the return type and event type are different then the
/// LambdaFunctionJsonSerializerContext must have two JsonSerializable attributes, one for each type.
///
// When using Native AOT extra testing with the deployed Lambda functions is required to ensure
// the libraries used in the Lambda function work correctly with Native AOT. If a runtime
// error occurs about missing types or methods the most likely solution will be to remove references to trim-unsafe
// code or configure trimming options. This sample defaults to partial TrimMode because currently the AWS
// SDK for .NET does not support trimming. This will result in a larger executable size, and still does not
// guarantee runtime trimming errors won't be hit.
/// </summary>
/// <param name="input">The event for the Lambda function handler to process.</param>
/// <param name="context">The ILambdaContext that provides methods for logging and describing the Lambda environment.</param>
/// <returns></returns>
public static string FunctionHandler(string input, ILambdaContext context)
{
return input.ToUpper();
}
}

/// <summary>
/// This class is used to register the input event and return type for the FunctionHandler method with the System.Text.Json source generator.
/// There must be a JsonSerializable attribute for each type used as the input and return type or a runtime error will occur
/// from the JSON serializer unable to find the serialization information for unknown types.
/// </summary>
[JsonSerializable(typeof(string))]
public partial class LambdaFunctionJsonSerializerContext : JsonSerializerContext
{
// By using this partial class derived from JsonSerializerContext, we can generate reflection free JSON Serializer code at compile time
// which can deserialize our class and properties. However, we must attribute this class to tell it what types to generate serialization code for.
// See https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-source-generation
}
25 changes: 25 additions & 0 deletions Libraries/test/NativeAOTFunction/NativeAOTFunction.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<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>true</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>
<TrimmerSingleWarn>false</TrimmerSingleWarn>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Amazon.Lambda.Core\Amazon.Lambda.Core.csproj" />
<ProjectReference Include="..\..\src\Amazon.Lambda.RuntimeSupport\Amazon.Lambda.RuntimeSupport.csproj" />
<ProjectReference Include="..\..\src\Amazon.Lambda.Serialization.SystemTextJson\Amazon.Lambda.Serialization.SystemTextJson.csproj" />
</ItemGroup>
</Project>
16 changes: 16 additions & 0 deletions Libraries/test/NativeAOTFunction/aws-lambda-tools-defaults.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"Information": [
"This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.",
"To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.",
"dotnet lambda help",
"All the command line options for the Lambda command can be specified in this file."
],
"profile": "default",
"region": "us-west-2",
"configuration": "Release",
"function-runtime": "dotnet8",
"function-memory-size": 512,
"function-timeout": 30,
"function-handler": "NativeAOTFunction",
"msbuild-parameters": "--self-contained true"
}
Loading