Skip to content

Commit 4e6d0b3

Browse files
fix logger message importance and other cleanup
1 parent 13903e0 commit 4e6d0b3

File tree

11 files changed

+74
-60
lines changed

11 files changed

+74
-60
lines changed

eng/Signing.props

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
<ItemsToSign Include="$(ArtifactsDir)\xsd\Update-MSBuildXsds.ps1" />
1212

1313
<FileSignInfo Include="RuntimeContracts.dll" CertificateName="3PartySHA2" />
14+
15+
<!-- For Telemetry in VS.-->
16+
<FileSignInfo Include="Newtonsoft.Json.dll" CertificateName="3PartySHA2" />
1417
</ItemGroup>
1518

1619
<!-- Remove existing .nupkg signing info and set to None for testing to workaround the existing issue in arcade. -->

src/Build.UnitTests/BackEnd/BuildManager_Tests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1801,7 +1801,6 @@ public void OverlappingBuildsOfTheSameProjectDifferentTargetsAreAllowed()
18011801
</Target>
18021802
</Project>
18031803
");
1804-
18051804
Project project = CreateProject(contents, MSBuildDefaultToolsVersion, _projectCollection, true);
18061805
ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project);
18071806
_buildManager.BeginBuild(_parameters);

src/Build.UnitTests/Telemetry/Telemetry_Tests.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,6 @@ public void WorkerNodeTelemetryCollection_CustomTargetsAndTasks()
154154
public void NodeTelemetryE2E()
155155
{
156156
using TestEnvironment env = TestEnvironment.Create();
157-
env.SetEnvironmentVariable("MSBUILD_TELEMETRY_OPTIN", "1");
158157
env.SetEnvironmentVariable("MSBUILD_TELEMETRY_OPTOUT", null);
159158
env.SetEnvironmentVariable("DOTNET_CLI_TELEMETRY_OPTOUT", null);
160159

@@ -245,7 +244,7 @@ public void NodeTelemetryE2E()
245244
tags["VS.MSBuild.BuildTarget"].ShouldNotBeNullOrEmpty();
246245

247246
// Verify task data
248-
var tasks = activity.TagObjects.FirstOrDefault(to => to.Key.Contains("VS.MSBuild.Tasks"));
247+
var tasks = activity.TagObjects.FirstOrDefault(to => to.Key == "VS.MSBuild.Tasks");
249248

250249
var tasksData = tasks.Value as List<TaskDetailInfo>;
251250
var messageTaskData = tasksData!.FirstOrDefault(t => t.Name == "Microsoft.Build.Tasks.Message");

src/Build/BackEnd/BuildManager/BuildManager.cs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,7 @@
1313
using System.IO;
1414
using System.Linq;
1515
using System.Reflection;
16-
17-
#if FEATURE_REPORTFILEACCESSES
1816
using System.Runtime.CompilerServices;
19-
#endif
20-
2117
using System.Runtime.ExceptionServices;
2218
using System.Threading;
2319
using System.Threading.Tasks;
@@ -30,13 +26,13 @@
3026
using Microsoft.Build.Exceptions;
3127
using Microsoft.Build.Experimental.BuildCheck;
3228
using Microsoft.Build.Experimental.BuildCheck.Infrastructure;
33-
using Microsoft.Build.ProjectCache;
3429
using Microsoft.Build.FileAccesses;
3530
using Microsoft.Build.Framework;
3631
using Microsoft.Build.Framework.Telemetry;
3732
using Microsoft.Build.Graph;
3833
using Microsoft.Build.Internal;
3934
using Microsoft.Build.Logging;
35+
using Microsoft.Build.ProjectCache;
4036
using Microsoft.Build.Shared;
4137
using Microsoft.Build.Shared.Debugging;
4238
using Microsoft.Build.Shared.FileSystem;
@@ -299,6 +295,7 @@ public BuildManager()
299295
public BuildManager(string hostName)
300296
{
301297
ErrorUtilities.VerifyThrowArgumentNull(hostName);
298+
302299
_hostName = hostName;
303300
_buildManagerState = BuildManagerState.Idle;
304301
_buildSubmissions = new Dictionary<int, BuildSubmissionBase>();
@@ -463,6 +460,11 @@ private void UpdatePriority(Process p, ProcessPriorityClass priority)
463460
/// <exception cref="InvalidOperationException">Thrown if a build is already in progress.</exception>
464461
public void BeginBuild(BuildParameters parameters)
465462
{
463+
#if NETFRAMEWORK
464+
// Collect telemetry unless explicitly opted out via environment variable.
465+
// The decision to send telemetry is made at EndBuild to avoid eager loading of telemetry assemblies.
466+
parameters.IsTelemetryEnabled |= !TelemetryManager.IsOptOut();
467+
#endif
466468
if (_previousLowPriority != null)
467469
{
468470
if (parameters.LowPriority != _previousLowPriority)
@@ -1103,6 +1105,7 @@ public void EndBuild()
11031105
{
11041106
host = "VSCode";
11051107
}
1108+
11061109
_buildTelemetry.BuildEngineHost = host;
11071110

11081111
_buildTelemetry.BuildCheckEnabled = _buildParameters!.IsBuildCheckEnabled;
@@ -1112,6 +1115,7 @@ public void EndBuild()
11121115
_buildTelemetry.SACEnabled = sacState == NativeMethodsShared.SAC_State.Evaluation || sacState == NativeMethodsShared.SAC_State.Enforcement;
11131116

11141117
loggingService.LogTelemetry(buildEventContext: null, _buildTelemetry.EventName, _buildTelemetry.GetProperties());
1118+
11151119
EndBuildTelemetry();
11161120

11171121
// Clean telemetry to make it ready for next build submission.
@@ -1156,9 +1160,13 @@ void SerializeCaches()
11561160
}
11571161
}
11581162

1163+
[MethodImpl(MethodImplOptions.NoInlining)]
11591164
private void EndBuildTelemetry()
11601165
{
1161-
using IActivity? activity = TelemetryManager.Instance?.DefaultActivitySource
1166+
TelemetryManager.Instance.Initialize(isStandalone: false);
1167+
1168+
using IActivity? activity = TelemetryManager.Instance
1169+
?.DefaultActivitySource
11621170
?.StartActivity(TelemetryConstants.Build)
11631171
?.SetTags(_buildTelemetry)
11641172
?.SetTags(_telemetryConsumingLogger?.WorkerNodeTelemetryData.AsActivityDataHolder(
@@ -3005,10 +3013,7 @@ private ILoggingService CreateLoggingService(
30053013
forwardingLoggers = forwardingLoggers?.Concat(forwardingLogger) ?? forwardingLogger;
30063014
}
30073015

3008-
TelemetryManager.Instance.Initialize(isStandalone: false, isExplicitlyRequested: _buildParameters.IsTelemetryEnabled);
3009-
3010-
// The telemetry is enabled - we need to add our consuming logger
3011-
if (TelemetryManager.Instance.DefaultActivitySource != null)
3016+
if (_buildParameters.IsTelemetryEnabled)
30123017
{
30133018
// We do want to dictate our own forwarding logger (otherwise CentralForwardingLogger with minimum transferred importance MessageImportance.Low is used)
30143019
// In the future we might optimize for single, in-node build scenario - where forwarding logger is not needed (but it's just quick pass-through)

src/Build/BackEnd/Components/Logging/LoggingService.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1848,6 +1848,11 @@ private void UpdateMinimumMessageImportance(ILogger logger)
18481848
// The null logger has no effect on minimum verbosity.
18491849
Execution.BuildManager.NullLogger => null,
18501850

1851+
// Telemetry loggers only consume WorkerNodeTelemetryLogged events, not message events.
1852+
// They have no effect on minimum message verbosity.
1853+
TelemetryInfra.InternalTelemetryConsumingLogger => null,
1854+
Framework.Telemetry.InternalTelemetryForwardingLogger => null,
1855+
18511856
TerminalLogger terminalLogger => terminalLogger.GetMinimumMessageImportance(),
18521857
_ =>
18531858
innerLogger.GetType().FullName == "Microsoft.Build.Logging.TerminalLogger"

src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1267,9 +1267,9 @@ private void UpdateStatisticsPostBuild()
12671267
{
12681268
ITelemetryForwarder telemetryForwarder =
12691269
((TelemetryForwarderProvider)_componentHost.GetComponent(BuildComponentType.TelemetryForwarder))
1270-
.Instance;
1270+
?.Instance;
12711271

1272-
if (!telemetryForwarder.IsTelemetryCollected)
1272+
if (telemetryForwarder == null || !telemetryForwarder.IsTelemetryCollected)
12731273
{
12741274
return;
12751275
}
@@ -1279,6 +1279,11 @@ private void UpdateStatisticsPostBuild()
12791279
// Hence we need to fetch the original result from the cache - to get the data for all executed targets.
12801280
BuildResult unfilteredResult = resultsCache.GetResultsForConfiguration(_requestEntry.Request.ConfigurationId);
12811281

1282+
if (unfilteredResult?.ResultsByTarget == null || _requestEntry.RequestConfiguration.Project?.Targets == null)
1283+
{
1284+
return;
1285+
}
1286+
12821287
foreach (var projectTargetInstance in _requestEntry.RequestConfiguration.Project.Targets)
12831288
{
12841289
bool wasExecuted =

src/Framework/Telemetry/TelemetryDataUtils.cs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ internal static class TelemetryDataUtils
3131
tasksSummary.Process(telemetryData.TasksExecutionData);
3232

3333
var buildInsights = new BuildInsights(
34-
GetTasksDetails(telemetryData.TasksExecutionData),
35-
GetTargetsDetails(telemetryData.TargetsExecutionData),
34+
includeTasksDetails ? GetTasksDetails(telemetryData.TasksExecutionData) : [],
35+
includeTargetDetails ? GetTargetsDetails(telemetryData.TargetsExecutionData) : [],
3636
GetTargetsSummary(targetsSummary),
3737
GetTasksSummary(tasksSummary));
3838

@@ -128,8 +128,6 @@ public static string Hash(string text)
128128
}
129129
#endif
130130
}
131-
132-
public static string HashWithNormalizedCasing(string text) => Hash(text.ToUpperInvariant());
133131
}
134132

135133
internal record TaskDetailInfo(string Name, double TotalMilliseconds, int ExecutionsCount, long TotalMemoryBytes, bool IsCustom, bool IsNuget);
@@ -230,7 +228,7 @@ private TargetInfo GetTargetInfo(TaskOrTargetTelemetryKey key, bool isExecuted)
230228
(true, true) => ExecutedCustomTargetInfo,
231229
(true, false) => LoadedCustomTargetInfo,
232230
(false, true) => ExecutedBuiltinTargetInfo,
233-
(false, false) => LoadedBuiltinTargetInfo
231+
(false, false) => LoadedBuiltinTargetInfo,
234232
};
235233

236234
internal class TargetInfo
@@ -294,12 +292,20 @@ Dictionary<string, object> IActivityTelemetryDataHolder.GetActivityProperties()
294292
{
295293
Dictionary<string, object> properties = new()
296294
{
297-
[nameof(BuildInsights.Tasks)] = insights.Tasks,
298-
[nameof(BuildInsights.Targets)] = insights.Targets,
299295
[nameof(BuildInsights.TargetsSummary)] = insights.TargetsSummary,
300296
[nameof(BuildInsights.TasksSummary)] = insights.TasksSummary,
301297
};
302298

299+
if (insights.Targets.Count > 0)
300+
{
301+
properties[nameof(BuildInsights.Targets)] = insights.Targets;
302+
}
303+
304+
if (insights.Tasks.Count > 0)
305+
{
306+
properties[nameof(BuildInsights.Tasks)] = insights.Tasks;
307+
}
308+
303309
return properties;
304310
}
305311
}

src/Framework/Telemetry/TelemetryManager.cs

Lines changed: 24 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ internal class TelemetryManager
2525
/// <summary>
2626
/// Lock object for thread-safe initialization and disposal.
2727
/// </summary>
28-
private static readonly object s_lock = new object();
28+
private static readonly LockType s_lock = new();
2929

3030
private static bool s_initialized;
3131
private static bool s_disposed;
@@ -49,11 +49,8 @@ private TelemetryManager()
4949
/// versus integrated mode (e.g., running within Visual Studio or dotnet CLI).
5050
/// When <c>true</c>, creates and manages its own telemetry session on .NET Framework.
5151
/// </param>
52-
/// <param name="isExplicitlyRequested">
53-
/// Indicates whether telemetry was explicitly requested through command line arguments.
54-
/// On .NET, telemetry is only enabled when this is <c>true</c>.
55-
/// </param>
56-
public void Initialize(bool isStandalone, bool isExplicitlyRequested)
52+
[MethodImpl(MethodImplOptions.NoInlining)]
53+
public void Initialize(bool isStandalone)
5754
{
5855
lock (s_lock)
5956
{
@@ -69,7 +66,7 @@ public void Initialize(bool isStandalone, bool isExplicitlyRequested)
6966
return;
7067
}
7168

72-
TryInitializeTelemetry(isStandalone, isExplicitlyRequested);
69+
TryInitializeTelemetry(isStandalone);
7370
}
7471
}
7572

@@ -93,16 +90,14 @@ internal static void ResetForTest()
9390
/// allowing the calling code to catch assembly loading exceptions.
9491
/// </summary>
9592
[MethodImpl(MethodImplOptions.NoInlining)]
96-
private void TryInitializeTelemetry(bool isStandalone, bool isExplicitlyRequested)
93+
private void TryInitializeTelemetry(bool isStandalone)
9794
{
9895
try
9996
{
10097
#if NETFRAMEWORK
10198
DefaultActivitySource = VsTelemetryInitializer.Initialize(isStandalone);
10299
#else
103-
DefaultActivitySource = isExplicitlyRequested
104-
? new MSBuildActivitySource(TelemetryConstants.DefaultActivitySourceNamespace)
105-
: null;
100+
DefaultActivitySource = new MSBuildActivitySource(TelemetryConstants.DefaultActivitySourceNamespace);
106101
#endif
107102
}
108103
catch (Exception ex) when (ex is FileNotFoundException or FileLoadException or TypeLoadException)
@@ -139,66 +134,58 @@ FileLoadException or
139134
}
140135
}
141136

142-
#if NETFRAMEWORK
143-
[MethodImpl(MethodImplOptions.NoInlining)]
144-
private static void DisposeVsTelemetry() => VsTelemetryInitializer.Dispose();
145-
#endif
146-
147137
/// <summary>
148138
/// Determines if the user has explicitly opted out of telemetry.
149139
/// </summary>
150-
private static bool IsOptOut() =>
140+
internal static bool IsOptOut() =>
151141
#if NETFRAMEWORK
152142
Traits.Instance.FrameworkTelemetryOptOut;
153143
#else
154144
Traits.Instance.SdkTelemetryOptOut;
155145
#endif
146+
147+
#if NETFRAMEWORK
148+
[MethodImpl(MethodImplOptions.NoInlining)]
149+
private static void DisposeVsTelemetry() => VsTelemetryInitializer.Dispose();
150+
#endif
156151
}
157152

