<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://tomasjurasek.github.io/feed/index.xml" rel="self" type="application/atom+xml" /><link href="https://tomasjurasek.github.io/" rel="alternate" type="text/html" /><updated>2024-12-28T20:07:40+00:00</updated><id>https://tomasjurasek.github.io/feed/index.xml</id><title type="html">Tomas Jurasek</title><subtitle>.NET</subtitle><author><name>Tomas Jurasek</name></author><entry><title type="html">Trace &amp;amp; Baggage Context Propagation in .NET with OpenTelemetry</title><link href="https://tomasjurasek.github.io/2024/12/27/trace-baggage-context-propagation-open-telementry/" rel="alternate" type="text/html" title="Trace &amp;amp; Baggage Context Propagation in .NET with OpenTelemetry" /><published>2024-12-27T00:00:00+00:00</published><updated>2024-12-27T00:00:00+00:00</updated><id>https://tomasjurasek.github.io/2024/12/27/trace-baggage-context-propagation-open-telementry</id><content type="html" xml:base="https://tomasjurasek.github.io/2024/12/27/trace-baggage-context-propagation-open-telementry/"><![CDATA[<p>Context propagation is a key part of distributed tracing. It enables transmitting both <code class="language-plaintext highlighter-rouge">trace</code> and <code class="language-plaintext highlighter-rouge">baggage</code> context across network boundaries so that different services in your system can participate in the same <code class="language-plaintext highlighter-rouge">trace</code>.</p>

<p>The W3C provides standards for propagation:</p>

<ul>
  <li><a href="https://www.w3.org/TR/trace-context/">Trace Context</a></li>
  <li><a href="https://www.w3.org/TR/baggage/">Baggage Context</a></li>
</ul>

<p>These contexts are transferred in HTTP headers (or other protocols), typically as <code class="language-plaintext highlighter-rouge">traceparent</code> for <code class="language-plaintext highlighter-rouge">trace context</code> and <code class="language-plaintext highlighter-rouge">baggage</code> for <code class="language-plaintext highlighter-rouge">baggage context</code>. Below, we will explore how this works in .NET with OpenTelemetry.</p>

<h2 id="tracecontext">TraceContext</h2>

<p>The <code class="language-plaintext highlighter-rouge">traceparent</code> header contains information about the current trace.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">traceparent</span><span class="p">:</span> <span class="m">00</span><span class="p">-</span><span class="m">0</span><span class="n">af7651916cd43dd8448eb211c80319c</span><span class="p">-</span><span class="n">b7ad6b7169203331</span><span class="p">-</span><span class="m">01</span>
</code></pre></div></div>

<p><strong>Format</strong></p>
<ul>
  <li>Version (00)</li>
  <li>Trace-Id (0af7651916cd43dd8448eb211c80319c)</li>
  <li>Parent-Id (b7ad6b7169203331)</li>
  <li>Trace-Flags (01)</li>
</ul>

<p>A trace (or span) is represented by an <code class="language-plaintext highlighter-rouge">Activity</code> object. Each <code class="language-plaintext highlighter-rouge">Activity </code> instance corresponds to one span in the trace.</p>

<h2 id="baggagecontext">BaggageContext</h2>

<p>The <code class="language-plaintext highlighter-rouge">baggage</code> header holds additional context in the form of <code class="language-plaintext highlighter-rouge">key=value</code> pairs.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">baggage</span><span class="p">:</span> <span class="n">key1</span><span class="p">=</span><span class="n">value1</span>
</code></pre></div></div>

<p>Do not use <code class="language-plaintext highlighter-rouge">Activity.Baggage</code> to read or modify baggage. Instead, use <code class="language-plaintext highlighter-rouge">Baggage.Current</code> from the <code class="language-plaintext highlighter-rouge">OpenTelemetry.Api</code> package.</p>

<p>This ensures that <code class="language-plaintext highlighter-rouge">baggage context</code> is managed consistently with the OpenTelemetry specifications.</p>

