Trace one Znode 9 request from browser to service
Follow one observed Znode 9 request through its actual implementation, record model and context changes, and time synchronous calls without logging their data.
In this guide
- browser action
- route and controller
- agent
- client
- API boundary
- service
- response
Start with one observable action
Choose a harmless action such as loading a fictional product note. In browser developer tools, record method, URL, response status, payload shape, and correlation ID if available. Redact tokens, cookies, account data, and customer values before sharing evidence.
Build the trace table
Start with the controller action selected by the observed route, then follow the exact method it calls. For each hop, record the input type, output type, transformation, and source of Store or account context. Typical Znode 9 paths can include a controller, agent, API client, API controller, and service, but the selected request determines the real sequence.
Use the JSON below as a small investigation record. Replace its example route and model descriptions with the observed names from your build. It does not configure routing or make those layers execute.
request-trace.json json
{
"request": { "method": "GET", "route": "/example" },
"hops": [
{ "boundary": "controller", "input": "request model", "output": "view model", "check": "input validation" },
{ "boundary": "agent", "input": "view model", "output": "domain request", "check": "server context" },
{ "boundary": "API client", "input": "DTO", "output": "DTO", "check": "authorization" },
{ "boundary": "service", "input": "domain request", "output": "result", "check": "business rule" }
]
}
If the controller calls a custom agent and that agent returns from a cache, the request may never reach an API service. Record the cache return as the observed path rather than adding an assumed service hop. When comparing two builds, differences in this record show where routing, context, or transformation changed.
Set breakpoints after the map
Send one synthetic request and set breakpoints on the mapped methods. Compare the resolved implementation types and the model values at each step. If execution diverges, inspect route selection, container registration, cache returns, and configuration before adding another call.
The supplemental RequestBoundary.Measure helper times a synchronous operation and preserves its typed result or exception. Reuse one server-generated correlation GUID across the measured hops. For async methods, use an async timing wrapper that awaits completion; timing only Task creation would miss the operation's duration.
Finish with a safe finding
Finish with the actual path, the source of trusted context, and the checks performed before business data is returned. If a hop remains unresolved, state the next route or implementation to inspect in the internal investigation record.
Retain a sanitized trace that another developer can repeat against the same build. A similarly named controller is not enough evidence: match the route values and resolved type to the request you observed.
Time a named request boundary without logging its inputs
Wrap one synchronous call with Measure, a fixed allowed operation name, the request's server-generated correlation GUID, and a configured TraceSource. The generic type T preserves the real return type, so a method returning ProductFinishViewModel still returns ProductFinishViewModel. A business exception continues to the caller while timing is logged in finally. Enable the TRACE compilation symbol. Use an awaiting async wrapper for Task-based work, because this helper measures only the synchronous delegate invocation.
Diagnostics/RequestBoundary.cs csharp
using System;
using System.Collections.Generic;
using System.Diagnostics;
public static class RequestBoundary
{
private static readonly ISet<string> Allowed = new HashSet<string>(StringComparer.Ordinal)
{ "controller.product-read", "agent.product-read", "service.product-read" };
public static T Measure<T>(string operation, Guid correlation, Func<T> action, TraceSource trace)
{
if (!Allowed.Contains(operation) || correlation == Guid.Empty || action == null || trace == null)
throw new ArgumentException("Use an approved boundary and server correlation.");
var watch = Stopwatch.StartNew();
bool successful = false;
try
{
T result = action();
successful = true;
return result;
}
finally
{
watch.Stop();
try
{
trace.TraceEvent(TraceEventType.Information, 1001,
"boundary={0} correlation={1:D} elapsedMs={2} successful={3}",
operation, correlation, watch.ElapsedMilliseconds, successful);
}
catch { /* Diagnostic listener failures must not replace the business outcome. */ }
}
}
}
Enable the named trace source for local debugging
Merge system.diagnostics into the existing Framework 4.8 development Web.config while retaining its other sections. Create TraceSource with the exact name Gcg.RequestBoundaries and pass it to RequestBoundary.Measure. With the TRACE compilation symbol enabled, DefaultTraceListener sends the Information events to the attached debugger's output. Choose the deployed application's approved listener separately; this local debugging listener is not a persistent operations log.
Web.config xml
<configuration>
<system.diagnostics>
<sources>
<source name="Gcg.RequestBoundaries" switchValue="Information">
<listeners>
<clear />
<add name="debug" type="System.Diagnostics.DefaultTraceListener" />
</listeners>
</source>
</sources>
<trace autoflush="true" />
</system.diagnostics>
</configuration>
References and further reading
Bring your next engineering question.
Need help untangling a legacy customization path?
Independent guidance from GCG. Znode is a trademark of its owner. Examples use fictional data and are not official platform documentation. Suggest a correction.