Skip to content

Commit

Permalink
Add support for hooking in additional instrumentation around Timings (#…
Browse files Browse the repository at this point in the history
…650)

Most of the code of _a project that I'm working on_ is instrumented via MiniProfiler's extension methods. In order to get the MiniProfiler instrumented sections/traces into 3rd party APM tools, we'd either need to hijack all the MiniProfiler extension method combinations, or add yet another `using` around everything we instrument via MiniProfiler. The latter is particularly impractical for the `.Inline(() => ....)` extension methods etc.
  • Loading branch information
m0sa authored May 23, 2023
1 parent 9f689c5 commit 1120572
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/Releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions src/MiniProfiler.Shared/Internal/MiniProfilerBaseOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,12 @@ public class MiniProfilerBaseOptions
/// </param>
public MiniProfiler? StartProfiler(string? profilerName = null) => ProfilerProvider.Start(profilerName, this);

/// <summary>
/// Called whenever a new <cref see="Timing" /> is started.
/// The <cref see="IDiposable.Dispose" /> method of the returned object is called at the same time as the <cref see="Timing" /> is <cref see="Timing.Stop" />ed.
/// </summary>
public Func<Timing, IDisposable>? TimingInstrumentationProvider { get; set; }

/// <summary>
/// Called when passed to <see cref="MiniProfiler.Configure{T}(T)"/>.
/// </summary>
Expand Down
6 changes: 6 additions & 0 deletions src/MiniProfiler.Shared/Timing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/// <summary>
/// Initializes a new instance of the <see cref="Timing"/> class.
Expand Down Expand Up @@ -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);
}

/// <summary>
Expand Down Expand Up @@ -282,6 +286,8 @@ public void Stop()
ParentTiming.RemoveChild(this);
}
}

_instrumentation?.Dispose();
}

/// <summary>
Expand Down
38 changes: 38 additions & 0 deletions tests/MiniProfiler.Tests/TimingInstrumentationTest.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}

0 comments on commit 1120572

Please sign in to comment.