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

Implement OpenTelemetry infrastructure #11255

Open
wants to merge 59 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
ff9341d
implement, add tests
JanProvaznik Jan 10, 2025
9fe19f8
Merge branch 'main' into otel-basic
JanProvaznik Jan 10, 2025
a2f0720
update app.config s
JanProvaznik Jan 10, 2025
96eeeee
Merge branch 'otel-basic' of https://github.com/JanProvaznik/msbuild …
JanProvaznik Jan 10, 2025
49467bb
fix test envvar behavior
JanProvaznik Jan 13, 2025
cdaa52a
apply feedback
JanProvaznik Jan 13, 2025
2c28a87
don't use ConversionUtilities
JanProvaznik Jan 13, 2025
cf089e9
fix: dispose before flushing
JanProvaznik Jan 13, 2025
7b88424
Move telemetry envvar handling to Traits
JanProvaznik Jan 14, 2025
49c429d
downgrade packages, manage lifetime of telemetry in BuildManager
JanProvaznik Jan 14, 2025
c95245c
test what happens in ci with otel
JanProvaznik Jan 14, 2025
9189121
testing in ci
JanProvaznik Jan 15, 2025
9dfb8c9
remove explicit adding source to exporter, wildcard from VS default h…
JanProvaznik Jan 15, 2025
3385704
remove assembly redirect
JanProvaznik Jan 16, 2025
a43acbd
remove binding redirect
JanProvaznik Jan 16, 2025
b2f0601
remove explicit Extensions primitives reference
JanProvaznik Jan 16, 2025
fab67b5
undo binding asyncinterface binding redirect deletion
JanProvaznik Jan 16, 2025
da883a9
reintroduce diagnosticsource redirect
JanProvaznik Jan 16, 2025
1c6a099
nit
JanProvaznik Jan 16, 2025
de12d05
reintroduce extensions binding redirect...
JanProvaznik Jan 16, 2025
a208e67
downgrade diagnosticsource, upgrade VSOTel to VS compatible verison
JanProvaznik Jan 17, 2025
eba5787
downgrade diagnosticsource, privateassets vs otel
JanProvaznik Jan 17, 2025
f05135f
remove Extensions primitives redirect
JanProvaznik Jan 17, 2025
6ef7496
Revert "remove Extensions primitives redirect"
JanProvaznik Jan 17, 2025
06ef018
Merge branch 'main' of https://github.com/dotnet/msbuild into otel-basic
JanProvaznik Jan 17, 2025
9ceb237
Avoid ValueTuple build error
rainersigwald Jan 17, 2025
5fee7a5
Merge branch 'otel-basic' of https://github.com/JanProvaznik/msbuild …
JanProvaznik Jan 20, 2025
e0f9312
reference correct diagnosticsource in version.details.xml
JanProvaznik Jan 20, 2025
735f6e2
include otel in test packages
JanProvaznik Jan 20, 2025
db283b0
mention places that need to upgrade vs version on major release
JanProvaznik Jan 20, 2025
91afbfa
17.14 changewave = optout
JanProvaznik Jan 20, 2025
41a7879
fix logic in GetProperties
JanProvaznik Jan 21, 2025
8c0c825
remove explicit references that are transitively included
JanProvaznik Jan 22, 2025
91643e3
remove vs-impl package source
JanProvaznik Jan 22, 2025
802dfc0
adjust VS package
JanProvaznik Jan 22, 2025
bb72029
move telemetrystate inside the class it's relevant to
JanProvaznik Jan 22, 2025
a1ffd51
Revert "remove vs-impl package source"
JanProvaznik Jan 22, 2025
f4befb6
Merge branch 'main' into otel-basic
JanProvaznik Jan 22, 2025
559f85d
add Otel to dependent assemblies
JanProvaznik Jan 22, 2025
17e7628
add all transitive dependencies to app.amd64.config and ngen
JanProvaznik Jan 23, 2025
6627e02
don't sign OpenTelemetry
JanProvaznik Jan 23, 2025
86fcf77
sign correctly
JanProvaznik Jan 23, 2025
039e929
thirdpartynotices new packages
JanProvaznik Jan 24, 2025
83bdf4f
extensions primitives to swr
JanProvaznik Jan 24, 2025
512ef64
more missing packages in swr
JanProvaznik Jan 24, 2025
da51175
remove otel assemblies from test project
JanProvaznik Jan 27, 2025
bfba539
gracefully handle load errors
JanProvaznik Jan 27, 2025
3789e8b
reintroduce reliable shutdown in standalone, and unreliable longrunning
JanProvaznik Jan 27, 2025
c2d550b
fix tests
JanProvaznik Jan 27, 2025
a5c2ce6
avoid assembly loads when not sampled, add samplerate as a telemetry …
JanProvaznik Jan 29, 2025
b885721
refactor tests
JanProvaznik Jan 30, 2025
e4fe866
disable telemetry by default
JanProvaznik Jan 30, 2025
26203bc
fix tests
JanProvaznik Jan 30, 2025
81664cb
handle envvars correctly in test context
JanProvaznik Jan 31, 2025
9e06c72
reformulate release checklist, needs hashing clarification, lock-free…
JanProvaznik Feb 3, 2025
99f8d0a
Merge branch 'main' into otel-basic
JanProvaznik Feb 3, 2025
d4c024e
format thirdpartynotices
JanProvaznik Feb 4, 2025
21c9d98
separate opt in and override sample rate concepts
JanProvaznik Feb 7, 2025
034c1af
update telemetry version
JanProvaznik Feb 7, 2025
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
6 changes: 6 additions & 0 deletions .vsts-dotnet-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ jobs:
inputs:
filename: 'eng/cibuild_bootstrapped_msbuild.cmd'
arguments: -onlyDocChanged $(onlyDocChanged)
env:
MSBUILD_TELEMETRY_SAMPLE_RATE: '0.5'
- task: PublishTestResults@2
displayName: Publish .NET Framework Test Results
inputs:
Expand Down Expand Up @@ -196,12 +198,16 @@ jobs:
inputs:
filename: 'eng/cibuild.cmd'
arguments: '-configuration Release -test'
env:
MSBUILD_TELEMETRY_SAMPLE_RATE: '0.5'
condition: eq(variables.onlyDocChanged, 0)
- task: BatchScript@1
displayName: cibuild.cmd without test
inputs:
filename: 'eng/cibuild.cmd'
arguments: '-configuration Release'
env:
MSBUILD_TELEMETRY_SAMPLE_RATE: '0.5'
condition: eq(variables.onlyDocChanged, 1)
- task: PublishTestResults@2
displayName: Publish .NET Framework Test Results
Expand Down
8 changes: 8 additions & 0 deletions NuGet.config
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@
<add key="dotnet8" value="https://dnceng.pkgs.visualstudio.com/public/_packaging/dotnet8/nuget/v3/index.json" />
<add key="dotnet8-transport" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet8-transport/nuget/v3/index.json" />
<add key="dotnet9" value="https://dnceng.pkgs.visualstudio.com/public/_packaging/dotnet9/nuget/v3/index.json" />
<add key="vs-impl" value="https://pkgs.dev.azure.com/azure-public/vside/_packaging/vs-impl/nuget/v3/index.json" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it a temporary thing?
I recall we had some limitations for using this feed in msbuild repo

Copy link
Member Author

@JanProvaznik JanProvaznik Jan 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's intended to be permanent. Under it, there is a restriction that this feed can pull only VS OpenTelemetry packages. (and it won't pull them outside Framework because they're conditionally included in Microsoft.Build.Framework.csproj

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rainersigwald to draw your attention here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PSM configuration doesn't exactly say that: it says that Microsoft.VisualStudio.OpenTelemetry* must come from vs-impl, not that vs-impl can only be used for that package.

We need to be careful here--have you talked to the VMR/sourcebuild folks about the dependency?

Copy link
Member Author

@JanProvaznik JanProvaznik Jan 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PSM configuration doesn't exactly say that: it says that Microsoft.VisualStudio.OpenTelemetry* must come from vs-impl, not that vs-impl can only be used for that package.

Thanks, I misunderstood that. Is it possible to do? From a quick look at docs I guess no ☹️

We're in contact and it does not impact sourcebuild, because it's references are gated only for .NET Framework in Microsoft.Build.Framework.csproj, and .NET Framework dependencies are not a part of sourcebuild.

There is no way to condition on that in NuGet.config though.

Copy link
Member Author

@JanProvaznik JanProvaznik Jan 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's in dotnet-tools feed, removed vs-impl. cache trolled me


<!-- We don't want other than VS OpenTelemetry libraries from vs-impl -->
<packageSourceMapping>
<packageSource key="vs-impl">
<package pattern="Microsoft.VisualStudio.OpenTelemetry*" />
</packageSource>
</packageSourceMapping>
</packageSources>
<disabledPackageSources>
<!--Begin: Package sources managed by Dependency Flow automation. Do not edit the sources below.-->
Expand Down
6 changes: 6 additions & 0 deletions eng/Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,11 @@
<PackageVersion Include="System.Threading.Channels" Version="$(SystemThreadingChannelsVersion)" />
<PackageVersion Include="System.Threading.Tasks.Dataflow" Version="$(SystemThreadingTasksDataflowVersion)" />
<PackageVersion Include="xunit.console" Version="$(XUnitVersion)" />
<PackageVersion Include="Microsoft.VisualStudio.OpenTelemetry.Collector" Version="$(MicrosoftVisualStudioOpenTelemetryVersion)" />
<PackageVersion Include="Microsoft.VisualStudio.OpenTelemetry.ClientExtensions" Version="$(MicrosoftVisualStudioOpenTelemetryVersion)" />
<PackageVersion Include="OpenTelemetry" Version="$(OpenTelemetryVersion)" />
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="$(SystemDiagnosticsDiagnosticSourceVersion)" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="$(MicrosoftBclAsyncInterfacesVersion)" />
<PackageVersion Include="Microsoft.Extensions.Primitives" Version="$(MicrosoftExtensionsPrimitivesVersion)" />
</ItemGroup>
</Project>
2 changes: 2 additions & 0 deletions eng/SourceBuildPrebuiltBaseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
<UsagePattern IdentityGlob="System.Threading.Tasks.Dataflow/*8.0.0*" />
<UsagePattern IdentityGlob="System.Formats.Asn1/*8.0.1*" />
<UsagePattern IdentityGlob="Microsoft.VisualStudio.SolutionPersistence/*1.0.*" />
<!-- Defined in Version.Details.xml - live version used in SB. -->
<UsagePattern IdentityGlob="System.Diagnostics.DiagnosticSource/*9.0.0*" />
</IgnorePatterns>
<Usages>
</Usages>
Expand Down
4 changes: 4 additions & 0 deletions eng/Version.Details.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@
<Uri>https://dev.azure.com/dnceng/internal/_git/dotnet-runtime</Uri>
<Sha>2aade6beb02ea367fd97c4070a4198802fe61c03</Sha>
</Dependency>
<Dependency Name="System.Diagnostics.DiagnosticSource" Version="9.0.0">
<Uri>https://dev.azure.com/dnceng/internal/_git/dotnet-runtime</Uri>
<Sha>981a85989d49daee6b2147113b7de639f5e5d903</Sha>
</Dependency>
</ProductDependencies>
<ToolsetDependencies>
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="9.0.0-beta.24623.3">
Expand Down
6 changes: 6 additions & 0 deletions eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@
<SystemTextJsonVersion>8.0.5</SystemTextJsonVersion>
<SystemThreadingChannelsVersion>8.0.0</SystemThreadingChannelsVersion>
<SystemThreadingTasksDataflowVersion>8.0.0</SystemThreadingTasksDataflowVersion>
<SystemDiagnosticsDiagnosticSourceVersion>9.0.0</SystemDiagnosticsDiagnosticSourceVersion>
<MicrosoftVisualStudioOpenTelemetryVersion>0.1.700-beta</MicrosoftVisualStudioOpenTelemetryVersion>
<OpenTelemetryVersion>1.10.0</OpenTelemetryVersion>
<SystemDiagnosticsDiagnosticSourceVersion>9.0.0</SystemDiagnosticsDiagnosticSourceVersion>
<MicrosoftBclAsyncInterfacesVersion>9.0.0</MicrosoftBclAsyncInterfacesVersion>
<MicrosoftExtensionsPrimitivesVersion>9.0.0</MicrosoftExtensionsPrimitivesVersion>
</PropertyGroup>
<!-- Toolset Dependencies -->
<PropertyGroup>
Expand Down
196 changes: 196 additions & 0 deletions src/Build.UnitTests/BackEnd/OpenTelemetryActivities_Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Xunit;
using Shouldly;

