Skip to content
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

[AzureMonitorExporter] resolve AOT warnings #38459

Merged
merged 26 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from 11 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
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net7.0;netstandard2.0</TargetFrameworks>
TimothyMothra marked this conversation as resolved.
Show resolved Hide resolved
<PublishAot>true</PublishAot>
<TrimmerSingleWarn>false</TrimmerSingleWarn>
<IsTestSupportProject>true</IsTestSupportProject>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\..\core\Azure.Core\src\Azure.Core.csproj" />
TimothyMothra marked this conversation as resolved.
Show resolved Hide resolved
<ProjectReference Include="..\..\src\Azure.Monitor.OpenTelemetry.Exporter.csproj" />

<TrimmerRootAssembly Include="Azure.Monitor.OpenTelemetry.Exporter" />

<!-- Update this dependency to its latest, which has all the annotations -->
<PackageReference Include="Microsoft.Extensions.Logging.Configuration" VersionOverride="8.0.0-rc.1.23419.4" />
</ItemGroup>

<!--https://github.com/dotnet/runtime/issues/91965-->
TimothyMothra marked this conversation as resolved.
Show resolved Hide resolved
<ItemGroup>
<_NoWarn Include="$(NoWarn)" />
</ItemGroup>
<PropertyGroup>
<NoWarn>@(_NoWarn)</NoWarn>
</PropertyGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace Azure.Monitor.OpenTelemetry.Exporter.AotCompatibilityTestApp;

internal class Program
{
public static void Main(string[] args)
{
System.Console.WriteLine("Hello, World!");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
param([string]$targetNetFramework)

dotnet restore
$publishOutput = dotnet publish --framework net7.0 -nodeReuse:false /p:UseSharedCompilation=false /p:ExposeExperimentalFeatures=true

if ($LASTEXITCODE -ne 0)
{
Write-Host "Publish failed."
Write-Host $publishOutput
Exit 2
}

$actualWarningCount = 0

foreach ($line in $($publishOutput -split "`r`n"))
{
if ($line -like "*analysis warning IL*")
{
Write-Host $line

$actualWarningCount += 1
}
}

Write-Host "Actual warning count is:", $actualWarningCount
$expectedWarningCount = 7

$testPassed = 0
if ($actualWarningCount -ne $expectedWarningCount)
{
$testPassed = 1
Write-Host "Actual warning count:", actualWarningCount, "is not as expected. Expected warning count is:", $expectedWarningCount
}

Exit $testPassed
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>An OpenTelemetry .NET exporter that exports to Azure Monitor</Description>
<AssemblyTitle>AzureMonitor OpenTelemetry Exporter</AssemblyTitle>
<Version>1.1.0-beta.1</Version>
<!--The ApiCompatVersion is managed automatically and should not generally be modified manually.-->
<ApiCompatVersion>1.0.0</ApiCompatVersion>
<!--<ApiCompatVersion>1.0.0</ApiCompatVersion>-->
<PackageTags>Azure Monitor OpenTelemetry Exporter ApplicationInsights</PackageTags>
<TargetFrameworks>$(RequiredTargetFrameworks)</TargetFrameworks>
<TargetFrameworks>net6.0;$(RequiredTargetFrameworks)</TargetFrameworks>
<IncludeOperationsSharedSource>true</IncludeOperationsSharedSource>

<!--TODO: ApiCompat is failing because I added net6. Will follow up how to correctly resolve.-->
<!--<ApiCompatBaselineTargetFramework Condition="'$(TargetFramework)' == 'net6.0'">netstandard2.0</ApiCompatBaselineTargetFramework>-->
<RunApiCompat>false</RunApiCompat>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Diagnostics.CodeAnalysis;
using Azure.Monitor.OpenTelemetry.Exporter.Internals;

namespace Azure.Monitor.OpenTelemetry.Exporter.Models
Expand All @@ -10,14 +11,19 @@ internal partial class StackFrame
/// <summary>
/// Converts a System.Diagnostics.StackFrame to a Azure.Monitor.OpenTelemetry.Exporter.Models.StackFrame.
/// </summary>
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We handle the null condition and call ToString() instead.")]
TimothyMothra marked this conversation as resolved.
Show resolved Hide resolved
public StackFrame(System.Diagnostics.StackFrame stackFrame, int frameId)
{
string fullName, assemblyName;

var methodInfo = stackFrame.GetMethod();

if (methodInfo == null)
{
fullName = "unknown";
// In an AOT scenario GetMethod() will return null.
// Instead, call ToString() which gives a string like this:
// "MethodName + 0x00 at offset 000 in file:line:column <filename unknown>:0:0"
fullName = stackFrame.ToString();
TimothyMothra marked this conversation as resolved.
Show resolved Hide resolved
assemblyName = "unknown";
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Tracing;
using System.Runtime.CompilerServices;
using Azure.Monitor.OpenTelemetry.Exporter.Internals.ConnectionString;
Expand Down Expand Up @@ -87,7 +88,7 @@ public void TransmissionFailed(int statusCode, TelemetryItemOrigin origin, bool
isAadEnabled: isAadEnabled,
instrumentationKey: connectionVars.InstrumentationKey,
configuredEndpoint: connectionVars.IngestionEndpoint,
actualEndpoint: requestEndpoint);
actualEndpoint: requestEndpoint ?? "null");
}
else
{
Expand All @@ -101,7 +102,7 @@ public void TransmissionFailed(int statusCode, TelemetryItemOrigin origin, bool
isAadEnabled: isAadEnabled,
instrumentationKey: connectionVars.InstrumentationKey,
configuredEndpoint: connectionVars.IngestionEndpoint,
actualEndpoint: requestEndpoint);
actualEndpoint: requestEndpoint ?? "null");
}
}
}
Expand All @@ -115,12 +116,13 @@ public void TransmissionFailed(int statusCode, TelemetryItemOrigin origin, bool
isAadEnabled: isAadEnabled,
instrumentationKey: connectionVars.InstrumentationKey,
configuredEndpoint: connectionVars.IngestionEndpoint,
actualEndpoint: requestEndpoint);
actualEndpoint: requestEndpoint ?? "null");
}
}

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Parameters to this method are primitive and are trimmer safe.")]
[Event(8, Message = "Transmission failed. StatusCode: {0}. Error from Ingestion: {1}. Action: {2}. Origin: {3}. AAD Enabled: {4}. Instrumentation Key: {5}. Configured Endpoint: {6}. Actual Endpoint: {7}", Level = EventLevel.Critical)]
public void TransmissionFailed(int statusCode, string errorMessage, string action, string origin, bool isAadEnabled, string instrumentationKey, string configuredEndpoint, string? actualEndpoint)
public void TransmissionFailed(int statusCode, string errorMessage, string action, string origin, bool isAadEnabled, string instrumentationKey, string configuredEndpoint, string actualEndpoint)
=> WriteEvent(8, statusCode, errorMessage, action, origin, isAadEnabled, instrumentationKey, configuredEndpoint, actualEndpoint);

