Overview
Cuemon.Diagnostics extends the System.Diagnostics namespace with two related concerns. The primary surface is TimeMeasure, a static class that wraps any Action or Func<TResult> delegate (synchronous or asynchronous) and returns a TimeMeasureProfiler containing the elapsed Stopwatch time, a reflected MethodDescriptor for the measured delegate, and a Data dictionary populated with the delegate's runtime argument values. Overloads cover zero to ten arguments for both the synchronous WithAction/WithFunc family and the asynchronous WithActionAsync/WithFuncAsync family. The static CompletedCallback property on TimeMeasure fires after measurement when elapsed time meets the configured TimeMeasureCompletedThreshold, enabling threshold-driven logging or alerting without instrumenting the delegate itself.
The secondary surface addresses structured exception description. FaultHandler<TDescriptor> is an abstract base class that pairs a validator predicate with a descriptor factory, enabling typed mapping from a caught Exception to a domain-specific descriptor. FaultResolver is the concrete implementation for the built-in ExceptionDescriptor type, covering cases where exception evidence needs to be surfaced as structured data rather than a raw message string.
Key APIs
TimeMeasure is the central entry point of the package. The synchronous overloads WithAction and WithFunc<TResult> accept a delegate and an optional Action<TimeMeasureOptions> setup, returning a TimeMeasureProfiler or TimeMeasureProfiler<TResult>. The asynchronous overloads WithActionAsync and WithFuncAsync<TResult> accept a delegate whose last parameter is a CancellationToken and return Task<TimeMeasureProfiler> or Task<TimeMeasureProfiler<TResult>>. All overloads capture a Stopwatch, populate MethodDescriptor, and optionally invoke CompletedCallback when the elapsed time exceeds the configured threshold.
TimeMeasureProfiler is the synchronous measurement result type. It exposes a Stopwatch Timer, a computed TimeSpan Elapsed, a bool IsRunning, the reflected MethodDescriptor Member describing the measured delegate, and an IDictionary<string, object> Data dictionary keyed by argument name and populated with the runtime argument values passed to the overload. Its ToString() renders a human-readable summary in the form Member took HH:mm:ss.mmm to execute. Parameters: { ... }.
TimeMeasureProfiler<TResult> extends TimeMeasureProfiler and adds a TResult Result property that holds the return value produced by a measured Func<TResult>. It cannot be constructed directly by consumers; instances are returned by TimeMeasure.WithFunc and TimeMeasure.WithFuncAsync.
TimeMeasureOptions configures callback threshold behavior. The TimeMeasureCompletedThreshold property (default TimeSpan.Zero) sets the minimum elapsed time before TimeMeasure.CompletedCallback fires; at TimeSpan.Zero every measurement triggers the callback. The static DefaultTimeMeasureCompletedThreshold property provides a global default that applies when no per-call setup is supplied.
AsyncTimeMeasureOptions extends TimeMeasureOptions and implements IAsyncOptions. It adds a CancellationToken CancellationToken property (default default) so callers can cancel in-flight async measurements; cancellation causes OperationCanceledException to propagate out of the WithActionAsync/WithFuncAsync call.
FaultHandler<TDescriptor> is the abstract base class for exception-to-descriptor mapping, constrained to where TDescriptor : ExceptionDescriptor. Derived types pass a validator Func<Exception, bool> and a descriptor factory Func<Exception, TDescriptor> to the base constructor. Calling TryResolveFault(Exception failure, out TDescriptor descriptor) evaluates the validator and, when it returns true, invokes the factory to produce the typed descriptor.
FaultResolver is the concrete FaultHandler<ExceptionDescriptor> and requires no further derivation when a plain ExceptionDescriptor is sufficient. It follows the same constructor shape as FaultHandler<TDescriptor> and is ready to use directly for straightforward exception-to-descriptor scenarios.
Basic usage
using System;
using Codebelt.Extensions.Xunit;
using Cuemon.Diagnostics;
using Xunit;
namespace MyProject.Tests;
public class TimeMeasureTest : Test
{
public TimeMeasureTest(ITestOutputHelper output) : base(output)
{
}
[Fact]
public void WithFunc_ShouldCaptureElapsedTimeAndResult()
{
var profiler = TimeMeasure.WithFunc(() =>
{
var sum = 0;
for (var i = 1; i <= 100; i++) sum += i;
return sum;
});
Assert.Equal(5050, profiler.Result);
Assert.True(profiler.Elapsed >= TimeSpan.Zero);
Assert.NotNull(profiler.Member);
TestOutput.WriteLine(profiler.ToString());
}
}
Use this pattern when you need both the return value and structured timing metadata from an operation in a single call. TimeMeasureProfiler<TResult> makes the elapsed time and the reflected method descriptor available without requiring a separate Stopwatch or reflection call alongside the measured logic.
Installation
dotnet add package Cuemon.Diagnostics
Usage guidance
Cuemon.Diagnostics is a good fit when you need execution timing coupled with structured method metadata: for example, performance logging that records the delegate signature and argument values alongside elapsed time, or threshold-based callback hooks that fire only when a measured operation exceeds a latency target via TimeMeasureCompletedThreshold. If you only need elapsed time without method-descriptor plumbing, System.Diagnostics.Stopwatch is simpler and introduces no extra dependency. For richer ASP.NET Core integration, such as middleware that serializes ExceptionDescriptor fault responses, Cuemon.Extensions.Diagnostics builds on top of these types.
Family packages
- 🌐Cuemon.AspNetCore
- 🏭Cuemon.AspNetCore.App
- 🌐Cuemon.AspNetCore.Authentication
- 🌐Cuemon.AspNetCore.Mvc
- 🌐Cuemon.AspNetCore.Razor.TagHelpers
- 📦Cuemon.Core
- 🏭Cuemon.Core.App
- 🗄️Cuemon.Data
- 🗄️Cuemon.Data.Integrity
- 🗄️Cuemon.Data.SqlClient
- 🌐Cuemon.Extensions.AspNetCore
- 🌐Cuemon.Extensions.AspNetCore.Authentication
- 🌐Cuemon.Extensions.AspNetCore.Mvc
- 🌐Cuemon.Extensions.AspNetCore.Mvc.Formatters.Text.Json
- 🌐Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml
- 🌐Cuemon.Extensions.AspNetCore.Mvc.RazorPages
- 🌐Cuemon.Extensions.AspNetCore.Text.Json
- 🌐Cuemon.Extensions.AspNetCore.Xml
- 📦Cuemon.Extensions.Collections.Generic
- 📦Cuemon.Extensions.Collections.Specialized
- 📦Cuemon.Extensions.Core
- 🗄️Cuemon.Extensions.Data
- 🗄️Cuemon.Extensions.Data.Integrity
- 📦Cuemon.Extensions.DependencyInjection
- 🩺Cuemon.Extensions.Diagnostics
- 🏗️Cuemon.Extensions.Hosting
- 📦Cuemon.Extensions.IO
- 📦Cuemon.Extensions.Net
- 📦Cuemon.Extensions.Reflection
- 📦Cuemon.Extensions.Runtime.Caching
- 📝Cuemon.Extensions.Text
- 📝Cuemon.Extensions.Text.Json
- 📦Cuemon.Extensions.Threading
- 📦Cuemon.Extensions.Xml
- 📦Cuemon.IO
- ⚙️Cuemon.Kernel
- 📦Cuemon.Net
- 📦Cuemon.Resilience
- 📦Cuemon.Runtime.Caching
- 🔐Cuemon.Security.Cryptography
- 📦Cuemon.Threading
- 📦Cuemon.Xml