NPipeline.Extensions.Observability.OpenTelemetry 0.18.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package NPipeline.Extensions.Observability.OpenTelemetry --version 0.18.0
                    
NuGet\Install-Package NPipeline.Extensions.Observability.OpenTelemetry -Version 0.18.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="NPipeline.Extensions.Observability.OpenTelemetry" Version="0.18.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NPipeline.Extensions.Observability.OpenTelemetry" Version="0.18.0" />
                    
Directory.Packages.props
<PackageReference Include="NPipeline.Extensions.Observability.OpenTelemetry" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add NPipeline.Extensions.Observability.OpenTelemetry --version 0.18.0
                    
#r "nuget: NPipeline.Extensions.Observability.OpenTelemetry, 0.18.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package NPipeline.Extensions.Observability.OpenTelemetry@0.18.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=NPipeline.Extensions.Observability.OpenTelemetry&version=0.18.0
                    
Install as a Cake Addin
#tool nuget:?package=NPipeline.Extensions.Observability.OpenTelemetry&version=0.18.0
                    
Install as a Cake Tool

NPipeline.Extensions.Observability.OpenTelemetry

OpenTelemetry integration for NPipeline observability - provides seamless integration with OpenTelemetry SDKs for distributed tracing.

Overview

This package enables NPipeline pipelines to export traces to OpenTelemetry-compatible backends such as Jaeger, Zipkin, Azure Monitor, AWS X-Ray, and others. It implements the IPipelineTracer interface using .NET's System.Diagnostics.ActivitySource and Activity APIs, following OpenTelemetry best practices for .NET applications.

Key Features

  • Seamless OpenTelemetry Integration: Uses standard ActivitySource pattern for compatibility with all OpenTelemetry exporters
  • Service Name Prefixing: Activities are prefixed with a service name for easy identification in trace visualizers
  • Automatic Parent-Child Relationships: Respects Activity.Current context for hierarchical tracing
  • Null-Optimized: Falls back to null tracer when sampling drops activities, avoiding unnecessary allocations
  • Multi-Service Support: Configure multiple pipeline services through a single tracer provider
  • Extension Methods: Fluent API for configuring OpenTelemetry with NPipeline sources

Installation

dotnet add package NPipeline.Extensions.Observability.OpenTelemetry

Requirements

  • .NET 8.0, 9.0, or 10.0
  • NPipeline.Extensions.Observability (automatically included as a dependency)
  • OpenTelemetry SDKs for your chosen exporter

Quick Start

Basic Setup with Dependency Injection

using Microsoft.Extensions.DependencyInjection;
using NPipeline.Extensions.Observability.OpenTelemetry;
using NPipeline.Observability.DependencyInjection;

// Register observability services
var services = new ServiceCollection();
services.AddNPipelineObservability();
services.AddOpenTelemetryPipelineTracer("MyPipeline");

// Configure OpenTelemetry export to Jaeger
using var tracerProvider = new TracerProviderBuilder()
    .AddNPipelineSource("MyPipeline")
    .AddJaegerExporter()
    .Build();

var serviceProvider = services.BuildServiceProvider();

Running a Pipeline with Tracing

// Get the tracer and context factory
var tracer = serviceProvider.GetRequiredService<IPipelineTracer>();
var contextFactory = serviceProvider.GetRequiredService<IObservablePipelineContextFactory>();

// Create a context with tracing enabled
await using var context = contextFactory.Create(
    PipelineContextConfiguration.WithObservability(tracer: tracer)
);

// Run your pipeline - traces are automatically exported
var runner = serviceProvider.GetRequiredService<IPipelineRunner>();
await runner.RunAsync<MyPipeline>(context);

Core Components

OpenTelemetryPipelineTracer

The main tracer implementation that creates Activity instances from an ActivitySource.

Why this design: Using ActivitySource is the recommended OpenTelemetry pattern for .NET. It ensures activities are captured by providers configured with AddSource(serviceName), providing automatic integration with all OpenTelemetry exporters.

var tracer = new OpenTelemetryPipelineTracer("MyPipeline");