[Event(9, Message = "{0} has been disposed.", Level = EventLevel.Informational)]
Expand Down Expand Up @@ -213,6 +215,7 @@ public void FailedToExtractActivityEvent(string activitySourceName, string activ
[Event(17, Message = "Failed to extract Activity Event due to an exception. This telemetry item will be lost. ActivitySource: {0}. Activity: {1}. {2}", Level = EventLevel.Error)]
public void FailedToExtractActivityEvent(string activitySourceName, string activityDisplayName, string exceptionMessage) => WriteEvent(17, activitySourceName, activityDisplayName, exceptionMessage);

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Parameters to this method are primitive and are trimmer safe.")]
[Event(18, Message = "Maximum count of {0} Activity Links reached. Excess Links are dropped. ActivitySource: {1}. Activity: {2}.", Level = EventLevel.Warning)]
public void ActivityLinksIgnored(int maxLinksAllowed, string activitySourceName, string activityDisplayName) => WriteEvent(18, maxLinksAllowed, activitySourceName, activityDisplayName);

Expand Down Expand Up @@ -279,9 +282,11 @@ public void FailedToTransmitFromStorage(bool isAadEnabled, string instrumentatio
}
}

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Parameters to this method are primitive and are trimmer safe.")]
[Event(25, Message = "Failed to transmit from storage due to an exception. AAD Enabled: {0}. Instrumentation Key: {1}. {2}", Level = EventLevel.Error)]
public void FailedToTransmitFromStorage(bool isAadEnabled, string instrumentationKey, string exceptionMessage) => WriteEvent(25, isAadEnabled, instrumentationKey, exceptionMessage);

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Parameters to this method are primitive and are trimmer safe.")]
[Event(26, Message = "Successfully transmitted a blob from storage. AAD Enabled: {0}. Instrumentation Key: {1}", Level = EventLevel.Verbose)]
public void TransmitFromStorageSuccess(bool isAadEnabled, string instrumentationKey) => WriteEvent(26, isAadEnabled, instrumentationKey);