158153
#if NETFRAMEWORK
159-
/// <summary>
160-
/// Isolated class that references Microsoft.VisualStudio.Telemetry types.
161-
/// This separation ensures the VS Telemetry assembly is only loaded when methods
162-
/// on this class are actually invoked.
163-
/// </summary>
164-
/// <remarks>
165-
/// Thread-safety: All public methods on this class must be called under the
166-
/// <see cref="TelemetryManager"/> lock to ensure thread-safe access to static state.
167-
/// Callers must not invoke <see cref="Initialize"/> or <see cref="Dispose"/> concurrently.
168-
/// </remarks>
169154
internal static class VsTelemetryInitializer
170155
{
171156
// Telemetry API key for Visual Studio telemetry service.
172157
private const string CollectorApiKey = "f3e86b4023cc43f0be495508d51f588a-f70d0e59-0fb0-4473-9f19-b4024cc340be-7296";
173158

174-
private static TelemetrySession? s_telemetrySession;
175-
private static bool s_ownsSession;
159+
// Store as object to avoid type reference at class load time
160+
private static object? s_telemetrySession;
161+
private static bool s_ownsSession = false;
176162

163+
[MethodImpl(MethodImplOptions.NoInlining)]
177164
public static MSBuildActivitySource Initialize(bool isStandalone)
178165
{
166+
TelemetrySession session;
179167
if (isStandalone)
180168
{
181-
s_telemetrySession = TelemetryService.CreateAndGetDefaultSession(CollectorApiKey);
169+
session = TelemetryService.CreateAndGetDefaultSession(CollectorApiKey);
182170
TelemetryService.DefaultSession.UseVsIsOptedIn();
183171
TelemetryService.DefaultSession.Start();
184172
s_ownsSession = true;
185173
}
186174
else
187175
{
188-
s_telemetrySession = TelemetryService.DefaultSession;
189-
s_ownsSession = false;
176+
session = TelemetryService.DefaultSession;
190177
}
191178

192-
return new MSBuildActivitySource(s_telemetrySession);
179+
s_telemetrySession = session;
180+
return new MSBuildActivitySource(session);
193181
}
194182

183+
[MethodImpl(MethodImplOptions.NoInlining)]
195184
public static void Dispose()
196185
{
197-
// Only dispose the session if we created it (standalone scenario).
198-
// In VS, the session is owned by VS and should not be disposed by MSBuild.
199-
if (s_ownsSession)
186+
if (s_ownsSession && s_telemetrySession is TelemetrySession session)
200187
{
201-
s_telemetrySession?.Dispose();
188+
session.Dispose();
202189
}
203190

204191
s_telemetrySession = null;

src/MSBuild/XMake.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ string[] args
250250
// Initialize new build telemetry and record start of this build.
251251
KnownTelemetry.PartialBuildTelemetry = new BuildTelemetry { StartAt = DateTime.UtcNow, IsStandaloneExecution = true };
252252

253-
TelemetryManager.Instance?.Initialize(isStandalone: true, isExplicitlyRequested: false);
253+
TelemetryManager.Instance?.Initialize(isStandalone: true);
254254

255255
using PerformanceLogEventListener eventListener = PerformanceLogEventListener.Create();
256256

src/MSBuild/app.amd64.config

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@
106106
<bindingRedirect oldVersion="0.0.0.0-9.0.0.11" newVersion="9.0.0.11" />
107107
<codeBase version="9.0.0.11" href="..\System.Collections.Immutable.dll"/>
108108
</dependentAssembly>
109+
<dependentAssembly>
110+
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
111+
<codeBase version="13.0.0.0" href="..\Newtonsoft.Json.dll" />
112+
</dependentAssembly>
109113
<dependentAssembly>
110114
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
111115
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />

0 commit comments

Comments
 (0)