// The tracer creates activities with the service name as the source
var activity = tracer.StartActivity("ProcessOrder");
// Activity is automatically exported to configured OpenTelemetry backends

Key behaviors:

  • Creates activities from an ActivitySource named after the service
  • Returns null activities when sampling drops them (no allocations)
  • Automatically establishes parent-child relationships via Activity.Current
  • Disposes the ActivitySource when the tracer is disposed

TracerProviderBuilder Extensions

Fluent extension methods for configuring OpenTelemetry to capture NPipeline traces.

AddNPipelineSource

Configures the tracer provider to listen for activities from a specific NPipeline service.

using var tracerProvider = new TracerProviderBuilder()
    .AddNPipelineSource("MyPipeline")  // Must match tracer service name
    .AddJaegerExporter()
    .Build();

Why this is needed: The service name used when creating OpenTelemetryPipelineTracer must match the source name added to the tracer provider. This extension ensures consistency and prevents configuration errors.

AddNPipelineSources

Configures multiple NPipeline services at once.

using var tracerProvider = new TracerProviderBuilder()
    .AddNPipelineSources(new[] { "PipelineA", "PipelineB", "PipelineC" })
    .AddZipkinExporter()
    .Build();

Use case: Ideal for microservices architectures where multiple pipeline services share a single OpenTelemetry configuration.

GetNPipelineInfo

Extracts NPipeline-specific metadata from activities for custom exporters or processors.

public class CustomExporter : BaseExporter<Activity>
{
    public override ExportResult Export(in Batch<Activity> batch)
    {
        foreach (var activity in batch)
        {
            var info = activity.GetNPipelineInfo();
            if (info != null)
            {
                Console.WriteLine($"Service: {info.ServiceName}, Activity: {info.ActivityName}");
            }
        }
        return ExportResult.Success;
    }
}

Configuration Examples

Jaeger Exporter

using var tracerProvider = new TracerProviderBuilder()
    .AddNPipelineSource("MyPipeline")
    .AddJaegerExporter(o =>
    {
        o.AgentHost = "localhost";
        o.AgentPort = 6831;
    })
    .Build();

Zipkin Exporter

using var tracerProvider = new TracerProviderBuilder()
    .AddNPipelineSource("MyPipeline")
    .AddZipkinExporter(o =>
    {
        o.Endpoint = new Uri("http://localhost:9411/api/v2/spans");
    })
    .Build();

OTLP (OpenTelemetry Protocol) Exporter

using var tracerProvider = new TracerProviderBuilder()
    .AddNPipelineSource("MyPipeline")
    .AddOtlpExporter(o =>
    {
        o.Endpoint = new Uri("http://localhost:4317");
    })
    .Build();

Azure Monitor Exporter

using var tracerProvider = new TracerProviderBuilder()
    .AddNPipelineSource("MyPipeline")
    .AddAzureMonitorTraceExporter(o =>
    {
        o.ConnectionString = "InstrumentationKey=your-key";
    })
    .Build();

AWS X-Ray Exporter

using var tracerProvider = new TracerProviderBuilder()
    .AddNPipelineSource("MyPipeline")
    .AddAWSXRayTraceExporter(o =>
    {
        o.SetResourceResourceDetector(new AWSEBSDetector());
    })
    .Build();

Architecture

Activity Flow

Pipeline Execution
    ↓
OpenTelemetryPipelineTracer.StartActivity()
    ↓
ActivitySource.StartActivity(name)
    ↓
Activity created (or null if sampled out)
    ↓
PipelineActivity wrapper (or NullPipelineActivity)
    ↓
Exported to configured OpenTelemetry backend

Service Name Matching

The service name serves as the bridge between the tracer and the exporter:

  1. Tracer Creation: new OpenTelemetryPipelineTracer("MyPipeline")
  2. Exporter Configuration: builder.AddNPipelineSource("MyPipeline")
  3. Activity Source: Activities have Source.Name = "MyPipeline"

If these don't match, traces won't be captured. The extension methods enforce this contract.

Null Activity Handling

When OpenTelemetry sampling drops an activity (returns null from StartActivity), the tracer automatically falls back to NullPipelineTracer.Instance. This design choice:

  • Prevents allocations: No PipelineActivity wrapper is created for dropped activities
  • Maintains consistency: The null tracer provides the same interface without side effects
  • Respects sampling: Allows OpenTelemetry to control trace sampling efficiently

Best Practices

1. Use Consistent Service Names

// Good: Service name is a constant
const string ServiceName = "OrderProcessingPipeline";

services.AddOpenTelemetryPipelineTracer(ServiceName);
builder.AddNPipelineSource(ServiceName);

2. Register as Singleton

// Register the tracer as a singleton
services.AddSingleton<IPipelineTracer>(sp =>
    new OpenTelemetryPipelineTracer("MyPipeline"));

3. Dispose Tracer Provider

// Ensure proper disposal of the tracer provider
await using var tracerProvider = new TracerProviderBuilder()
    .AddNPipelineSource("MyPipeline")
    .AddJaegerExporter()
    .Build();

try
{
    // Run pipelines
}
finally
{
    // Tracer provider is disposed automatically
}

4. Configure Sampling

using var tracerProvider = new TracerProviderBuilder()
    .AddNPipelineSource("MyPipeline")
    .SetSampler(new TraceIdRatioBasedSampler(0.1)) // 10% sampling
    .AddJaegerExporter()
    .Build();

5. Add Resource Attributes

using var tracerProvider = new TracerProviderBuilder()
    .AddNPipelineSource("MyPipeline")
    .ConfigureResource(r => r
        .AddService("MyPipeline", "1.0.0")
        .AddAttributes(new Dictionary<string, object>
        {
            ["environment"] = "production",
            ["deployment.region"] = "us-west-2"
        }))
    .AddJaegerExporter()
    .Build();

Performance Considerations

  • Allocation Optimization: Null activities don't allocate PipelineActivity wrappers
  • Sampling: Use OpenTelemetry sampling to reduce trace volume in high-throughput scenarios
  • Batch Exporting: Most exporters batch traces for efficient network usage
  • Async Export: Exporters typically use async I/O to avoid blocking pipeline execution

Troubleshooting

Traces Not Appearing in Backend

Problem: Traces are not visible in Jaeger, Zipkin, or other backends.

Solutions:

  1. Verify service name matching between tracer and exporter configuration
  2. Check that the tracer provider is built before pipeline execution
  3. Ensure the exporter endpoint is accessible
  4. Verify sampling configuration (may be dropping all traces)
  5. Check exporter logs for connection errors

Activities Not Created

Problem: StartActivity returns null activities.

Solutions:

  1. Verify ActivitySource is registered with the tracer provider
  2. Check sampling configuration
  3. Ensure OpenTelemetryPipelineTracer is properly initialized
  4. Verify the service name is not empty or whitespace

High Memory Usage

Problem: Tracing causes increased memory consumption.

Solutions:

  1. Implement aggressive sampling (e.g., 1-5% trace rate)
  2. Use batch exporters with appropriate batch sizes
  3. Consider exporting to a backend with efficient storage
  4. Monitor and adjust exporter buffer sizes

License

MIT License - see LICENSE file for details.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.18.1 0 2/26/2026
0.18.0 39 2/25/2026
0.17.0 53 2/25/2026
0.16.0 75 2/24/2026
0.15.0 80 2/19/2026
0.14.0 85 2/17/2026
0.13.1 91 2/13/2026
0.13.0 82 2/13/2026
0.12.0 91 2/9/2026
0.11.0 85 2/8/2026
0.10.0 87 2/6/2026
0.9.1 94 2/5/2026
0.9.0 87 2/5/2026
0.8.0 85 2/3/2026
0.7.1 88 2/1/2026
0.7.0 102 1/31/2026
0.6.6 94 1/21/2026
0.6.5 96 1/19/2026
0.6.4 89 1/18/2026
0.6.3 89 1/14/2026
Loading failed