Expand Down Expand Up @@ -327,6 +332,7 @@ public void TransmissionSuccess(TelemetryItemOrigin origin, bool isAadEnabled, s
}
}

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Parameters to this method are primitive and are trimmer safe.")]
[Event(32, Message = "Successfully transmitted a batch of telemetry Items. Origin: {0}. AAD: {1}. Instrumentation Key: {2}", Level = EventLevel.Verbose)]
public void TransmissionSuccess(string origin, bool isAadEnabled, string instrumentationKey) => WriteEvent(32, origin, isAadEnabled, instrumentationKey);

Expand All @@ -339,9 +345,11 @@ public void TransmitterFailed(TelemetryItemOrigin origin, bool isAadEnabled, str
}
}

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Parameters to this method are primitive and are trimmer safe.")]
[Event(33, Message = "Transmitter failed due to an exception. Origin: {0}. AAD: {1}. Instrumentation Key: {2}. {3}", Level = EventLevel.Error)]
public void TransmitterFailed(string origin, bool isAadEnabled, string instrumentationKey, string exceptionMessage) => WriteEvent(33, origin, isAadEnabled, instrumentationKey, exceptionMessage);

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Parameters to this method are primitive and are trimmer safe.")]
[Event(34, Message = "Exporter encountered a transmission failure and will wait {0} milliseconds before transmitting again.", Level = EventLevel.Warning)]
public void BackoffEnabled(double milliseconds) => WriteEvent(34, milliseconds);

Expand Down Expand Up @@ -372,6 +380,7 @@ public void PartialContentResponseInvalid(int totalTelemetryItems, TelemetryErro
}
}

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Parameters to this method are primitive and are trimmer safe.")]
[Event(38, Message = "Received a partial success from ingestion that does not match telemetry that was sent. Total telemetry items sent: {0}. Error Index: {1}. Error StatusCode: {2}. Error Message: {3}", Level = EventLevel.Warning)]
public void PartialContentResponseInvalid(int totalTelemetryItems, string errorIndex, string errorStatusCode, string errorMessage) => WriteEvent(38, totalTelemetryItems, errorIndex, errorStatusCode, errorMessage);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,13 @@ internal static class IngestionResponseHelper

var responseContent = response.Content.ToString();

var responseObj = JsonSerializer.Deserialize<ResponseObject>(responseContent);
ResponseObject? responseObj;

#if NET6_0_OR_GREATER
responseObj = JsonSerializer.Deserialize<ResponseObject>(responseContent, SourceGenerationContext.Default.ResponseObject);
#else
responseObj = JsonSerializer.Deserialize<ResponseObject>(responseContent);
#endif

if (responseObj == null || responseObj.Errors == null)
{
Expand All @@ -39,7 +45,7 @@ internal static class IngestionResponseHelper
}
}

private class ResponseObject
internal class ResponseObject
{
[JsonPropertyName("itemsReceived")]
public int ItemsReceived { get; set; }
Expand All @@ -51,7 +57,7 @@ private class ResponseObject
public List<ErrorObject>? Errors { get; set; }
}

