Processors
A processor sees every record the .NET pipeline handles before it is queued, and can change it or drop it: add a tenant id to every span, rename a noisy log, drop health checks and static files, strip an attribute only your app knows is sensitive. It is one small interface with three optional methods.
IBelemetryProcessor
OnSpan, OnLog and OnMetric each receive the record - a
BelemetrySpan, BelemetryLogRecord or BelemetryMetric, every member of
which may be changed - and return false to drop it. All three have default implementations that
keep the record, so a processor implements only what it cares about. Add processors with
options.AddProcessor; they run in the order they were added, after the built-in privacy rules.
public sealed class TenantProcessor(ITenantAccessor tenants) : IBelemetryProcessor
{
public bool OnSpan(BelemetrySpan span)
{
span.Attributes.Set("tenant.id", tenants.Current);
return span.Name != "GET /health"; // false drops it
}
public bool OnLog(BelemetryLogRecord log) => log.Body?.Contains("noise") is not true;
// OnMetric is not implemented: the default keeps every metric.
}
builder.Services.AddBitBelemetryServices(options =>
{
options.AddProcessor(new TenantProcessor(tenantAccessor));
});
Which records they see
Processors run on every record of the .NET pipeline:
ILoggerentries,Activityspans andMetermetrics.IBelemetrycalls on WebAssembly and Hybrid, where they go through the .NET pipeline.- Browser records relayed to .NET with the Relay transport (the default in Hybrid) - so one processor can enrich page views, clicks and fetch spans as well.
- Everything the server pipeline collects, when it is on.
With the Direct transport the browser script exports its own records, and .NET never sees them. Shape those
with the browser options instead - IgnoreUrls, IgnoreErrorMessages,
data-bit-belemetry-ignore and the privacy rules.
A real one: DemoNoiseFilter
This site runs the processor below on both of its pipelines, the WebAssembly one and the server's. The live
feeds poll /api/telemetry every two seconds with HttpClient, and the .NET side
exports to /otlp; without it every reader would see a span for each of those, and the server would
record a request span for every static file and every piece of the WebAssembly payload. The browser script
does not need it: the same URLs are in its IgnoreUrls.
No .NET HTTP spans yet - press the first button; the second never shows up here.
public sealed class DemoNoiseFilter : IBelemetryProcessor
{
private static readonly string[] _noise =
[
.. DemoTelemetry.SelfUrls, // "/otlp/", "/api/telemetry"
"/_framework/", "/_content/", "/_blazor", "/images/", "/.well-known/", "/_vs/", "/browserLink",
];
// The site's stylesheets, scripts and icons, served at the root under fingerprinted names.
private static readonly string[] _staticFiles = [".css", ".js", ".map", ".svg", ".png", ".ico", ".woff2"];
public bool OnSpan(BelemetrySpan span)
{
var url = span.Attributes.GetString(BelemetrySemanticConventions.UrlFull)
?? span.Attributes.GetString(BelemetrySemanticConventions.UrlPath)
?? span.Attributes.GetString(BelemetrySemanticConventions.HttpRoute);
if (url is null) return true;
foreach (var noise in _noise)
{
if (url.Contains(noise, StringComparison.OrdinalIgnoreCase)) return false;
}
var path = url.AsSpan();
var end = path.IndexOfAny('?', '#');
if (end >= 0) path = path[..end];
foreach (var extension in _staticFiles)
{
if (path.EndsWith(extension, StringComparison.OrdinalIgnoreCase)) return false;
}
return true;
}
}
// Shared by the client and the server (Services/DemoTelemetry.cs):
options.AddProcessor(new DemoNoiseFilter());Enrich, rewrite, drop
Enriching is setting attributes. Rewriting is assigning members: a span's Name, a log's
Body or Severity, a metric's points. Dropping is returning false. The
same processor can do all three, for any of the three signals.
API
IBelemetryProcessor
Interface
Inspects, enriches or drops records on the .NET side before they are queued for export - including records relayed from the browser.
| Name | Type and default | Description |
|---|---|---|
OnSpan(BelemetrySpan span) |
bool |
Called for every finished span. Return false to drop it. |
OnLog(BelemetryLogRecord log) |
bool |
Called for every log record or event. Return false to drop it. |
OnMetric(BelemetryMetric metric) |
bool |
Called for every collected metric. Return false to drop it. |