Skip to content
57 changes: 56 additions & 1 deletion troubleshoot/ingest/opentelemetry/edot-sdks/dotnet/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,59 @@ To avoid this scenario, make sure each placeholder uses a unique name. For examp

```csharp
Logger.LogInformation("Eat your {fruit1} {fruit2} {fruit3}!", "apple", "banana", "mango");
```
```

### Custom processors are not applied to exported data

__TL;DR;__ When using custom processors, they may not run before data is exported.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, we don't really use this. Just removing TL;DR should suffice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed!


By default, EDOT .NET simplifies the getting started experience by applying [opinionated defaults](elastic-otel-dotnet://reference/edot-dotnet/setup/edot-defaults.md).

These defaults include registering the OTLP exporter with the OpenTelemetry SDK so that telemetry signal data
is exported without requiring additional code.

In advanced scenarios, you may develop custom processors to enrich signal data with custom logic before it
passes to the next processor in the pipeline. In such circumstances, you will add the processor to the relevant
signal provider builder.

For example, one may try to use the following code to register a custom processor for trace data using
the `TracerProviderBuilder`.

```csharp
builder.AddElasticOpenTelemetry(b => b
.WithTracing(t => t.AddProcessor<SpanRollupProcessor>())
.WithMetrics(m => m.AddMeter("MyAppMeter")));
```

This code will not work as desired due to EDOT .NET registering the OTLP exporter before the processor,
therefore running earlier in the pipeline than `SpanRollupProcessor`. The exact behaviour may vary or appear to
work because trace data is exported in batches and the custom processor may partially apply to trace data before
the batch is exported.

EDOT .NET provides a configuration option that should be used in this scenario to disable the automatic registration
of the OTLP exporter. Once disabled, you should manually add the exporter ***after*** registering your custom
processor(s).

Taking the prior example, the correct code should be as follows:

```csharp
builder.AddElasticOpenTelemetry(new ElasticOpenTelemetryOptions() { SkipOtlpExporter = true }, b => b
.WithLogging(l => l.AddOtlpExporter())
.WithTracing(t =>
{
t.AddProcessor<SpanRollupProcessor>();
t.AddOtlpExporter();
})
.WithMetrics(m => m
.AddMeter("MyAppMeter")
.AddOtlpExporter()));
```

The preceding code uses an overload of `AddElasticOpenTelemetry` which accepts an `ElasticOpenTelemetryOptions`
instance. The code configures `SkipOtlpExporter` as `true`. If preferred, this may also be configured using the
`appSettings.json` file.

With `SkipOtlpExporter` enabled, the exporter must be added to __each__ signal that should be exported. In
this example, the OTLP exporter is manually added for logs, traces and metrics. Crucially, for traces, the
exporter is registered after the custom `SpanRollupProcessor` to ensure that trace data is batched for export
after the processor has completed.