Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
40fe48e
Implementation
pavelsavara Mar 14, 2025
7cd04e2
fix
pavelsavara Mar 14, 2025
ed04043
move System.Net.Http.EnableMetrics to https://github.com/dotnet/runti…
pavelsavara Mar 14, 2025
88e5ba5
fix
pavelsavara Mar 14, 2025
0a22b09
fix
pavelsavara Mar 14, 2025
5d287d3
fix
pavelsavara Mar 14, 2025
b8d3f61
Merge branch 'main' into browser_managed_diag_events
pavelsavara Mar 15, 2025
bfdbda8
ActivityTracker_Instance_Enable
pavelsavara Mar 15, 2025
7a6f106
fix
pavelsavara Mar 15, 2025
04df34b
fix
pavelsavara Mar 17, 2025
d1a8712
more tests
pavelsavara Mar 18, 2025
46d63a9
Merge branch 'main' into browser_managed_diag_events
pavelsavara Mar 18, 2025
be60fbe
fix
pavelsavara Mar 18, 2025
963f14c
fix
pavelsavara Mar 18, 2025
948f7f6
Update src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tr…
pavelsavara Mar 18, 2025
84ec7ff
fix
pavelsavara Mar 18, 2025
ff65c1f
fix
pavelsavara Mar 18, 2025
60a60d0
Merge branch 'main' into browser_managed_diag_events
pavelsavara Mar 19, 2025
5003ac5
feedback
pavelsavara Mar 19, 2025
b059f30
feedbak - keep DiagnosticCounter not supported API
pavelsavara Mar 19, 2025
48be899
more
pavelsavara Mar 19, 2025
c97ce30
fix
pavelsavara Mar 19, 2025
ea72741
fix
pavelsavara Mar 19, 2025
d1d4f27
fix
pavelsavara Mar 19, 2025
f242d64
fix
pavelsavara Mar 19, 2025
f4cb864
fix
pavelsavara Mar 19, 2025
84af319
Merge branch 'main' into browser_managed_diag_events
pavelsavara Mar 20, 2025
52daddc
feedback
pavelsavara Mar 20, 2025
149746b
fix
pavelsavara Mar 20, 2025
4c4ef09
just browser
pavelsavara Mar 20, 2025
53a2188
Merge branch 'main' into browser_managed_diag_events
pavelsavara Mar 21, 2025
6fb21e9
disable RequestDuration_EnrichmentHandler_ContentLengthError_Recorded…
pavelsavara Mar 21, 2025
fedacae
Merge branch 'main' into browser_managed_diag_events
pavelsavara Mar 21, 2025
57c9692
RequestDuration_Success_Recorded not firefox
pavelsavara Mar 21, 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
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ public void NegativeTest()
}

