@azure/mcp-win32-x64
Version:
Azure MCP Server - Model Context Protocol implementation for Azure, for win32 on x64
120 lines (93 loc) • 5.97 kB
Markdown
---
title: ActivityProcessors
category: api-reference
applies-to: 1.x
---
# Activity Processors — Filtering & Enrichment
## Key concepts
- Subclass `BaseProcessor<Activity>` to create a custom processor.
- **`OnStart`** = enrichment (add tags early). **`OnEnd`** = filtering (all data finalized).
- Register via `.AddProcessor<T>()` — see WithTracing.md.
## Filtering — drop spans
Clear the `Recorded` flag to prevent export. Export processors (`BatchActivityExportProcessor`, `SimpleActivityExportProcessor`) check `Activity.Recorded` and skip when `false`.
```csharp
using System.Diagnostics;
using OpenTelemetry;
public class HealthCheckFilterProcessor : BaseProcessor<Activity>
{
private static readonly HashSet<string> SuppressedPaths = new(StringComparer.OrdinalIgnoreCase)
{
"/health", "/ready", "/healthz", "/liveness"
};
public override void OnEnd(Activity data)
{
var path = data.GetTagItem("url.path")?.ToString()
?? data.GetTagItem("http.route")?.ToString();
if (path != null && SuppressedPaths.Contains(path))
{
data.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded;
}
}
}
```
> **Important:** Do not rely on skipping `base.OnEnd()` — `CompositeProcessor` iterates all processors regardless. The `Recorded` flag is the correct filtering mechanism.
## Enrichment — add tags
Use `OnStart` to attach tags before the span completes:
```csharp
using System.Diagnostics;
using OpenTelemetry;
public class EnrichmentProcessor : BaseProcessor<Activity>
{
public override void OnStart(Activity data)
{
data.SetTag("deployment.environment", "production");
data.SetTag("cloud.region", "eastus");
}
}
```
## Inspectable `Activity` properties
`DisplayName`, `Source.Name`, `Kind`, `Status`, `Duration`, `Tags` (via `GetTagItem()`), `Events`, `Links`, `ParentId`.
## Migration from Application Insights 2.x
> **Key difference:** A single `ITelemetryInitializer` or `ITelemetryProcessor` handled all signal types (requests, dependencies, traces, events). In OpenTelemetry, traces, logs, and metrics are separate pipelines — each processor only sees its own signal. If the old initializer/processor touched both signals (e.g., checked for `RequestTelemetry` and `TraceTelemetry`), you need two OpenTelemetry processors: `BaseProcessor<Activity>` for traces + `BaseProcessor<LogRecord>` for logs. If it only touched trace types (`RequestTelemetry`, `DependencyTelemetry`), a single `BaseProcessor<Activity>` is sufficient. Any `if (telemetry is MetricTelemetry) return;` guards can be removed — metrics have their own pipeline and never reach activity or log processors.
| 2.x Pattern | 3.x Equivalent |
| --- | --- |
| `ITelemetryInitializer.Initialize(ITelemetry)` | `BaseProcessor<Activity>.OnStart(Activity)` — enrich traces |
| `ITelemetryInitializer` touching `TraceTelemetry`/`EventTelemetry` | `BaseProcessor<LogRecord>.OnEnd(LogRecord)` — enrich logs (see LogProcessors.md) |
| `ITelemetryProcessor.Process(ITelemetry)` | `BaseProcessor<Activity>.OnEnd(Activity)` — filter traces |
| `ITelemetryProcessor` filtering `TraceTelemetry` by severity | `ILoggingBuilder.AddFilter<OpenTelemetryLoggerProvider>()` — filter logs (see LogProcessors.md) |
| `services.AddApplicationInsightsTelemetryProcessor<T>()` | `.AddProcessor<T>()` on `TracerProviderBuilder` |
| Not calling `next.Process(item)` to drop | `data.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded;` |
### Property mapping
| 2.x Property | Activity equivalent | LogRecord equivalent |
| --- | --- | --- |
| `telemetry.Context.GlobalProperties["key"]` | `data.SetTag("key", value)` | Add to `data.Attributes` |
| `telemetry.Properties["key"]` | `data.SetTag("key", value)` | Add to `data.Attributes` |
| `request.Url.AbsolutePath` | `data.GetTagItem("url.path")` | N/A |
| `request.ResponseCode` / `dependency.ResultCode` | `data.GetTagItem("http.response.status_code")` | N/A |
| `request.Success = false` | `data.SetStatus(ActivityStatusCode.Error)` | N/A |
| `request.Name` / `dependency.Name` | `data.DisplayName` (get/set) | N/A |
| `dependency.Type` | `data.GetTagItem("db.system")` or `data.GetTagItem("rpc.system")` | N/A |
| `dependency.Target` | `data.GetTagItem("server.address")` | N/A |
| `dependency.Duration` | `data.Duration` | N/A |
| `trace.SeverityLevel` | N/A | `data.LogLevel` |
| `trace.Message` | N/A | `data.FormattedMessage` or `data.Attributes` |
| `telemetry.Context.User.AuthenticatedUserId` | `data.SetTag("enduser.id", value)` | N/A |
| `telemetry.Context.Operation.Id` | `data.TraceId` (read-only, set by framework) | `data.TraceId` |
| `telemetry.Context.Cloud.RoleName` | Not a processor concern — use `ConfigureResource(r => r.AddService("name"))` | Same |
### Telemetry type mapping
Old processors often check `if (telemetry is RequestTelemetry)` or `if (telemetry is DependencyTelemetry)`. In 3.x, check `Activity.Kind`:
| `ActivityKind` | Application Insights telemetry type |
| --- | --- |
| `Server` | `RequestTelemetry` |
| `Consumer` | `RequestTelemetry` (message-driven) or `DependencyTelemetry` (when acting as downstream call) |
| `Client` | `DependencyTelemetry` |
| `Producer` | `DependencyTelemetry` |
| `Internal` | `DependencyTelemetry` (if parent is `Server`/`Consumer`) |
Other signal types:
| Source | Application Insights telemetry type |
| --- | --- |
| `ILogger` logs | `TraceTelemetry` |
| `ILogger` logs with `microsoft.custom_event.name` attribute | `EventTelemetry` |
| Exceptions logged via `ILogger.LogError` / `LogCritical` | `ExceptionTelemetry` — now a `LogRecord` with exception attributes |
| Exceptions on spans | `ExceptionTelemetry` — now an `Activity.Event` named `exception` (check `data.Events`) |
| `Meter` measurements | `MetricTelemetry` |