<blockquote>
  <p>Important: These header values are transferred in plaintext to outgoing HTTP requests — internal, third parties, etc. Avoid placing sensitive or personally identifiable information in the baggage context.</p>
</blockquote>

<h1 id="net-usage">.NET Usage</h1>

<p>Tracing configuration registers <code class="language-plaintext highlighter-rouge">TraceContextPropagator</code> and <code class="language-plaintext highlighter-rouge">BaggageContextPropagator</code> out of the box.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddOpenTelemetry</span><span class="p">()</span>
    <span class="p">.</span><span class="nf">UseOtlpExporter</span><span class="p">()</span>
    <span class="p">.</span><span class="nf">ConfigureResource</span><span class="p">(</span><span class="n">configure</span> <span class="p">=&gt;</span> <span class="p">{</span>
        <span class="n">configure</span><span class="p">.</span><span class="nf">AddService</span><span class="p">(</span><span class="n">builder</span><span class="p">.</span><span class="n">Environment</span><span class="p">.</span><span class="n">ApplicationName</span><span class="p">)</span>
    <span class="p">})</span>
    <span class="p">.</span><span class="nf">WithTracing</span><span class="p">();</span>
</code></pre></div></div>

<p>These propagators enable extracting <code class="language-plaintext highlighter-rouge">traceparent</code> and <code class="language-plaintext highlighter-rouge">baggage</code> headers format from incoming requests and injecting them into outgoing requests.</p>

<h2 id="custom-propagators">Custom Propagators</h2>
<p>If you need support for alternative or custom propagators (e.g., Zipkin headers), you can set them explicitly using a CompositeTextMapPropagator</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Sdk</span><span class="p">.</span><span class="nf">SetDefaultTextMapPropagator</span><span class="p">(</span><span class="k">new</span> <span class="nf">CompositeTextMapPropagator</span><span class="p">(</span>
    <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">TextMapPropagator</span><span class="p">&gt;()</span> <span class="p">{</span>
        <span class="k">new</span> <span class="nf">MyCustomPropagator</span><span class="p">()</span>
    <span class="p">}));</span>
</code></pre></div></div>

<h2 id="http-propagation">HTTP Propagation</h2>

<p>By adding the following instrumentation, OpenTelemetry will automatically handle context propagation for incoming and outgoing HTTP requests.</p>

<ul>
  <li><strong>AddAspNetCoreInstrumentation</strong> extracts traceparent and baggage from incoming HTTP requests.</li>
  <li><strong>AddHttpClientInstrumentation</strong> injects traceparent and baggage into outgoing HTTP requests.</li>
</ul>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddOpenTelemetry</span><span class="p">()</span>
    <span class="p">.</span><span class="nf">UseOtlpExporter</span><span class="p">()</span>
    <span class="p">.</span><span class="nf">ConfigureResource</span><span class="p">(</span><span class="n">configure</span> <span class="p">=&gt;</span> <span class="p">{</span>
        <span class="n">configure</span><span class="p">.</span><span class="nf">AddService</span><span class="p">(</span><span class="n">builder</span><span class="p">.</span><span class="n">Environment</span><span class="p">.</span><span class="n">ApplicationName</span><span class="p">)</span>
    <span class="p">})</span>
    <span class="p">.</span><span class="nf">WithTracing</span><span class="p">(</span><span class="n">tracing</span> <span class="p">=&gt;</span>
    <span class="p">{</span>
        <span class="n">tracing</span>
            <span class="p">.</span><span class="nf">AddAspNetCoreInstrumentation</span><span class="p">()</span>
            <span class="p">.</span><span class="nf">AddHttpClientInstrumentation</span><span class="p">();</span>
    <span class="p">});</span>
</code></pre></div></div>

