We hold regular meetings. See details at community page.
Approvers (@open-telemetry/dotnet-approvers):
- Bruno Garcia, Sentry
- Christoph Neumueller, Dynatrace
- Cijo Thomas, Microsoft
- Liudmila Molkova, Microsoft
Find more about the approver role in community repository.
Maintainers (@open-telemetry/dotnet-maintainers):
- Mike Goldsmith, LightStep
- Sergey Kanzhelev, Microsoft
Find more about the maintainer role in community repository.
OpenTelemetry is a toolkit for collecting application performance and behavior data.
The library is in Alpha stage. The library is expected to move to GA stage after v1.0.0 major release.
Please join gitter for help or feedback on this project.
We encourage contributions. Use tags up-for-grabs and good first issue to get started with the project. Follow CONTRIBUTING guide to report issues or submit a proposal.
Myget feeds:
- NuGet V3 feed: https://www.myget.org/F/opentelemetry/api/v3/index.json
- NuGet V2 feed: https://www.myget.org/F/opentelemetry/api/v2
Package | MyGet (CI) | NuGet (releases) |
---|---|---|
OpenTelemetry | ||
OpenTelemetry.Api |
Package | MyGet (CI) | NuGet (releases) |
---|---|---|
Zipkin | ||
Prometheus | ||
Application Insights | ||
Stackdriver | ||
Jaeger | ||
LightStep | ||
Honeycomb | ||
NewRelic | ||
Console |
You can use OpenTelemetry API to instrument code and report data. Check out Tracing API overview to learn more about distributed tracing.
In the examples below we demonstrate how to create and enrich spans though OpenTelemetry API.
OpenTelemetry also provides auto-adapters for ASP.NET Core & ASP.NET incoming requests and HttpClient (.NET Core & .NET Framework), SqlClient, & Azure SDK outgoing requests. See the configuration section for examples detailing how to enable auto-adapter.
Applications should follow configuration section to find out how to create/obtain tracer.
Libraries must take dependency on OpenTelemetry API package only and should never instantiate tracer or configure OpenTelemetry. Libraries will be able to obtain global tracer that may either be noop (if user application is not instrumented with OpenTelemetry) or real tracer implemented in the SDK package.
To create the most basic span, you only specify the name. OpenTelemetry SDK collects start/end timestamps, assigns tracing context and assumes status of this span is OK
.
var span = tracer.StartSpan("basic span");
// ...
span.End();
In many cases you want to collect nested operations. You can propagate parent spans explicitly in your code or use implicit context propagation embedded into OpenTelemetry and .NET.
var parentSpan = tracer.StartSpan("parent span");
// explicitly assigning parent here
var childSpan = tracer.StartSpan("child span", parentSpan);
childSpan.End();
parentSpan.End();
// calling StartActiveSpan starts a span and puts parentSpan into the ambient context
// that flows in async calls. When child is created, it implicitly becomes child of current span
using (tracer.StartActiveSpan("parent span", out _))
{
var childSpan = tracer.StartSpan("child span");
childSpan.End();
}
// parent span is ended when StartActiveSpan result is disposed
Attributes provide additional context on span specific to specific operation it tracks such as HTTP/DB/etc call properties.
// spans have Client, Server, Internal, Producer and Consumer kinds to help visualize them
var span = tracer.StartSpan("span with attributes", SpanKind.Client);
// attributes specific to the call
span.SetAttribute("db.type", "redis");
span.SetAttribute("db.instance", "localhost:6379[0]");
span.SetAttribute("db.statement", "SET");
span.End();
Links allow to create relationships between different traces i.e. allow spans to have multiple relatives. They are typically used to trace batching scenarios where multiple traces are merged into another one.
Links affect sampling decision and should be added before sampling decision is made (i.e. before span starts).
SpanContext link1 = ExtractContext(eventHubMessage1);
SpanContext link2 = ExtractContext(eventHubMessage2);
var span = tracer.StartSpan("span with links", SpanKind.Server, DateTime.UtcNow, new [] {link1, link2});
span.End();
Events are timed text (with optional attributes) annotations on the span. Events can be added to current span (or any running span).
using (tracer.StartActiveSpan("incoming HTTP request", SpanKind.Server, out var span))
{
span.AddEvent("routes resolved");
}
// span is ended when StartActiveSpan result is disposed
Examples will be added once Global Propagators API is added.
System.Diagnostics.Activity
is similar to OpenTelemetry Span. HttpClient, ASP.NET Core, SqlClient, & Azure SDKs use them to expose diagnostics events and context.
Leaving aside subscription mechanism, here is an example how you may implement callbacks for Start/Stop Activity
void StartActivity()
{
tracer.StartActiveSpanFromActivity ("GET api/values", Activity.Current, out var span);
// extract other things from Activity and set them on span (tags to attributes)
// ...
}
void StopActivity()
{
var span = tracer.CurrentSpan;
span.End();
if (span is IDisposable disposableSpan)
{
disposableSpan.Dispose();
}
}
Configuration is done by user application: it should configure exporter and may also tune sampler and other properties.
-
Install packages to your project: OpenTelemetry OpenTelemetry.Exporter.Zipkin
-
Create
TracerFactory
using (var tracerFactory = TracerFactory.Create(builder => builder .UseZipkin(options => {}) .SetResource(Resources.CreateServiceResource("http-client-test"))) { // Obtain Tracer from the factory above. var tracer = tracerFactory.GetTracer("zipkin-test"); }
-
Install packages to your project: OpenTelemetry.Extensions.Hosting to provide
AddOpenTelemetry
helper method OpenTelemetry.Adapter.AspNetCore to collect incoming HTTP requests OpenTelemetry.Adapter.Dependencies to collect outgoing HTTP requests, SqlClient calls, and Azure SDK calls -
Use the
AddOpenTelemetry
helper method to add Open Telemetry. This registersTracerFactory
in the Dependency Injection Container.services.AddOpenTelemetry(builder => { builder .SetSampler(new AlwaysSampleSampler()) .UseZipkin(options => {}) // you may also configure request and dependencies adapters .AddRequestAdapter() .AddDependencyAdapter() .SetResource(Resources.CreateServiceResource("my-service")) });
-
Obtain
TracerFactory
using DI, and createTracer
from it.
-
Add a reference to the
OpenTelemetry.Adapter.AspNet
package. Add any other adapters & exporters you will need. -
Add the Microsoft telemetry module in your
Web.config
:<system.webServer> <modules> <add name="TelemetryCorrelationHttpModule" type="Microsoft.AspNet.TelemetryCorrelation.TelemetryCorrelationHttpModule, Microsoft.AspNet.TelemetryCorrelation" preCondition="integratedMode,managedHandler"/> </modules> </system.webServer>
-
Configure OpenTelemetry in your application startup:
public class WebApiApplication : HttpApplication { private TracerFactory tracerFactory; protected void Application_Start() { this.tracerFactory = TracerFactory.Create(builder => { builder .UseJaeger(c => { c.AgentHost = "localhost"; c.AgentPort = 6831; }) .AddRequestAdapter() .AddDependencyAdapter(); }); } protected void Application_End() { this.tracerFactory?.Dispose(); } }
Outgoing http calls to Redis made using StackExchange.Redis library can be automatically tracked.
-
Install package to your project: OpenTelemetry.Adapter.StackExchangeRedis
-
Configure Redis adapter
// connect to the server var connection = ConnectionMultiplexer.Connect("localhost:6379"); using (TracerFactory.Create(b => b .SetSampler(new AlwaysSampleSampler()) .UseZipkin(options => {}) .SetResource(Resources.CreateServiceResource("my-service")) .AddAdapter(t => { var adapter = new StackExchangeRedisCallsAdapter(t); connection.RegisterProfiler(adapter.GetProfilerSessionsFactory()); return adapter; }))) { }
You can combine it with dependency injection as shown in previous example.
You may configure sampler of your choice
using (TracerFactory.Create(b => b
.SetSampler(new ProbabilitySampler(0.1))
.UseZipkin(options => {})
.SetResource(Resources.CreateServiceResource("my-service")))
{
}
You can also implement custom sampler by implementing ISampler
interface
class MySampler : Sampler
{
public override string Description { get; } = "my custom sampler";
public override Decision ShouldSample(SpanContext parentContext, ActivityTraceId traceId, ActivitySpanId spanId, string name,
IDictionary<string, object> attributes, IEnumerable<Link> links)
{
bool sampledIn;
if (parentContext != null && parentContext.IsValid)
{
sampledIn = (parentContext.TraceOptions & ActivityTraceFlags.Recorded) != 0;
}
else
{
sampledIn = Stopwatch.GetTimestamp() % 2 == 0;
}
return new Decision(sampledIn);
}
}
The Jaeger exporter communicates to a Jaeger Agent through the compact thrift protocol on the Compact Thrift API port. You can configure the Jaeger exporter by following the directions below:
- Get Jaeger.
- Configure the
JaegerExporter
ServiceName
: The name of your application or service.AgentHost
: Usuallylocalhost
since an agent should usually be running on the same machine as your application or service.AgentPort
: The compact thrift protocol port of the Jaeger Agent (default6831
)MaxPacketSize
: The maximum size of each UDP packet that gets sent to the agent. (default65000
)
- See the sample for an example of how to use the exporter.
using (var tracerFactory = TracerFactory.Create(
builder => builder.UseJaeger(o =>
{
o.ServiceName = "jaeger-test";
o.AgentHost = "<jaeger server>";
})))
{
var tracer = tracerFactory.GetTracer("jaeger-test");
using (tracer.StartActiveSpan("incoming request", out var span))
{
span.SetAttribute("custom-attribute", 55);
await Task.Delay(1000);
}
}
Configure Zipkin exporter to see traces in Zipkin UI.
- Get Zipkin using getting started guide.
- Configure
ZipkinTraceExporter
as below: - See sample for example use.
using (var tracerFactory = TracerFactory.Create(builder => builder
.UseZipkin(o =>
{
o.ServiceName = "test-zipkin";
o.Endpoint = new Uri(zipkinUri);
})))
{
var tracer = tracerFactory.GetTracer("zipkin-test");
// Create a scoped span. It will end automatically when using statement ends
using (tracer.WithSpan(tracer.StartSpan("Main")))
{
Console.WriteLine("About to do a busy work");
for (var i = 0; i < 10; i++)
{
DoWork(i, tracer);
}
}
}
Configure Prometheus exporter to have stats collected by Prometheus.
- Get Prometheus using getting started guide.
- Start
PrometheusExporter
as below. - See sample for example use.
var exporter = new PrometheusExporter(
new PrometheusExporterOptions()
{
Url = "http://+:9184/metrics/"
},
Stats.ViewManager);
exporter.Start();
try
{
// record metrics
statsRecorder.NewMeasureMap().Put(VideoSize, values[0] * MiB).Record();
}
finally
{
exporter.Stop();
}
The New Relic OpenTelemetry Trace Exporter is a OpenTelemetry Provider that sends data from .NET applications to New Relic. It uses the NewRelic SDK to send Traces to the New Relic backend
Please refer to the New Relic Exporter Documentation
This sample assumes your code authenticates to Stackdriver APIs using service account with credentials stored in environment variable GOOGLE_APPLICATION_CREDENTIALS. When you run on GAE, GKE or locally with gcloud sdk installed - this is typically the case. There is also a constructor for specifying path to the service account credential. See sample for details.
- Add Stackdriver Exporter package reference.
- Enable Stackdriver Trace API.
- Enable Stackdriver Monitoring API.
- Instantiate a new instance of
StackdriverExporter
with your Google Cloud's ProjectId - See sample for example use.
You may want to filter on enrich spans and send them to multiple destinations (e.g. for debugging or telemetry self-diagnostics purposes). You may configure multiple processing pipelines for each destination like shown in below example.
In this example
- First pipeline sends all sampled in spans to Zipkin
- Second pipeline sends spans to ApplicationInsights, but filters them first with custom built
FilteringSpanProcessor
- Third pipeline adds custom
DebuggingSpanProcessor
that simply logs all calls to debug output
using (var tracerFactory = TracerFactory.Create(builder => builder
.UseZipkin(o =>
{
o.Endpoint = new Uri(zipkinUri);
})
.UseApplicationInsights(
o => o.InstrumentationKey = "your-instrumentation-key",
p => p.AddProcessor(nextProcessor => new FilteringSpanProcessor(nextProcessor)))
.AddProcessorPipeline(pipelineBuilder => pipelineBuilder.AddProcessor(_ => new DebuggingSpanProcessor()))))
.SetResource(Resources.CreateServiceResource("test-zipkin"))
{
// ...
}
var spanExporter = new StackdriverTraceExporter(projectId);
using var tracerFactory = TracerFactory.Create(builder => builder.AddProcessorPipeline(c => c.SetExporter(spanExporter)));
var tracer = tracerFactory.GetTracer("stackdriver-test");
using (tracer.StartActiveSpan("/getuser", out TelemetrySpan span))
{
span.AddEvent("Processing video.");
span.PutHttpMethodAttribute("GET");
span.PutHttpHostAttribute("localhost", 8080);
span.PutHttpPathAttribute("/resource");
span.PutHttpStatusCodeAttribute(200);
span.PutHttpUserAgentAttribute("Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0");
Thread.Sleep(TimeSpan.FromMilliseconds(10));
}
var metricExporter = new StackdriverExporter(
"YOUR-GOOGLE-PROJECT-ID",
Stats.ViewManager);
metricExporter.Start();
- Create Application Insights resource.
- Set instrumentation key via telemetry configuration object
(
new TelemetryConfiguration("iKey")
). This object may be injected via dependency injection as well. - Instantiate a new instance of
ApplicationInsightsExporter
. - See sample for example use.
using var tracerFactory = TracerFactory.Create(builder => builder
.SetResource(Resources.CreateServiceResource("my-service"))
.UseApplicationInsights(config => config.InstrumentationKey = "instrumentation-key"));
var tracer = tracerFactory.GetTracer("application-insights-test");
using (tracer.StartActiveSpan("incoming request", out var span))
{
span.AddEvent("Start processing video.");
Thread.Sleep(TimeSpan.FromMilliseconds(10));
span.AddEvent("Finished processing video.");
}
- Create Application Insights resource.
- In Startup class, ConfigureServices method add a call of AddOpenTelemetry
- Configurate Application Insights inside AddOpenTelemetry with call UseApplicationInsights
- Set instrumentation key via telemetry configuration object telemetryConfiguration.InstrumentationKey = instrumentationKey;
- See sample for example use.
services.AddOpenTelemetry((sp, builder) =>
{
builder.UseApplicationInsights(telemetryConfiguration =>
{
var instrumentationKey = this.Configuration.GetValue<string>("ApplicationInsights:InstrumentationKey");
telemetryConfiguration.InstrumentationKey = instrumentationKey;
})
.AddRequestAdapter()
.AddDependencyAdapter();
});
Configure LightStep exporter to see traces in LightStep.
- Setup LightStep using getting started guide
- Configure
LightStepTraceExporter
(see below) - See sample for example use
using (var tracerFactory = TracerFactory.Create(
builder => builder.UseLightStep(o =>
{
o.AccessToken = "<access-token>";
o.ServiceName = "lightstep-test";
})))
{
var tracer = tracerFactory.GetTracer("lightstep-test");
using (tracer.StartActiveSpan("incoming request", out var span))
{
span.SetAttribute("custom-attribute", 55);
await Task.Delay(1000);
}
}
Exporters should subclass SpanExporter
and implement ExportAsync
and Shutdown
methods.
Depending on user's choice and load on the application ExportAsync
may get called concurrently with zero or more spans.
Exporters should expect to receive only sampled-in ended spans. Exporters must not throw. Exporters should not modify spans they receive (the same span may be exported again by different exporter).
It's a good practice to make exporter IDisposable
and shut it down in IDispose unless it was shut down explicitly. This helps when exporters are registered with dependency injection framework and their lifetime is tight to the app lifetime.
class MyExporter : SpanExporter
{
public override Task<ExportResult> ExportAsync(IEnumerable<Span> batch, CancellationToken cancellationToken)
{
foreach (var span in batch)
{
Console.WriteLine($"[{span.StartTimestamp:o}] {span.Name} {span.Context.TraceId.ToHexString()} {span.Context.SpanId.ToHexString()}");
}
return Task.FromResult(ExportResult.Success);
}
public override Task ShutdownAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
Users may configure the exporter similarly to other exporters.
You should also provide additional methods to simplify configuration similarly to UseZipkin
extension method.
var exporter = new MyExporter();
using (var tracerFactory = TracerFactory.Create(
builder => builder.AddProcessorPipeline(b => b.SetExporter(new MyExporter())))
{
// ...
}
This library follows Semantic Versioning.
GA: Libraries defined at a GA quality level are stable, and will not introduce backwards-incompatible changes in any minor or patch releases. We will address issues and requests with the highest priority. If we were to make a backwards-incompatible changes on an API, we will first mark the existing API as deprecated and keep it for 18 months before removing it.
Beta: Libraries defined at a Beta quality level are expected to be mostly stable and we're working towards their release candidate. We will address issues and requests with a higher priority. There may be backwards incompatible changes in a minor version release, though not in a patch release. If an element is part of an API that is only meant to be used by exporters or other OpenTelemetry libraries, then there is no deprecation period. Otherwise, we will deprecate it for 18 months before removing it, if possible.