[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/93754", TestPlatforms.Browser)]
public void MeterDisposeTest()
{
ServiceCollection services = new ServiceCollection();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ private class FakeListener : IMetricsListener
public void MeasurementsCompleted(Instrument instrument, object? userState) => throw new NotImplementedException();
}

[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
[Fact]
public void TestSubscriptionManagerDisposal()
{
var meter = new Meter("TestMeter");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
<IncludeRemoteExecutor>true</IncludeRemoteExecutor>
</PropertyGroup>

<PropertyGroup Condition="'$(TargetOS)' == 'browser'">
<MetricsSupport>true</MetricsSupport>
<EventSourceSupport>true</EventSourceSupport>
</PropertyGroup>

<ItemGroup>
<Compile Include="..\src\Metrics\Configuration\MetricsConfigureOptions.cs" />
<Compile Include="..\src\Metrics\DebugConsoleMetricListener.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum)</TargetFrameworks>
<TargetFrameworks>$(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum);$(NetCoreAppCurrent)-browser</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<NoWarn>$(NoWarn);SA1205</NoWarn>
<EnableTrimAnalyzer Condition="$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)')) == '.NETFramework'">false</EnableTrimAnalyzer>
Expand All @@ -16,8 +16,9 @@ System.Diagnostics.DiagnosticSource</PackageDescription>

<!-- DesignTimeBuild requires all the TargetFramework Derived Properties to not be present in the first property group. -->
<PropertyGroup>
<TargetPlatformIdentifier>$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)'))</TargetPlatformIdentifier>
<DefineConstants Condition="$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)')) == '.NETFramework'">$(DefineConstants);ENABLE_HTTP_HANDLER</DefineConstants>
<DefineConstants Condition="$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)')) == '.NETCoreApp'">$(DefineConstants);W3C_DEFAULT_ID_FORMAT;MEMORYMARSHAL_SUPPORT;OS_ISBROWSER_SUPPORT</DefineConstants>
<DefineConstants Condition="$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)')) == '.NETCoreApp'">$(DefineConstants);W3C_DEFAULT_ID_FORMAT;MEMORYMARSHAL_SUPPORT;OS_ISWASI_SUPPORT</DefineConstants>
<IncludePlatformAttributes>true</IncludePlatformAttributes>
<!-- TODO: Add package README file: https://github.com/dotnet/runtime/issues/99358 -->
<EnableDefaultPackageReadmeFile>false</EnableDefaultPackageReadmeFile>
Expand Down Expand Up @@ -57,6 +58,8 @@ System.Diagnostics.DiagnosticSource</PackageDescription>
<Compile Include="System\Diagnostics\PassThroughPropagator.cs" />
<Compile Include="System\Diagnostics\RandomNumberGenerator.cs" />
<Compile Include="System\Diagnostics\Metrics\AggregationManager.cs" />
<Compile Include="System\Diagnostics\Metrics\AggregationManager.Threads.cs" Condition="('$(TargetPlatformIdentifier)' != 'wasi' and '$(TargetPlatformIdentifier)' != 'browser') or '$(FeatureWasmManagedThreads)' == 'true'" />
<Compile Include="System\Diagnostics\Metrics\AggregationManager.Wasm.cs" Condition="('$(TargetPlatformIdentifier)' == 'wasi' or '$(TargetPlatformIdentifier)' == 'browser') and '$(FeatureWasmManagedThreads)' != 'true'" />
<Compile Include="System\Diagnostics\Metrics\Aggregator.cs" />
<Compile Include="System\Diagnostics\Metrics\AggregatorStore.cs" />
<Compile Include="System\Diagnostics\Metrics\Counter.cs" />
Expand Down Expand Up @@ -146,7 +149,7 @@ System.Diagnostics.DiagnosticSource</PackageDescription>
<Compile Include="System\Diagnostics\Metrics\RuntimeMetrics.cs" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">
<ItemGroup Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)' or '$(TargetPlatformIdentifier)' == 'browser' or '$(TargetPlatformIdentifier)' == 'wasi'">
<Reference Include="System.Collections" />
<Reference Include="System.Collections.Concurrent" />
<Reference Include="System.Diagnostics.Tracing" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Versioning;
using System.Security;
using System.Threading;
using System.Threading.Tasks;

namespace System.Diagnostics.Metrics
{
internal sealed partial class AggregationManager
{
private Thread? _collectThread;

public void Start()
{
// if already started or already stopped we can't be started again
Debug.Assert(_collectThread == null && !_cts.IsCancellationRequested);
Debug.Assert(CollectionPeriod.TotalSeconds >= MinCollectionTimeSecs);

// This explicitly uses a Thread and not a Task so that metrics still work
// even when an app is experiencing thread-pool starvation. Although we
// can't make in-proc metrics robust to everything, this is a common enough
// problem in .NET apps that it feels worthwhile to take the precaution.
_collectThread = new Thread(() => CollectWorker(_cts.Token));
_collectThread.IsBackground = true;
_collectThread.Name = "MetricsEventSource CollectWorker";
#pragma warning disable CA1416 // 'Thread.Start' is unsupported on: 'browser', there the actual implementation is in AggregationManager.Wasm.cs
_collectThread.Start();
#pragma warning restore CA1416

_listener.Start();
_initialInstrumentEnumerationComplete();
}

private void CollectWorker(CancellationToken cancelToken)
{
try
{
double collectionIntervalSecs = -1;
lock (this)
{
collectionIntervalSecs = CollectionPeriod.TotalSeconds;
}
Debug.Assert(collectionIntervalSecs >= MinCollectionTimeSecs);

DateTime startTime = DateTime.UtcNow;
DateTime intervalStartTime = startTime;
while (!cancelToken.IsCancellationRequested)
{
// pause until the interval is complete
DateTime now = DateTime.UtcNow;
DateTime nextIntervalStartTime = CalculateDelayTime(now, startTime, intervalStartTime, collectionIntervalSecs);
TimeSpan delayTime = nextIntervalStartTime - now;
if (cancelToken.WaitHandle.WaitOne(delayTime))
{
// don't do collection if timer may not have run to completion
break;
}

// collect statistics for the completed interval
_beginCollection(intervalStartTime, nextIntervalStartTime);
Collect();
_endCollection(intervalStartTime, nextIntervalStartTime);
intervalStartTime = nextIntervalStartTime;
}
}
catch (Exception e)
{
_collectionError(e);
}
}

public void Dispose()
{
_cts.Cancel();
if (_collectThread != null)
{
_collectThread.Join();
_collectThread = null;
}
_listener.Dispose();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Versioning;
using System.Security;
using System.Threading;
using System.Threading.Tasks;

namespace System.Diagnostics.Metrics
{
internal sealed partial class AggregationManager
{
private Timer? _pollingTimer;
private DateTime _startTime;
private DateTime _intervalStartTime;
private DateTime _nextIntervalStartTime;

public void Start()
{
// if already started or already stopped we can't be started again
Debug.Assert(_pollingTimer == null && !_cts.IsCancellationRequested);
Debug.Assert(CollectionPeriod.TotalSeconds >= MinCollectionTimeSecs);

DateTime now = _startTime = _intervalStartTime = DateTime.UtcNow;
_nextIntervalStartTime = CalculateDelayTime(now, _startTime, _intervalStartTime, CollectionPeriod.TotalSeconds);
TimeSpan delayTime = _nextIntervalStartTime - now;
_pollingTimer = new Timer(CollectOnce, null, (int)delayTime.TotalMilliseconds, 0);

_listener.Start();
_initialInstrumentEnumerationComplete();
}

private void CollectOnce(object? state)
{
try
{
if (_cts.Token.IsCancellationRequested)
{
return;
}

// collect statistics for the completed interval
_beginCollection(_intervalStartTime, _nextIntervalStartTime);
Collect();
_endCollection(_intervalStartTime, _nextIntervalStartTime);

DateTime now = DateTime.UtcNow;
_nextIntervalStartTime = CalculateDelayTime(now, _startTime, _intervalStartTime, CollectionPeriod.TotalSeconds);
TimeSpan delayTime = _nextIntervalStartTime - now;
_intervalStartTime = _nextIntervalStartTime;
// schedule the next collection
_pollingTimer!.Change((int)delayTime.TotalMilliseconds, 0);
}
catch (Exception e)
{
_collectionError(e);
}
}

public void Dispose()
{
_cts.Cancel();
_pollingTimer?.Dispose();
_listener.Dispose();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@

namespace System.Diagnostics.Metrics
{
[UnsupportedOSPlatform("browser")]
[SecuritySafeCritical]
internal sealed class AggregationManager
internal sealed partial class AggregationManager
{
public const double MinCollectionTimeSecs = 0.1;
private static readonly QuantileAggregation s_defaultHistogramConfig = new QuantileAggregation(new double[] { 0.50, 0.95, 0.99 });
Expand All @@ -28,7 +27,6 @@ internal sealed class AggregationManager
private Dictionary<Instrument, bool> _instruments = new();
private readonly ConcurrentDictionary<Instrument, InstrumentState> _instrumentStates = new();
private readonly CancellationTokenSource _cts = new();
private Thread? _collectThread;
private readonly MeterListener _listener;
private int _currentTimeSeries;
private int _currentHistograms;
Expand Down Expand Up @@ -154,25 +152,6 @@ private void PublishedInstrument(Instrument instrument, MeterListener _)
}
}

public void Start()
{
// if already started or already stopped we can't be started again
Debug.Assert(_collectThread == null && !_cts.IsCancellationRequested);
Debug.Assert(CollectionPeriod.TotalSeconds >= MinCollectionTimeSecs);

// This explicitly uses a Thread and not a Task so that metrics still work
// even when an app is experiencing thread-pool starvation. Although we
// can't make in-proc metrics robust to everything, this is a common enough
// problem in .NET apps that it feels worthwhile to take the precaution.
_collectThread = new Thread(() => CollectWorker(_cts.Token));
_collectThread.IsBackground = true;
_collectThread.Name = "MetricsEventSource CollectWorker";
_collectThread.Start();

_listener.Start();
_initialInstrumentEnumerationComplete();
}

public void Update()
{
// Creating (and destroying) a MeterListener to leverage the existing
Expand All @@ -186,76 +165,32 @@ public void Update()

_initialInstrumentEnumerationComplete();
}

private void CollectWorker(CancellationToken cancelToken)
private static DateTime CalculateDelayTime(DateTime now, DateTime startTime, DateTime intervalStartTime, double collectionIntervalSecs)
{
try
// intervals end at startTime + X*collectionIntervalSecs. Under normal
// circumstance X increases by 1 each interval, but if the time it
// takes to do collection is very large then we might need to skip
// ahead multiple intervals to catch back up.
//
double secsSinceStart = (now - startTime).TotalSeconds;
double alignUpSecsSinceStart = Math.Ceiling(secsSinceStart / collectionIntervalSecs) *
collectionIntervalSecs;
DateTime nextIntervalStartTime = startTime.AddSeconds(alignUpSecsSinceStart);

// The delay timer precision isn't exact. We might have a situation
// where in the previous loop iterations intervalStartTime=20.00,
// nextIntervalStartTime=21.00, the timer was supposed to delay for 1s but
// it exited early so we looped around and DateTime.Now=20.99.
// Aligning up from DateTime.Now would give us 21.00 again so we also need to skip
// forward one time interval
DateTime minNextInterval = intervalStartTime.AddSeconds(collectionIntervalSecs);
if (nextIntervalStartTime <= minNextInterval)
{
double collectionIntervalSecs = -1;
lock (this)
{
collectionIntervalSecs = CollectionPeriod.TotalSeconds;
}
Debug.Assert(collectionIntervalSecs >= MinCollectionTimeSecs);

DateTime startTime = DateTime.UtcNow;
DateTime intervalStartTime = startTime;
while (!cancelToken.IsCancellationRequested)
{
// intervals end at startTime + X*collectionIntervalSecs. Under normal
// circumstance X increases by 1 each interval, but if the time it
// takes to do collection is very large then we might need to skip
// ahead multiple intervals to catch back up.
//
DateTime now = DateTime.UtcNow;
double secsSinceStart = (now - startTime).TotalSeconds;
double alignUpSecsSinceStart = Math.Ceiling(secsSinceStart / collectionIntervalSecs) *
collectionIntervalSecs;
DateTime nextIntervalStartTime = startTime.AddSeconds(alignUpSecsSinceStart);

// The delay timer precision isn't exact. We might have a situation
// where in the previous loop iterations intervalStartTime=20.00,
// nextIntervalStartTime=21.00, the timer was supposed to delay for 1s but
// it exited early so we looped around and DateTime.Now=20.99.
// Aligning up from DateTime.Now would give us 21.00 again so we also need to skip
// forward one time interval
DateTime minNextInterval = intervalStartTime.AddSeconds(collectionIntervalSecs);
if (nextIntervalStartTime <= minNextInterval)
{
nextIntervalStartTime = minNextInterval;
}

// pause until the interval is complete
TimeSpan delayTime = nextIntervalStartTime - now;
if (cancelToken.WaitHandle.WaitOne(delayTime))
{
// don't do collection if timer may not have run to completion
break;
}

// collect statistics for the completed interval
_beginCollection(intervalStartTime, nextIntervalStartTime);
Collect();
_endCollection(intervalStartTime, nextIntervalStartTime);
intervalStartTime = nextIntervalStartTime;
}
}
catch (Exception e)
{
_collectionError(e);
nextIntervalStartTime = minNextInterval;
}
return nextIntervalStartTime;
}

public void Dispose()
{
_cts.Cancel();
if (_collectThread != null)
{
_collectThread.Join();
_collectThread = null;
}
_listener.Dispose();
}

private void RemoveInstrumentState(Instrument instrument)
{
Expand Down
Loading
Loading