<p>If you want to see how this works under the hood, check out the OpenTelemetry .NET code for <a href="https://github.com/open-telemetry/opentelemetry-dotnet-contrib/blob/main/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs#L109">incoming HTTP instrumentation</a> and <a href="(https://github.com/open-telemetry/opentelemetry-dotnet-contrib/blob/main/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerDiagnosticListener.cs#L94)">outgoing HTTP instrumentation</a>.</p>

<h2 id="manual-propagation">Manual Propagation</h2>

<p>For protocols not natively instrumented (messaging systems, outbox, etc), you’ll need to manually propagate the context by calling the <code class="language-plaintext highlighter-rouge">Inject</code> and <code class="language-plaintext highlighter-rouge">Extract</code> methods on the <code class="language-plaintext highlighter-rouge">Propagators.DefaultTextMapPropagator</code>.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">void</span> <span class="nf">SendMessage</span><span class="p">(</span><span class="n">IMessage</span> <span class="n">message</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">var</span> <span class="n">propagationContext</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">PropagationContext</span><span class="p">(</span><span class="n">Activity</span><span class="p">.</span><span class="n">Current</span><span class="p">?.</span><span class="n">Context</span> <span class="p">??</span> <span class="k">default</span><span class="p">,</span> <span class="n">Baggage</span><span class="p">.</span><span class="n">Current</span><span class="p">);</span>

    <span class="n">Propagators</span><span class="p">.</span><span class="n">DefaultTextMapPropagator</span><span class="p">.</span><span class="nf">Inject</span><span class="p">(</span>
        <span class="n">propagationContext</span><span class="p">,</span>
        <span class="n">message</span><span class="p">.</span><span class="n">Headers</span><span class="p">,</span>
        <span class="p">(</span><span class="n">headers</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="k">value</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">headers</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="p">=</span> <span class="k">value</span>
    <span class="p">);</span>

    <span class="c1">// Publishing ...</span>
<span class="p">}</span>

<span class="k">public</span> <span class="k">void</span> <span class="nf">ProcessMessage</span><span class="p">(</span><span class="n">IMessage</span> <span class="n">message</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">var</span> <span class="n">propagationContext</span> <span class="p">=</span> <span class="n">Propagators</span><span class="p">.</span><span class="n">DefaultTextMapPropagator</span><span class="p">.</span><span class="nf">Extract</span><span class="p">(</span>
        <span class="k">default</span><span class="p">,</span>
        <span class="n">message</span><span class="p">.</span><span class="n">Headers</span><span class="p">,</span>
        <span class="p">(</span><span class="n">headers</span><span class="p">,</span> <span class="n">key</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">headers</span><span class="p">.</span><span class="nf">TryGetValue</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="k">out</span> <span class="kt">var</span> <span class="k">value</span><span class="p">)</span> <span class="p">?</span> <span class="k">new</span><span class="p">[]</span> <span class="p">{</span> <span class="k">value</span> <span class="p">}</span> <span class="p">:</span> <span class="n">Array</span><span class="p">.</span><span class="n">Empty</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;()</span>
    <span class="p">);</span>
    
    <span class="k">using</span> <span class="nn">var</span> <span class="n">activity</span> <span class="p">=</span> <span class="n">ActivityProvider</span><span class="p">.</span><span class="n">ActivitySource</span><span class="p">.</span><span class="nf">StartActivity</span><span class="p">(</span><span class="s">"receive"</span><span class="p">,</span> <span class="n">ActivityKind</span><span class="p">.</span><span class="n">Consumer</span><span class="p">,</span> <span class="n">propagationContext</span><span class="p">.</span><span class="n">ActivityContext</span><span class="p">);</span>

    <span class="n">Baggage</span><span class="p">.</span><span class="n">Current</span> <span class="p">=</span> <span class="n">propagationContext</span><span class="p">.</span><span class="n">Baggage</span><span class="p">;</span>

    <span class="c1">// Processing ...</span>
  
<span class="p">}</span>

</code></pre></div></div>

<p>Using this approach, you maintain consistent <code class="language-plaintext highlighter-rouge">trace</code> and <code class="language-plaintext highlighter-rouge">baggage</code> context across any protocol or communication mechanism in your distributed system.</p>]]></content><author><name>Tomas Jurasek</name></author><summary type="html"><![CDATA[Context propagation is a key part of distributed tracing. It enables transmitting both trace and baggage context across network boundaries so that different services in your system can participate in the same trace.]]></summary></entry><entry><title type="html">Metrics in .NET with OpenTelemetry</title><link href="https://tomasjurasek.github.io/2024/06/08/metrics-open-telemetry-net/" rel="alternate" type="text/html" title="Metrics in .NET with OpenTelemetry" /><published>2024-06-08T00:00:00+00:00</published><updated>2024-06-08T00:00:00+00:00</updated><id>https://tomasjurasek.github.io/2024/06/08/metrics-open-telemetry-net</id><content type="html" xml:base="https://tomasjurasek.github.io/2024/06/08/metrics-open-telemetry-net/"><![CDATA[<p>In the previous article, I wrote about <a href="https://tomasjurasek.github.io/2024/06/06/tracing-open-telemetry-net/">Distributed Tracing with OpenTelementry</a>. 
Now, let’s write about <a href="https://opentelemetry.io/docs/specs/otel/metrics/">Metrics</a>.</p>

<h2 id="opentelementry-net-metrics-api">OpenTelementry .NET Metrics API</h2>

<p>The .NET runtime contains most of <a href="https://opentelemetry.io/docs/specs/otel/metrics/api/">Metric API</a>, that allows applications to emit OpenTelemetry metrics using the standard .NET runtime.</p>

<h2 id="metrics">Metrics</h2>

<p>Use the <code class="language-plaintext highlighter-rouge">Meter</code> class, which is responsible for creating metric instruments to create manual metrics from the <code class="language-plaintext highlighter-rouge">System.Diagnostics.DiagnosticSource</code> nuget package.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">static</span> <span class="k">readonly</span> <span class="n">Meter</span> <span class="n">meter</span> <span class="p">=</span> <span class="k">new</span><span class="p">(</span><span class="s">"serviceName"</span><span class="p">,</span> <span class="s">"1.0"</span><span class="p">);</span>
</code></pre></div></div>

<p>The default, there is no listener to meters; they need to be explicit configured into the <code class="language-plaintext highlighter-rouge">MeterProvider.</code> The <code class="language-plaintext highlighter-rouge">MeterProvider</code> is the entry point to the configuration of all <code class="language-plaintext highlighter-rouge">Meter</code>’s and their instrumentation.</p>

<blockquote>
  <p>It’s not recommended to initialize the instrument for every metric execution. Create a (static) wrapper over metrics.</p>
</blockquote>

<h4 id="initialize">Initialize</h4>
<p>Initialize some kinds of instruments - <code class="language-plaintext highlighter-rouge">Counter</code>, <code class="language-plaintext highlighter-rouge">Gauge</code>, <code class="language-plaintext highlighter-rouge">Histogram</code>, …</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">static</span> <span class="k">class</span> <span class="nc">ApplicationMetrics</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">static</span> <span class="k">readonly</span> <span class="n">Meter</span> <span class="n">meter</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Meter</span><span class="p">(</span><span class="s">"applicationName"</span><span class="p">,</span> <span class="s">"1.0.0"</span><span class="p">);</span>
    <span class="k">private</span> <span class="k">static</span> <span class="k">readonly</span> <span class="n">Counter</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;</span> <span class="n">applicationCounter</span> <span class="p">=</span> <span class="n">meter</span><span class="p">.</span><span class="n">CreateCounter</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;(</span><span class="s">"applicationCounter"</span><span class="p">);</span>

    <span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">IncreaceCounter</span><span class="p">()</span> <span class="p">=&gt;</span> <span class="n">applicationCounter</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="m">1</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="usage">Usage</h4>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">void</span> <span class="nf">SomeMethod</span><span class="p">()</span>
<span class="p">{</span>
    <span class="c1">// Code</span>
    <span class="n">ApplicationMetrics</span><span class="p">.</span><span class="nf">IncreaceCounter</span><span class="p">();</span>
    <span class="c1">// Code</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="aggregation">Aggregation</h3>
<p>It provides a way to aggregate large number of measurements. More detailed description <a href="https://opentelemetry.io/docs/specs/otel/metrics/sdk/#aggregation">here</a>.</p>

<h3 id="views">Views</h3>
<p>It provides a way to customize the metrics output - which metric instruments are to be processed, which attributes report. More detailed description <a href="https://opentelemetry.io/docs/specs/otel/metrics/sdk/#view-examples">here</a>.</p>
<h2 id="configure-opentelemetry">Configure OpenTelemetry</h2>

<p>As tracing, also metrics are part of the <code class="language-plaintext highlighter-rouge">OpenTelemetry.Extensions.Hosting</code> nuget package.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddOpenTelemetry</span><span class="p">()</span>
    <span class="p">.</span><span class="nf">WithMetrics</span><span class="p">(</span><span class="n">metrics</span> <span class="p">=&gt;</span>
    <span class="p">{</span>
        <span class="n">metrics</span><span class="p">.</span><span class="nf">SetResourceBuilder</span><span class="p">(</span><span class="n">ResourceBuilder</span><span class="p">.</span><span class="nf">CreateDefault</span><span class="p">()</span>
                    <span class="p">.</span><span class="nf">AddService</span><span class="p">(</span><span class="s">"serviceName"</span><span class="p">))</span>
                <span class="p">.</span><span class="nf">AddMeter</span><span class="p">(</span><span class="s">"serviceName"</span><span class="p">);</span> <span class="c1">// Configure meter to listen</span>
    <span class="p">});</span>
</code></pre></div></div>

<h3 id="instrumentations">Instrumentations</h3>

<p>Metrics instruments must be registered by <code class="language-plaintext highlighter-rouge">AddMeter("meterName")</code> to <code class="language-plaintext highlighter-rouge">MeterProvider</code>. There exist some build-in <code class="language-plaintext highlighter-rouge">Meter</code>’s to instrument ASP.NET application
 <code class="language-plaintext highlighter-rouge">Microsoft.AspNetCore.Hosting</code>, <code class="language-plaintext highlighter-rouge">Microsoft.AspNetCore.Server.Kestrel</code>, <code class="language-plaintext highlighter-rouge">System.Net.Http</code>.</p>

<p>To instrument the application’s runtime (ram, cpu, gc,…) install <code class="language-plaintext highlighter-rouge">OpenTelemetry.Instrumentation.Runtime</code> and configure it by <code class="language-plaintext highlighter-rouge">AddRuntimeInstrumentation</code>.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddOpenTelemetry</span><span class="p">()</span>
    <span class="p">.</span><span class="nf">WithMetrics</span><span class="p">(</span><span class="n">metrics</span> <span class="p">=&gt;</span>
    <span class="p">{</span>
        <span class="n">metrics</span><span class="p">.</span><span class="nf">SetResourceBuilder</span><span class="p">(</span><span class="n">ResourceBuilder</span><span class="p">.</span><span class="nf">CreateDefault</span><span class="p">()</span>
                    <span class="p">.</span><span class="nf">AddService</span><span class="p">(</span><span class="s">"serviceName"</span><span class="p">))</span>
                <span class="p">.</span><span class="nf">AddMeter</span><span class="p">(</span><span class="s">"serviceName"</span><span class="p">);</span> <span class="c1">// Configure meter to listen</span>
                <span class="p">.</span><span class="nf">AddMeter</span><span class="p">(</span><span class="s">"Microsoft.AspNetCore.Hosting"</span><span class="p">,</span> <span class="s">"Microsoft.AspNetCore.Server.Kestrel"</span><span class="p">,</span> <span class="s">"System.Net.Http"</span><span class="p">);</span>
                <span class="p">.</span><span class="nf">AddRuntimeInstrumentation</span><span class="p">()</span>
    <span class="p">});</span>
</code></pre></div></div>

<h3 id="exporters">Exporters</h3>

<p>There are several ways to export metrics to backends - OTLP, Console, and Prometheus in the namespace of the <code class="language-plaintext highlighter-rouge">OpenTelemetry.Exporter.NAME</code> nuget package.</p>

<p>To enable OTLP exporter for all signals, there is exists an extension <code class="language-plaintext highlighter-rouge">UseOtlpExporter</code>.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddOpenTelemetry</span><span class="p">()</span>
    <span class="p">.</span><span class="nf">UseOtlpExporter</span><span class="p">();</span>
</code></pre></div></div>]]></content><author><name>Tomas Jurasek</name></author><summary type="html"><![CDATA[In the previous article, I wrote about Distributed Tracing with OpenTelementry. Now, let’s write about Metrics.]]></summary></entry><entry><title type="html">Distributed Tracing in .NET with OpenTelemetry</title><link href="https://tomasjurasek.github.io/2024/06/06/tracing-open-telemetry-net/" rel="alternate" type="text/html" title="Distributed Tracing in .NET with OpenTelemetry" /><published>2024-06-06T00:00:00+00:00</published><updated>2024-06-06T00:00:00+00:00</updated><id>https://tomasjurasek.github.io/2024/06/06/tracing-open-telemetry-net</id><content type="html" xml:base="https://tomasjurasek.github.io/2024/06/06/tracing-open-telemetry-net/"><![CDATA[<p>This article is part of a series about <a href="https://opentelemetry.io/">OpenTelemetry</a> in .NET. OpenTelemetry establishes a de facto standard for application observability by providing standardized APIs, SDKs, and tools to collect signals - metrics, traces, and logs.</p>

<h2 id="opentelementry-net-tracing-api">OpenTelementry .NET Tracing API</h2>

<p>The .NET runtime contains an <code class="language-plaintext highlighter-rouge">Activity</code> class, which is used for tracing purposes and represents a <a href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#span">Span</a> in OpenTelemetry terminology. The <code class="language-plaintext highlighter-rouge">Activity</code> class is part of the <code class="language-plaintext highlighter-rouge">System.Diagnostics.DiagnosticSource</code> package.</p>

<p>This allows applications to emit OpenTelemetry traces using the standard .NET Runtime.</p>

<p>To align more closely with OpenTelemetry terminology (like tracer, span instead of activitySource, activity) in the application, use the <a href="https://www.nuget.org/packages/opentelemetry.api">OpenTelementry.API</a>, which wraps the .NET <code class="language-plaintext highlighter-rouge">Activity</code> classes.</p>

<h2 id="manual-instrumentation">Manual Instrumentation</h2>

<p>Use the <code class="language-plaintext highlighter-rouge">ActivitySource</code> class, which provides the name and version of the application doing the instrumentation. The instance of <code class="language-plaintext highlighter-rouge">ActivitySource</code> should be created once and reused throughout the application.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">static</span> <span class="n">ActivitySource</span> <span class="n">activitySource</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">ActivitySource</span><span class="p">(</span>
    <span class="s">"serviceName"</span><span class="p">,</span>
    <span class="s">"1.0.0"</span><span class="p">);</span>
</code></pre></div></div>

<p>Use the <code class="language-plaintext highlighter-rouge">ActivitySource</code> instance to create an <code class="language-plaintext highlighter-rouge">Activity</code> instance, which represents a single-span operation.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">using</span> <span class="nn">var</span> <span class="n">activity</span> <span class="p">=</span> <span class="n">activitySource</span><span class="p">.</span><span class="nf">StartActivity</span><span class="p">(</span><span class="s">"ActivityName"</span><span class="p">);</span>
</code></pre></div></div>

