-
Notifications
You must be signed in to change notification settings - Fork 1
Filtering and Redacting Diagram Content
This page provides copy-paste recipes for the most common diagram content problems, organised by what you're trying to fix. For the full formatting pipeline reference (pre/mid/post processors, DiagramsFetcherOptions), see Content Formatting.
Large HTTP bodies — JWT tokens, base64 blobs, verbose JSON — make diagram notes stretch horizontally, pushing the sequence diagram well beyond the viewport.
Set a maximum character limit on all captured content. Anything longer is trimmed with a marker:
// In test setup (e.g. fixture constructor or GlobalSetup)
RequestResponseLogger.MaxContentLength = 2000;Content exceeding 2,000 characters is trimmed to: …truncated (N chars total). This applies globally to all extensions (HTTP, Cosmos, Redis, Kafka, etc.) since truncation happens in the core Log() method.
Tokens and base64 strings are single unbroken words that prevent line wrapping. The built-in SplitLongWords() extension inserts zero-width spaces so PlantUML can wrap them:
new ReportConfigurationOptions
{
RequestResponsePostProcessor = content => content.SplitLongWords()
};When you only care about specific JSON fields, hide everything else:
new ReportConfigurationOptions
{
FocusDeEmphasis = FocusDeEmphasis.Hidden
};Then in your test, declare which fields to show:
var response = await client.WithDiagramFocus()
.OnResponse<OrderResponse>(x => x.Total, x => x.Status)
.GetAsync("/api/orders/123");Non-focused fields are completely removed from the diagram note, replaced with .... See Content Formatting#DiagramFocus (Field Highlighting) for full details.
Authorization and correlation headers add width without value. Remove them from diagrams entirely:
new ReportConfigurationOptions
{
ExcludedHeaders = ["Authorization", "X-Request-Id", "X-Correlation-Id", "Cookie"]
};See Excluded Headers for more details.
If the problem is too many arrows (not content width), the library automatically splits diagrams when they exceed height/URL thresholds. See Large Response and Diagram Handling for configuration.
Tokens, secrets, PII, and API keys should not appear in diagrams — especially if reports are shared or stored in CI artifacts.
The simplest and most common recipe. Use a mid-processor for clean regex without PlantUML markup interference:
new ReportConfigurationOptions
{
RequestResponseMidProcessor = content =>
Regex.Replace(content, @"Bearer [A-Za-z0-9\-._~+/]+=*", "Bearer ***")
};Token endpoints return tokens in JSON bodies. Target them by property name:
private static readonly Regex AccessTokenRegex = new("(?<=\"access_token\": \")[^\"]+(?=\")");
private static readonly Regex RefreshTokenRegex = new("(?<=\"refresh_token\": \")[^\"]+(?=\")");
private static readonly Regex IdTokenRegex = new("(?<=\"id_token\": \")[^\"]+(?=\")");
new ReportConfigurationOptions
{
RequestResponseMidProcessor = content => content
.RegexReplace(AccessTokenRegex, "***")
.RegexReplace(RefreshTokenRegex, "***")
.RegexReplace(IdTokenRegex, "***")
};new ReportConfigurationOptions
{
RequestResponsePostProcessor = content =>
Regex.Replace(content, @"(?<=\[(?:Set-)?Cookie=)[^\]]{50,}(?=])", match =>
match.Value[..20] + "…REDACTED")
};private static readonly Regex EmailRegex = new("(?<=\"email\": \")[^\"]+(?=\")");
private static readonly Regex PhoneRegex = new("(?<=\"phone\": \")[^\"]+(?=\")");
new ReportConfigurationOptions
{
RequestResponseMidProcessor = content => content
.RegexReplace(EmailRegex, "***@***.***")
.RegexReplace(PhoneRegex, "***-***-****")
};If you don't need to see the token format at all, exclude the header from diagrams:
new ReportConfigurationOptions
{
ExcludedHeaders = ["Authorization"]
};If you want to show what the token contains (for debugging) without the 500-character noise, decode the JWT payload and replace the raw token:
RequestResponseMidProcessor = content =>
Regex.Replace(content, @"Bearer ([A-Za-z0-9\-._~+/]+=*)", match =>
{
try
{
var parts = match.Groups[1].Value.Split('.');
if (parts.Length != 3) return "Bearer ***";
var payload = Encoding.UTF8.GetString(Convert.FromBase64String(
parts[1].PadRight(parts[1].Length + (4 - parts[1].Length % 4) % 4, '=')));
var json = JsonSerializer.Deserialize<JsonElement>(payload);
var sub = json.TryGetProperty("sub", out var s) ? s.GetString() : "?";
var role = json.TryGetProperty("role", out var r) ? r.GetString() : null;
return role != null ? $"Bearer [sub={sub}, role={role}]" : $"Bearer [sub={sub}]";
}
catch { return "Bearer ***"; }
})Sometimes you need to correlate which requests share the same token across a sequence diagram, but the full token is too long to display. Hash-based shortening replaces each token with a short deterministic hash — the same token always produces the same short string, so you can visually trace token reuse across diagram arrows.
Bearer eyJhbGciOiJSUzI1NiIs... → Bearer #a3f29c01
Bearer eyJhbGciOiJSUzI1NiIs... → Bearer #a3f29c01 (same token = same hash)
Bearer eyJhbGciOiJIUzI1NiIs... → Bearer #7b1e4d92 (different token = different hash)
using System.IO.Hashing;
RequestResponseMidProcessor = content =>
Regex.Replace(content, @"Bearer [A-Za-z0-9\-._~+/]+=*", match =>
{
var bytes = Encoding.UTF8.GetBytes(match.Value);
var hash = XxHash64.HashToUInt64(bytes);
return $"Bearer #{hash:x8}"; // 8 hex chars, deterministic
})using System.Security.Cryptography;
RequestResponseMidProcessor = content =>
Regex.Replace(content, @"Bearer [A-Za-z0-9\-._~+/]+=*", match =>
{
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(match.Value));
return $"Bearer #{Convert.ToHexString(hash)[..8].ToLower()}";
})| Approach | Use when | Example output |
|---|---|---|
Full redaction (***) |
You never need to distinguish tokens | Bearer *** |
Hash shortening (#a3f29c01) |
You need to see which requests share a token | Bearer #a3f29c01 |
| RedactMiddle (keep start+end) | You need to identify the token type/issuer | Bearer eyJhbGc…KxwRJSM |
| JWT claims extraction | You need to see the subject/role | Bearer [sub=user1, role=admin] |
The same pattern works for access_token / refresh_token in JSON bodies:
RequestResponseMidProcessor = content =>
{
// Hash bearer tokens in headers
content = Regex.Replace(content, @"Bearer [A-Za-z0-9\-._~+/]+=*", match =>
{
var hash = XxHash64.HashToUInt64(Encoding.UTF8.GetBytes(match.Value));
return $"Bearer #{hash:x8}";
});
// Hash JSON token fields
content = Regex.Replace(content, "(?<=\"(?:access|refresh|id)_token\": \")[^\"]+(?=\")", match =>
{
var hash = XxHash64.HashToUInt64(Encoding.UTF8.GetBytes(match.Value));
return $"#{hash:x8}";
});
return content;
}Test setup (Given/Arrange) often involves seeding data, creating users, or configuring state — operations that clutter diagrams without adding value to the scenario under test.
new TestTrackingMessageHandlerOptions
{
TrackDuringSetup = false
};All HTTP calls made during the Setup phase are silently dropped.
new TestTrackingMessageHandlerOptions
{
SetupVerbosity = Verbosity.Summarised
};Setup interactions appear as arrows with method + URL, but request/response bodies are omitted. Action interactions retain full detail.
BDD frameworks (BDDfy, LightBDD, ReqNRoll) set the phase automatically from step keywords. For xUnit/NUnit/MSTest, mark the boundary explicitly:
// Setup
await SeedTestData();
await CreateUser("test-user");
// Mark the start of the action phase
trackingDiagramOverride.StartAction();
// Action — this is what we're testing
var response = await client.PostAsJsonAsync("/api/orders", order);See Phase-Aware Tracking for full details on phase configuration, per-extension options, and BDD auto-detection.
If a response contains rendered HTML that's meaningless in a diagram:
RequestResponsePostProcessor = content =>
Regex.Replace(content, "<[^>]+>", "").Trim()The library only auto-formats JSON. For XML/SOAP responses, use a pre-processor:
new DiagramsFetcherOptions
{
ResponsePreFormattingProcessor = body =>
{
try { return XDocument.Parse(body).ToString(); }
catch { return body; }
}
}If you're tracking blob storage operations, the binary content is useless in diagrams:
RequestResponseMidProcessor = content =>
content.Length > 5000 ? $"[Binary content: {content.Length:N0} chars]" : contentnew ReportConfigurationOptions
{
// Clean text: redact tokens, PII
RequestResponseMidProcessor = content => content
.RedactBearerTokens()
.RedactAccessTokens()
.RedactPii(),
// Final layout: split long words that survived redaction
RequestResponsePostProcessor = content => content
.SplitLongWords()
};- Content Formatting — Full formatting pipeline reference (pre/mid/post processors, DiagramsFetcherOptions, DiagramFocus)
- Excluded Headers — Remove specific HTTP headers from diagram notes
- Large Response and Diagram Handling — Automatic diagram splitting when content exceeds thresholds
- Phase-Aware Tracking — Setup vs Action phase configuration
-
Report Configuration — All
ReportConfigurationOptionsproperties - Diagram Customisation — Visual customisation (colours, participants, separators)
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 ClickHouse 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
- Merging Parallel Reports
Reference