Skip to content

Internal Flow Tracking

Aryeh Citron edited this page Apr 4, 2026 · 13 revisions

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.


How It Works

  1. OpenTelemetry spans are captured during test execution using the TestTrackingDiagrams.Extensions.OpenTelemetry package. A custom exporter stores all Activity spans in memory.

  2. HTTP boundaries are timestampedTestTrackingMessageHandler and MessageTracker record Activity.Current?.TraceId, Activity.Current?.SpanId, and DateTimeOffset.UtcNow on every request and response log entry.

  3. Segments are built at report generation time. InternalFlowSegmentBuilder correlates 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.

  4. 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.

  5. A popup script intercepts clicks on these SVG links. When you click an arrow, it looks up the segment data from window.__iflowSegments and 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. It is not supported with Mermaid output.


Prerequisites

  1. Install the OpenTelemetry extension package:
dotnet add package TestTrackingDiagrams.Extensions.OpenTelemetry
  1. Configure OpenTelemetry in your test setup to capture spans from your SUT's activity sources:
builder.ConfigureTestServices(services =>
{
    services.AddOpenTelemetry()
        .WithTracing(tracing => tracing
            .AddSource("MyApi.Services")           // Your SUT's ActivitySource names
            .AddSource("Microsoft.AspNetCore")     // ASP.NET Core auto-instrumentation
            .AddSource("System.Net.Http")          // HttpClient auto-instrumentation
            .AddTestTrackingExporter());            // Captures spans for diagram popups
});

See Integration OpenTelemetry Extension for full setup details.


Enabling Internal Flow Tracking

Set InternalFlowTracking = true on your ReportConfigurationOptions:

new ReportConfigurationOptions
{
    InternalFlowTracking = true,
    // ... 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 using PlantUmlRendering.Server or PlantUmlRendering.Local (required for SVG-based interaction)

Note: PlantUmlRendering.BrowserJs already renders inline SVG, so no override is needed for that mode.


Diagram Styles

The popup content can be rendered in two styles, controlled by InternalFlowDiagramStyle:

ActivityDiagram (default)

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.ActivityDiagram

CallTree

Renders 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.CallTree

SequenceDiagram

Reserved for future use.


Span Granularity

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

Well-Known Auto-Instrumentation Sources

The AutoInstrumentation filter recognises these ActivitySource names:

  • Microsoft.AspNetCore
  • System.Net.Http
  • Microsoft.EntityFrameworkCore
  • Npgsql
  • StackExchange.Redis
  • Azure.Cosmos
  • Azure.Storage
  • Microsoft.Azure.Cosmos
  • OpenTelemetry.Instrumentation.Http
  • OpenTelemetry.Instrumentation.AspNetCore
  • OpenTelemetry.Instrumentation.SqlClient
  • OpenTelemetry.Instrumentation.EntityFrameworkCore

Manual Source Filtering

To show only specific activity sources, use Manual granularity with InternalFlowActivitySources:

new ReportConfigurationOptions
{
    InternalFlowTracking = true,
    InternalFlowSpanGranularity = InternalFlowSpanGranularity.Manual,
    InternalFlowActivitySources = ["MyApi.Services", "MyApi.Repositories"]
}

Configuration Reference

Property Type Default Description
InternalFlowTracking bool false 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 ShowMessage What happens when an arrow has no captured spans. ShowMessage shows "No internal activity captured". HideLink removes the clickable link. VisualDistinction visually marks the arrow differently.
InternalFlowShowFlameChart bool false 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.

No-Data Behaviour

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
ShowMessage (default) The arrow is still clickable but the popup shows "No internal activity captured for this segment."
HideLink The arrow is not clickable — no [[link]] is injected for segments without span data.
VisualDistinction The arrow is clickable but visually styled differently (e.g. dashed or lighter colour) to indicate no data is available.

Content Strategy

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.


Full Example

// In your test setup (e.g. WebApplicationFactory ConfigureTestServices)
builder.ConfigureTestServices(services =>
{
    services.AddOpenTelemetry()
        .WithTracing(tracing => tracing
            .AddSource("MyApi.Services")
            .AddSource("Microsoft.AspNetCore")
            .AddSource("System.Net.Http")
            .AddSource("Microsoft.EntityFrameworkCore")
            .AddTestTrackingExporter());
});

// In your report generation
var options = new ReportConfigurationOptions
{
    InternalFlowTracking = true,
    InternalFlowDiagramStyle = InternalFlowDiagramStyle.ActivityDiagram,
    InternalFlowSpanGranularity = InternalFlowSpanGranularity.AutoInstrumentation,
    InternalFlowNoDataBehavior = InternalFlowNoDataBehavior.ShowMessage,
    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.


Architecture

Test Execution                                    Report Generation
                                                  
┌──────────────────────────┐                      ┌──────────────────────────────┐
│ OTel TracerProvider       │                      │ InternalFlowSpanCollector    │
│  └─ AddTestTrackingExporter()──┐                │  └─ Loads spans via          │
│                              │                │     reflection from           │
│ Activity spans               │                │     TestTrackingSpanStore     │
│  └─ Captured to              ▼                │                              │
│     TestTrackingSpanStore ◄──┘                │ InternalFlowSegmentBuilder   │
│                                               │  └─ Correlates spans with    │
│ TestTrackingMessageHandler  ──────────────►   │     HTTP boundary timestamps │
│  └─ Timestamps + TraceId                      │                              │
│     on RequestResponseLog                     │ InternalFlowRenderer         │
│                                               │  └─ ActivityDiagram or       │
│                                               │     CallTree output          │
│                                               │                              │
│                                               │ InternalFlowHtmlGenerator    │
│                                               │  └─ window.__iflowSegments   │
│                                               │     JSON data block          │
│                                               │                              │
│                                               │ DiagramContextMenu           │
│                                               │  └─ Popup JS/CSS for         │
│                                               │     interactive arrows       │
└──────────────────────────┘                      └──────────────────────────────┘

Requirements and Limitations

  • PlantUML only — Internal Flow Tracking requires DiagramFormat.PlantUml. Mermaid does not support clickable links with the same mechanism.
  • 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).
  • OpenTelemetry package optional — The core library uses reflection to load spans from TestTrackingSpanStore. If the TestTrackingDiagrams.Extensions.OpenTelemetry package is not installed, internal flow arrows will show "No internal activity captured" (or behave according to InternalFlowNoDataBehavior).
  • 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.

Home


Demo


Getting Started

Common Tasks

Integration Guides

Extensions

Configuration

Features

Reference

Clone this wiki locally