Web request performance

  • Latest Dynatrace
  • Explanation
  • 1-min read

The Dynatrace MAUI SDK provides comprehensive web request monitoring capabilities.

  • Automatic instrumentation: Automatically capture all web requests made via HttpClient using the Dynatrace HttpMessageHandler.
  • W3C Trace Context: Enable distributed tracing by propagating trace context headers.
  • Manual reporting: Report web requests from custom networking implementations.
  • Event enrichment: Add custom properties to web request events.

Turn on automatic web request instrumentation

You can optionally use the following method to turn on the auto-instrumentation of web requests. The HttpMessageHandler used by HttpClient takes care of the web request instrumentation.

using Dynatrace.MAUI;
var httpHandler = Agent.Instance.GetHttpMessageHandler(null, null);
var httpClient = new HttpClient(httpHandler);

Moreover, you can also have your own HTTP handler:

using Dynatrace.MAUI;
var defaultHttpHandler = new HttpClientHandler();
var httpHandler = Agent.Instance.GetHttpMessageHandler(defaultHttpHandler, null);
var httpClient = new HttpClient(httpHandler);

What is captured automatically

When you use the Dynatrace HttpMessageHandler, the following data is captured for every request.

  • Request URL and HTTP method
  • Response status code and reason phrase
  • Request and response body sizes
  • Request duration
  • W3C trace context headers for distributed tracing

Request size calculation

The HttpClientOptions parameter in GetHttpMessageHandler is used for accurate request size calculation. By default, HttpClient might append headers that aren't visible to the handler at request time. If you modify or add custom headers that affect request size, provide them in HttpClientOptions so the size calculation remains accurate:

using Dynatrace.MAUI;
using Dynatrace.MAUI.Abstraction.Core;
using System.Net.Http;
var customHeaders = new Dictionary<string, string[]>
{
["User-Agent"] = new[] { "MyApp/1.0" },
["Accept-Encoding"] = new[] { "gzip", "deflate" }
};
var options = new HttpClientOptions(customHeaders);
var handler = Agent.Instance.GetHttpMessageHandler(null, options);
var httpClient = new HttpClient(handler);

The requestHeaders parameter doesn't add headers to your requests. It only informs the handler about headers that are automatically added by HttpClient for accurate size calculation.

W3C Trace Context (distributed tracing)

The SDK supports W3C Trace Context (external) for distributed tracing, allowing you to correlate mobile requests with backend services instrumented by Dynatrace.

For background and examples, see W3C Trace Context.

How it works

When automatic web request instrumentation is turned on, the Dynatrace HttpMessageHandler may propagate W3C trace context on outgoing requests by setting the following headers.

  • traceparent: Identifies the request as part of a distributed trace.
  • tracestate: Carries vendor-specific context. Dynatrace adds Dynatrace-specific information here.

The Dynatrace HttpMessageHandler sets these headers automatically as part of its web request instrumentation.

W3C Trace Context headers (traceparent / tracestate) are used for distributed tracing correlation across services.

Automatic trace context header propagation

In most cases, you don't need to set W3C Trace Context headers yourself. When automatic web request instrumentation is turned on, the Dynatrace HttpMessageHandler inspects each outgoing request and decides whether to generate, preserve, or leave trace context headers unchanged based on what it finds.

  • No existing headers: This is the typical case. If the request has no trace headers, the handler generates a new valid traceparent and adds a corresponding tracestate entry with Dynatrace-specific context. It activates correlation between the mobile request and the service-side trace. See Mobile firewall constraints.
  • Existing valid traceparent header: If the request already contains a valid traceparent, the handler keeps it and updates tracestate by adding Dynatrace vendor-specific information, without overwriting existing vendor entries.

If the combined tracestate grows too large, entries may be trimmed to stay within W3C limits. Dynatrace data is preserved whenever possible.

Manually propagate trace context headers

If you use a custom networking stack or don't use the Dynatrace HttpMessageHandler, you can propagate W3C Trace Context headers yourself and still correlate the request with Dynatrace distributed traces.

Use Agent.Instance.GenerateTraceContext(traceparent, tracestate) to create or enrich trace context according to the official W3C Trace Context specification.

  • If traceparent is null, the SDK generates a new traceparent and a corresponding tracestate (including Dynatrace-specific information).
  • If traceparent is present and valid, the SDK keeps it and enriches tracestate with Dynatrace vendor-specific information (without overwriting existing vendor entries).
  • If the trace context can't be created (for example, invalid traceparent or capture/tagging isn't allowed), the API returns null and you shouldn't change the request headers.