<p>If there are no listeners/samplers interested in this activity, the <code class="language-plaintext highlighter-rouge">StartActivity</code> returns null.</p>

<p>The <code class="language-plaintext highlighter-rouge">StartActivity</code> method starts an activity  and sets it as the current activity in the <code class="language-plaintext highlighter-rouge">ActivityContext.Current</code> and propagates the <code class="language-plaintext highlighter-rouge">ActivityContext</code> from the parent activity.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">var</span> <span class="n">activity</span> <span class="p">=</span> <span class="n">activitySource</span><span class="p">.</span><span class="nf">StartActivity</span><span class="p">(</span><span class="s">"ActivityName"</span><span class="p">);</span>
<span class="n">activity</span><span class="p">?.</span><span class="nf">SetTag</span><span class="p">(</span><span class="s">"http.url"</span><span class="p">,</span> <span class="s">"url"</span><span class="p">);</span>
<span class="n">activity</span><span class="p">?.</span><span class="nf">AddEvent</span><span class="p">(</span><span class="k">new</span> <span class="nf">ActivityEvent</span><span class="p">(</span><span class="s">"activity event"</span><span class="p">));</span>
<span class="n">activity</span><span class="p">?.</span><span class="nf">SetStatus</span><span class="p">(</span><span class="n">ActivityStatusCode</span><span class="p">.</span><span class="n">Error</span><span class="p">,</span> <span class="s">"error"</span><span class="p">);</span>
</code></pre></div></div>

<p>Populate activity with tags follow the <a href="https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/trace.md"> OpenTelemetry semantic conventions</a>.</p>

<h2 id="configure-opentelemetry">Configure OpenTelemetry</h2>

<p>Install the nuget package <code class="language-plaintext highlighter-rouge">OpenTelemetry.Extensions.Hosting</code> to expose the OpenTelemetry SDK.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddOpenTelemetry</span><span class="p">()</span>
    <span class="p">.</span><span class="nf">WithTracing</span><span class="p">(</span><span class="n">tracing</span> <span class="p">=&gt;</span>
    <span class="p">{</span>
        <span class="n">tracing</span><span class="p">.</span><span class="nf">SetResourceBuilder</span><span class="p">(</span><span class="n">ResourceBuilder</span><span class="p">.</span><span class="nf">CreateDefault</span><span class="p">()</span>
                    <span class="p">.</span><span class="nf">AddService</span><span class="p">(</span><span class="s">"serviceName"</span><span class="p">))</span>
                <span class="p">.</span><span class="nf">AddSource</span><span class="p">(</span><span class="s">"serviceName"</span><span class="p">);</span> <span class="c1">// Configure sources to listen</span>
    <span class="p">});</span>
</code></pre></div></div>
<h3 id="instrumentations">Instrumentations</h3>
<p>There are two ways how to instrument.</p>
<ul>
  <li>Manual instrumentation means adding custom instruments to .NET application using the <code class="language-plaintext highlighter-rouge">Activity</code> class.</li>
  <li><a href="https://opentelemetry.io/docs/zero-code/net/instrumentations/">Automatic instrumentation</a> adds external instruments (HTTP, SQL, Elastic, Redis,…) to .NET application without modify a source code by adding <code class="language-plaintext highlighter-rouge">OpenTelemetry.AutoInstrumentation</code> nuget package.</li>
</ul>

<p>Automatic instrumentation can also enabled by <code class="language-plaintext highlighter-rouge">OpenTelemetry.Instrumentation.X</code> nuget packages that are specific per resource.</p>

