-
Notifications
You must be signed in to change notification settings - Fork 1
Internal Flow Tracking
Internal Flow Tracking adds interactive internal flow visualisation to your sequence diagrams. When enabled, clicking an arrow in a PlantUML sequence diagram opens a popup showing the internal class/method activity that occurred inside the SUT between that HTTP boundary and the next — captured via OpenTelemetry spans.
This lets you see not just what your API called, but what happened inside the API while processing each request and response.
Setup order: This page covers the report configuration for Internal Flow Tracking. For the OpenTelemetry exporter setup that captures the spans, see Integration OpenTelemetry Extension first. Complete that setup, then return here to configure how internal flow data is displayed in your reports.
-
OpenTelemetry spans are captured during test execution using the
Kronikol.Extensions.OpenTelemetrypackage. A custom exporter stores allActivityspans in memory. -
HTTP boundaries are timestamped —
TestTrackingMessageHandlerandMessageTrackerrecordActivity.Current?.TraceId,Activity.Current?.SpanId, andDateTimeOffset.UtcNowon every request and response log entry. -
Segments are built at report generation time.
InternalFlowSegmentBuildercorrelates the captured OTel spans with the HTTP boundary timestamps, grouping spans into segments — one segment per arrow in the sequence diagram. Each segment contains the spans that started between two consecutive HTTP boundaries. -
Clickable links are injected into the PlantUML sequence diagram arrows using PlantUML's
[[link]]syntax. Each arrow becomes a clickable hyperlink with an#iflow-{id}anchor. -
A popup script intercepts clicks on these SVG links. When you click an arrow, it looks up the segment data from
window.__iflowSegmentsand displays the internal flow as either a PlantUML activity diagram (rendered in-browser) or an HTML call tree.
Note: Internal Flow Tracking requires
DiagramFormat = DiagramFormat.PlantUml.
Internal flow tracking is enabled by default — no extra configuration is needed for the standard case.
Span capture is auto-started by the TestTrackingMessageHandler constructor. All well-known auto-instrumentation sources (ASP.NET Core, HttpClient, EF Core, Redis, Cosmos, etc.) are captured automatically.
To also capture your SUT's custom ActivitySources, add their names to the handler options:
new XUnitTestTrackingMessageHandlerOptions
{
CallerName = "Tests",
PortsToServiceNames = { ... },
InternalFlowActivitySources = ["MyApi.Services", "MyApi.Database"] // optional
}Alternatively, you can explicitly start a listener via DI with AddActivityListenerForInternalFlowTracking(), or use the manual OTel SDK AddTestTrackingExporter() approach. See Integration OpenTelemetry Extension for details.
InternalFlowTracking defaults to true on ReportConfigurationOptions. To disable it, set it to false:
new ReportConfigurationOptions
{
InternalFlowTracking = false, // opt out
// ... other options
}When InternalFlowTracking is enabled, the following are automatically forced:
-
InlineSvgRendering = true— SVG diagrams are embedded inline in the HTML (required for clickable arrows) -
PlantUmlImageFormat = PlantUmlImageFormat.Svg— when usingPlantUmlRendering.ServerorPlantUmlRendering.Local(required for SVG-based interaction)
Note:
PlantUmlRendering.BrowserJsalready renders inline SVG, so no override is needed for that mode.
The popup content can be rendered in two styles, controlled by InternalFlowDiagramStyle:
Renders a PlantUML activity diagram with swimlanes grouped by ActivitySource name. Each span appears as an action in its source's swimlane, with duration shown in milliseconds. The activity diagram is rendered client-side using the PlantUML TeaVM JS engine.
InternalFlowDiagramStyle = InternalFlowDiagramStyle.ActivityDiagramRenders an HTML nested list showing the span parent-child hierarchy as a call tree. Each node shows the source name, span display name, and duration. This is lightweight and doesn't require the PlantUML JS engine.
InternalFlowDiagramStyle = InternalFlowDiagramStyle.CallTreeReserved for future use.
All internal flow content types support the right-click context menu:
- Activity diagrams (SVG) — Full menu with PNG, PNG (no transparency), SVG, and source options
-
Flame charts (
data-diagram-type="flamechart") — PNG copy/save only -
Call trees (
data-diagram-type="calltree") — PNG copy/save only
The context menu renders at z-index: 20001 to appear above popup overlays. See Inline SVG Rendering#context-menu for the full menu reference.
Control which spans are included using InternalFlowSpanGranularity:
| Value | Behaviour | Best for |
|---|---|---|
AutoInstrumentation (default) |
Only includes spans from well-known auto-instrumentation sources (ASP.NET Core, HttpClient, EF Core, Redis, Cosmos, etc.) | Most projects — shows framework-level flow without noise |
Manual |
Only includes spans from sources listed in InternalFlowActivitySources
|
When you want to show only your own custom activity sources |
Full |
Includes all captured spans regardless of source | Debugging or when you need complete visibility |
The AutoInstrumentation filter recognises these ActivitySource names:
Microsoft.AspNetCoreSystem.Net.HttpMicrosoft.EntityFrameworkCoreNpgsqlStackExchange.RedisAzure.CosmosAzure.StorageMicrosoft.Azure.CosmosOpenTelemetry.Instrumentation.HttpOpenTelemetry.Instrumentation.AspNetCoreOpenTelemetry.Instrumentation.SqlClientOpenTelemetry.Instrumentation.EntityFrameworkCoreKronikol.GrpcGrpc.Net.Client
To show only specific activity sources, use Manual granularity with InternalFlowActivitySources:
new ReportConfigurationOptions
{
InternalFlowTracking = true,
InternalFlowSpanGranularity = InternalFlowSpanGranularity.Manual,
InternalFlowActivitySources = ["MyApi.Services", "MyApi.Repositories"]
}| Property | Type | Default | Description |
|---|---|---|---|
InternalFlowTracking |
bool |
true |
Master switch. When true, enables internal flow popups on PlantUML sequence diagram arrows. Forces InlineSvgRendering = true and PlantUmlImageFormat.Svg for Server/Local. |
InternalFlowDisplay |
InternalFlowDisplay |
Popup |
How the internal flow content is shown. Popup shows it in a modal overlay. Inline embeds it directly below the diagram. |
InternalFlowTrigger |
InternalFlowTrigger |
Click |
User interaction that opens the popup. Click requires clicking the arrow. Hover shows on mouse hover. |
InternalFlowDiagramStyle |
InternalFlowDiagramStyle |
ActivityDiagram |
Visual style for the internal flow content. ActivityDiagram renders a PlantUML activity diagram with swimlanes. CallTree renders an HTML nested list. |
InternalFlowSpanGranularity |
InternalFlowSpanGranularity |
AutoInstrumentation |
Controls which spans are included. AutoInstrumentation filters to well-known sources. Manual uses InternalFlowActivitySources. Full includes everything. |
InternalFlowActivitySources |
string[]? |
null |
Activity source names to include when InternalFlowSpanGranularity is Manual. Ignored for other granularity settings. |
InternalFlowNoDataBehavior |
InternalFlowNoDataBehavior |
HideLink |
What happens when an arrow has no captured spans. HideLink (default) removes the clickable link. ShowMessage shows "No internal activity captured". VisualDistinction visually marks the arrow differently. |
InternalFlowHasDataBehavior |
InternalFlowHasDataBehavior |
ShowLinkOnHover |
What happens when an arrow has captured spans. ShowLinkOnHover (default) shows the clickable link only on hover. ShowLink always shows the link. |
InternalFlowShowFlameChart |
bool |
true |
When true, adds a flame chart visualisation alongside the main content. |
InternalFlowFlameChartPosition |
InternalFlowFlameChartPosition |
BehindWithToggle |
Where the flame chart appears. Underneath places it below the main content. BehindWithToggle shows a toggle button to switch between the main view and the flame chart. |
InternalFlowContentStrategy |
InternalFlowContentStrategy |
Embedded |
How segment data is stored. Embedded includes all data inline in the HTML. SeparateFragments writes each segment to a separate file in InternalFlowFragmentsFolderName. |
InternalFlowFragmentsFolderName |
string |
"spans" |
Folder name for separate fragment files when InternalFlowContentStrategy is SeparateFragments. Relative to the reports folder. |
InternalFlowPopupCustomStyleSheet |
string? |
null |
Custom CSS injected into the popup. When set, allows overriding or extending the default popup styles. |
When the OpenTelemetry extension is not installed, or spans are not captured for a particular segment, the InternalFlowNoDataBehavior setting controls how empty segments are handled:
| Value | Effect |
|---|---|
HideLink (default) |
The arrow is not clickable — no [[link]] is injected for segments without span data. |
ShowMessage |
The arrow is still clickable but the popup shows a diagnostic message including the current granularity setting, configured sources, total span count, and actionable suggestions. See Diagnostics and Debugging#Empty Diagram Diagnostics. |
VisualDistinction |
The arrow is clickable but visually styled differently (e.g. dashed or lighter colour) to indicate no data is available. |
When spans are captured for a segment, the InternalFlowHasDataBehavior setting controls how the clickable link is displayed:
| Value | Effect |
|---|---|
ShowLinkOnHover (default) |
The clickable link is only visible when hovering over the diagram arrow. This keeps the diagram clean while maintaining interactivity. |
ShowLink |
The clickable link is always visible on arrows that have internal flow data. |
By default, all segment data is embedded inline in the HTML report as a <script> block containing window.__iflowSegments. For large test suites with many spans, this can increase file size significantly.
Use InternalFlowContentStrategy.SeparateFragments to write each segment's data to a separate file:
new ReportConfigurationOptions
{
InternalFlowTracking = true,
InternalFlowContentStrategy = InternalFlowContentStrategy.SeparateFragments,
InternalFlowFragmentsFolderName = "spans" // default
}This produces files like Reports/spans/iflow-{guid}.html that are loaded on demand when the user clicks an arrow.
// Span capture is automatic — the TestTrackingMessageHandler auto-starts
// an ActivityListener for well-known instrumentation sources.
// To capture custom ActivitySources, add them to handler options:
new XUnitTestTrackingMessageHandlerOptions
{
CallerName = "Tests",
PortsToServiceNames = { [5001] = "MyApi" },
InternalFlowActivitySources = ["MyApi.Services", "Microsoft.EntityFrameworkCore"]
};
// In your report generation (InternalFlowTracking defaults to true)
var options = new ReportConfigurationOptions
{
// InternalFlowTracking = true, // already the default
InternalFlowDiagramStyle = InternalFlowDiagramStyle.ActivityDiagram,
InternalFlowSpanGranularity = InternalFlowSpanGranularity.AutoInstrumentation,
InternalFlowNoDataBehavior = InternalFlowNoDataBehavior.HideLink,
PlantUmlRendering = PlantUmlRendering.BrowserJs // Recommended for internal flow
};When you open the HTML report and click a sequence diagram arrow, a popup will show the internal flow activity that occurred inside your SUT during that segment of the request processing.
In addition to per-arrow flow popups, you can visualise the entire test's internal activity as a single flame chart or activity diagram. This shows all OTel spans captured during the test, across all HTTP boundaries, with optional boundary markers showing where each HTTP request occurred.
The WholeTestFlowVisualization property on ReportConfigurationOptions controls this feature:
| Value | Behaviour |
|---|---|
None |
No whole-test flow section is rendered. |
FlameChart |
Shows a flame chart with vertical dashed boundary markers at HTTP request timestamps. |
ActivityDiagram |
Shows a PlantUML activity diagram with all spans. |
Both (default) |
Shows a toggle between Activity and Flame Chart views. |
var options = new ReportConfigurationOptions
{
WholeTestFlowVisualization = WholeTestFlowVisualization.Both // default
};When enabled, a collapsed <details> block titled "Whole Test Flow (N spans)" appears after the sequence diagrams for each scenario. Expanding it shows:
-
Activity view — A full PlantUML activity diagram with swimlanes grouped by
ActivitySourcename. - Flame Chart view — An HTML flame chart where each span is a horizontal bar proportional to its duration. Vertical dashed markers show when HTTP requests were made. Supports click-to-zoom — click any bar to zoom into that time range, double-click to reset. Tooltips show the source name, span name, duration, and percentage of total.
The toggle buttons let you switch between views without reloading.
Whole-test flow is automatically integrated into LightBDD HTML reports when InternalFlowTracking is enabled. The WholeTestFlowHtmlProvider is wired up in both Kronikol.LightBDD.xUnit2 and Kronikol.LightBDD.xUnit3 packages.
Test Execution Report Generation
┌──────────────────────────────┐ ┌──────────────────────────────┐
│ │ │ InternalFlowSpanCollector │
│ Auto-started (default): │ │ └─ Reads spans directly │
│ ActivityListener (BCL) │ │ from InternalFlowSpanStore│
│ └─ Started by │ │ (no reflection) │
│ TestTrackingMessageHandler│ │ │
│ └─ AllData sampling ────────┼──┐ │ InternalFlowSegmentBuilder │
│ (non-invasive) │ │ │ └─ Correlates spans with │
│ │ │ │ HTTP boundary timestamps │
│ Optional (manual): │ │ │ │
│ OTel TracerProvider │ │ │ InternalFlowRenderer │
│ └─ AddTestTrackingExporter()│ │ │ └─ ActivityDiagram or │
│ └─ TestTrackingSpanExporter ┼──┤ │ CallTree output │
│ │ │ │ │
│ Both paths write to: │ ▼ │ InternalFlowHtmlGenerator │
│ InternalFlowSpanStore ◄─────┼──┘ │ └─ window.__iflowSegments │
│ (core package, static) │ │ JSON data block │
│ (dedup by reference) │ │ │
│ │ │ DiagramContextMenu │
│ TestTrackingMessageHandler ──┼──────────────► │ └─ Popup JS/CSS for │
│ └─ Timestamps + TraceId │ │ interactive arrows │
│ on RequestResponseLog │ │ │
└──────────────────────────────┘ └──────────────────────────────┘
-
PlantUML only — Internal Flow Tracking requires
DiagramFormat.PlantUml. -
Inline SVG required — The feature relies on injecting
[[link]]anchors into PlantUML arrows and intercepting them in the rendered SVG. This requires inline SVG rendering (automatically enabled). -
Zero-config by default — The
TestTrackingMessageHandlerauto-starts anInternalFlowActivityListenerfor well-known sources.InternalFlowTrackingdefaults totrue. No extra packages, no extension methods needed for the standard case. UseInternalFlowActivitySourcesonTestTrackingMessageHandlerOptionsfor custom sources, orAddActivityListenerForInternalFlowTracking()for DI-based registration. TheKronikol.Extensions.OpenTelemetrypackage is only needed for manual OTel SDK exporter control viaAddTestTrackingExporter(). The store deduplicates by reference, so both paths can be active without duplicates. - Span correlation by timestamp — Spans are correlated with HTTP boundaries using timestamps, not parent-child span relationships. This means spans are grouped into the segment whose HTTP boundary immediately precedes them.
- Step Tracking — BDD-style step attributes for structuring tests (complementary to internal flow tracking)
Getting Started
Common Tasks
Integration Guides
- Integration xUnit3
- Integration xUnit2
- Integration NUnit
- Integration MSTest
- Integration TUnit
- Integration BDDfy xUnit3
- Integration LightBDD xUnit2
- Integration LightBDD xUnit3
- Integration LightBDD TUnit
- Integration ReqNRoll xUnit2
- Integration ReqNRoll xUnit3
- Integration ReqNRoll TUnit
Extensions
- Integration AtlasDataApi Extension
- Integration BigQuery Extension
- Integration Bigtable Extension
- Integration BlobStorage Extension
- Integration CloudStorage Extension
- Integration CosmosDB Extension
- Integration Dapper Extension
- Integration DynamoDB Extension
- Integration EF Core Relational Extension
- Integration Elasticsearch Extension
- Integration EventBridge Extension
- Integration EventHubs Extension
- Integration Grpc Extension
- Integration Kafka Extension
- Integration MassTransit Extension
- Integration MongoDB Extension
- Integration MySqlConnector Extension
- Integration Npgsql Extension
- Integration Oracle Extension
- Integration PubSub Extension
- Integration Redis Extension
- Integration S3 Extension
- Integration ServiceBus Extension
- Integration SNS Extension
- Integration Spanner Extension
- Integration SqlClient Extension
- Integration Sqlite Extension
- Integration SQS Extension
- Integration StorageQueues Extension
- Integration OpenTelemetry Extension
- Integration DispatchProxy Extension
- Integration MediatR Extension
- Integration PlantUML IKVM
Configuration
- Tracking Dependencies
- Tracking Custom Dependencies
- HTTP Tracking Setup
- Report Configuration
- Diagram Customisation
- Phase-Aware Tracking
- Content Formatting
- PlantUML Server Configuration
Features
- Generated Reports
- Search Syntax
- Component Diagrams
- PlantUML Browser Rendering
- Inline SVG Rendering
- Internal Flow Tracking
- Tags and Attributes
- Excluding Requests
- Excluded Headers
- Multi-Host Test Architectures
- Event-Driven Architecture Testing
- Service Bus Tracking Patterns
- Background Thread Correlation
- Parallel-Safe Background Correlation
- Event & Message Tracking
- Assertion Tracking
- Step Tracking
- Tabular Attributes
- Large Response and Diagram Handling
- Diagnostics and Debugging
- CI Summary Integration
- CI Artifact Upload
Reference