namespace Microsoft.Build.Framework.Telemetry.Tests
{
public class ActivityExtensionsTests
{
[Fact]
public void WithTag_ShouldSetUnhashedValue()
{
var activity = new Activity("TestActivity");
activity.Start();

var telemetryItem = new TelemetryItem(
Name: "TestItem",
Value: "TestValue",
Hashed: false);

activity.WithTag(telemetryItem);

var tagValue = activity.GetTagItem("VS.MSBuild.TestItem");
tagValue.ShouldNotBeNull();
tagValue.ShouldBe("TestValue");

activity.Dispose();
}

[Fact]
public void WithTag_ShouldSetHashedValue()
{
var activity = new Activity("TestActivity");
var telemetryItem = new TelemetryItem(
Name: "TestItem",
Value: "SensitiveValue",
Hashed: true);

activity.WithTag(telemetryItem);

var tagValue = activity.GetTagItem("VS.MSBuild.TestItem");
tagValue.ShouldNotBeNull();
tagValue.ShouldNotBe("SensitiveValue"); // Ensure it’s not the plain text
activity.Dispose();
}

[Fact]
public void WithTags_ShouldSetMultipleTags()
{
var activity = new Activity("TestActivity");
var tags = new List<TelemetryItem>
{
new("Item1", "Value1", false),
new("Item2", "Value2", true) // hashed
};

activity.WithTags(tags);

var tagValue1 = activity.GetTagItem("VS.MSBuild.Item1");
var tagValue2 = activity.GetTagItem("VS.MSBuild.Item2");

tagValue1.ShouldNotBeNull();
tagValue1.ShouldBe("Value1");

tagValue2.ShouldNotBeNull();
tagValue2.ShouldNotBe("Value2"); // hashed

activity.Dispose();
}

[Fact]
public void WithTags_DataHolderShouldSetMultipleTags()
{
var activity = new Activity("TestActivity");
var dataHolder = new MockTelemetryDataHolder(); // see below

activity.WithTags(dataHolder);

var tagValueA = activity.GetTagItem("VS.MSBuild.TagA");
var tagValueB = activity.GetTagItem("VS.MSBuild.TagB");

tagValueA.ShouldNotBeNull();
tagValueA.ShouldBe("ValueA");

tagValueB.ShouldNotBeNull();
tagValueB.ShouldNotBe("ValueB"); // should be hashed
activity.Dispose();
}

[Fact]
public void WithStartTime_ShouldSetActivityStartTime()
{
var activity = new Activity("TestActivity");
var now = DateTime.UtcNow;

activity.WithStartTime(now);

activity.StartTimeUtc.ShouldBe(now);
activity.Dispose();
}

[Fact]
public void WithStartTime_NullDateTime_ShouldNotSetStartTime()
{
var activity = new Activity("TestActivity");
var originalStartTime = activity.StartTimeUtc; // should be default (min) if not started

activity.WithStartTime(null);

activity.StartTimeUtc.ShouldBe(originalStartTime);

activity.Dispose();
}
}

/// <summary>
/// A simple mock for testing IActivityTelemetryDataHolder.
/// Returns two items: one hashed, one not hashed.
/// </summary>
internal sealed class MockTelemetryDataHolder : IActivityTelemetryDataHolder
{
public IList<TelemetryItem> GetActivityProperties()
{
return new List<TelemetryItem>
{
new("TagA", "ValueA", false),
new("TagB", "ValueB", true),
};
}
}


public class MSBuildActivitySourceTests
{
[Fact]
public void StartActivity_ShouldPrefixNameCorrectly_WhenNoRemoteParent()
{
var source = new MSBuildActivitySource(TelemetryConstants.DefaultActivitySourceNamespace);
using var listener = new ActivityListener
{
ShouldListenTo = activitySource => activitySource.Name == TelemetryConstants.DefaultActivitySourceNamespace,
Sample = (ref ActivityCreationOptions<ActivityContext> options) => ActivitySamplingResult.AllData,
};
ActivitySource.AddActivityListener(listener);


var activity = source.StartActivity("Build");

activity.ShouldNotBeNull();
activity?.DisplayName.ShouldBe("VS/MSBuild/Build");

activity?.Dispose();
}

[Fact]
public void StartActivity_ShouldUseParentId_WhenRemoteParentExists()
{
// Arrange
var parentActivity = new Activity("ParentActivity");
parentActivity.SetParentId("|12345.abcde."); // Simulate some parent trace ID
parentActivity.AddTag("sampleTag", "sampleVal");
parentActivity.Start();

var source = new MSBuildActivitySource(TelemetryConstants.DefaultActivitySourceNamespace);
using var listener = new ActivityListener
{
ShouldListenTo = activitySource => activitySource.Name == TelemetryConstants.DefaultActivitySourceNamespace,
Sample = (ref ActivityCreationOptions<ActivityContext> options) => ActivitySamplingResult.AllData,
};
ActivitySource.AddActivityListener(listener);

// Act
var childActivity = source.StartActivity("ChildBuild");

// Assert
childActivity.ShouldNotBeNull();
// If HasRemoteParent is true, the code uses `parentId: Activity.Current.ParentId`.
// However, by default .NET Activity doesn't automatically set HasRemoteParent = true
// unless you explicitly set it. If you have logic that sets it, you can test it here.
// For demonstration, we assume the ParentId is carried over if HasRemoteParent == true.
if (Activity.Current?.HasRemoteParent == true)
{
childActivity?.ParentId.ShouldBe("|12345.abcde.");
}

parentActivity.Dispose();
childActivity?.Dispose();
}
}
}
Loading