diff --git a/docs/Releases.md b/docs/Releases.md index 5aaac0c1..a1242f94 100644 --- a/docs/Releases.md +++ b/docs/Releases.md @@ -16,6 +16,7 @@ This page tracks major changes included in any update starting with version 4.0. - Added `MiniProfiler.Minimal` headless package with is a standalone bare-bones build with no depdencies and no UI, useful for mass scale applications that are viewing the results elsewhere ([#636](https://github.com/MiniProfiler/dotnet/pull/636)) - Fixed [#578](https://github.com/MiniProfiler/dotnet/issues/578): Making SQLite data types compatible for more use cases ([#582](https://github.com/MiniProfiler/dotnet/pull/582) - thanks [MarkZither](https://github.com/MarkZither)) - Add Nullable Reference Type annotations to the entire codebase ([#640](https://github.com/MiniProfiler/dotnet/pull/640)) + - Add `MiniProfilerOptions.TimingInstrumentationProvider` allowing to hook when `Timing`s are created, e.g. to drive `Activity` if desired ([#650](https://github.com/MiniProfiler/dotnet/pull/650) - thanks [m0sa](https://github.com/m0sa)) #### Version 4.2.22 - Minor fixes to build versioning diff --git a/src/MiniProfiler.Shared/Internal/MiniProfilerBaseOptions.cs b/src/MiniProfiler.Shared/Internal/MiniProfilerBaseOptions.cs index b76e78bd..c798a546 100644 --- a/src/MiniProfiler.Shared/Internal/MiniProfilerBaseOptions.cs +++ b/src/MiniProfiler.Shared/Internal/MiniProfilerBaseOptions.cs @@ -221,6 +221,12 @@ public class MiniProfilerBaseOptions /// public MiniProfiler? StartProfiler(string? profilerName = null) => ProfilerProvider.Start(profilerName, this); + /// + /// Called whenever a new is started. + /// The method of the returned object is called at the same time as the is ed. + /// + public Func? TimingInstrumentationProvider { get; set; } + /// /// Called when passed to . /// diff --git a/src/MiniProfiler.Shared/Timing.cs b/src/MiniProfiler.Shared/Timing.cs index 6298553a..9f80d2ec 100644 --- a/src/MiniProfiler.Shared/Timing.cs +++ b/src/MiniProfiler.Shared/Timing.cs @@ -20,6 +20,7 @@ public class Timing : IDisposable private readonly decimal? _minSaveMs; private readonly bool _includeChildrenWithMinSave; private readonly object _syncRoot = new(); + private readonly IDisposable? _instrumentation = null; /// /// Initializes a new instance of the class. @@ -77,6 +78,9 @@ public Timing(MiniProfiler profiler, Timing? parent, string? name, decimal? minS { DebugInfo = new TimingDebugInfo(this, debugStackShave); } + + // DataContractSerializer doesn't call this so it should be fine + _instrumentation = profiler.Options.TimingInstrumentationProvider?.Invoke(this); } /// @@ -282,6 +286,8 @@ public void Stop() ParentTiming.RemoveChild(this); } } + + _instrumentation?.Dispose(); } /// diff --git a/tests/MiniProfiler.Tests/TimingInstrumentationTest.cs b/tests/MiniProfiler.Tests/TimingInstrumentationTest.cs new file mode 100644 index 00000000..a8e1a230 --- /dev/null +++ b/tests/MiniProfiler.Tests/TimingInstrumentationTest.cs @@ -0,0 +1,38 @@ +using System; +using Xunit; +using Xunit.Abstractions; + +namespace StackExchange.Profiling.Tests +{ + public class TimingInstrumentationTest : BaseTest + { + public TimingInstrumentationTest(ITestOutputHelper output) : base(output) { } + + private class TimingInstrumentation : IDisposable + { + public Timing Timing { get; set; } + public bool Disposed { get; set; } + public void Dispose() => Disposed = true; + } + + [Fact] + public void IsInstrumented() + { + TimingInstrumentation instrumentation = null; + Timing timing = null; + Options.TimingInstrumentationProvider = t => instrumentation = new TimingInstrumentation { Timing = t }; + var mp = Options.StartProfiler(); + + using (timing = mp.Step("Test timing")) + { + Assert.NotNull(instrumentation); + Assert.False(instrumentation.Disposed); + mp.Increment(); + } + + Assert.NotNull(instrumentation); + Assert.Equal(timing, instrumentation.Timing); + Assert.True(instrumentation.Disposed); + } + } +}