You can then reuse the resulting traceparent to correlate a manually reported web request event.

using Dynatrace.MAUI;
using System.Net.Http;
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.example.com/data");
// Read existing headers (if any)
string? existingTraceparent = request.Headers.Contains("traceparent")
? request.Headers.GetValues("traceparent").FirstOrDefault()
: null;
string? existingTracestate = request.Headers.Contains("tracestate")
? request.Headers.GetValues("tracestate").FirstOrDefault()
: null;
// Generate/enrich trace context
var traceContext = Agent.Instance.GenerateTraceContext(existingTraceparent, existingTracestate);
string? traceparentForReporting = traceContext?.GetTraceparent();
if (traceContext != null)
{
traceContext.ApplyTo(request.Headers);
}
// ...execute your HTTP request here...
var requestData = new HttpRequestEventData(request.RequestUri.ToString(), request.Method.Method)
.WithDuration(duration)
.WithStatusCode(statusCode);
if (traceparentForReporting != null)
{
requestData.WithTraceparentHeader(traceparentForReporting);
}
Agent.Instance.SendHttpRequestEvent(requestData);

Instrument web requests

Use the following code snippet to instrument web requests:

using Dynatrace.MAUI;
// Create an action
IRootAction webAction = Agent.Instance.EnterAction(actionName: "WebRequest Action");
// Generate a new unique tag associated with the web request action
string requestTag = webAction.GetRequestTag(url);
string requestTagHeader = webAction.GetRequestTagHeader();
// Place the Dynatrace HTTP header on your web request
httpClient.DefaultRequestHeaders.Add(requestTagHeader, requestTag);
// Generate a WebRequestTiming object based on the unique tag
IWebRequestTiming timing = Agent.Instance.GetWebRequestTiming(requestTag, url);
// Start web request timing before the HTTP request is sent
timing.StartWebRequestTiming();
try
{
var response = await httpClient.GetAsync(url);
// Stop web request timing when the HTTP response is received and the response body is obtained
timing.StopWebRequestTiming(url, (int)response.StatusCode, response.ReasonPhrase);
}
catch (HttpRequestException exception)
{
// Stop web request timing when a connection exception occurs
timing.StopWebRequestTiming(url, -1, exception.ToString());
}
finally
{
// Leave an action
webAction.LeaveAction();
}

Report web requests manually

For HTTP libraries or custom implementations not supported by automatic instrumentation, you can manually report web requests using SendHttpRequestEvent.

Using SendHttpRequestEvent

Use SendHttpRequestEvent to report a completed HTTP request. The URL and request method are required; all other fields are optional.

using Dynatrace.MAUI;
using System.Net.Http;
using System.Diagnostics;
var url = "https://api.example.com/data";
var httpClient = new HttpClient();
var stopwatch = Stopwatch.StartNew();
var response = await httpClient.GetAsync(url);
stopwatch.Stop();
var eventData = new HttpRequestEventData(url, "GET")
.WithStatusCode((int)response.StatusCode)
.WithDuration(stopwatch.ElapsedMilliseconds)
.WithBytesReceived((int)response.Content.Headers.ContentLength);
Agent.Instance.SendHttpRequestEvent(eventData);

With distributed tracing

To correlate manually reported requests with distributed traces, include the traceparent header in the event. For details on generating trace context, see W3C Trace Context (distributed tracing).

using Dynatrace.MAUI;
var requestData = new HttpRequestEventData(url, "GET")
.WithDuration(duration)
.WithStatusCode(statusCode)
.WithTraceparentHeader(traceContext?.GetTraceparent());
Agent.Instance.SendHttpRequestEvent(requestData);

With error reporting

You can report failed HTTP requests by attaching the exception:

using Dynatrace.MAUI;
using System.Net.Http;
try
{
var response = await httpClient.GetAsync("https://api.example.com/data");
}
catch (Exception ex)
{
var eventData = new HttpRequestEventData("https://api.example.com/data", "GET")
.WithException(ex);
Agent.Instance.SendHttpRequestEvent(eventData);
}

Add custom properties

You can enrich HTTP request events with custom properties for additional context:

using Dynatrace.MAUI;
using System.Net.Http;
using System.Diagnostics;
var stopwatch = Stopwatch.StartNew();
var response = await httpClient.PostAsync(url, content);
stopwatch.Stop();
var eventData = new HttpRequestEventData(url, "POST")
.WithStatusCode((int)response.StatusCode)
.WithDuration(stopwatch.ElapsedMilliseconds)
.WithBytesSent(1024)
.WithBytesReceived(2048)
.WithReasonPhrase(response.ReasonPhrase)
.AddEventProperty("event_properties.endpoint", "/data")
.AddEventProperty("event_properties.api_version", "v2");
Agent.Instance.SendHttpRequestEvent(eventData);

SendHttpRequestEvent parameters

MethodTypeDescription

HttpRequestEventData(url, method)

Constructor

Creates a new HTTP request event. URL and method are required.

.WithDuration(milliseconds)

long

Duration of the request in milliseconds. Negative or non-finite values default to 0.

.WithStatusCode(code)

int

HTTP response status code. Codes 400–599 mark the request as failed. Negative values are dropped.

.WithReasonPhrase(phrase)

string

HTTP response reason phrase (for example, "OK", "Not Found").

.WithBytesSent(bytes)

int

Number of bytes sent in the request body. Negative values are dropped.

.WithBytesReceived(bytes)

int

Number of bytes received in the response body. Negative values are dropped.

.WithTraceparentHeader(header)

string?

W3C traceparent header value for distributed tracing. Use GenerateTraceContext to obtain a value.

.WithException(exception)

Exception

Exception that occurred during the request. Adds type, message, and stack trace to the event.

.AddEventProperty(key, value)

string, int, long, double, bool

Adds a custom event property. Keys need to be prefixed with event_properties..

HTTP event modifier

HTTP event modifiers allow you to customize HTTP event data before it's sent to Dynatrace. Common use cases include adding custom properties based on request or response details, redacting sensitive information from URLs, filtering out specific requests, and enriching events with business context.

HTTP event modifiers only work with events automatically captured by the Dynatrace HttpMessageHandler and don't apply to manually reported HTTP events.

HTTP events can also be modified via the general event modifier API, but the HTTP event modifier provides additional parameters to access the HTTP request and response details. When both modifier types are registered, general event modifiers run first, followed by HTTP event modifiers.

Add an HTTP event modifier

Implement the IHttpEventModifier interface and register it with AddHttpEventModifier:

using Dynatrace.MAUI;
using System.Net.Http;
class MyHttpEventModifier : IHttpEventModifier
{
public Dictionary<string, object>? ModifyHttpEvent(
Dictionary<string, object> fields,
HttpRequestMessage request,
HttpResponseMessage? response,
Exception? exception)
{
// Add custom metadata based on the request
fields["event_properties.endpoint"] = request.RequestUri?.AbsolutePath ?? "unknown";
// Redact sensitive URL parts
if (fields.ContainsKey("url.full"))
{
var url = fields["url.full"]?.ToString() ?? "";
fields["url.full"] = url.Replace("/users/12345/", "/users/{id}/");
}
// Filter out requests to internal health check endpoints
if (request.RequestUri?.AbsolutePath == "/health")
{
return null; // Drop the event
}
return fields;
}
}
// Register the modifier
IHttpEventModifier modifier = new MyHttpEventModifier();
Agent.Instance.AddHttpEventModifier(modifier);

Remove HTTP event modifiers

Remove modifiers when they're no longer needed:

using Dynatrace.MAUI;
Agent.Instance.RemoveHttpEventModifier(modifier);

HTTP modifier parameters

ParameterTypeDescription

fields

Dictionary<string, object>

The HTTP event data containing fields like url.full, http.request.method, http.response.status_code.

request

HttpRequestMessage

The HTTP request object with URL, method, and headers.

response

HttpResponseMessage?

The HTTP response. Null if the request failed before a response was received.

exception

Exception?

Any exception that occurred. Null for successful requests.

Most fields entries are read-only. The fields open for modification are url.full, exception.stack_trace, and any key prefixed with event_properties..

Modifiers run in the order they were added. If any modifier returns null, the event is discarded and subsequent modifiers don't run. Exceptions thrown inside a modifier are logged but don't block other modifiers.

Related tags
Digital Experience