Skip to content

Instantly share code, notes, and snippets.

@egil
Last active March 26, 2026 18:50
Show Gist options
  • Select an option

  • Save egil/748340ce5899c118ee45db502f680acf to your computer and use it in GitHub Desktop.

Select an option

Save egil/748340ce5899c118ee45db502f680acf to your computer and use it in GitHub Desktop.
ErrorAndSlowRateSamplerProcessor

Usage:

public static TBuilder ConfigureOpenTelemetry<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
{
    builder.Logging.AddOpenTelemetry(logging =>
    {
        logging.IncludeFormattedMessage = true;
        logging.IncludeScopes = true;
    });

    builder.AddOpenTelemetryExporters()
        .Select(telemetryBuilder => telemetryBuilder
            .WithMetrics(metrics =>
            {
                metrics.AddAspNetCoreInstrumentation()
                    .AddHttpClientInstrumentation()
                    .AddRuntimeInstrumentation();
            })
            .WithTracing(tracing =>
            {
                tracing.AddAspNetCoreInstrumentation()
                    .AddHttpClientInstrumentation()
                    .AddProcessor(new ErrorAndSlowRateSamplerProcessor(
                        predicate: activity => activity.OperationName == "CustomThingy",
                        samplingProbability: builder.Environment.IsProduction() ? 0.02 : 1,
                        maxDurationPredicate: TimeSpan.FromSeconds(1)))
                    .SetSampler(new ParentBasedSampler(
                        rootSampler: new AlwaysOnSampler(),
                        remoteParentNotSampled: new AlwaysOnSampler()));
            }))
        .ToArray();

    return builder;
}

private static IEnumerable<IOpenTelemetryBuilder> AddOpenTelemetryExporters<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
{
    if (!string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]))
    {
        yield return builder.Services
            .AddOpenTelemetry()
            .UseOtlpExporter();
    }

    if (!string.IsNullOrWhiteSpace(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]))
    {
        yield return builder.Services
            .AddOpenTelemetry()
            .UseAzureMonitor();
    }
}
using System.Diagnostics;
using OpenTelemetry.Trace;
namespace OpenTelemetry;
/// <summary>
/// A high-performance probabilistic processor that samples activities based on a predicate and rate.
/// </summary>
public sealed class ErrorAndSlowRateSamplerProcessor : BaseProcessor<Activity>
{
private readonly Func<Activity, bool> predicate;
private readonly TimeSpan maxDurationPredicate;
private readonly TraceIdRatioBasedSampler sampler;
/// <summary>
/// Initializes a new instance of the <see cref="ErrorAndSlowRateSamplerProcessor"/> class.
/// </summary>
/// <param name="predicate">The predicate to determine if an activity should be sampled.</param>
public ErrorAndSlowRateSamplerProcessor(Func<Activity, bool> predicate, double samplingProbability, TimeSpan maxDurationPredicate)
{
ArgumentNullException.ThrowIfNull(predicate);
this.predicate = predicate;
this.maxDurationPredicate = maxDurationPredicate;
this.sampler = new TraceIdRatioBasedSampler(samplingProbability);
}
/// <inheritdoc/>
public override void OnStart(Activity? data)
{
if (data is null || !predicate(data))
{
return;
}
var samplingParameters = new SamplingParameters(
new ActivityContext(
data.TraceId,
data.SpanId,
data.ActivityTraceFlags
),
data.TraceId,
data.DisplayName,
data.Kind,
data.TagObjects,
data.Links
);
var result = sampler.ShouldSample(in samplingParameters);
data.IsAllDataRequested = result.Decision is SamplingDecision.RecordAndSample;
data.ActivityTraceFlags = result.Decision is SamplingDecision.RecordAndSample
? ActivityTraceFlags.Recorded
: ActivityTraceFlags.None;
}
/// <inheritdoc/>
public override void OnEnd(Activity? data)
{
if (data is not null && predicate(data) && (data.Status is ActivityStatusCode.Error || data.Duration > maxDurationPredicate))
{
data.IsAllDataRequested = true;
data.ActivityTraceFlags = ActivityTraceFlags.Recorded;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment