WorkflowForge.Extensions.Observability.Performance
2.1.2
dotnet add package WorkflowForge.Extensions.Observability.Performance --version 2.1.2
NuGet\Install-Package WorkflowForge.Extensions.Observability.Performance -Version 2.1.2
<PackageReference Include="WorkflowForge.Extensions.Observability.Performance" Version="2.1.2" />
<PackageVersion Include="WorkflowForge.Extensions.Observability.Performance" Version="2.1.2" />
<PackageReference Include="WorkflowForge.Extensions.Observability.Performance" />
paket add WorkflowForge.Extensions.Observability.Performance --version 2.1.2
#r "nuget: WorkflowForge.Extensions.Observability.Performance, 2.1.2"
#:package WorkflowForge.Extensions.Observability.Performance@2.1.2
#addin nuget:?package=WorkflowForge.Extensions.Observability.Performance&version=2.1.2
#tool nuget:?package=WorkflowForge.Extensions.Observability.Performance&version=2.1.2
WorkflowForge.Extensions.Observability.Performance
Measure per-operation timing and memory from middleware, and read aggregate stats when the foundry exposes IFoundryPerformanceStatistics.
Install
dotnet add package WorkflowForge.Extensions.Observability.Performance
Targets .NET Standard 2.0. Beyond WorkflowForge core, it depends on System.Diagnostics.DiagnosticSource and System.ComponentModel.Annotations (both provided by the runtime on modern .NET).
Quick Start
Call EnablePerformanceMonitoring() before running the workflow, then read the aggregated
statistics afterward with GetPerformanceStatistics():
using WorkflowForge;
using WorkflowForge.Extensions.Observability.Performance;
using var foundry = WorkflowForge.CreateFoundry("PerformanceMonitored");
foundry.EnablePerformanceMonitoring();
using var smith = WorkflowForge.CreateSmith();
var workflow = WorkflowForge.CreateWorkflow("PerformanceMonitored")
.AddOperation(new ActionWorkflowOperation("Step1", async (input, foundry, ct) => { /* ... */ }))
.Build();
// Pass the SAME foundry you enabled monitoring on so the stats survive the run.
await smith.ForgeAsync(workflow, foundry);
var stats = foundry.GetPerformanceStatistics();
Console.WriteLine($"Operations: {stats!.TotalOperations}, success rate: {stats.SuccessRate:P0}");
foreach (var op in stats.GetAllOperationStatistics())
Console.WriteLine($"{op.OperationName}: avg {op.AverageExecutionTime.TotalMilliseconds:F2} ms");
EnablePerformanceMonitoring() registers a PerformanceStatisticsMiddleware and stores a
FoundryPerformanceStatistics on the foundry; the middleware records timing, success/failure, and
approximate memory per operation. For custom needs you can add your own middleware instead (see
Custom Timing Middleware).
Key points
- Middleware can log durations, flag slow calls, and track allocations per operation.
IFoundryPerformanceStatisticsandIOperationStatisticsdescribe the contract when a foundry exposes built-in counters.- Toggle or tune behavior in code; there is no separate JSON schema in this package.
Configuration
- Ships middleware types and
IFoundryPerformanceStatistics/IOperationStatisticsfor foundries that expose statistics. - Typical pattern: timing middleware on the foundry:
using var foundry = WorkflowForge.CreateFoundry("PerformanceMonitored");
foundry.AddMiddleware(new DetailedTimingMiddleware(foundry.Logger, TimeSpan.FromMilliseconds(500)));
IFoundryPerformanceStatisticsandIOperationStatisticsdefine the contract for built-in performance statistics on a foundry.
Advanced usage
Custom timing middleware
public class DetailedTimingMiddleware : IWorkflowOperationMiddleware
{
private readonly IWorkflowForgeLogger _logger;
private readonly TimeSpan _slowThreshold;
public DetailedTimingMiddleware(IWorkflowForgeLogger logger, TimeSpan slowThreshold)
{
_logger = logger;
_slowThreshold = slowThreshold;
}
public async Task<object?> ExecuteAsync(
IWorkflowOperation operation,
IWorkflowFoundry foundry,
object? inputData,
Func<CancellationToken, Task<object?>> next,
CancellationToken cancellationToken = default)
{
var sw = Stopwatch.StartNew();
try
{
var result = await next(cancellationToken).ConfigureAwait(false);
sw.Stop();
if (sw.Elapsed > _slowThreshold)
{
_logger.LogWarning(
"SLOW: Operation {Name} took {Duration}ms (threshold: {Threshold}ms)",
operation.Name,
sw.Elapsed.TotalMilliseconds,
_slowThreshold.TotalMilliseconds);
}
else
{
_logger.LogInformation(
"Operation {Name} completed in {Duration}ms",
operation.Name,
sw.Elapsed.TotalMilliseconds);
}
return result;
}
catch (Exception ex)
{
sw.Stop();
_logger.LogError(ex,
"Operation {Name} failed after {Duration}ms",
operation.Name,
sw.Elapsed.TotalMilliseconds);
throw;
}
}
}
Memory tracking
public class MemoryTrackingMiddleware : IWorkflowOperationMiddleware
{
public async Task<object?> ExecuteAsync(
IWorkflowOperation operation,
IWorkflowFoundry foundry,
object? inputData,
Func<CancellationToken, Task<object?>> next,
CancellationToken cancellationToken = default)
{
var gen0Before = GC.CollectionCount(0);
var memoryBefore = GC.GetTotalMemory(false);
var result = await next(cancellationToken).ConfigureAwait(false);
var gen0After = GC.CollectionCount(0);
var memoryAfter = GC.GetTotalMemory(false);
foundry.Logger.LogInformation(
"Operation {Name}: Memory delta {MemoryDelta} bytes, Gen0 collections: {Gen0Collections}",
operation.Name,
memoryAfter - memoryBefore,
gen0After - gen0Before);
return result;
}
}
Available statistics
Foundry-level (IFoundryPerformanceStatistics)
- TotalOperations / SuccessfulOperations / FailedOperations: Operation counts
- SuccessRate: Percentage of successful operations
- AverageDuration / MinimumDuration / MaximumDuration: Timing statistics
- TotalMemoryAllocated / AverageMemoryPerOperation: Memory metrics
- OperationsPerSecond: Throughput
- StartTime / EndTime / TotalDuration: Workflow timing
Per-operation (IOperationStatistics)
var stats = foundry.GetPerformanceStatistics();
foreach (var opStats in stats.GetAllOperationStatistics())
{
Console.WriteLine($"{opStats.OperationName}: avg {opStats.AverageExecutionTime.TotalMilliseconds:F2}ms");
}
Links
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- System.ComponentModel.Annotations (>= 5.0.0)
- System.Diagnostics.DiagnosticSource (>= 10.0.10)
- WorkflowForge (>= 2.1.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
v2.1.2: Fixed ILRepacked extensions (Polly, OpenTelemetry, Serilog) that were missing transitive dependencies and threw a runtime FileNotFoundException (e.g. Microsoft.Bcl.TimeProvider) — the required BCL/Microsoft.Extensions packages now flow to consumers. Implemented foundry performance monitoring so EnablePerformanceMonitoring()/GetPerformanceStatistics() work end-to-end. Fixed pooled-foundry state leaking across executions and audit entries recording "Unknown" workflow names. Centralized package versioning and shared metadata. Hardened lifecycle-event exception isolation and smith disposal.