<p>For example, instruments for HTTP Requests (In) and Requests (Out) are in the <code class="language-plaintext highlighter-rouge">OpenTelemetry.Instrumentation.AspNetCore</code> and <code class="language-plaintext highlighter-rouge">OpenTelemetry.Instrumentation.Http</code> packages, which also configures and propagates trace context through <a href="https://opentelemetry.io/docs/specs/otel/context/api-propagators/">Propagator API</a> via HTTP Headers.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddOpenTelemetry</span><span class="p">()</span>
    <span class="p">.</span><span class="nf">WithTracing</span><span class="p">(</span><span class="n">tracing</span> <span class="p">=&gt;</span>
    <span class="p">{</span>
        <span class="n">tracing</span><span class="p">.</span><span class="nf">SetResourceBuilder</span><span class="p">(</span><span class="n">ResourceBuilder</span><span class="p">.</span><span class="nf">CreateDefault</span><span class="p">()</span>
                    <span class="p">.</span><span class="nf">AddService</span><span class="p">(</span><span class="s">"serviceName"</span><span class="p">))</span>
                <span class="p">.</span><span class="nf">AddSource</span><span class="p">(</span><span class="s">"serviceName"</span><span class="p">);</span> <span class="c1">// Configures sources to listen (instrument)</span>
                <span class="p">.</span><span class="nf">AddAspNetCoreInstrumentation</span><span class="p">()</span> <span class="c1">// HTTP Requests (In) - Configures tracing context from headers</span>
                <span class="p">.</span><span class="nf">AddHttpClientInstrumentation</span><span class="p">()</span> <span class="c1">// HTTP Requests (Out) - Propagates tracing context to headers</span>
    <span class="p">});</span>
</code></pre></div></div>

<p><a href="https://opentelemetry.io/docs/concepts/sampling/">Sampling</a> (especially  Head Sampling) can be configured by <code class="language-plaintext highlighter-rouge">tracing.SetSampler(...)</code>.</p>

<h3 id="exporters">Exporters</h3>

<p>Exporters allow an application to send instrumentation data to various backends, such as Console, Zipkin, or OTLP.</p>

<p>For example, export traces to the OTLP collector by <code class="language-plaintext highlighter-rouge">OpenTelemetry.Exporter.OpenTelemetryProtocol</code> and configure it using <a href="https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/">OTLP Exporter Configuration</a>.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddOpenTelemetry</span><span class="p">()</span>
    <span class="p">.</span><span class="nf">WithTracing</span><span class="p">(</span><span class="n">tracing</span> <span class="p">=&gt;</span>
    <span class="p">{</span>
        <span class="n">tracing</span><span class="p">.</span><span class="nf">SetResourceBuilder</span><span class="p">(</span><span class="n">ResourceBuilder</span><span class="p">.</span><span class="nf">CreateDefault</span><span class="p">()</span>
                    <span class="p">.</span><span class="nf">AddService</span><span class="p">(</span><span class="s">"serviceName"</span><span class="p">))</span>
                <span class="p">.</span><span class="nf">AddSource</span><span class="p">(</span><span class="s">"serviceName"</span><span class="p">);</span> <span class="c1">// Configure sources to listen (instrument)</span>
                <span class="p">.</span><span class="nf">AddAspNetCoreInstrumentation</span><span class="p">()</span> <span class="c1">// HTTP Requests (In)</span>
                <span class="p">.</span><span class="nf">AddHttpClientInstrumentation</span><span class="p">()</span> <span class="c1">// HTTP Requests (Out)</span>
                <span class="p">.</span><span class="nf">AddOtlpExporter</span><span class="p">()</span>
    <span class="p">});</span>
</code></pre></div></div>]]></content><author><name>Tomas Jurasek</name></author><summary type="html"><![CDATA[This article is part of a series about OpenTelemetry in .NET. OpenTelemetry establishes a de facto standard for application observability by providing standardized APIs, SDKs, and tools to collect signals - metrics, traces, and logs.]]></summary></entry></feed>