private class ErrorObject
internal class ErrorObject
{
[JsonPropertyName("index")]
public int Index { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
using System.Text;
Expand Down Expand Up @@ -129,6 +130,7 @@ void ProcessScope(LogRecordScope scope, IDictionary<string, string> properties)
}
}

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We handle the null condition and call ToString() instead.")]
internal static string GetProblemId(Exception exception)
{
string methodName = "UnknownMethod";
Expand All @@ -142,7 +144,14 @@ internal static string GetProblemId(Exception exception)
{
MethodBase? methodBase = exceptionStackFrame.GetMethod();

if (methodBase != null)
if (methodBase == null)
{
// In an AOT scenario GetMethod() will return null.
// Instead, call ToString() which gives a string like this:
// "MethodName + 0x00 at offset 000 in file:line:column <filename unknown>:0:0"
methodName = exceptionStackFrame.ToString();
}
else
{
methodName = (methodBase.DeclaringType?.FullName ?? "Global") + "." + methodBase.Name;
methodOffset = exceptionStackFrame.GetILOffset();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Azure.Monitor.OpenTelemetry.Exporter.Internals.Statsbeat;
using System.Text.Json.Serialization;

namespace Azure.Monitor.OpenTelemetry.Exporter.Internals;

#if NET6_0_OR_GREATER
// "Source Generation" is feature added to System.Text.Json in .NET 6.0.
// This is a performance optimization that avoids runtime reflection when performing serialization.
// Serialization metadata will be computed at compile-time and included in the assembly.
// https://learn.microsoft.com/dotnet/standard/serialization/system-text-json/source-generation-modes
TimothyMothra marked this conversation as resolved.
Show resolved Hide resolved
// https://learn.microsoft.com/dotnet/standard/serialization/system-text-json/source-generation
// https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-source-generator/
[JsonSerializable(typeof(VmMetadataResponse))]
[JsonSerializable(typeof(string))]
[JsonSerializable(typeof(IngestionResponseHelper.ResponseObject))]
[JsonSerializable(typeof(IngestionResponseHelper.ErrorObject))]
[JsonSerializable(typeof(int))]
internal partial class SourceGenerationContext : JsonSerializerContext
{
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,12 @@ private Measurement<int> GetAttachStatsbeat()
{
httpClient.DefaultRequestHeaders.Add("Metadata", "True");
var responseString = httpClient.GetStringAsync(StatsbeatConstants.AMS_Url);
var vmMetadata = JsonSerializer.Deserialize<VmMetadataResponse>(responseString.Result);
VmMetadataResponse? vmMetadata;
#if NET6_0_OR_GREATER
vmMetadata = JsonSerializer.Deserialize<VmMetadataResponse>(responseString.Result, SourceGenerationContext.Default.VmMetadataResponse);
#else
vmMetadata = JsonSerializer.Deserialize<VmMetadataResponse>(responseString.Result);
#endif

return vmMetadata;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,15 @@ public void TestNullMethodInfoInStack()
#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type.
#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.

// In AOT, StackFrame.GetMethod() can return null.
// In this instance, we fall back to StackFrame.ToString()
frameMock.Setup(x => x.ToString()).Returns("MethodName + 0x00 at offset 000 in file:line:column <filename unknown>:0:0");

Models.StackFrame stackFrame = new Models.StackFrame(frameMock.Object, 0);

Assert.Equal("unknown", stackFrame.Assembly);
Assert.Null(stackFrame.FileName);
Assert.Equal("unknown", stackFrame.Method);
Assert.Equal("MethodName + 0x00 at offset 000 in file:line:column <filename unknown>:0:0", stackFrame.Method);
Assert.Null(stackFrame.Line);
}

Expand Down
13 changes: 13 additions & 0 deletions sdk/monitor/Azure.Monitor.OpenTelemetry.sln
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Monitor.OpenTelemetry
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Monitor.OpenTelemetry.AspNetCore.Tests", "Azure.Monitor.OpenTelemetry.AspNetCore\tests\Azure.Monitor.OpenTelemetry.AspNetCore.Tests\Azure.Monitor.OpenTelemetry.AspNetCore.Tests.csproj", "{8E1E087C-6620-44D9-871B-9E2E512F5C4B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Core", "..\core\Azure.Core\src\Azure.Core.csproj", "{B355FA7D-5B4F-41AF-8ADD-D52D70006D55}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Monitor.OpenTelemetry.Exporter.AotCompatibilityTestApp", "Azure.Monitor.OpenTelemetry.Exporter\aotcompat\Azure.Monitor.OpenTelemetry.Exporter.AotCompatibilityTestApp\Azure.Monitor.OpenTelemetry.Exporter.AotCompatibilityTestApp.csproj", "{8D4C4C1A-E9F0-416A-9153-102382071448}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -102,6 +106,14 @@ Global
{8E1E087C-6620-44D9-871B-9E2E512F5C4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8E1E087C-6620-44D9-871B-9E2E512F5C4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8E1E087C-6620-44D9-871B-9E2E512F5C4B}.Release|Any CPU.Build.0 = Release|Any CPU
{B355FA7D-5B4F-41AF-8ADD-D52D70006D55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B355FA7D-5B4F-41AF-8ADD-D52D70006D55}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B355FA7D-5B4F-41AF-8ADD-D52D70006D55}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B355FA7D-5B4F-41AF-8ADD-D52D70006D55}.Release|Any CPU.Build.0 = Release|Any CPU
{8D4C4C1A-E9F0-416A-9153-102382071448}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8D4C4C1A-E9F0-416A-9153-102382071448}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8D4C4C1A-E9F0-416A-9153-102382071448}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8D4C4C1A-E9F0-416A-9153-102382071448}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -111,6 +123,7 @@ Global
{49762850-701A-4C50-AF47-0C09A43BFF04} = {A24DE545-2C5C-4AD8-8FFB-D44CBE51C26F}
{920DAB58-3162-4D94-9AC1-10C97B9B306F} = {0B1EBB34-30DC-40EE-BC3E-8871B2C22CE2}
{A40C99C0-774F-48CC-AE25-304011E6265B} = {0B1EBB34-30DC-40EE-BC3E-8871B2C22CE2}
{B355FA7D-5B4F-41AF-8ADD-D52D70006D55} = {A24DE545-2C5C-4AD8-8FFB-D44CBE51C26F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A97F4B90-2591-4689-B1F8-5F21FE6D6CAE}
Expand Down
Loading