<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Michael Haberman</title>
    <description>The latest articles on DEV Community by Michael Haberman (@habmic).</description>
    <link>https://dev.to/habmic</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F557319%2F2b537263-7e15-4f2e-bb11-7a13eb04964f.jpeg</url>
      <title>DEV Community: Michael Haberman</title>
      <link>https://dev.to/habmic</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/habmic"/>
    <language>en</language>
    <item>
      <title>How to Get Started with OpenTelemetry Go</title>
      <dc:creator>Michael Haberman</dc:creator>
      <pubDate>Wed, 31 Aug 2022 07:28:19 +0000</pubDate>
      <link>https://dev.to/aspecto/how-to-get-started-with-opentelemetry-go-373j</link>
      <guid>https://dev.to/aspecto/how-to-get-started-with-opentelemetry-go-373j</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pOtn4tui--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oz61x16mo6txyduzcm35.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pOtn4tui--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oz61x16mo6txyduzcm35.png" alt="OpenTelemetry Go The Mandalorian" width="880" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this guide, you will learn hands-on how to create and visualize traces with OpenTelemetry Go without prior knowledge.&lt;/p&gt;

&lt;p&gt;We will start with creating a simple to-do app that uses Mongo and the Gin framework. Then, we will send tracing data to Jaeger Tracing and to Aspecto for visualization. You can find all the relevant files in this  &lt;a href="https://github.com/aspecto-io/opentelemetry-examples/tree/master/go"&gt;Github repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Expect
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; Intro to OpenTelemetry
&lt;/li&gt;
&lt;li&gt; Hello world: OpenTelemetry GO example

&lt;ol&gt;
&lt;li&gt; Create main.go file with Gin and Mongo
&lt;/li&gt;
&lt;li&gt; Install OpenTelemetry GO
&lt;/li&gt;
&lt;li&gt; Gin instrumentation: gin.Context
&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt; Visualization with Jaeger and Aspecto

&lt;ol&gt;
&lt;li&gt; OpenTelemetry Go and Jaeger Tracing
&lt;/li&gt;
&lt;li&gt; OpenTelemetry Go and Aspecto
&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Intro to OpenTelemetry &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;OpenTelemetry is a collection of APIs and SDKs that allows us to collect, export, and generate  &lt;strong&gt;traces, logs, and metrics&lt;/strong&gt;  (also known as the three pillars of observability).&lt;/p&gt;

&lt;p&gt;It is a CNCF community-driven open-source project (Cloud Native Computing Foundation, the folks in charge of Kubernetes).&lt;/p&gt;

&lt;p&gt;In a cloud-native environment, we use OpenTelemetry (OTel for short) to gather data from our system operations and events. In other words, to  &lt;strong&gt;instrument our distributed services.&lt;/strong&gt; This data enables us to understand and investigate our software’s behavior and troubleshoot performance issues and errors.&lt;/p&gt;

&lt;p&gt;OpenTelemetry serves as a standard observability framework that captures all data under a single specification. It provides several components, including:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; APIs and SDKs per programming language for generating telemetry&lt;/li&gt;
&lt;li&gt; The OpenTelemetry Collector; receives, processes, and exports telemetry data to different destinations.&lt;/li&gt;
&lt;li&gt; &lt;a href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md"&gt;OTLP&lt;/a&gt;  protocol for shipping telemetry data&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To get a deeper understanding of this technology, including its structure and the primary motivation,  &lt;a href="https://www.aspecto.io/blog/what-is-opentelemetry-the-infinitive-guide/"&gt;visit this guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For this OpenTelemetry Golang guide, here are the terms you need to know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Span&lt;/strong&gt;: The most basic unit. A span represents an event in our system (e.g., an HTTP request or a database operation that spans over time). A span would usually be the parent of another span, its child, or both.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Trace&lt;/strong&gt;:  &lt;a href="https://www.aspecto.io/blog/guide-to-distributed-tracing/"&gt;‘Call-stacks’ for distributed services&lt;/a&gt;. Traces represent a tree of spans connected in a child/parent relationship. Traces specify the progression of requests across different services and components in our app (DB, data sources, queues, etc.). For example, sending an API call to user-service resulted in a DB query to users-db.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Exporter&lt;/strong&gt;: Once we create a span, the exporter handles sending the data to our backend (e.g., in memory, Jaeger Tracing, or console output)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Context propagation&lt;/strong&gt; – The mechanism that allows us to correlate events across distributed services.  &lt;strong&gt;Context&lt;/strong&gt;  is referred to as the metadata we collect and transfer*&lt;em&gt;. Propagation&lt;/em&gt;*  is how the context is packaged and transferred across services, often via HTTP headers. Context propagation is one of the areas where OpenTelemetry shines.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Instrumentation&lt;/strong&gt;  – instrumentation libraries gather the data and generate spans based on different libraries in our applications (Kafka, Mongo, Gin, etc.).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are two ways to instrument our app – manually and automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Auto instrumentation:&lt;/strong&gt;  Automatically create spans from the application libraries we use with ready-to-use OpenTelemetry libraries.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Manual instrumentation&lt;/strong&gt;: Manually add code to your application to define the beginning and end of each span and the payload.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To understand more of the OpenTelemetry jargon,  &lt;a href="https://opentelemetry.io/docs/concepts/data-sources/"&gt;visit the official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hello world: OpenTelemetry Go example &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;We will start by creating our to-do service and installing two libraries (Gin and Mongo) to understand how instrumentations work.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 1: Create main.go file for our to-do app&lt;/strong&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;1) Install Gin and Mongo-driver&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;gonic&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;gin&lt;/span&gt;
&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mongodb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mongo&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mongo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) Set up gin and mongo to listen on “/todo”&lt;/p&gt;

&lt;p&gt;3) Create some to-do’s to seed Mongo&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s"&gt;"context"&lt;/span&gt;
  &lt;span class="s"&gt;"net/http"&lt;/span&gt;
  &lt;span class="s"&gt;"github.com/gin-gonic/gin"&lt;/span&gt;
  &lt;span class="s"&gt;"go.mongodb.org/mongo-driver/bson"&lt;/span&gt;
  &lt;span class="s"&gt;"go.mongodb.org/mongo-driver/mongo"&lt;/span&gt;
  &lt;span class="s"&gt;"go.mongodb.org/mongo-driver/mongo/options"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mongo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;connectMongo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;setupWebServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;connectMongo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ApplyURI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mongodb://localhost:27017"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mongo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c"&gt;//Seed the database with todo's&lt;/span&gt;
  &lt;span class="n"&gt;docs&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}{&lt;/span&gt;
      &lt;span class="n"&gt;bson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Buy groceries"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
      &lt;span class="n"&gt;bson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"install Aspecto.io"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
      &lt;span class="n"&gt;bson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"3"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Buy dogz.io domain"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todos"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InsertMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;setupWebServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/todo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;collection&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todos"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="c"&gt;//Important: Make sure to pass c.Request.Context() as the context and not c itself - TBD&lt;/span&gt;
      &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;findErr&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;bson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;findErr&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AbortWithError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;findErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;curErr&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;All&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;curErr&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AbortWithError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;curErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusOK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":8080"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that our small todo app is ready, let’s introduce OpenTelemetry.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 2: Install OpenTelemetry Go&lt;/strong&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;We will be configuring OpenTelemetry to instrument our Go app.&lt;/p&gt;

&lt;p&gt;1) To install the OTel SDK, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;opentelemetry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;otel&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;opentelemetry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;otel&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sdk&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) Instrument our Gin and Mongo libraries to generate traces.&lt;/p&gt;

&lt;p&gt;3) Gin instrumentation: Install otelgin&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;opentelemetry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;contrib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;instrumentation&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;gonic&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;otelgin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4) Mongo instrumentation: Install otelmongo&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;opentelemetry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;contrib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;instrumentation&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mongodb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mongo&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mongo&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;otelmongo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Gin instrumentation: gin.Context&lt;/strong&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;We previously discussed the idea of context propagation – the way to transfer metadata between distributed services to correlate events in our system.&lt;/p&gt;

&lt;p&gt;The Gin framework has its own type  &lt;code&gt;gin.Context&lt;/code&gt;  which gets passed as a parameter to an HTTP handler. However, the context that should be passed down to the mongo operations is the standard Go library Context object, available in  &lt;code&gt;gin.Context.Request.Context&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;//Make sure to pass c.Request.Context() as the context and not c itself&lt;/span&gt;
&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;findErr&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;bson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So make sure that you pass the Context to the mongodb operation. Check out  &lt;a href="https://github.com/open-telemetry/opentelemetry-go-contrib/issues/319#issuecomment-683867633"&gt;this issue&lt;/a&gt;  for more info.&lt;/p&gt;

&lt;p&gt;We now have our todo app ready and instrumented. It’s time to utilize OpenTelemetry to its full potential. Our ability to visualize traces is where the true troubleshooting power of this technology comes into play.&lt;/p&gt;

&lt;p&gt;For visualization, we’ll be using the open-source Jaeger Tracing and  &lt;a href="https://www.aspecto.io/"&gt;Aspecto&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Visualization with Jaeger and Aspecto &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The setup to export traces to Jaeger or Aspecto is relatively similar. Follow along with the Jaeger setup, then switch to Aspecto by changing a few lines of code.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;OpenTelemetry Go and Jaeger Tracing: Export traces to Jaeger&lt;/strong&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.aspecto.io/blog/jaeger-tracing-the-ultimate-guide/"&gt;Jaeger Tracing&lt;/a&gt;  is a suite of open source projects managing the entire distributed tracing “stack”: client, collector, and UI. Jaeger UI is the most commonly used open-source to visualize traces.&lt;/p&gt;

&lt;p&gt;Here’s what the setup looks like:&lt;/p&gt;

&lt;p&gt;1) Install the Jaeger exporter&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;opentelemetry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;otel&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;exporters&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;jaeger&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) Create a tracing folder and a jaeger.go file&lt;/p&gt;

&lt;p&gt;3) Add the following code to the file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;tracing&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s"&gt;"go.opentelemetry.io/otel/exporters/jaeger"&lt;/span&gt;
  &lt;span class="s"&gt;"go.opentelemetry.io/otel/sdk/resource"&lt;/span&gt;
  &lt;span class="n"&gt;sdktrace&lt;/span&gt; &lt;span class="s"&gt;"go.opentelemetry.io/otel/sdk/trace"&lt;/span&gt;
  &lt;span class="n"&gt;semconv&lt;/span&gt; &lt;span class="s"&gt;"go.opentelemetry.io/otel/semconv/v1.4.0"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;JaegerTraceProvider&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sdktrace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TracerProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;jaeger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jaeger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithCollectorEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jaeger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:14268/api/traces"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;tp&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sdktrace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTracerProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;sdktrace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithBatcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="n"&gt;sdktrace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewWithAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;semconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SchemaURL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;semconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServiceNameKey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todo-service"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="n"&gt;semconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeploymentEnvironmentKey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4) Go back to the main.go file and modify our code to use the  &lt;code&gt;JaegerTraceProvider&lt;/code&gt;  function we just created&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tpErr&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;tracing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JaegerTraceProvider&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tpErr&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tpErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;otel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetTracerProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;otel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetTextMapPropagator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;propagation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewCompositeTextMapPropagator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;propagation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TraceContext&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;propagation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Baggage&lt;/span&gt;&lt;span class="p"&gt;{}))&lt;/span&gt;
  &lt;span class="n"&gt;connectMongo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;setupWebServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we are going to hook up the instrumentations we installed.&lt;/p&gt;

&lt;p&gt;5) Add the Mongo instrumentation. In our  &lt;code&gt;connectMongo&lt;/code&gt;  function by adding this line&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Monitor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;otelmongo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewMonitor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function shold look like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;connectMongo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="c"&gt;//Mongo OpenTelemetry instrumentation&lt;/span&gt;
  &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Monitor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;otelmongo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewMonitor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ApplyURI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mongodb://localhost:27017"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mongo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c"&gt;//Seed the database with some todo's&lt;/span&gt;
  &lt;span class="n"&gt;docs&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}{&lt;/span&gt;
      &lt;span class="n"&gt;bson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Buy groceries"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
      &lt;span class="n"&gt;bson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"install Aspecto.io"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
      &lt;span class="n"&gt;bson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"3"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Buy dogz.io domain"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todos"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InsertMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, add the Gin instrumentation.&lt;/p&gt;

&lt;p&gt;6) Go to the  &lt;code&gt;startWebServer&lt;/code&gt;  function and add this line right after we create the gin instance&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;otelgin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todo-service"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function should look like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;startWebServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="c"&gt;//Gin OpenTelemetry instrumentation&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;otelgin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todo-service"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/todo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;collection&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todos"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="c"&gt;//make sure to pass c.Request.Context() as the context and not c itself&lt;/span&gt;
      &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;findErr&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;bson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;findErr&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AbortWithError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;findErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;curErr&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;All&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;curErr&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AbortWithError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;curErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusOK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":8080"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below is the complete main.go file. Now we’re finally ready to export to Jaeger.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s"&gt;"context"&lt;/span&gt;
  &lt;span class="s"&gt;"log"&lt;/span&gt;
  &lt;span class="s"&gt;"net/http"&lt;/span&gt;
  &lt;span class="s"&gt;"github.com/aspecto-io/opentelemerty-examples/tracing"&lt;/span&gt;
  &lt;span class="s"&gt;"github.com/gin-gonic/gin"&lt;/span&gt;
  &lt;span class="s"&gt;"go.mongodb.org/mongo-driver/bson"&lt;/span&gt;
  &lt;span class="s"&gt;"go.mongodb.org/mongo-driver/mongo"&lt;/span&gt;
  &lt;span class="s"&gt;"go.mongodb.org/mongo-driver/mongo/options"&lt;/span&gt;
  &lt;span class="s"&gt;"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"&lt;/span&gt;
  &lt;span class="s"&gt;"go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo"&lt;/span&gt;
  &lt;span class="s"&gt;"go.opentelemetry.io/otel"&lt;/span&gt;
  &lt;span class="s"&gt;"go.opentelemetry.io/otel/propagation"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mongo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;   &lt;span class="c"&gt;//Export traces to Jaeger&lt;/span&gt;
  &lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tpErr&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;tracing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JaegerTraceProvider&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tpErr&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tpErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;otel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetTracerProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;otel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetTextMapPropagator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;propagation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewCompositeTextMapPropagator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;propagation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TraceContext&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;propagation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Baggage&lt;/span&gt;&lt;span class="p"&gt;{}))&lt;/span&gt;
  &lt;span class="n"&gt;connectMongo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;startWebServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;connectMongo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="c"&gt;//Mongo OpenTelemetry instrumentation&lt;/span&gt;
  &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Monitor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;otelmongo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewMonitor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ApplyURI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mongodb://localhost:27017"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mongo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c"&gt;//Seed the database with some todo's&lt;/span&gt;
  &lt;span class="n"&gt;docs&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}{&lt;/span&gt;
      &lt;span class="n"&gt;bson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Buy groceries"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
      &lt;span class="n"&gt;bson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"install Aspecto.io"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
      &lt;span class="n"&gt;bson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"3"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Buy dogz.io domain"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todos"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InsertMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;startWebServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="c"&gt;//gin OpenTelemetry instrumentation&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;otelgin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todo-service"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/todo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;collection&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todos"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="c"&gt;//Make sure to pass c.Request.Context() as the context and not c itself&lt;/span&gt;
      &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;findErr&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;bson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;findErr&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AbortWithError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;findErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;curErr&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;All&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;curErr&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AbortWithError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;curErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusOK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":8080"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Export traces to Jaeger&lt;/strong&gt;
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt; Run the todo-service with go run main.go&lt;/li&gt;
&lt;li&gt; Make an HTTP GET request to  &lt;em&gt;localhost:8080/todo&lt;/em&gt;  to generate some traces in Go&lt;/li&gt;
&lt;li&gt; Open Jaeger at  &lt;a href="http://localhost:16686/search"&gt;http://localhost:16686/search&lt;/a&gt;  to view those traces&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can now see the Jaeger UI. Select todo-service and click on Find traces. You should see your trace on the right:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Pt1PQ9n0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/ZeFbAE9-XVSc-5GHjZkslHuJ3f01VQqSrObOgLY9yDSjuTyJdvvAzIapIvTQqumFTUP2BZE4gxd-Vt2JXjvqO1ep3JUBhkHKiry_m8bSAwwvEf3kKNfzFiKwzFP8E3btWtQV0pLZZWnsbY-sUA" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Pt1PQ9n0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/ZeFbAE9-XVSc-5GHjZkslHuJ3f01VQqSrObOgLY9yDSjuTyJdvvAzIapIvTQqumFTUP2BZE4gxd-Vt2JXjvqO1ep3JUBhkHKiry_m8bSAwwvEf3kKNfzFiKwzFP8E3btWtQV0pLZZWnsbY-sUA" alt="Jaeger UI displays opentelemetry traces in go for our todo-service" width="880" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By clicking the trace, you can drill down and see more details about it that allow you to further investigate on your own:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BRyxpGGq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/5KI-tGGriWaMf98vNjewZZTwE1f-g7dQJXCEaCWmklT_xmCc5E_2VSGcRDeKf4GNZwRSNnSpQCQFH-1nUXIF7a5gd6Y7odFiEukSbaWaukFKP0cXXylHIqGJvAMfbQ2p60nt3wmeOwTtRr3eKQ" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BRyxpGGq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/5KI-tGGriWaMf98vNjewZZTwE1f-g7dQJXCEaCWmklT_xmCc5E_2VSGcRDeKf4GNZwRSNnSpQCQFH-1nUXIF7a5gd6Y7odFiEukSbaWaukFKP0cXXylHIqGJvAMfbQ2p60nt3wmeOwTtRr3eKQ" alt="Jaeger UI. To-do service drill down." width="880" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Visualization with OpenTelemetry Go and Aspecto&lt;/strong&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;So now you know the basic concepts of OpenTelemetry. You used it on your own to instrument your Go libraries, create traces and export the data to Jaeger for visualization.&lt;/p&gt;

&lt;p&gt;Let’s take our visualization capabilities to the next level with  &lt;a href="https://www.aspecto.io/"&gt;Aspecto&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Exporting and visualizing our data in Aspecto is easy (we will see that in a moment). You can try it yourself with the  &lt;a href="https://www.aspecto.io/pricing/"&gt;free-forever plan&lt;/a&gt;  that has no limited features. Give this  &lt;a href="https://app.aspecto.io/play/search"&gt;Live Playground&lt;/a&gt;  a try to get a better idea.&lt;/p&gt;

&lt;p&gt;This is how we do it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Create a new free account at  &lt;a href="https://www.aspecto.io/"&gt;www.aspecto.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; Install the otlp exporter by running
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;opentelemetry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;otel&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;exporters&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;otlp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;otlptrace&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;otlptracegrpc&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt; Create a file called  &lt;strong&gt;aspecto.go&lt;/strong&gt;  in your tracing folder and add the code below&lt;/li&gt;
&lt;li&gt; Replace the {ASPECTO_AUTH} with your unique Aspecto token ID –  &lt;a href="https://app.aspecto.io/app/integration/token"&gt;https://app.aspecto.io/app/integration/token&lt;/a&gt;  (Settings &amp;gt; Integrations &amp;gt; Tokens)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;AspectoTraceProvider&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sdktrace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TracerProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;otlptracegrpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="n"&gt;otlptracegrpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"collector.aspecto.io:4317"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="n"&gt;otlptracegrpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithHeaders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="s"&gt;"Authorization"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;ADD YOUR TOKEN HERE&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}))&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;tp&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sdktrace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTracerProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;sdktrace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithBatcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="n"&gt;sdktrace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewWithAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;semconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SchemaURL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;semconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServiceNameKey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todo-service"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="n"&gt;semconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeploymentEnvironmentKey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt; Back in the main.go file, replace the  &lt;code&gt;JaegerTraceProvider&lt;/code&gt;  with  &lt;code&gt;AspectoTraceProvider&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The main function should now look like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;   &lt;span class="c"&gt;//Export traces to Aspecto&lt;/span&gt;
  &lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tpErr&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;tracing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AspectoTraceProvider&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tpErr&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tpErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;otel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetTracerProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;otel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetTextMapPropagator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;propagation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewCompositeTextMapPropagator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;propagation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TraceContext&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;propagation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Baggage&lt;/span&gt;&lt;span class="p"&gt;{}))&lt;/span&gt;
  &lt;span class="n"&gt;connectMongo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;startWebServer&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Exporting traces to Aspecto&lt;/strong&gt;
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt; Run the todo-service with go run main.go&lt;/li&gt;
&lt;li&gt; Make an HTTP GET request to localhost:8080/todo to generate some traces&lt;/li&gt;
&lt;li&gt; Navigate to  &lt;a href="http://app.aspecto.io/"&gt;app.aspecto.io&lt;/a&gt;  to view the traces&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We also sent a few traces with errors to see what it looks like. Our traces should look something like this:&lt;/p&gt;

&lt;p&gt;In the Trace Search view, using the left filters pane, simply filter out by the service name (you can also filter by error, HTTP method, and much more).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PBFU2jRF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/RtD9xjCm2v2T03FxWO3haMSWTDqzXK7BzmOBQJskZBX2fN1PxYIRqcMmEQuYRyqAh_y4HPeRhpfzEU2hAbBMrVc_KTf_ryPZhkq-i7MfB_Ay64Ed3x6utk8Tw_43gehUpLRM0CJnwkUQL_dQdA" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PBFU2jRF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/RtD9xjCm2v2T03FxWO3haMSWTDqzXK7BzmOBQJskZBX2fN1PxYIRqcMmEQuYRyqAh_y4HPeRhpfzEU2hAbBMrVc_KTf_ryPZhkq-i7MfB_Ay64Ed3x6utk8Tw_43gehUpLRM0CJnwkUQL_dQdA" alt="Aspecto Trace Search. Displays OpenTelemetry traces for our todo app in Go." width="880" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we drill down into one of these traces, we can see in more detail how long each request took and  &lt;a href="https://www.aspecto.io/"&gt;clear visualization&lt;/a&gt;  of the entire workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lZY41VZq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/d_rK4I6sTRwZwkSNYQmk2aEbPbZzE1T8M4VhwMays2QRCyWVMsCUDdZGSGi4zDNQbAIG8fyXs-nIppyDSgomZ1vdXi7QftrSadbd19BWFkgtpay8seQkjM6noBTxua4ZnAfjcrBISkL09-Pk8A" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lZY41VZq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/d_rK4I6sTRwZwkSNYQmk2aEbPbZzE1T8M4VhwMays2QRCyWVMsCUDdZGSGi4zDNQbAIG8fyXs-nIppyDSgomZ1vdXi7QftrSadbd19BWFkgtpay8seQkjM6noBTxua4ZnAfjcrBISkL09-Pk8A" alt="Aspecto Trace Viewer UI. Drill down into specific trace." width="880" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In more complex workflows, traces would look something like the below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3TbAIHqS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh4.googleusercontent.com/i6ecm2NVc3mOIflf7iFGgx1ycLuE_Vm2Po0eSLVm6OKZy-H8BV7RNDPFtiMHAwczjMOaqVa2Qm77vCdfDnx558XA_XnOAw5_0z9NbDgsxB6L-mqOIUnukn_iKU0ZFGdiQITDYJvCvDsIRpiX2w" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3TbAIHqS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh4.googleusercontent.com/i6ecm2NVc3mOIflf7iFGgx1ycLuE_Vm2Po0eSLVm6OKZy-H8BV7RNDPFtiMHAwczjMOaqVa2Qm77vCdfDnx558XA_XnOAw5_0z9NbDgsxB6L-mqOIUnukn_iKU0ZFGdiQITDYJvCvDsIRpiX2w" alt="Aspecto Trace Viewer UI. Drill down into specific trace with a lot of services." width="880" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s all folks! We hope this guide was informative and easy to follow. You can find all files ready to use in our Github  &lt;a href="https://github.com/aspecto-io/opentelemetry-examples/tree/master/go"&gt;repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have any questions or issues, feel free to reach out to us via our chat.&lt;/p&gt;

&lt;p&gt;To learn more about it, check out our OpenTelemetry  &lt;a href="https://www.aspecto.io/opentelemetry-bootcamp/"&gt;Bootcamp&lt;/a&gt;. It’s a 6 episode YouTube series that covers OpenTelemetry from zero to 100 including the implementation of opentelemetry in production, security, sampling, and more. Completely free and vendor-neutral.&lt;/p&gt;

&lt;p&gt;Follow these guides below, if you want to install OpenTelemetry in more officially supported languages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://www.aspecto.io/blog/getting-started-with-opentelemetry-python/"&gt;Python&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.aspecto.io/blog/getting-started-with-opentelemetry-java/"&gt;Java&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.aspecto.io/blog/getting-started-with-opentelemetry-node/"&gt;Node&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>go</category>
      <category>opentelemetry</category>
      <category>monitoring</category>
      <category>observability</category>
    </item>
    <item>
      <title>OpenTelemetry Java: Getting Started Guide</title>
      <dc:creator>Michael Haberman</dc:creator>
      <pubDate>Mon, 29 Aug 2022 06:39:16 +0000</pubDate>
      <link>https://dev.to/aspecto/opentelemetry-java-getting-started-guide-53h1</link>
      <guid>https://dev.to/aspecto/opentelemetry-java-getting-started-guide-53h1</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--00xTKkaz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yu922ufdfqmc4b2nbyom.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--00xTKkaz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yu922ufdfqmc4b2nbyom.png" alt="OpenTelemetry Java (1)" width="800" height="546"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a practical guide that brings you just what you need to get started with OpenTelemetry Java. No prior OpenTelemetry knowledge is needed – we will cover the basics here.&lt;/p&gt;

&lt;p&gt;For this OpenTelemetry Java example, we’ll be using the  &lt;a href="https://github.com/spring-projects/spring-petclinic"&gt;Spring Pet Clinic&lt;/a&gt;  project, to save you some time setting up an example project.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Expect
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Intro to OpenTelemetry&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Hello World: OpenTelemetry Java&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Visualization with Jaeger Tracing&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Advanced Visualization with Aspecto&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Keep Learning: Mastering OpenTelemetry&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is OpenTelemetry?&lt;/strong&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;OpenTelemetry is a collection of SDKs and APIs – an open-source project – that allows us to collect, generate, and export logs, metrics, and traces (also known as the three pillars of observability).&lt;/p&gt;

&lt;p&gt;OpenTelemetry enables us to instrument our distributed services. Instrumenting means capturing telemetry data from events and operations in our distributed system.&lt;/p&gt;

&lt;p&gt;Ultimately, we use this data to understand and investigate our system’s behavior and troubleshoot and debug performance issues and errors.&lt;/p&gt;

&lt;p&gt;Led by the CNCF (Cloud Native Computing Foundation, the foundation responsible for Kubernetes), it serves as a single library that gathers data under a single specification.&lt;/p&gt;

&lt;p&gt;It allows us to be vendor-agnostic and not tied down to a single tool and ship our data to any dedicated location (e.g., backend, OpenTelemetry collector, supporting open sources, and more).&lt;/p&gt;

&lt;p&gt;Since its implementation for most modern programming languages, it has been growing in popularity and usage by developers everywhere. It combines all three aspects needed for proper monitoring (leading with tracing, followed by metrics).&lt;/p&gt;

&lt;p&gt;There is much more to learn about OpenTelemetry and if you want to dive deeper into its structure,  &lt;a href="https://www.aspecto.io/blog/what-is-opentelemetry-the-infinitive-guide/"&gt;follow this guide&lt;/a&gt;. For this guide, here are the key terms you need to know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Span&lt;/strong&gt;: A span represents an action/operation that occurred in our system. An HTTP request or a database operation that spans over time. A span would usually be the parent and/or the child of another span.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Trace&lt;/strong&gt;: Represents a tree of spans connected in a child/parent relationship. Traces describe the progression of requests across different services and components in our system (DB, data sources, queues, etc.). For example, sending an API call to user-service resulted in a DB query to users-db.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WxTBGyS---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/02/graphtimeline-1.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WxTBGyS---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/02/graphtimeline-1.svg" alt="Aspecto UI distributed tracing with OpenTelemetry diagram and timeline" width="538" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Instrumentation&lt;/strong&gt;  – instrumentation libraries are what allow us to gather the data and create spans based on the different libraries in our system such as Kafka, MySQL, Spring, etc. There are 2 ways to instrument our app – manually or automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Auto instrumentation&lt;/strong&gt;: Automatically create spans from the application libraries we use with ready-to-use OpenTelemetry libraries.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Manual instrumentation&lt;/strong&gt;: Manually create spans by adding code to your application to define the beginning and end of each span.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Exporter&lt;/strong&gt;: Once we create a span, we need to send it to a dedicated backend. It may be in memory, console output, vendor, or an open-source such as Jaeger Tracing. The exporter handles sending the data to our backend.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more OpenTelemetry terminology,  &lt;a href="https://opentelemetry.io/docs/concepts/data-sources/"&gt;visit the official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Hello World: OpenTelemetry Java&lt;/strong&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 1: Download the latest OpenTelemetry&lt;/strong&gt;  &lt;strong&gt;Java agent&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Download the latest Java agent ‘JAR’ from  &lt;a href="https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases"&gt;the official repo on GitHub&lt;/a&gt;  and copy the opentelemetry-javaagent-all.jar file to your project.&lt;/p&gt;

&lt;p&gt;Note: Make sure you cloned the Spring Pet Clinic project we mentioned before.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 2: Logging OpenTelemetry spans to console&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;1) Run the following commands in your terminal&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; We are setting up our service name  &lt;em&gt;my-service&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt; Set the exporter to log spans to our console &lt;code&gt;OTEL_TRACES_EXPORTER=logging&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="no"&gt;OTEL_SERVICE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="no"&gt;OTEL_TRACES_EXPORTER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nl"&gt;javaagent:&lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;opentelemetry&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;javaagent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;jar&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;jar&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;/*.&lt;/span&gt;&lt;span class="na"&gt;jar&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We mentioned instrumentations earlier, the libraries that actually allow us to gather data from our apps. The above command will automatically instrument most common libraries. You can view the  &lt;a href="https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/#suppressing-specific-auto-instrumentation"&gt;complete list here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In terms of implementing OpenTelemetry Java instrumentation in our project to collect traces, we’re pretty much done (keep reading, there’s more to it).&lt;/p&gt;

&lt;p&gt;Now, the final output should look something like the one below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Console output&lt;/span&gt;

&lt;span class="no"&gt;INFO&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;opentelemetry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exporter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LoggingSpanExporter&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;owners&lt;/span&gt;&lt;span class="o"&gt;/{&lt;/span&gt;&lt;span class="n"&gt;ownerId&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="n"&gt;af87eeaa19b83d014463b046884e56&lt;/span&gt; &lt;span class="mi"&gt;632&lt;/span&gt;&lt;span class="n"&gt;e2d2a5931bd17&lt;/span&gt; &lt;span class="no"&gt;SERVER&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nl"&gt;tracer:&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;opentelemetry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tomcat&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;7.0&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;1.13&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;
&lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nc"&gt;AttributesMap&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;={&lt;/span&gt;&lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;transport&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ip_tcp&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="n"&gt;owners&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;flavor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;peer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;nio&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nl"&gt;localhost:&lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="n"&gt;owners&lt;/span&gt;&lt;span class="o"&gt;/{&lt;/span&gt;&lt;span class="n"&gt;ownerId&lt;/span&gt;&lt;span class="o"&gt;},&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;user_agent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Mozilla&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Macintosh&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nc"&gt;Intel&lt;/span&gt;
&lt;span class="nc"&gt;Mac&lt;/span&gt; &lt;span class="no"&gt;OS&lt;/span&gt; &lt;span class="no"&gt;X&lt;/span&gt; &lt;span class="mi"&gt;10_15_7&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;AppleWebKit&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;537.36&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;KHTML&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;like&lt;/span&gt; &lt;span class="nc"&gt;Gecko&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Chrome&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;4896.127&lt;/span&gt; &lt;span class="nc"&gt;Safari&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;537.36&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="no"&gt;GET&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;peer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50778&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;scheme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;},&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;totalAddedValues&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: This is not the best-looking console output 😬 We thought about making it prettier, however, this is the way it looks in the console so we decided to stick to the source to prevent any confusion.&lt;/p&gt;

&lt;p&gt;If you look at the JSON, you can see that, for example,  &lt;code&gt;http.route=/owners/{ownerId}&lt;/code&gt;  represents an https call to fetch an owner by ID.&lt;/p&gt;

&lt;p&gt;At this point, we created spans and log them to our console. But we’re not here only to have beautiful spans in our console – it’s all about visualization.&lt;/p&gt;

&lt;p&gt;Our ability to visualize trace data is where the true troubleshooting power of this technology comes into play.&lt;/p&gt;

&lt;p&gt;For visualization, we’ll be using the open-source Jaeger Tracing and Aspecto.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Getting Started with OpenTelemetry Java and Jaeger Tracing&lt;/strong&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.aspecto.io/blog/jaeger-tracing-the-ultimate-guide/"&gt;Jaeger Tracing&lt;/a&gt;  is a suite of open source projects managing the entire distributed tracing “stack”: client, collector, and UI. Jaeger UI is the most commonly used open-source to visualize traces.&lt;/p&gt;

&lt;p&gt;This is how we send traces to Jaeger:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Export to Jaeger&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;1) Run Jaeger locally with the following docker command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="n"&gt;jaeger&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="no"&gt;COLLECTOR_ZIPKIN_HOST_PORT&lt;/span&gt;&lt;span class="o"&gt;=:&lt;/span&gt;&lt;span class="mi"&gt;9411&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;5775&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5775&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;udp&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;6831&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;6831&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;udp&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;6832&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;6832&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;udp&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;5778&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5778&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;16686&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;16686&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;14250&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;14250&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;14268&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;14268&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;14269&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;14269&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;9411&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;9411&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;
  &lt;span class="n"&gt;jaegertracing&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nl"&gt;one:&lt;/span&gt;&lt;span class="mf"&gt;1.32&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) Previously, we set the exporter to log to our console. Now, we need to make the following changes and run this command:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Change the exporter to Jaeger’s  &lt;code&gt;OTEL_TRACES_EXPORTER=jaeger&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Specify the Jaeger’s endpoint  &lt;code&gt;OTEL_EXPORTER_JAEGER_ENDPOINT&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="no"&gt;OTEL_SERVICE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="no"&gt;OTEL_TRACES_EXPORTER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;jaeger&lt;/span&gt; &lt;span class="no"&gt;OTEL_EXPORTER_JAEGER_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nl"&gt;http:&lt;/span&gt;&lt;span class="c1"&gt;//localhost:14250 java -javaagent:./opentelemetry-javaagent.jar -jar target/*.jar&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) Run your Pet Clinic project and execute a few actions. For example, we created a new owner, then searched the owner by the last name.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--55E9qF_f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/04/Spring-Clinic-Java-OpenTelemetry-Project-2-1024x358.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--55E9qF_f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/04/Spring-Clinic-Java-OpenTelemetry-Project-2-1024x358.png" alt="Spring project, search the owner by last name" width="880" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4) Use your browser to view Jaeger UI at &lt;a href="http://localhost:16686/"&gt;http://localhost:16686/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;5) We can now view our trace in the Jaeger UI. Select ‘my-service’ from the search pane on the right and click on Find Traces.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Av1oGWLm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/04/image-69-1024x566.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Av1oGWLm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/04/image-69-1024x566.png" alt="Jaeger UI trace search" width="880" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can drill down into a specific trace. For example, a request to search the owner by owner ID.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Hj8zZu6Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/04/Jaeger-Trace-Pet-Clinic-Java-Project-Trace-Drilldown-1-1024x429.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Hj8zZu6Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/04/Jaeger-Trace-Pet-Clinic-Java-Project-Trace-Drilldown-1-1024x429.png" alt="Jaeger Trace Pet Clinic Java Project Trace Drill Down" width="880" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Advanced Visualization with OpenTelemetry Java and Aspecto&lt;/strong&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;So now you know the basics of instrumentations, spans, traces, and how we can use OpenTelemetry to generate traces for our Java app.&lt;/p&gt;

&lt;p&gt;You can take your tracing visualization to the next level with  &lt;a href="https://www.aspecto.io/"&gt;Aspecto&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Sending traces to Aspecto is as easy as what we were doing so far. Before jumping in, you can try it yourself with the  &lt;a href="https://www.aspecto.io/pricing/"&gt;free-forever plan&lt;/a&gt;  that has no limited features.&lt;/p&gt;

&lt;p&gt;Give this  &lt;a href="https://app.aspecto.io/play/search"&gt;Live Playground&lt;/a&gt;  a try to get a better idea.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Export to Aspecto&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Here’s how it’s done:&lt;/p&gt;

&lt;p&gt;1) Create a free account at  &lt;a href="https://www.aspecto.io/"&gt;www.aspecto.io&lt;/a&gt;  or log in to your existing account&lt;/p&gt;

&lt;p&gt;2) Previously, we set the exporter to send traces to Jaeger. Now, we need to make the following changes and run these commands:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Change the exporter to  &lt;code&gt;OTEL_EXPORTER_OTLP_HEADERS&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Specify the Aspecto endpoint  &lt;code&gt;OTEL_EXPORTER_OTLP_TRACES_ENDPOINT&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Replace the  &lt;code&gt;{ASPECTO_AUTH}&lt;/code&gt; with your unique Aspecto token ID –  &lt;a href="https://app.aspecto.io/app/integration/token"&gt;https://app.aspecto.io/app/integration/token&lt;/a&gt;  (Settings &amp;gt; Integrations &amp;gt; Tokens)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="no"&gt;OTEL_SERVICE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt; 
&lt;span class="no"&gt;OTEL_EXPORTER_OTLP_HEADERS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Authorization&lt;/span&gt;&lt;span class="o"&gt;={&lt;/span&gt;&lt;span class="no"&gt;ASPECTO_AUTH&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="no"&gt;OTEL_EXPORTER_OTLP_TRACES_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nl"&gt;https:&lt;/span&gt;&lt;span class="c1"&gt;//otelcol.aspecto.io:4317 java -javaagent:./opentelemetry-javaagent.jar -jar target/*.jar &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) Execute a few actions in your project. In your Aspecto account, the main Trace Search view should look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I12glNmp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/04/Aspecto-UI-Trace-Search-View-Pet-Clinic-Java-Project-1024x553.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I12glNmp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/04/Aspecto-UI-Trace-Search-View-Pet-Clinic-Java-Project-1024x553.png" alt="Aspecto UI Trace Search View Pet Clinic Java Project" width="880" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using the left Filters pane, we can quickly find any error we have in our system (this error was embedded in the Pet Clinic project by default):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hdHazLnj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/04/Java-Pet-Clinic-Trace-View-with-Error-Filter-Aspecto-UI-1024x407.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hdHazLnj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/04/Java-Pet-Clinic-Trace-View-with-Error-Filter-Aspecto-UI-1024x407.png" alt="Java Pet Clinic Trace View with Error Filter Aspecto UI" width="880" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, drill down into this trace to investigate and troubleshoot the root cause:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lj2vg-ug--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/04/Java-Pet-Clinic-Single-Trace-with-Error-Aspecto-UI-1024x567.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lj2vg-ug--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/04/Java-Pet-Clinic-Single-Trace-with-Error-Aspecto-UI-1024x567.png" alt="Java Pet Clinic Single Trace with Error Aspecto UI" width="880" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can also filter traces out by HTTP route name. For example, here we’re filtering out only the  &lt;code&gt;http.route=/owners/{ownerId}&lt;/code&gt;  we mentioned before:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q4dMqOlq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/04/Java-Project-Aspecto-Trace-Search-Route-Filtering-1024x566.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q4dMqOlq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/04/Java-Project-Aspecto-Trace-Search-Route-Filtering-1024x566.png" alt="Aspecto Trace Search Route Filtering" width="880" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our ability to quickly solve issues and fix errors in production relies heavily on how we visualize traces and the ease with which we can filter out and deep dive into our data.&lt;/p&gt;

&lt;p&gt;That’s about it for this OpenTelemetry Java guide, folks. If you have any questions or issues with any of these steps, feel free to  &lt;a href="https://www.aspecto.io/"&gt;reach out to us via chat&lt;/a&gt;  or join our OpenTelemetry  &lt;a href="https://cloud-native.slack.com/messages/opentelemetry-bootcamp"&gt;Slack channel&lt;/a&gt;  (part of the CNCF Slack).&lt;/p&gt;

&lt;h2&gt;
  
  
  Mastering OpenTelemetry &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;To keep your OpenTelemetry journey going, we created a 6-episode video series that brings you everything you need to know about OpenTelemetry. From zero to mastering, it contains everything from the very basics to advanced concepts such as sampling, security, and scaling for production.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.aspecto.io/opentelemetry-bootcamp/"&gt;&lt;strong&gt;OpenTelemetry Bootcamp&lt;/strong&gt;&lt;/a&gt; – free and vendor-neutral.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.aspecto.io/opentelemetry-bootcamp/"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9o6Lsc4A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2021/12/Banner-OTel-Bootcamp.svg" alt="OpenTelemetry tutorial: complete OpenTelemetry tutorial with the OpenTelemetry bootcamp" width="743" height="211"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use this as your &lt;a href="https://www.aspecto.io/opentelemetry-bootcamp/"&gt;OpenTelemetry playbook&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Episode 1: OpenTelemetry Fundamentals&lt;/li&gt;
&lt;li&gt; Episode 2: Integrate Your Code (logs, metrics, and traces)&lt;/li&gt;
&lt;li&gt; Episode 3: Deploy to Production + Collector&lt;/li&gt;
&lt;li&gt; Episode 4: Sampling and Dealing with High Volumes&lt;/li&gt;
&lt;li&gt; Episode 5: Custom Instrumentation&lt;/li&gt;
&lt;li&gt; Episode 6: Testing with OpenTelemetry&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>java</category>
      <category>opentelemetry</category>
      <category>monitoring</category>
      <category>devops</category>
    </item>
    <item>
      <title>Jaeger Tracing: The Ultimate Guide</title>
      <dc:creator>Michael Haberman</dc:creator>
      <pubDate>Tue, 23 Aug 2022 11:05:16 +0000</pubDate>
      <link>https://dev.to/aspecto/jaeger-tracing-the-ultimate-guide-47cb</link>
      <guid>https://dev.to/aspecto/jaeger-tracing-the-ultimate-guide-47cb</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--STJvu03g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j8prsby266gqmec14cty.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--STJvu03g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j8prsby266gqmec14cty.png" alt="Jaeger Tracing Spiderman" width="880" height="605"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this guide, you’ll learn what Jaeger tracing is, what distributed tracing is, and how to set it up in your system. We’ll go over Jaeger’s UI and touch on advanced concepts such as sampling and deploying in production.&lt;/p&gt;

&lt;p&gt;You’ll leave this guide knowing how to create spans with OpenTelemetry and send them to Jaeger tracing for visualization. All that, from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Jaeger Tracing: Contents&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;What is Distributed Tracing? Introduction&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;What is Jaeger Tracing?&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Jaeger Tracing Architecture&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Running Jaeger locally using Docker&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Jaeger Tracing and OpenTelemetry&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Jaeger Tracing Python Example&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Jaeger Tracing UI Review&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Jaeger Tracing Advanced Concepts&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Jaeger Tracing Production Deployment&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The Costs of Running Jaeger&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Jaeger Tracing Glossary&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is Distributed Tracing? Introduction&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Before we dive into explaining everything you need to know from 0 to 100 about Jaeger tracing, it’s important to understand the umbrella term that Jaeger is part of – distributed tracing.&lt;/p&gt;

&lt;p&gt;In the world of microservices, most issues occur due to networking issues and the relations between the different microservices.&lt;/p&gt;

&lt;p&gt;A distributed architecture (as opposed to a monolith) makes it a lot harder to get to the root of an issue.&lt;/p&gt;

&lt;p&gt;To resolve these issues, we need to see which service sent what parameters to another service or a component (a DB, queue, etc.).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Distributed tracing&lt;/strong&gt; helps us achieve just that by enabling us to collect data from the different parts of our system, to enable this desired observability into our system. You can think of it as ‘call-stacks’ for distributed services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To learn more about the benefits of distributed tracing vs. logs,  &lt;a href="https://www.aspecto.io/blog/logging-vs-tracing-why-logs-arent-enough-to-debug-your-microservices/"&gt;read this quick guide&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In addition, traces are a visual tool, allowing us to visualize our system to better understand the relationships between services, making it easier to investigate and pinpoint issues.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WxTBGyS---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/02/graphtimeline-1.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WxTBGyS---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/02/graphtimeline-1.svg" alt="Aspecto UI distributed tracing with OpenTelemetry diagram and timeline" width="538" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Distributed tracing visualization with Aspecto&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is Jaeger Tracing?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Now that you know what distributed tracing is, we can safely talk about Jaeger.&lt;/p&gt;

&lt;p&gt;Jaeger is an open-source distributed tracing system created by Uber back in 2015.&lt;/p&gt;

&lt;p&gt;The Jaeger data model is compatible with OpenTracing – which is a specification that defines how the collected tracing data would look, as well as libraries of implementations in different languages.&lt;/p&gt;

&lt;p&gt;(More on OpenTracing and OpenTelemetry later)&lt;/p&gt;

&lt;p&gt;As in most distributed tracing systems, Jaeger works with  &lt;strong&gt;spans and traces&lt;/strong&gt;, as defined in the OpenTracing specification.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IKwQU_5Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/01/Spans-Diagram-edited-1024x682.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IKwQU_5Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/01/Spans-Diagram-edited-1024x682.jpg" alt="Jaeger tracing works with spans and traces diagram with timeline" width="880" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A  &lt;strong&gt;span&lt;/strong&gt;  represents an action (HTTP request, call to a DB, etc) and is Jaeger’s most basic unit of work. A span must have an operation name, start time, and duration.&lt;/p&gt;

&lt;p&gt;A  &lt;strong&gt;trace&lt;/strong&gt;  is a collection/list of spans connected in a child/parent relationship (and can also be thought of as a directed acyclic graph of spans). Traces specify how requests are propagated through our services and other components.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Jaeger Tracing Architecture&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here’s what Jaeger architecture looks like&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tlHqPeug--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/02/All-Jaeger-Architecture--1024x612.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tlHqPeug--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/02/All-Jaeger-Architecture--1024x612.jpg" alt="Jaeger Architecture" width="880" height="526"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It consists of a few parts, all of which I explain below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Jaeger client:&lt;/strong&gt;  The Jaeger clients are libraries written in various programming languages, and are responsible for span creation. Note: they are being deprecated in favor of OpenTelemetry. Again, more on that later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Jaeger Agent:&lt;/strong&gt;  Jaeger agent is a network daemon that listens for spans received from the Jaeger client over UDP. It gathers batches of them and then sends them together to the collector.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Jaeger Collector:&lt;/strong&gt;  The Jaeger collector is responsible for receiving traces from the Jaeger agent and performs validations and transformations. After that, it saves them to the selected storage backends.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Storage Backends:&lt;/strong&gt;  Jaeger supports various storage backends – which store the spans &amp;amp; traces for later retrieving them. Supported storage backends are In-Memory, Cassandra, Elasticsearch, and Kafka (only as a buffer to another storage like Cassandra or elasticsearch).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Jaeger Query:&lt;/strong&gt;  This is a service responsible for retrieving traces from the jaeger storage backend and making them accessible for the jaeger UI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Jaeger UI&lt;/strong&gt;: a React application that lets you visualize the traces and analyze them. Useful for debugging system issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ingester:&lt;/strong&gt;  Ingester is relevant only if we use Kafka as a buffer between the collector and the storage backend. It is responsible for receiving data from Kafka and ingesting it into the storage backend. More info can be found in the  &lt;a href="https://www.jaegertracing.io/docs/1.30/architecture/#ingester"&gt;official Jaeger Tracing docs&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n6dUrtwC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/01/Visualizing-Traces-with-Jaeger-Tracing-1024x406.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n6dUrtwC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/01/Visualizing-Traces-with-Jaeger-Tracing-1024x406.png" alt="Jaeger UI" width="880" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Running Jaeger locally using Docker&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Jaeger comes with a ready-to-use all-in-one docker image that contains all the components necessary for Jaeger to run.&lt;/p&gt;

&lt;p&gt;It’s really simple to get it up and running on your local machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;docker&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="nx"&gt;jaeger&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="nx"&gt;COLLECTOR_ZIPKIN_HOST_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;9411&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;5775&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5775&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;udp&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;6831&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;6831&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;udp&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;6832&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;6832&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;udp&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;5778&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5778&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;16686&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;16686&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;14250&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;14250&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;14268&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;14268&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;14269&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;14269&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;9411&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;9411&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;
  &lt;span class="nx"&gt;jaegertracing&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;one&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;1.30&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you can simply open the jaeger UI on  &lt;a href="http://localhost:16686/"&gt;http://localhost:16686&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Below you’ll learn how to send data to it using  &lt;a href="https://www.aspecto.io/blog/getting-started-with-opentelemetry-python/"&gt;Python and OpenTelemetry&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Jaeger Tracing and OpenTelemetry&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Yes, you’re right. I did mention before that Jaeger’s data model is compatible with the OpenTracing specification.&lt;/p&gt;

&lt;p&gt;You may already know that OpenTracing and OpenCensus have merged to form OpenTelemetry and are wondering why does Jaeger use OpenTracing and if you can use OpenTelemetry to report to jaeger instead.&lt;/p&gt;

&lt;p&gt;As to why Jaeger uses OpenTracing – well, the reason is that Jaeger exists from before the above-mentioned merger.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To get a full understanding of OpenTelemetry, what is it, its components, and how you can use with,  &lt;a href="https://www.aspecto.io/blog/what-is-opentelemetry-the-infinitive-guide/"&gt;read this comprehensive guide&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.aspecto.io/blog/what-is-opentelemetry-the-infinitive-guide/"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CMDbVcu2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/02/What-is-OpenTelemetry_-The-Definitive-Guide-2-1.png" alt="What is OpenTelemetry? The definitive guide" width="743" height="211"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Deprecation of Jaeger Client in favor of OpenTelemetry Distro:&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;I also mentioned that Jaeger clients are now deprecating.&lt;/p&gt;

&lt;p&gt;You can find more info about this deprecation  &lt;a href="https://github.com/jaegertracing/jaeger/issues/3362"&gt;here&lt;/a&gt;, but essentially the idea is that you should now use the OpenTelemetry SDK in the programming language of your choice, alongside a Jaeger exporter.&lt;/p&gt;

&lt;p&gt;This way created spans would be converted to a format Jaeger knows how to work with, passing all the way through to the Jaeger collector and then to the storage backend.  &lt;/p&gt;

&lt;p&gt;At the time of writing this, the OpenTelemetry collector is not considered a replacement for the Jaeger collector [&lt;a href="https://github.com/jaegertracing/jaeger/milestone/12"&gt;1&lt;/a&gt;]. In the future, the idea is that the OpenTelemetry collector would be used instead of the Jaeger collector that would be deprecated [&lt;a href="https://medium.com/jaegertracing/jaeger-embraces-opentelemetry-collector-90a545cbc24"&gt;2&lt;/a&gt;].&lt;/p&gt;

&lt;p&gt;If you can’t wait and want to try using the OpenTelemetry collector with Jaeger now – see  &lt;a href="https://medium.com/jaegertracing/jaeger-embraces-opentelemetry-collector-90a545cbc24"&gt;this guide&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Jaeger Tracing Python Example&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Here is a Python example of creating spans and sending them to Jaeger. Note that you could also use automatic instrumentations and still use the Jaeger exporter (assuming you’re running Jaeger locally like shown above):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;jaeger_tracing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;py&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;opentelemetry&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;trace&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;opentelemetry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exporter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jaeger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;thrift&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;JaegerExporter&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;opentelemetry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;SERVICE_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Resource&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;opentelemetry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trace&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;TracerProvider&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;opentelemetry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;BatchSpanProcessor&lt;/span&gt;
&lt;span class="nx"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;set_tracer_provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="nx"&gt;TracerProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;SERVICE_NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-hello-service&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
   &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;jaeger_exporter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JaegerExporter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="nx"&gt;agent_host_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;agent_port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;6831&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get_tracer_provider&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;add_span_processor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="nx"&gt;BatchSpanProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jaeger_exporter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;tracer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get_tracer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rootSpan&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;childSpan&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
           &lt;span class="nx"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello world!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is what they would look like in the Jaeger UI:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--djlZykB4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/02/image3-1024x402.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--djlZykB4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/02/image3-1024x402.png" alt="Distributed tracing in Jaeger Tracing UI" width="880" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To learn how to hands-on use OpenTelemetry in Python from scratch,  &lt;a href="https://www.aspecto.io/blog/getting-started-with-opentelemetry-python/"&gt;read this guide&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Jaeger Tracing UI Review&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The jaeger UI is a powerful tool for us to debug and understand our distributed services better.&lt;/p&gt;

&lt;p&gt;Here’s what you need to know about it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The search pane:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3EY8MIx7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/02/image4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3EY8MIx7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/02/image4.png" alt="Jaeger search pane" width="479" height="728"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can use the search pane to search for traces with specific properties.&lt;/p&gt;

&lt;p&gt;Which service they come from, what operation was made, specific tags that were included within the trace (for example, the http status code), how long in the past to look for and result amount limiting.&lt;/p&gt;

&lt;p&gt;When you’re done defining your search in this pane, click on Find Traces.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The search results section:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pu4xkJx3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/02/image9-1024x492.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pu4xkJx3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/02/image9-1024x492.png" alt="Jaeger: search results section" width="880" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this example, I chose to query the jaeger-query service. I can see my traces on a timeline or as a list. Click on the desired trace to drill down into it.&lt;/p&gt;

&lt;p&gt;The specific trace view:&lt;/p&gt;

&lt;p&gt;When you find a specific trace where you think there might be an issue and click on it, you’d see a screen that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FXaK53Qd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/02/image5-1-1024x428.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FXaK53Qd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aspecto.io/wp-content/uploads/2022/02/image5-1-1024x428.png" alt="Jaeger specific trace drill down" width="880" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here you can find specific information about execution times, which calls were made and their durations, specific properties like http status code, route path (in the case of an http call), and more.&lt;/p&gt;

&lt;p&gt;Feel free to play around and investigate for yourself with your actual data.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Jaeger Tracing Advanced Concepts&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Jaeger Sampling&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Sampling is a complex aspect by itself.&lt;/p&gt;

&lt;p&gt;You should know that there are 2 places where you can make the sampling decision: at the client code (at the distro level – head-based sampling), and at the collector level (tail-based sampling).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Sampling at the Distro (SDK) level&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The deprecating Jaeger client has 4 sampling modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Remote&lt;/strong&gt;: the default, and is used to tell the Jaeger client that sampling is taken from the Jaeger backend. More on this when we talk about tail based sampling.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Constant&lt;/strong&gt;: either take all traces or take none. Nothing in between. Receives 1 for all and 0 for none&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rate limiting:&lt;/strong&gt;  choose how many traces would be sampled per second.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Probabilistic&lt;/strong&gt;: choose a percentage of traces that would be sampled, for example – choose 0.1 to have 1 of each 10 traces to be sampled.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Collector level sampling&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If we choose to enable sampling at the Jaeger collector, Jaeger supports 2 modes: file sampling and adaptive sampling.&lt;/p&gt;

&lt;p&gt;File sampling – you specify a configuration file path to the collector that contains the per-service and per-operation sampling configuration.&lt;/p&gt;

&lt;p&gt;Adaptive sampling – let Jaeger learn the amount of traffic each endpoint receives and calculate the most appropriate rate for that endpoint. Note that at the time of writing only Memory and Cassandra backends support this.&lt;/p&gt;

&lt;p&gt;More info on Jaeger sampling can be found here:  &lt;a href="https://www.jaegertracing.io/docs/1.30/sampling/"&gt;https://www.jaegertracing.io/docs/1.30/sampling/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Jaeger Tracing Production Deployment&lt;/strong&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;All-in-one or separate containers ?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Jaeger all-in-one is a pre-built Docker image containing all the Jaeger components needed to get up and running quickly with Jaeger tracing by launching a single command.&lt;/p&gt;

&lt;p&gt;A lot of people (including my past self) ask themselves what’s the correct way to launch Jaeger in production. If it’s safe to use Jaeger all-in-one in production, etc.&lt;/p&gt;

&lt;p&gt;While at the time of writing I could not find any official answer to use or not to use it, I think the right answer is – you could, but you probably shouldn’t.&lt;/p&gt;

&lt;p&gt;Using it as in production means you have a single source of failure which is not distributed.&lt;/p&gt;

&lt;p&gt;Theoretically, an issue even with the Jaeger UI might crush the entire container and you wouldn’t be able to receive critical spans created by your system.&lt;/p&gt;

&lt;p&gt;The best way to go about this would be to run each image separately, without the all-in-one.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Costs of Running Jaeger (and why you may want an alternative)&lt;/strong&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;As you can probably tell by now, Jaeger is a powerful but complex beast.&lt;/p&gt;

&lt;p&gt;Deploying and maintaining it is time-consuming and can be costly as it involves storage, compute and would usually need constant uptime for a production use case.&lt;/p&gt;

&lt;p&gt;Of course, there is no amount I could measure now and claim it fits all, as it varies depending on your cloud provider and infrastructure choices.&lt;/p&gt;

&lt;p&gt;However, you may be glad to know you don’t have to do all this on your own.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.aspecto.io/"&gt;Aspecto&lt;/a&gt;  has a free-forever tier and provides everything included in Jaeger and more. Sort of like Jaeger on steroids.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feel free to try the  &lt;a href="https://app.aspecto.io/play/search"&gt;playground&lt;/a&gt;  yourself and  &lt;a href="https://www.aspecto.io/pricing/"&gt;sign up for the free forever plan&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A quick look into Aspecto&lt;/p&gt;

&lt;p&gt;If you have any questions, feel free to reach out to me on Twitter  &lt;a href="https://twitter.com/thetomzach"&gt;@thetomzach&lt;/a&gt;, and to join our  &lt;a href="https://cloud-native.slack.com/messages/opentelemetry-bootcamp"&gt;#OpenTelemetry-Bootcamp&lt;/a&gt;  slack channel to be on top of what’s happening in observability.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Jaeger Tracing Glossary&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Span&lt;/strong&gt;  – a description of an action/operation that occurs in our system; an HTTP request or a database operation that spans over time (start at X and has a duration of Y milliseconds). Usually, it will be the parent and/or child of another span.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trace&lt;/strong&gt;  – a tree/list of spans representing the progression of requests as it is handled by the different services and components in our system. For example, sending an API call to user-service resulted in a DB query to users-db. They are ‘call-stacks’ for distributed services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observability&lt;/strong&gt;  – a measure of how well we can understand the internal states of a system based on its external outputs. When you have logs, metrics, and traces you have the “3 pillars of observability”.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenTelemetry&lt;/strong&gt;  – OpenTelemetry is an open-source tool. A collection of tools, APIs, and SDKs, led by the CNCF (Cloud Native Computing Function). OpenTelemetry enables the automated collection and generation of traces, logs, and metrics with a single specification.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenTracing&lt;/strong&gt;  – an open-source project for distributed tracing. It was deprecated and “merged” into OpenTelemetry. OpenTelemetry offers backward compatibility for OpenTracing.&lt;/p&gt;

</description>
      <category>opentelemetry</category>
      <category>devops</category>
      <category>monitoring</category>
      <category>jaeger</category>
    </item>
    <item>
      <title>Get Started with OpenTelemetry Node: A Practical Guide for Devs</title>
      <dc:creator>Michael Haberman</dc:creator>
      <pubDate>Sun, 24 Apr 2022 13:58:20 +0000</pubDate>
      <link>https://dev.to/aspecto/get-started-with-opentelemetry-node-a-practical-guide-for-devs-380g</link>
      <guid>https://dev.to/aspecto/get-started-with-opentelemetry-node-a-practical-guide-for-devs-380g</guid>
      <description>&lt;p&gt;In this guide, you will learn exactly how to get started with OpenTelemetry in Node, from scratch, and without any prior knowledge of OpenTelemetry.&lt;/p&gt;

&lt;p&gt;You will set up a simple to-do app that uses Express and MongoDB, then use OpenTelemetry to generate spans, and send them to Jaeger and Aspecto for visualization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intro to OpenTelemetry
&lt;/h2&gt;

&lt;p&gt;OpenTelemetry is an open-source project, a collection of APIs and SDKs that allows us to collect, export, and generate traces, logs, and metrics (also known as the three pillars of observability).&lt;/p&gt;

&lt;p&gt;Led by the CNCF (Cloud Native Computing Foundation, the folks responsible for Kubernetes), we use OpenTelemetry to gather data from operations and events happening in our system. In other words, it enables us to instrument our distributed services.&lt;/p&gt;

&lt;p&gt;This data ultimately helps us understand and investigate our software’s behavior and troubleshoot any performance issues and errors.&lt;/p&gt;

&lt;p&gt;OpenTelemetry serves as a single library that captures all this information under a single specification and ships it to any dedicated location (e.g., backend, collector, supporting open sources, and more). &lt;/p&gt;

&lt;p&gt;We covered OpenTelemetry in multiple posts so if you want to get a deeper understanding of the technology, including its structure, &lt;a href="https://www.aspecto.io/blog/what-is-opentelemetry-the-infinitive-guide/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=get-started-with-opentelemetry-node"&gt;visit this guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But for this OpenTelemetry in node guide, here are the terms you need to know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Span&lt;/strong&gt;: A span represents an action/operation that occurred in our system. An HTTP request or a database operation that spans over time (starts at X and has a duration of Y milliseconds). A span would usually be the parent and/or the child of another span.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trace&lt;/strong&gt;: &lt;a href="https://www.aspecto.io/blog/guide-to-distributed-tracing/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=get-started-with-opentelemetry-node"&gt;‘Call-stacks’ for distributed services&lt;/a&gt;. Traces represent a tree of spans connected in a child/parent relationship. Traces specify the progression of requests across different services and components in our app (DB, data sources, queues, etc.). For example, sending an API call to user-service resulted in a DB query to users-db.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5kW66v-B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y5xhp0doodbhwsoydvot.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5kW66v-B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y5xhp0doodbhwsoydvot.jpg" alt="Traces and spans" width="880" height="651"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Exporter&lt;/strong&gt;: Once we create a span, we need to send it to a dedicated backend. It may be in memory, Jaeger Tracing, or even as console output. The exporter handles sending the data to our backend.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Instrumentation&lt;/strong&gt; – instrumentation libraries are what allow us to gather the data and generate spans based on different libraries in our applications such as Kafka, Mongo, Express, etc. There are two ways to instrument our app – manually or automatically:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;1)  &lt;strong&gt;Auto instrumentation&lt;/strong&gt;: Automatically create spans from the application libraries we use with ready-to-use OpenTelemetry libraries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2)  &lt;strong&gt;Manual instrumentation&lt;/strong&gt;: Manually add code to your application to define the beginning and end of each span and the payload.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To understand more of the OpenTelemetry jargon, &lt;a href="https://opentelemetry.io/docs/concepts/data-sources/"&gt;visit the official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hello World: OpenTelemetry Node
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Create the tracing.js File to generate spans
&lt;/h3&gt;

&lt;p&gt;We’ll start by installing the relevant packages and then create a tracing.js file, which contains everything you need to generate spans using OpenTelemetry.&lt;/p&gt;

&lt;p&gt;1) Start with a new Node project (Node v10+ is supported by OpenTelemetry see &lt;a href="https://github.com/open-telemetry/opentelemetry-js#supported-runtimes"&gt;here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;2) Install the following packages. Since our to-do app uses Express, HTTP, and MongoDB, notice we’re installing the instrumentations –  &lt;code&gt;@opentelemetry/instrumentation&lt;/code&gt; – for all three libraries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --save @opentelemetry/api
npm install --save @opentelemetry/sdk-trace-node
npm install --save opentelemetry-instrumentation-express
npm install --save @opentelemetry/instrumentation-mongodb
npm install --save @opentelemetry/instrumentation-http

npm install --save express
npm install --save mongodb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) Create a tracing.js file with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// tracing.js


//OpenTelemetry
const { Resource } = require("@opentelemetry/resources");
const { SemanticResourceAttributes } = require("@opentelemetry/semantic-conventions");
const { ConsoleSpanExporter } = require('@opentelemetry/sdk-trace-base');
const { SimpleSpanProcessor } = require("@opentelemetry/sdk-trace-base");
const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
const { trace } = require("@opentelemetry/api");

//instrumentations
const { ExpressInstrumentation } = require("opentelemetry-instrumentation-express");
const { MongoDBInstrumentation } = require("@opentelemetry/instrumentation-mongodb");
const { HttpInstrumentation } = require("@opentelemetry/instrumentation-http");
const { registerInstrumentations } = require("@opentelemetry/instrumentation");


//Exporter
module.exports = (serviceName) =&amp;gt; {
  const exporter = new ConsoleSpanExporter();

  const provider = new NodeTracerProvider({
    resource: new Resource({
      [SemanticResourceAttributes.SERVICE_NAME]: serviceName,
    }),
  });
  provider.addSpanProcessor(new SimpleSpanProcessor(exporter));

  provider.register();

  registerInstrumentations({
    instrumentations: [
      new HttpInstrumentation(),
      new ExpressInstrumentation(),
      new MongoDBInstrumentation(),
    ],
    tracerProvider: provider,
  });

  return trace.getTracer(serviceName);
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Create our to-do service
&lt;/h3&gt;

&lt;p&gt;1) Create a file named index.js&lt;/p&gt;

&lt;p&gt;2) Add the following code to your index.js file (we also created 3 new tasks so our to-do list isn’t empty):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const tracer = require("./tracing")("todo-service");
const express = require("express");
const { MongoClient } = require("mongodb");

const app = express();
app.use(express.json());
const port = 3000;
let db;
app.get("/todo", async (req, res) =&amp;gt; {
  const todos = await db.collection("todos").find({}).toArray();
  res.send(todos);
});

app.get("/todo/:id", async (req, res) =&amp;gt; {
  const todo = await db
    .collection("todos")
    .findOne({ id: req.params.id });
  res.send(todo);
});

const startServer = () =&amp;gt; {
  MongoClient.connect("mongodb://localhost:27017", (err, client) =&amp;gt; {
    db = client.db("todo");
db.collection("todos").insertMany([
     { id: "1", title: "Buy groceries" },
     { id: "2", title: "Install Aspecto" },
     { id: "3", title: "buy my own name domain" },
   ]);
 });
  });

  app.listen(port, () =&amp;gt; {
    console.log(`Example app listening on port ${port}`);
  });
};
startServer();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) Run Mongo&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -d -p 27017:27017 mongo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4) Execute a request to the todo service&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl http://localhost:3000/todo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, we created spans and log them to our console. Now, even though our spans look absolutely gorgeous in the console, we’re here not only to log them but rather to visualize them. &lt;/p&gt;

&lt;p&gt;Our ability to visualize traces is where the true troubleshooting power of this technology comes into play. &lt;/p&gt;

&lt;p&gt;For visualization, we’ll be using the open-source Jaeger Tracing and Aspecto.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started with OpenTelemetry Node and Jaeger Tracing
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.aspecto.io/blog/jaeger-tracing-the-ultimate-guide/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=get-started-with-opentelemetry-node"&gt;Jaeger Tracing&lt;/a&gt; is a suite of open source projects managing the entire distributed tracing “stack”: client, collector, and UI. Jaeger UI is the most commonly used open-source to visualize traces. &lt;/p&gt;

&lt;p&gt;This is how it’s done:&lt;/p&gt;

&lt;h3&gt;
  
  
  Export to Jaeger
&lt;/h3&gt;

&lt;p&gt;1) Run Jaeger locally with the following docker command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -d --name jaeger \
  -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
  -p 5775:5775/udp \
  -p 6831:6831/udp \
  -p 6832:6832/udp \
  -p 5778:5778 \
  -p 16686:16686 \
  -p 14250:14250 \
  -p 14268:14268 \
  -p 14269:14269 \
  -p 9411:9411 \
  jaegertracing/all-in-one:1.32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) Install the Jaeger exporter in your node project&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --save @opentelemetry/exporter-jaeger
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To send the spans to Jaeger, in our tracing.js file, we’d use the OpenTelemetry &lt;code&gt;JaegerExporter&lt;/code&gt; instead of the &lt;code&gt;ConsoleSpanExporter&lt;/code&gt; we used before.&lt;/p&gt;

&lt;p&gt;3) We’re going to modify your tracing.js file to use the Jaeger Exporter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;const { JaegerExporter } = require(“@opentelemetry/exporter-jaeger”);&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Replace your exporter with the new imported JaegerExporter
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//OpenTelemetry
const { Resource } = require("@opentelemetry/resources");
const { SemanticResourceAttributes } = require("@opentelemetry/semantic-conventions");
const { SimpleSpanProcessor } = require("@opentelemetry/sdk-trace-base");
const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
const { trace } = require("@opentelemetry/api");

//exporter
const { JaegerExporter } = require("@opentelemetry/exporter-jaeger");

//instrumentations
const { ExpressInstrumentation } = require("opentelemetry-instrumentation-express");
const { MongoDBInstrumentation } = require("@opentelemetry/instrumentation-mongodb");
const { HttpInstrumentation } = require("@opentelemetry/instrumentation-http");
const { registerInstrumentations } = require("@opentelemetry/instrumentation");


//Exporter
module.exports = (serviceName) =&amp;gt; {
 const exporter = new JaegerExporter({
   endpoint: "http://localhost:14268/api/traces",
 });

 const provider = new NodeTracerProvider({
   resource: new Resource({
     [SemanticResourceAttributes.SERVICE_NAME]: serviceName,
   }),
 });
 provider.addSpanProcessor(new SimpleSpanProcessor(exporter));

 provider.register();

 registerInstrumentations({
   instrumentations: [
     new HttpInstrumentation(),
     new ExpressInstrumentation(),
     new MongoDBInstrumentation(),
   ],
   tracerProvider: provider,
 });

 return trace.getTracer(serviceName);
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4) Run your application and execute a few requests&lt;/p&gt;

&lt;p&gt;5) Use your browser to view Jaeger UI at  &lt;a href="http://localhost:16686/"&gt;http://localhost:16686/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;6) We can now view our trace in the Jaeger UI. Select our to-do service from the search pane on the right and click on Find Traces.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hpNyfmQs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dfo9s5b9xq86xdf0q84i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hpNyfmQs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dfo9s5b9xq86xdf0q84i.png" alt="Image description" width="880" height="88"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see more details about it (e.g., how long each operation took) which you can further investigate on your own:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t3R2AxPM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xkqb9fardbzxj32sb637.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t3R2AxPM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xkqb9fardbzxj32sb637.png" alt="Image description" width="880" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Visualization with OpenTelemetry Node and Aspecto
&lt;/h2&gt;

&lt;p&gt;So now you know the basics of spans, traces, instrumentations, and how we can use OpenTelemetry to create traces for code written in Node.&lt;/p&gt;

&lt;p&gt;You can take your visualization capabilities from Jaeger to the next level using &lt;a href="https://www.aspecto.io/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=get-started-with-opentelemetry-node"&gt;Aspecto&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Visualizing our data in Aspecto is super easy (we’ll see that in a moment), and you can try it yourself with the &lt;a href="https://www.aspecto.io/pricing/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=get-started-with-opentelemetry-node"&gt;free-forever plan&lt;/a&gt; that has no limited features. Give this &lt;a href="https://app.aspecto.io/play/search?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=get-started-with-opentelemetry-node"&gt;Live Playground&lt;/a&gt; a try to get a better idea.&lt;/p&gt;

&lt;h3&gt;
  
  
  Export to Aspecto
&lt;/h3&gt;

&lt;p&gt;Here’s how to do it:&lt;/p&gt;

&lt;p&gt;1) Create a new free account at &lt;a href="https://www.aspecto.io/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=get-started-with-opentelemetry-node"&gt;www.aspecto.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2) We’ll make two modifications to our tracing.js file (the complete file is below):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install the following packages:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --save @opentelemetry/exporter-trace-otlp-http
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Replace your existing exporter import with the exporter-trace-otlp-http:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { OTLPTraceExporter } = require("@opentelemetry/exporter-trace-otlp-http");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) Set the environment variable ASPECTO_API_KEY to the token you got from Aspecto (Settings &amp;gt; Integrations &amp;gt; Tokens)&lt;/p&gt;

&lt;p&gt;4) Configure the exporter to send data to the Aspecto endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const traceExporter = new OTLPTraceExporter({
  url: "https://collector.aspecto.io/v1/traces",
  headers: {
    Authorization: process.env.YOUR_ASPECTO_API_KEY,
  },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your tracing.js file should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//OpenTelemetry
const { Resource } = require("@opentelemetry/resources");
const { SemanticResourceAttributes } = require("@opentelemetry/semantic-conventions");
const { SimpleSpanProcessor } = require("@opentelemetry/sdk-trace-base");
const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
const { trace } = require("@opentelemetry/api");

//Exporter
const { OTLPTraceExporter } = require("@opentelemetry/exporter-trace-otlp-http");

//instrumentations
const { ExpressInstrumentation } = require("opentelemetry-instrumentation-express");
const { MongoDBInstrumentation } = require("@opentelemetry/instrumentation-mongodb");
const { HttpInstrumentation } = require("@opentelemetry/instrumentation-http");
const { registerInstrumentations } = require("@opentelemetry/instrumentation");


//Exporter
module.exports = (serviceName) =&amp;gt; {
 const exporter = new OTLPTraceExporter({
   url: "https://collector.aspecto.io/v1/traces",
   headers: {
     Authorization: process.env.ASPECTO_API_KEY,
   },
 });

 const provider = new NodeTracerProvider({
   resource: new Resource({
     [SemanticResourceAttributes.SERVICE_NAME]: serviceName,
   }),
 });
 provider.addSpanProcessor(new SimpleSpanProcessor(exporter));

 provider.register();

 registerInstrumentations({
   instrumentations: [
     new HttpInstrumentation(),
     new ExpressInstrumentation(),
     new MongoDBInstrumentation(),
   ],
   tracerProvider: provider,
 });

 return trace.getTracer(serviceName);
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should now be able to see these traces in our Aspecto account (notice, in the image below we sent many to-do requests, therefore we see multiple traces).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UCOow0EI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7z7zdbui94r4fik8eneg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UCOow0EI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7z7zdbui94r4fik8eneg.png" alt="Image description" width="880" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can use filters and textual search to quickly find any errors. You can also use secondary dimension grouping to slice tracing data into which specific services, operations, or message brokers are the troublemakers.&lt;/p&gt;

&lt;p&gt;While this is a simple to-do app, with few traces, in production, these capabilities are extremely powerful.&lt;/p&gt;

&lt;p&gt;Quick note: The “startServer” trace you see in the image was created manually,  follow the bonus section below to learn how to do that.&lt;/p&gt;

&lt;p&gt;If you drill down into the specific to-do request, we are able to also see the request’s payload (to add that, follow the bonus section below).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DuUpHsPa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wclhf204y0l47sra9nad.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DuUpHsPa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wclhf204y0l47sra9nad.png" alt="Image description" width="880" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: Creating Manual Span and Collecting Additional Data
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Manual spans
&lt;/h3&gt;

&lt;p&gt;In your to-do service file, modify the &lt;code&gt;startServer&lt;/code&gt; function to create a manual span when your todo service is started:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Manual span

const startServer = () =&amp;gt; {
 tracer.startSpan("startServer").end();
 MongoClient.connect("mongodb://localhost:27017", (err, client) =&amp;gt; {
   db = client.db("todo");
   db.collection("todos").insertMany([
     { id: "1", title: "Buy groceries" },
     { id: "2", title: "Install Aspecto" },
     { id: "3", title: "buy my own name domain" },
   ]);
 });

 app.listen(port, () =&amp;gt; {
   console.log(`Example app listening on port ${port}`);
 });
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Collecting the request and response
&lt;/h3&gt;

&lt;p&gt;To collect the http body request you need to configure the &lt;code&gt;ExpressInstrumentation&lt;/code&gt; and add it to the span, you should add this to &lt;code&gt;registerInstrumentations&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new ExpressInstrumentation({
       requestHook: (span, requestInfo) =&amp;gt; {
         span.setAttribute("http.request.body", JSON.stringify(requestInfo.req.body));
       },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s it, folks! We hope this tutorial was helpful and practical for you. &lt;a href="https://www.aspecto.io/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=get-started-with-opentelemetry-node"&gt;Reach out via chat&lt;/a&gt; to us if you have any questions.&lt;/p&gt;

&lt;p&gt;You can find the complete project in this &lt;a href="https://github.com/aspecto-io/opentelemetry-examples/tree/master/node"&gt;GitHub repository&lt;/a&gt;. We created three versions for the tracing.js file (for Aspecto, Jaeger,  and console) to make it easier to use.&lt;/p&gt;

&lt;p&gt;P.S. If you want to learn more about OpenTelemetry, watch this 6-episode video series – The OpenTelemetry &lt;a href="https://www.aspecto.io/opentelemetry-bootcamp/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=get-started-with-opentelemetry-node"&gt;Bootcamp&lt;/a&gt; (vendor-neutral and perfect for binge-watching). Use it as your OpenTelemetry roadmap, from basics to scaling, production, and security.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Episode 1: OpenTelemetry Fundamentals&lt;/li&gt;
&lt;li&gt;Episode 2: Integrate Your Code (logs, metrics, and traces)&lt;/li&gt;
&lt;li&gt;Episode 3: Deploy to Production + Collector&lt;/li&gt;
&lt;li&gt;Episode 4: Sampling and Dealing with High Volumes&lt;/li&gt;
&lt;li&gt;Episode 5: Custom Instrumentation&lt;/li&gt;
&lt;li&gt;Episode 6: Testing with OpenTelemetry&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>opentelemetry</category>
      <category>node</category>
      <category>microservices</category>
      <category>webdev</category>
    </item>
    <item>
      <title>What is OpenTelemetry and What is it Used For?</title>
      <dc:creator>Michael Haberman</dc:creator>
      <pubDate>Mon, 14 Mar 2022 16:35:29 +0000</pubDate>
      <link>https://dev.to/aspecto/what-is-opentelemetry-and-what-is-it-used-for-449f</link>
      <guid>https://dev.to/aspecto/what-is-opentelemetry-and-what-is-it-used-for-449f</guid>
      <description>&lt;p&gt;In this guide, you will learn everything you should know to get started with OpenTelemetry, why logs and metrics aren’t enough to debug our distributed services, what distributed tracing is, and why we would need it in a distributed environment.&lt;/p&gt;

&lt;p&gt;We will then take a deep dive into OpenTelemetry, its structure, deployment methods, and some best practices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As developers, one of the biggest aspects of our job is to respond quickly to incidents that occur in production and to resolve these issues as fast as possible.&lt;/p&gt;

&lt;p&gt;To be able to do that, we need to gather a lot of information fast so we can understand the full picture of what’s going on in production and tackle these incidents as soon as they arise.&lt;/p&gt;

&lt;p&gt;But with the adoption of distributed architecture, and as the number of services in many organizations increases, understanding and troubleshooting our system has become more complex.&lt;/p&gt;

&lt;p&gt;The tools we used to rely on the most – metrics and logs – fail to deliver the necessary visibility across our distributed system.&lt;/p&gt;

&lt;p&gt;This is where OpenTelemetry and distributed tracing come into play.&lt;/p&gt;

&lt;p&gt;Distributed tracing is slowly becoming a crucial tool to pinpoint, troubleshoot and solve performance issues, errors, and more in distributed systems.&lt;/p&gt;

&lt;p&gt;OpenTelemetry is currently the standard open-source for collecting distributed traces, which eventually help us solve issues in our system (more on that later).&lt;/p&gt;

&lt;h3&gt;
  
  
  Logs and Metrics Aren’t Enough to Debug our Distributed System
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Metrics
&lt;/h4&gt;

&lt;p&gt;Metrics provide us with a high-level view of our system’s health and if it behaves within our desired boundaries. &lt;/p&gt;

&lt;p&gt;Metrics are great at showing you when behavior has changed. However, since metrics are so high level, we only know which application is experiencing a change in behavior (e.g., database) and what metric was changed (e.g. high CPU). &lt;/p&gt;

&lt;p&gt;We do not have the relevant information as to why it’s happening, what the root cause is, and how we can fix it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Logs
&lt;/h4&gt;

&lt;p&gt;Logs are the trail of breadcrumbs we leave within our application to read them later and understand how the application is behaving. For example, if your application failed to write to the database, you’d be able to read that in your logs.&lt;/p&gt;

&lt;p&gt;But in distributed systems, your code is also getting distributed, and with that, your logs. You end up having a distributed trail of breadcrumbs, which is extremely difficult to follow. &lt;/p&gt;

&lt;p&gt;If logs are the story each service tells us, in a distributed system, suddenly we have 3 different stories, which makes it difficult to understand where we should investigate.&lt;br&gt;
Our ability to understand where a problem lies diminishes as we continue to distribute our applications. &lt;/p&gt;

&lt;p&gt;To be more specific, we lose the ability to correlate where an operation has started, where a simple request came from and the process it went through.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Logs&lt;/strong&gt; - Low-level data point - breadcrumbs trail we leave within our application to read them later and understand how the application behaved.&lt;br&gt;
&lt;strong&gt;Metrics&lt;/strong&gt;   - High-level data point - High-level health and behavior of our entire system; something is not working as expected from the system point of view&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So metrics are too high level and only provide us with the status of the overall system and logs (while helpful as they offer more details) are too low level and don’t provide the full picture. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is where distributed tracing makes the difference.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Distributed Tracing?
&lt;/h2&gt;

&lt;p&gt;Distributed tracing tells us what happens between different services and components and showcases their relationships. This is extremely important for distributed services architecture, where many issues are caused due to the failed communication between components.&lt;/p&gt;

&lt;p&gt;Traces specify how requests are propagated through our services. It solves a lot of the gaps that we had when we relied solely on metrics and logs&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Llz3iAcv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mmqkhih0iflmz3kc78ub.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Llz3iAcv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mmqkhih0iflmz3kc78ub.jpg" alt="OpenTelemetry spans and traces" width="880" height="651"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each trace consists of spans. A span is a description of an action/operation that occurs in our system; an HTTP request or a database operation that spans over time (start at X and has a duration of Y milliseconds). Usually, it will be the parent and/or child of another span.&lt;/p&gt;

&lt;p&gt;A trace is a tree/list of spans representing the progression of requests as it is handled by the different services and components in our system. For example, sending an API call to user-service resulted in a DB query to users-db. They are ‘call-stacks’ for distributed services.&lt;/p&gt;

&lt;p&gt;Traces tell us how long each request took, which components and services it interacted with, and the latency introduced during each step, giving you a complete picture, end-to-end.&lt;/p&gt;

&lt;p&gt;To learn more about the benefits of distributed tracing, &lt;a href="https://www.aspecto.io/blog/guide-to-distributed-tracing/?utm_source=post&amp;amp;utm_medium=dev.to&amp;amp;utm_campaign=what-is-opentelemetry-guide"&gt;read this quick article&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Distributed Traces with Jaeger Tracing
&lt;/h2&gt;

&lt;p&gt;Traces are visual instrumentation, allowing us to visualize our system to better understand the relationships between services, making it easier to investigate and pinpoint issues.&lt;/p&gt;

&lt;p&gt;Here’s what traces look like in &lt;a href="https://www.aspecto.io/blog/jaeger-tracing-the-ultimate-guide/?utm_source=post&amp;amp;utm_medium=dev.to&amp;amp;utm_campaign=what-is-opentelemetry-guide"&gt;Jaeger Tracing&lt;/a&gt; – an open-source tool that allows you to visualize traces.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qvzdzuLs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5wc2fr8yluc28qs36dgb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qvzdzuLs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5wc2fr8yluc28qs36dgb.png" alt="Jaeger tracing UI" width="880" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Root Request and Event Order
&lt;/h3&gt;

&lt;p&gt;This tracing data “tree” explains what the root request is and what was triggered by this action.  &lt;/p&gt;

&lt;p&gt;It essentially lays out the hierarchy and order in which each request was executed. &lt;/p&gt;

&lt;p&gt;Within this tree, we have a parent-child relationship for each span. Each child span happened because of its parent span.&lt;/p&gt;

&lt;p&gt;The very first request is made by the orders-service, which you can see is the very first span listed in the hierarchy. This is the “parent” span, which initiated the trace.&lt;br&gt;
The order-service then communicates with the users-service to /users/verify.&lt;/p&gt;

&lt;p&gt;Then the order-service sends an API call to stock-service to update the stock, which leads to the stock-service to run a .find operation on Mongoose.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EctACwMG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nj9ggdxp9px27nia1b69.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EctACwMG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nj9ggdxp9px27nia1b69.png" alt="Jaeger Root Request and Event Order" width="880" height="837"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Jaeger Timeline
&lt;/h3&gt;

&lt;p&gt;This visualization details how long each request took, when it started, when it ended, and what happened in parallel or in sequence. That’s super important when we’re trying to identify performance issues and optimize different operations. &lt;/p&gt;

&lt;p&gt;Each row within the timeline graph represents a span and the corresponding request listed in the hierarchy. You can now see which request took the most time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1BLN-kkn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7xtnlxr6h9mz9gof9dw8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1BLN-kkn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7xtnlxr6h9mz9gof9dw8.png" alt="Jaeger Tracing Timeline" width="880" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s important to mention that each operation is able to produce its own logs and metrics but now, by adding traces, we’re getting the full story, the full and visual context of the whole process.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Types of Telemetry
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Telemetry&lt;/strong&gt; is the data we use to monitor our applications. It’s a term meant to cover a wide range of data types, such as metrics, logs, and traces.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To be able to troubleshoot our distributed system as fast as possible, and to get complete visibility into the process happening, we want to have these three data types – logs, metrics, and traces.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Logs&lt;/strong&gt; - Breadcrumbs trail we leave within our application to read them later and understand how the application behaved.&lt;br&gt;
&lt;strong&gt;Metrics&lt;/strong&gt; - High level; Health and behavior of our entire system. Great for showing when behavior has changed.&lt;br&gt;
&lt;strong&gt;Distributed&lt;/strong&gt; &lt;strong&gt;traces&lt;/strong&gt; - The context of what and why things are happening and the story of the communication between services, allow us to visualize the progression of requests as they move throughout our system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Having a holistic view of all three gives you the visibility you need to pinpoint problems in production and solve them as fast as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is OpenTelemetry?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;OpenTelemetry is an open source project, which, among other things, allows you to collect and export traces, logs, and metrics (also known as the three pillars of observability). It is a set of APIs and SDKs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Led by the &lt;a href="https://www.cncf.io/"&gt;CNCF&lt;/a&gt; (Cloud Native Computing Foundation, same foundation responsible for Kubernetes), it enables us to instrument our distributed services, meaning, to gather data from the events that happen in our systems, which ultimately help us understand our software’s performance and behavior.&lt;/p&gt;

&lt;p&gt;OpenTelemetry is unique for 3 main reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It is an open-source.&lt;/li&gt;
&lt;li&gt;Collects the three pillars of observability (logs, metrics and traces) and acts as the glue that brings them together. &lt;/li&gt;
&lt;li&gt;One specification respected by all vendors.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;It is worth mentioning that at this time of writing, OpenTelemetry is still in its early stages, and bringing these telemetry data together is currently more of a vision than actual practice. You can follow the &lt;a href="https://opentelemetry.io/status/"&gt;project’s status here&lt;/a&gt;. We also covered this topic in our &lt;a href="https://www.aspecto.io/blog/the-future-of-observability/?utm_source=post&amp;amp;utm_medium=dev.to&amp;amp;utm_campaign=what-is-opentelemetry-guide"&gt;Future of Observability&lt;/a&gt; article.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;OpenTelemetry operates as a single library that captures all of this information, in a unified way and under a single specification, and ships it to your dedicated location (backend, collector, etc). This means that the OpenTelemetry data you collect can be distributed to many open sources (Jaeger and Prometheus) and vendors.&lt;/p&gt;

&lt;p&gt;Since its implementation for most modern programming languages, it has been growing in popularity and usage by developers everywhere since it combines all three key aspects needed for proper monitoring (leading with tracing, followed by metrics).&lt;/p&gt;

&lt;p&gt;As of today, many vendors are aligning to OpenTelemetry, which means: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You can be vendor agnostic and not tied down to a single tool or platform. &lt;/li&gt;
&lt;li&gt;We’re able to send OpenTelemetry data to different vendors and open-sources for testing and comparison purposes with only a simple configuration change.&lt;/li&gt;
&lt;li&gt;Use fewer platforms and get the most out of each one.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s go over the OpenTelemetry stack, what components you need in order to run OpenTelemetry, and how the SDK works. &lt;/p&gt;

&lt;h2&gt;
  
  
  The OpenTelemetry Stack
&lt;/h2&gt;

&lt;p&gt;Generally speaking, the OpenTelemetry stack consists of 3 layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your application (in which you’d implement the OpenTelemetry SDK)&lt;/li&gt;
&lt;li&gt;The OpenTelemetry collector &lt;/li&gt;
&lt;li&gt;A visualization layer &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aUQul-PE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qdlyb8l23joyytqaauvu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aUQul-PE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qdlyb8l23joyytqaauvu.png" alt="The OpenTelemetry Stack" width="880" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your application&lt;/strong&gt; – As soon as the SDK is implemented in your application and traffic starts to flow, data from all your services will immediately be sent to the OpenTelemetry collector. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenTelemetry collector&lt;/strong&gt; – Once gathered, you can choose to send the data to a dedicated location (in the image above, we are sending the data to a database).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Visualization&lt;/strong&gt; – You can then work with a third-party or an open-source to visualize the traces, as we did above with Jaeger.&lt;/p&gt;

&lt;p&gt;Depending on your &lt;a href="https://www.aspecto.io/blog/opentelemetry-deployment-methods-sdk-and-collector/?utm_source=post&amp;amp;utm_medium=dev.to&amp;amp;utm_campaign=what-is-opentelemetry-guide"&gt;deployment strategy&lt;/a&gt;, you may not need to use all aspects of the stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenTelemetry Deployment Strategies
&lt;/h2&gt;

&lt;p&gt;When discussing deploying OpenTelemetry, there are two components we need to consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The SDK: in charge of collecting the data.&lt;/li&gt;
&lt;li&gt;The Collector: the component responsible for how to receive, process, and export telemetry data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For each component, you can choose between the open source-only path, the vendor path, or a combination of the two.&lt;/p&gt;

&lt;p&gt;The first thing you should ask yourself is: am I going to work with a vendor or do I want to only work with open source solutions?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9gT-ZwlW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vxt6jspmnmlolls2mv2m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9gT-ZwlW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vxt6jspmnmlolls2mv2m.png" alt="OpenTelemetry deployment strategy decision tree" width="880" height="654"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Using both Vendor and Open-Source
&lt;/h3&gt;

&lt;h4&gt;
  
  
  How to collect the data (Native SDK or Distro)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Vendor’s Distro – This option basically works by using the vendor’s OpenTelemetry distribution (also called distro) to instrument your services. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;An OpenTelemetry distribution (distro)&lt;/strong&gt; is a customized version of an OpenTelemetry component. Essentially, it is an OpenTelmetry SDK with some customizations that are vendor or end-user-specific.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenTelemetry Native SDK – if you prefer not to use a vendor for this part of the process, you can use the OpenTelemetry native SDK.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Where to send the data (Collector)
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;OpenTelemetry collector&lt;/strong&gt; receives the data that is being sent by the SDK. It then processes the data and sends it to any destination you’ve configured it to, like a database or a visualization tool. &lt;/p&gt;

&lt;p&gt;If you choose to install the OpenTelemetry native SDK, you can send data directly to the vendor or send data to your own OpenTelemetry collector that then sends it to the vendor for visualization.&lt;/p&gt;

&lt;p&gt;In any case, you must install some OpenTelemetry SDK in your services. It can be either the OpenTelemetry out-of-the-box SDK or a vendor distro. &lt;/p&gt;

&lt;p&gt;Pro tip: go first with vendor neutral unless you get significant value from using the distro.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using pure Open-source
&lt;/h3&gt;

&lt;p&gt;If you choose to go with pure open-source, you will be using the native OpenTelemetry SDK, the native OpenTelemetry collector, and an open-source visualization layer, such as Jaeger. &lt;/p&gt;

&lt;p&gt;Bear in mind that if you choose this path, you’ll need to run the whole stack on your own, which will allow you to be extremely flexible but will require a lot of management and manpower. Everything you need to do to run a backend application, you will need to do here as well. &lt;/p&gt;

&lt;h3&gt;
  
  
  How does the OpenTelemetry SDK work?
&lt;/h3&gt;

&lt;p&gt;So now that you know what OpenTelemetry is, what the stack looks like, and which deployment options you have, it’s time to take a look at what’s under the hood.&lt;/p&gt;

&lt;p&gt;Let’s say your application has two services: service A and service B.  &lt;/p&gt;

&lt;p&gt;Service A sends an API call to service B and once that happens, service B writes to the database. &lt;/p&gt;

&lt;p&gt;Both services have the OpenTelemetry SDK and we’re using the OpenTelemetry collector. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_0lu9WvV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w76ob69mc6hikb2b11fl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_0lu9WvV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w76ob69mc6hikb2b11fl.png" alt="The OpenTelemetry SDK Architecture" width="880" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once service A makes an API call to Service B, Service A also sends to the collector a span that describes the call to service B, essentially letting it know that it sent an API call (making it the parent in the trace).&lt;/p&gt;

&lt;p&gt;There’s now a “parent / child” relationship between Service A and Service B (Service A being the parent of all the actions that follow). &lt;/p&gt;

&lt;p&gt;OpenTelemetry injects the details about the parent span within the API call to Service B. It uses the HTTP headers to inject the trace context (trace ID, span ID, and more)  into a custom header.&lt;/p&gt;

&lt;p&gt;Once service B receives the HTTP call, it extracts the same header. From that point, any following action Service B takes is reported as its child.&lt;/p&gt;

&lt;p&gt;All this magic happens out of the box.&lt;/p&gt;

&lt;p&gt;This action brings another key aspect of OpenTelemetry – &lt;strong&gt;context propagation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Essentially, it’s the mechanism that allows us to correlate spans across services. The context is transferred over the network using metadata such as HTTP headers.&lt;/p&gt;

&lt;p&gt;The Header will include a trace ID, which represents the sequence of HTTP calls that were performed. It will also include a span ID, which represents the event – or span – that just took place. &lt;/p&gt;

&lt;p&gt;Because service B has the SDK implemented as well, it will also send data to the collector, informing it that it has received an API call from service A (its parent).&lt;/p&gt;

&lt;p&gt;The same goes for Service B’s call to the DB. It’ll create another span with the same trace ID as the initial trace ID created by service A.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenTelemetry Auto and Manual Instrumentation
&lt;/h2&gt;

&lt;p&gt;As mentioned, OpenTelemetry allows us to instrument our system, meaning, to gather data from different libraries in our applications (e.g, AWS, SDK, Redis Client) and produce spans that represent their behavior. &lt;/p&gt;

&lt;p&gt;There are two ways spans are created – Automatic instrumentation and manual instrumentations. &lt;/p&gt;

&lt;p&gt;Here’s a quick TL;DR&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auto instrumentations – you don’t need to write code to collect spans&lt;/li&gt;
&lt;li&gt;Manual instrumentations – you do need to write code to collect spans&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Auto Instrumentation
&lt;/h3&gt;

&lt;p&gt;These are ready-to-use libraries developed by the OpenTelemetry community. They automatically create spans from your application libraries (e.g., Redis client, HTTP client). &lt;/p&gt;

&lt;p&gt;Once you send an HTTP GET using an HTTP library, the instrumentation will automatically create a new span with the corresponding data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Manual Instrumentation
&lt;/h3&gt;

&lt;p&gt;Manual instrumentation works by manually adding code to your application in order to define the beginning and end of each span as well as the payload. &lt;/p&gt;

&lt;p&gt;Pro tip: aim to use auto instrumentations – Check the OpenTelemetry &lt;a href="https://opentelemetry.io/registry/"&gt;registry&lt;/a&gt; to find all available instrumentations libraries.&lt;/p&gt;

&lt;p&gt;However, there are a few common use cases for using manual instrumentations: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unsupported auto instrumentations&lt;/strong&gt; – Not all libraries in all languages have a ready-to-use instrumentation. In that case, you want to instrument these libraries manually and even create and contribute an instrumentation on your own.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Internal libraries&lt;/strong&gt; – many organizations create their own libraries (for various reasons), which require you to create your own instrumentation. If that is the case, here’s what you can do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Take inspiration from a similar open-source that has instrumentations.&lt;/li&gt;
&lt;li&gt;Follow the OpenTelemetry specification so that your visualization tool would work as expected.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before you start working on manual instrumentation, you should know that manual instrumentations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Require good knowledge of OpenTelemetry&lt;/li&gt;
&lt;li&gt;Are time-consuming and hard to maintain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Learn hands-on how to use instrumentations in the OpenTelemetry Bootcamp: &lt;/p&gt;

&lt;p&gt;Episode 2 – &lt;a href="https://www.aspecto.io/opentelemetry-bootcamp/?utm_source=post&amp;amp;utm_medium=dev.to&amp;amp;utm_campaign=what-is-opentelemetry-guide"&gt;How to run an auto instrumentation&lt;/a&gt;&lt;br&gt;
Episode 5 – &lt;a href="https://www.aspecto.io/opentelemetry-bootcamp/?utm_source=post&amp;amp;utm_medium=dev.to&amp;amp;utm_campaign=what-is-opentelemetry-guide"&gt;How to create your own custom instrumentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Started with OpenTelemetry
&lt;/h2&gt;

&lt;p&gt;In this guide, we covered the very fundamentals of OpenTelemetry. There is a lot more to learn and understand, especially if you plan on implementing OpenTelemetry in your company.&lt;/p&gt;

&lt;p&gt;If you want to learn more, you can check out this free, 6 episodes, OpenTelemetry &lt;a href="https://www.aspecto.io/opentelemetry-bootcamp/?utm_source=post&amp;amp;utm_medium=dev.to&amp;amp;utm_campaign=what-is-opentelemetry-guide"&gt;Bootcamp&lt;/a&gt; (vendor-neutral). &lt;/p&gt;

&lt;p&gt;The Bootcamp contains live coding examples where you can follow along.&lt;/p&gt;

&lt;p&gt;It’s basically your OpenTelemetry &lt;a href="https://www.aspecto.io/opentelemetry-bootcamp/?utm_source=post&amp;amp;utm_medium=dev.to&amp;amp;utm_campaign=what-is-opentelemetry-guide"&gt;playbook&lt;/a&gt; where you will learn everything, from very hands-on basics to scaling and deploying to production:&lt;/p&gt;

&lt;p&gt;Episode 1: OpenTelemetry Intro and Basic Deployment&lt;br&gt;
Episode 2: Integrate Your Code (logs, metrics and traces)&lt;br&gt;
Episode 3: Deploy to Production + Collector&lt;br&gt;
Episode 4: Sampling and Dealing with High Volumes&lt;br&gt;
Episode 5: Custom Instrumentation&lt;br&gt;
Episode 6: Testing with OpenTelemetry&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenTelemetry Glossary
&lt;/h2&gt;

&lt;p&gt;Visit &lt;a href="https://www.aspecto.io/blog/what-is-opentelemetry-the-infinitive-guide/#opentelemetry-glossary"&gt;the guide&lt;/a&gt; for the full glossary.&lt;/p&gt;

</description>
      <category>microservices</category>
      <category>opensource</category>
      <category>webdev</category>
      <category>opentelemetry</category>
    </item>
    <item>
      <title>OpenTelemetry and Istio: Everything you need to know</title>
      <dc:creator>Michael Haberman</dc:creator>
      <pubDate>Thu, 03 Feb 2022 15:38:35 +0000</pubDate>
      <link>https://dev.to/aspecto/opentelemetry-and-istio-everything-you-need-to-know-2865</link>
      <guid>https://dev.to/aspecto/opentelemetry-and-istio-everything-you-need-to-know-2865</guid>
      <description>&lt;p&gt;There is a high correlation between R&amp;amp;D teams using Istio (k8s) and OpenTelemetry. &lt;/p&gt;

&lt;p&gt;The reason is that OpenTelemetry shines when there are a lot of services communicating with each other and the best platform to host it is k8s.&lt;/p&gt;

&lt;p&gt;Getting started with OpenTelemetry can be a challenge as it requires a lot of knowledge. Having a high entry point effort, it seems like a good idea to set up &lt;a href="https://istio.io/" rel="noopener noreferrer"&gt;Istio&lt;/a&gt; to produce distributed traces because the setup is relatively fast and simple. &lt;/p&gt;

&lt;p&gt;In this article, you will learn about the good, the bad, and the ugly of using Istio to collect traces. By the end, you will be able to decide whether using Istio is right for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenTelemetry and Istio: The Good
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Easy to set up.&lt;/li&gt;
&lt;li&gt;No code level changes are required. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because Istio is responsible for managing traffic, it can also report logs, metrics, and traces that allow visibility to Istio and the application behavior.&lt;/p&gt;

&lt;p&gt;Istio knows where each API call was originated from and where it is heading, making it easy to build inter-service communication (this comes with a caveat. More on that below).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8fsj7fbulb57798b774g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8fsj7fbulb57798b774g.png" alt="OpenTelemetry Istio, Jaeger UI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The setup is quite easy:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--set tracing.enabled=true&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now, just point it to the right destination to enable tracing and you are good to go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`--set values.global.tracer.Zipkin.address=&amp;lt;jaeger-collector-address&amp;gt;:9411`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is easy compared to the other options for collecting trace data where you need to implement an SDK within your application code. &lt;/p&gt;

&lt;p&gt;Implementing an SDK in our code is a far more complex and demeaning assignment (though it allows us to collect more data and provide more flexibility).&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenTelemetry and Istio: The Bad
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Single “hop” between services (“hop” explained below)&lt;/li&gt;
&lt;li&gt;Only allows service to service communication (No databases, messaging systems, etc.)&lt;/li&gt;
&lt;li&gt;Sampling is an issue.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Single “hop” between services
&lt;/h3&gt;

&lt;p&gt;Since there is no code running within the application to collect data, you can collect partial data with partial context. &lt;/p&gt;

&lt;p&gt;Take a look at the diagram below. When service A calls service B, Istio creates a span that represents this event. However, when service B calls service C, Istio cannot recognize that it’s the same continual trace originating from service A.&lt;/p&gt;

&lt;p&gt;It creates a new trace (which is what I meant by using the term “hop”).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhhdxa0sgwdatxkgkew9a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhhdxa0sgwdatxkgkew9a.png" alt="OpenTeoemetry Istio, Services A communicating with service B that communicating with service C, Istio creates two separate traces."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To solve this issue, you need to install OpenTelemetry SDK in each service to extract the context propagation from Istio and inject it into the downstream services. &lt;/p&gt;

&lt;p&gt;Meaning, you probably have to install OpenTelemetry to get proper traces.&lt;/p&gt;

&lt;p&gt;(Note that OpenTelemetry uses, by default, the &lt;a href="https://www.w3.org/TR/trace-context/" rel="noopener noreferrer"&gt;W3C context propagation&lt;/a&gt; specification, while Istio uses the &lt;a href="https://github.com/openzipkin/b3-propagation" rel="noopener noreferrer"&gt;B3 context propagation&lt;/a&gt; specification – this can be modified).&lt;/p&gt;

&lt;h3&gt;
  
  
  Service to service communication
&lt;/h3&gt;

&lt;p&gt;When visualizing traces with Istio, you only see HTTP and gRPC communication. Databases, cache, messaging systems are not visible. &lt;/p&gt;

&lt;h3&gt;
  
  
  Sampling
&lt;/h3&gt;

&lt;p&gt;We use traces to debug issues in production when we cannot solve them with more traditional methods like logs and metrics. These are the cases where you need more granular data, and any piece of information is crucial. &lt;/p&gt;

&lt;p&gt;Setting up Istio to collect traces is easy, but it may not solve the problem you set out to solve. &lt;/p&gt;

&lt;p&gt;100% of the organizations I met that collect traces do not collect 100% of traffic. Each and every one of them uses &lt;a href="https://www.aspecto.io/blog/opentelemetry-sampling-everything-you-need-to-know/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=opentelemetry-and-istio-everything-you-need-to-know"&gt;sampling&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Sampling is a complex issue by itself, and you need lots of tools to address it. Unfortunately, Istio only gives you the ability to set the percentage of the traffic to collect. Features like different percentiles for different HTTP routes are not supported. &lt;/p&gt;

&lt;p&gt;Here, again, OpenTelemetry comes in handy.&lt;/p&gt;

&lt;p&gt;To allow yourself to make a better decision, you might want to dig deeper into sampling with OpenTelemetry. &lt;a href="https://www.aspecto.io/blog/opentelemetry-sampling-everything-you-need-to-know/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=opentelemetry-and-istio-everything-you-need-to-know"&gt;Here is an article that covers everything you should know about sampling&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  OpenTelemetry and Istio: The Ugly
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Istio supports Jaeger and Zipkin format, but both are sunsetting for OpenTelemetry to rise.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most organizations today choose to use OTel to collect traces. This is because Jaeger (at least the collecting / SDK clients parts) is in sunset mode. The official project you should use is OpenTelemetry.&lt;/p&gt;

&lt;p&gt;If you choose to run both Istio and OpenTelemetry, as mentioned above, make sure to configure OpenTelemetry to use B3 or write dedicated code that transforms B3 to W3C (unless you’re into broken traces. You do you, we don’t judge).&lt;/p&gt;

&lt;p&gt;In any case, this is quite ugly (which makes sense since this is the ugly part of this article). &lt;/p&gt;

&lt;p&gt;I assume it is just a point time, and Istio will be able to produce traces with the correct context propagation.&lt;/p&gt;

&lt;p&gt;Btw, if at this point you feel like OpenTelemetry is for you and want to look into its different deployment strategies, &lt;a href="https://www.aspecto.io/blog/opentelemetry-deployment-methods-sdk-and-collector/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=opentelemetry-and-istio-everything-you-need-to-know"&gt;here is a super quick guide for you&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Started with OpenTelemetry
&lt;/h2&gt;

&lt;p&gt;Almost all organizations I know did try Istio’s distributed traces. However, within a short time, they either added OpenTelemetry or removed Istio for using only OpenTelemetry implemented within the code. &lt;/p&gt;

&lt;p&gt;Running both is possible but requires some configuration. It also increases the amount of data being collected – meaning a single HTTP request has spans from both Istio and OpenTelemetry SDK.&lt;/p&gt;

&lt;p&gt;Try it for yourself, and see what works best for you. &lt;/p&gt;

&lt;p&gt;P.S. If you want to learn more about OpenTelemetry, you can check out this free, 6 episodes, &lt;strong&gt;OpenTelemetry &lt;a href="https://www.aspecto.io/opentelemetry-bootcamp/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=opentelemetry-and-istio-everything-you-need-to-know"&gt;Bootcamp&lt;/a&gt;&lt;/strong&gt; (vendor-neutral). &lt;/p&gt;

&lt;p&gt;It’s basically your &lt;strong&gt;OpenTelemetry &lt;a href="https://www.aspecto.io/opentelemetry-bootcamp/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=opentelemetry-and-istio-everything-you-need-to-know"&gt;playbook&lt;/a&gt;&lt;/strong&gt; where you will learn everything, from the very basics to scaling and deploying to production:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.aspecto.io/opentelemetry-bootcamp/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=opentelemetry-and-istio-everything-you-need-to-know"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F84h2vpr27quuh1j62usd.png" alt="OpenTelemetry Bootcamp"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Episode 1: OpenTelemetry Fundamentals&lt;br&gt;
Episode 2: Integrate Your Code (logs, metrics and traces)&lt;br&gt;
Episode 3: Deploy to Production + Collector&lt;br&gt;
Episode 4: Sampling and Dealing with High Volumes&lt;br&gt;
Episode 5: Custom Instrumentation&lt;br&gt;
Episode 6: Testing with OpenTelemetry&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>microservices</category>
      <category>opensource</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>OpenTelemetry SDK and Collector Deployment Strategies</title>
      <dc:creator>Michael Haberman</dc:creator>
      <pubDate>Tue, 07 Dec 2021 17:09:55 +0000</pubDate>
      <link>https://dev.to/aspecto/opentelemetry-sdk-and-collector-deployment-strategies-5848</link>
      <guid>https://dev.to/aspecto/opentelemetry-sdk-and-collector-deployment-strategies-5848</guid>
      <description>&lt;p&gt;So you are starting your journey with &lt;a href="https://opentelemetry.io/"&gt;OpenTelemetry&lt;/a&gt;, congrats! &lt;/p&gt;

&lt;p&gt;When looking back on my OpenTelemetry journey, it wasn’t always smooth sailing. So I’m writing this to try and help.&lt;/p&gt;

&lt;p&gt;In this article, I will walk you through the different deployment methods. More specifically, what options are available for us when it comes to the SDK and Collector. &lt;/p&gt;

&lt;p&gt;By the end of reading this, you will hopefully have a clearer view of which option you should choose.&lt;/p&gt;

&lt;h1&gt;
  
  
  OpenTelemetry Components
&lt;/h1&gt;

&lt;p&gt;The first thing that you should do is get yourself familiarized with the different OpenTelemetry components and their roles. So let’s jump right into it.&lt;/p&gt;

&lt;p&gt;Similar to any full-stack system, you have a frontend, a backend, and a database. OpenTelemetry is a bit like a full-stack system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SDK / Distro – Collecting telemetry data from your services.&lt;/li&gt;
&lt;li&gt;Collector – Ingest the telemetry data and persist it.&lt;/li&gt;
&lt;li&gt;DB – Stores all the telemetry data.&lt;/li&gt;
&lt;li&gt;Visualization – UI to display traces.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your end goal is to visualize quality traces and have the ability to understand your system and troubleshoot issues. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Here is the TL;DR version of how it works:&lt;/em&gt;&lt;/strong&gt; the OpenTelemetry SDK sends data to the collector that saves the data in a database. The visualization layer then queries the DB and displays your beautiful traces. &lt;/p&gt;

&lt;p&gt;Once you have this setup, you are a happy engineer!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Vhl43WDh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/treo3tuhwv5b0tsw8hzv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Vhl43WDh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/treo3tuhwv5b0tsw8hzv.png" alt="OpenTelemetry deployment architecture" width="880" height="580"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When it comes to the visualization layer, you have two options: a vendor (like &lt;a href="https://www.aspecto.io/?utm_source=post&amp;amp;utm_medium=dev.to&amp;amp;utm_campaign=opentelemetry-deployment-methods-sdk-and-collector"&gt;Aspecto&lt;/a&gt;) or an open-source (&lt;a href="https://www.jaegertracing.io/"&gt;Jaeger&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--f3UgePRp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zojhgsusms25avw71o7o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--f3UgePRp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zojhgsusms25avw71o7o.png" alt="Aspecto trace visualization" width="880" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Deployment Methods
&lt;/h1&gt;

&lt;p&gt;When it comes to collecting and ingesting the data, there are two components in play:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;a href="https://opentelemetry.io/docs/concepts/components/#specification"&gt;SDK&lt;/a&gt;: in charge of collecting the data.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://opentelemetry.io/docs/collector/"&gt;Collector&lt;/a&gt;: the component responsible for how to receive, process, and export telemetry data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can choose between the open source-only path, the vendor path, or a combination of the two.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing a vendor / an open source-vendor combo
&lt;/h2&gt;

&lt;p&gt;There are a few ways to go about it:&lt;/p&gt;

&lt;h3&gt;
  
  
  How you collect the data (SDK / Distro)
&lt;/h3&gt;

&lt;p&gt;Vendors may tell you to install their OpenTelemetry distribution (also referred to as distro), or you can install the native out-of-the-box OpenTelemetry SDK.&lt;/p&gt;

&lt;p&gt;An OpenTelemetry distribution (&lt;a href="https://opentelemetry.io/docs/concepts/distributions/#what-is-a-distribution"&gt;distro&lt;/a&gt;) is a customized version of an OpenTelemetry component. Essentially, it is an OpenTelemetry SDK with some customizations that are vendor, or end-user-specific.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where to send the data (Collector)
&lt;/h3&gt;

&lt;p&gt;If you choose to install the OpenTelemetry native SDK, you can send data directly to the vendor or send data to your OpenTelemetry collector that then sends it to the vendor.&lt;/p&gt;

&lt;p&gt;In any case, &lt;strong&gt;&lt;em&gt;you must install some OpenTelemetry SDK&lt;/em&gt;&lt;/strong&gt; in your services. It can be either the OpenTelemetry out-of-the-box SDK or a vendor distro. &lt;/p&gt;

&lt;p&gt;Pro tip: always prefer going vendor-neutral unless you get significant value for using the vendor distro.&lt;/p&gt;

&lt;p&gt;As for the OpenTelemetry Collector, you do not have to use it. As I mentioned above, you can send data directly to the vendor from the OpenTelemetry SDK or send it to a collector you manage and from there to the vendor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choosing an open source
&lt;/h3&gt;

&lt;p&gt;If you choose the open-source-only path, you must run the whole stack yourself (i.e., OpenTelemetry SDK, Collector, Jaeger as a visualization layer). It has now become your operational responsibility (scaling, securing, DRP, etc.) – Are you ready for the challenge?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SQoID3ze--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jickjwohgmzwm6bff3vm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SQoID3ze--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jickjwohgmzwm6bff3vm.jpg" alt="OpenTelemetry deployment decision tree" width="880" height="651"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Using a Vendor Distro: Pros &amp;amp; Cons
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Pros
&lt;/h2&gt;

&lt;p&gt;The easiest and simplest way. Many times it ends with a few lines of code, and the vendor does the heavy lifting for you. You also have the vendor’s full support if anything goes wrong.     &lt;/p&gt;

&lt;h2&gt;
  
  
  Cons
&lt;/h2&gt;

&lt;p&gt;Vendor lock. If you ever decide to change vendors, make sure you have enough capacity to do so and that the new vendor’s offer is worth it because it will take plenty of time and effort.&lt;/p&gt;

&lt;h1&gt;
  
  
  Managing Collector on Your Own: Pros &amp;amp; Cons
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Pros
&lt;/h2&gt;

&lt;p&gt;This option gives you the most flexibility to decide where you want to send your data.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Cons
&lt;/h2&gt;

&lt;p&gt;Managing a collector on your own takes time and knowledge – do you have that?&lt;/p&gt;

&lt;h1&gt;
  
  
  Should You Manage an OpenTelemetry Collector?
&lt;/h1&gt;

&lt;p&gt;Here are a few tips to help you decide if you should manage a collector on your own:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The collector receives a lot of data. For every API call, DB query, cloud vendor interaction, or 3rd party API call your back end is handing, the collector will receive one event (one span in OpenTelemetry terminology). &lt;strong&gt;Are you experienced in managing data on a large scale?&lt;/strong&gt; Do you have time to learn how to optimize the OpenTelemetry Collector? If yes, consider running it yourself.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Do you have the capacity to make a disaster recovery plan for when things go sideways with the collector?&lt;/strong&gt; If the answer is no, I would suggest not running it on your own. Telemetry data becomes the data to debug/troubleshoot your production, which means it has to be available at all times.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Are you running OpenTelemetry primarily for traces, or are you interested in metrics and logs as well?&lt;/strong&gt; OpenTelemetry can collect logs, traces, and metrics. Sending them all to one backend and then distributing them to different vendors / open sources gives you a lot of power.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Running your collector allows you to ship data anywhere and change it as you see fit. For example, you can start with one vendor and flip it in a simple configuration change. You can even run multiple POCs with vendors and open sources at the same time quite easily. The price is operational.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Bottom Line
&lt;/h1&gt;

&lt;p&gt;Here is a quick summary of our deployment options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use a vendor’s distro.&lt;/li&gt;
&lt;li&gt;Use the OpenTelemetry native SDK, and send data directly to the vendor’s collector.&lt;/li&gt;
&lt;li&gt;Use an OpenTelemetry SDK, send data to your collector, and then to the collector provided by the vendor.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It all mainly comes down to whether you have the capacity and resources to set up and manage all the components on your own – in this case, you should give the open-source path a try. &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>opentelemetry</category>
      <category>opensource</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Trace-Based Testing with OpenTelemetry: Meet Open Source Malabi</title>
      <dc:creator>Michael Haberman</dc:creator>
      <pubDate>Thu, 12 Aug 2021 14:11:40 +0000</pubDate>
      <link>https://dev.to/aspecto/trace-based-testing-with-opentelemetry-meet-open-source-malabi-3d2m</link>
      <guid>https://dev.to/aspecto/trace-based-testing-with-opentelemetry-meet-open-source-malabi-3d2m</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://www.cncf.io/blog/2021/08/11/trace-based-testing-with-opentelemetry-meet-open-source-malabi/" rel="noopener noreferrer"&gt;CNCF blog&lt;/a&gt;. Written by Yuri Shkuro, creator and maintainer of &lt;a href="https://www.jaegertracing.io/" rel="noopener noreferrer"&gt;Jaeger&lt;/a&gt;, and Michael Haberman, Co-Founder &amp;amp; CTO of &lt;a href="https://www.aspecto.io/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=malabi"&gt;Aspecto&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;If you deal with distributed applications at scale, you probably use tracing. And if you use tracing data, you already realize its crucial role in understanding your system and the relationships between system components, as many software issues are caused by failed interactions between these components. &lt;/p&gt;

&lt;p&gt;Most of you reading this article are well aware of how powerful tracing is, but here’s the thing, &lt;strong&gt;you are not using tracing to its full potential&lt;/strong&gt;. Let us explain why and also introduce you to &lt;a href="https://github.com/aspecto-io/malabi" rel="noopener noreferrer"&gt;Malabi&lt;/a&gt;, the new open-source tool for trace-based testing.&lt;/p&gt;

&lt;h1&gt;
  
  
  How We Use Tracing Today
&lt;/h1&gt;

&lt;p&gt;Companies these days use tracing data for critical functions such as application performance monitoring, allowing DevOps teams and SREs to find and fix issues in production after they happen. Developers usually use it when trying to debug a complex issue that happened in production.&lt;/p&gt;

&lt;p&gt;In most cases, they are being reactive to issues, trying to understand what went wrong. But there’s another use case for tracing data: &lt;strong&gt;utilizing trace output to test and validate your application’s behavior&lt;/strong&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  What is trace-based testing
&lt;/h1&gt;

&lt;p&gt;Trace-based testing is a method that allows us to improve assertion capabilities by leveraging traces data and make it accessible while setting our expectations from a test. That enables us to validate essential relationships between software components that otherwise are put to the test only in production.&lt;/p&gt;

&lt;p&gt;Trace-based validation enables developers to become proactive to issues instead of reactive. &lt;/p&gt;

&lt;h1&gt;
  
  
  Why trace-based testing
&lt;/h1&gt;

&lt;p&gt;Trace-based testing answers the complexities of current testing strategies and techniques for distributed applications. We will later see how trace-based testing can help us overcome these challenges.&lt;/p&gt;

&lt;p&gt;To set the stage for the example that follows (and since there’s always a terminology mess around testing), we will discuss all types of tests that make actual network calls: integration tests, system tests, service tests, end-to-end tests, contract tests, API tests, you-name-it tests. Unit tests and usage of mocks are out of scope.&lt;/p&gt;

&lt;p&gt;It is also important to mention that you can leverage trace data for testing without the need to add another layer to your existing tests.&lt;/p&gt;

&lt;p&gt;Why traditional testing techniques aren’t enough &lt;br&gt;
Let’s take a look at a use case where “regular” tests fall short and what we can do to combat these challenges. We’ll use a real use case that many people can easily relate to ordering food via an app such as Uber Eats or DoorDash.&lt;br&gt;
Here is a pseudo-code representing the logic when ordering food:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// This function is called when you order food via an app
async function orderFoodDelivery(order: Order) {
 if (!valid(order)) {
   throw new Error('Bad order')
 }

 const orderDetails = await restaurantApproval(order);

 if(orderDetails.failed){
   throw new Error('Restaurant too busy')
 }

 Promise.all([
   // Send a notification to mobile app
   notifyClient(orderDetails),

   // Start delivery
   enqueueDelivery(orderDetails),

   // Call stripe and if successful upload invoice to s3.
   // if failed, put in queue for retry
   paymentAndInvoice(orderDetails),
 ]);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code flow is pretty simple. &lt;/p&gt;

&lt;p&gt;The backend gets the order details, validates them, and asks the restaurant to deliver the food. &lt;/p&gt;

&lt;p&gt;Then we need to notify the customer, find a delivery person, and charge the customer.&lt;/p&gt;

&lt;p&gt;One major concern we can all agree on is that &lt;strong&gt;getting someone else’s food could make you mad. Really mad!&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz0ka3wd5mrgd5lz9lbii.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz0ka3wd5mrgd5lz9lbii.png" alt="Got someone else's food!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We want the delivery person, customer, and restaurant to be all aligned with the order details. Making it happen with “traditional” testing techniques can be a significant hassle.&lt;/p&gt;

&lt;p&gt;Essentially, the problem is we’re not fully testing the whole process. Usually, we can only validate the response, which is, by design, limiting us from peeking into its internal workflow and does not offer the level of reliability we’re looking for when testing. &lt;/p&gt;

&lt;p&gt;For example, the fact that we got an order approval from the restaurant, doesn’t mean the delivery person received our correct address.&lt;/p&gt;
&lt;h1&gt;
  
  
  How can trace-based testing help?
&lt;/h1&gt;

&lt;p&gt;There are a few common cases in which traces are useful for testing internal workflows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When dealing with multiple components, your test needs to be familiar with different APIs and configure many clients. Testing against trace uses a single uniform API.&lt;/li&gt;
&lt;li&gt;A trace can capture a transient state which is difficult to retrieve after the workflow has finished. For instance, there may be no record anywhere other than logs that an error occurred in a given component.&lt;/li&gt;
&lt;li&gt; Each component may not even expose its internal state in sufficient detail to validate the workflow. Tracing provides us with granular data to overcome this.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Trace-based testing validates that the proper interaction between components occurs as part of the tested workflow. We want to validate there’s complete sync between the customer, restaurant, and delivery. &lt;/p&gt;

&lt;p&gt;We mentioned we’d introduce you to a new open-source for trace-based testing called Malabi. Take a look at the following integration test to see how we use Malabi to easily validate this sync:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Nothing worse than getting the wrong food.
it('Making sure the right person get the food.', ()=&amp;gt;{
 const order = {id:1, userId};
 orderFoodDelivery(order);

// Using Malabi to make sure the internal calls to Kafka and WS are synced with the same order ID.
 expect(malabi.kafka({topic:'match-delivery-order'}).toMatch(id:order.id})
 expect(malabi.websocket({room:order.userId}}).toMatch({id:order.id})
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Meet “Malabi”
&lt;/h1&gt;

&lt;p&gt;In the code example above, you can see how Malabi helps us by making traces accessible in the assertion phase while setting our expectations from the test. We can then validate the internals of the API call.&lt;/p&gt;

&lt;p&gt;Malabi is an open-source Javascript framework (still in its early days) based on OpenTelemetry that allows you to leverage trace data and improve assertion capabilities. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;(It is also a delicious milk pudding dessert made of rice, sugar, rice flour, and milk).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh38b8ntbfs1hnmhft1kj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh38b8ntbfs1hnmhft1kj.jpg" alt="This is what Malabi looks like"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Credit: @malabi_marshmallow&lt;/em&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  This is how open source Malabi works
&lt;/h1&gt;

&lt;p&gt;Let’s review the diagram below that shows what happens in Malabi when running the integration test from above. We are validating that the Order Service submitted correct messages to Kafka and to a Websocket.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz7lc4d323rpsz8lo7kzo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz7lc4d323rpsz8lo7kzo.png" alt="validating that the Order Service submitted correct messages to Kafka and to a Websocket"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Malabi lib that runs inside the Test Runner is fetching traces collected in the OpenTelemetry SDK. This is done by adding a custom exporter to OTEL SDK that stores the trace in memory (the exporter is already provided by Malabi). You can add the custom exporter by yourself, or by using the OpenTelemetry SDK we created for you (with a good amount of auto instrumentations from both &lt;a href="https://github.com/open-telemetry/opentelemetry-js-contrib" rel="noopener noreferrer"&gt;OTEL contrib&lt;/a&gt; and Aspecto’s &lt;a href="https://github.com/aspecto-io/opentelemetry-ext-js" rel="noopener noreferrer"&gt;repo&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Currently, Malabi supports validating spans created by the service under test (order service in the diagram above). For supporting downstream services and async message brokers, you will need to spin up some backend (Jaeger, OTEL Collector) and store those spans as well. That is in Malabi’s roadmap.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;By collecting the output traces of each test and serving them to the developer in the assertion phase we’re easily getting deeper visibility into the workflow’s internal processes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can check Malabi’s repo for more technical details and a demo you can run locally: &lt;a href="https://github.com/aspecto-io/malabi" rel="noopener noreferrer"&gt;https://github.com/aspecto-io/malabi&lt;/a&gt; ⭐️ &lt;/p&gt;

&lt;p&gt;A quick code example for the assertion (this is how we use it internally):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;it('happy flow - pull_request opened event is written to db and sqs', async () =&amp;gt; {
      const event = 'pull_request';
      const eventId = 'my_eventId';
      const payload = {
          action: 'opened',
          name: 'Rick',
          lastName: 'Sanchez',
          phrase: 'Wubba Lubba Dub-Dub',
          organization: {
              login: 'aspecto-io',
          },
      };
      const res = await client.post('/api/v1/webhook', payload, {
          headers: {
              [sigHeaderName]: getGithubSignature(payload),
              'X-Github-Event': event,
              'X-GitHub-Delivery': eventId,
           },
      });

      expect(res.status).toBe(200);
      const spans = await getMalabiExtract();

      // Assert DB write
      expect(spans.mongo().length).toBe(1);
      const mongo = spans.mongo().first;
      expect(mongo.hasError).toBeFalsy();
      expect(mongo.dbOperation).toBe('save');
      expect(mongo.mongoCollection).toBe('github-events');
      const doc = JSON.parse(mongo.dbResponse);
      expect(doc.payload).toEqual(payload);
      expect(doc.eventId).toEqual(eventId);
      expect(doc.type).toEqual(event);
      // Make sure timestamps are written
      expect(doc.createdAt).toBeDefined();
      expect(doc.updatedAt).toBeDefined();

      // Assert SQS Send
      expect(sqs.hasError).toBeFalsy();
      expect(spans.awsSqs().length).toBe(1);
      const sqs = spans.awsSqs().first;
      expect(sqs.rpcMethod).toBe('sendMessage');
      expect(JSON.parse(sqs.messagingPayload)).toEqual({
          type: 'git-hub-pr',
          data: payload,
      });

      const incomingHttpSpan = spans.http().incoming().first;
      expect(incomingHttpSpan.hasError).toBe(false);
  });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Getting started with Malabi
&lt;/h1&gt;

&lt;p&gt;Malabi’s installation is easy and requires only two parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An OpenTelemetry SDK is installed in the service under test.&lt;/li&gt;
&lt;li&gt;A test assertion NPM package is installed in the test runner.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Calling all early adopters and the bottom line
&lt;/h1&gt;

&lt;p&gt;It seems that there’s a whole world of opportunities to optimize and increase the effectiveness of testing – by getting more visibility into the processes running across our entire system and by leveraging available tracing data to increase test reliability and possibly preventing issues early in the development cycle.&lt;/p&gt;

&lt;p&gt;This is what Malabi is meant to do.&lt;/p&gt;

&lt;p&gt;Malabi is an early-stage open source and there is much work to be done – this is where you, brilliant people, come in!  We would love your support in this project, feel free to help in any shape or form (contribute code, knowledge, ideas, or just share it. Oh yeah! GitHub stars make traces better).&lt;/p&gt;

&lt;p&gt;If that sounds interesting, &lt;a href="https://www.aspecto.io/malabi/" rel="noopener noreferrer"&gt;leave your email&lt;/a&gt; and &lt;strong&gt;one of our developers&lt;/strong&gt;, in love with OpenTelemetry, will reach out to you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fszj835loulisut7umopw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fszj835loulisut7umopw.png" alt="Malabi logo"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>microservices</category>
      <category>opensource</category>
      <category>webdev</category>
      <category>testing</category>
    </item>
    <item>
      <title>Distributed Tracing: Build vs Buy</title>
      <dc:creator>Michael Haberman</dc:creator>
      <pubDate>Thu, 05 Aug 2021 13:57:12 +0000</pubDate>
      <link>https://dev.to/aspecto/distributed-tracing-build-vs-buy-5c8k</link>
      <guid>https://dev.to/aspecto/distributed-tracing-build-vs-buy-5c8k</guid>
      <description>&lt;p&gt;As microservices observability becomes more and more of a challenge for engineers, companies are turning to distributed tracing solutions.&lt;/p&gt;

&lt;p&gt;Now, they have to decide whether to buy or to build their distributed tracing infrastructure. While building a solution from scratch might sound compelling, and can even be supported with open source &lt;a href="https://www.jaegertracing.io/"&gt;Jaeger&lt;/a&gt; and &lt;a href="https://opentelemetry.io/"&gt;OpenTelemetry&lt;/a&gt;, buying solutions provide more features and easier usage.&lt;/p&gt;

&lt;p&gt;Let’s see how the two compare and when you should choose each one. But first, let’s remind ourselves about what distributed tracing is.&lt;/p&gt;

&lt;h1&gt;
  
  
  What is Distributed Tracing?
&lt;/h1&gt;

&lt;p&gt;Logging is a difficult and time-consuming practice, which doesn’t always provide the relevant information for solving performance issues and regressions. Traces complement logs, by presenting the relationships between services and components and tracking requests across them. It’s no surprise, then, that many developers are adding tracing to their workflows, and engineering departments are using it to reduce their MTTR.&lt;/p&gt;

&lt;p&gt;But we live in a world of microservices, and we need tracing to adapt.&lt;/p&gt;

&lt;p&gt;That’s where distributed tracing comes in. Distributed tracing is tracing that is suited for microservices. It provides observability into the microservices architecture. For example, it shows why failed integrations between components occur. These insights are used for faster troubleshooting and accelerating time-to-market.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read more about distributed tracing, its advantages, and when to use it &lt;a href="https://www.aspecto.io/blog/logging-vs-tracing-why-logs-arent-enough-to-debug-your-microservices/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=distributed-tracing-build-vs-buy"&gt;in this blog post&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Building vs. Buying Distributed Tracing
&lt;/h1&gt;

&lt;p&gt;So you’ve determined your organization needs a distributed tracing solution. Congratulations! Now you need to decide whether you’re going to build one or to buy one.&lt;/p&gt;

&lt;p&gt;Building a solution means the product is built internally, from the ground up. This includes researching the problem, designing the tracing solution with open source solutions like &lt;a href="https://www.jaegertracing.io/"&gt;Jaeger&lt;/a&gt;, using &lt;a href="https://opentelemetry.io/"&gt;OpenTelemetry&lt;/a&gt; to instrument, gather and analyze data, allocating resources to build it, and continuously maintaining it.&lt;/p&gt;

&lt;p&gt;One of the most famous examples of building an internal distributed testing solution is Netflix, whose developers wrote a &lt;a href="https://netflixtechblog.com/building-netflixs-distributed-tracing-infrastructure-bb856c319304"&gt;blog post&lt;/a&gt; about the progress they’ve made four years after they started building it.&lt;/p&gt;

&lt;p&gt;But even if you’re not Netflix, you can probably still build your own basic tracing solution.&lt;/p&gt;

&lt;p&gt;What if you need a tracing solution that can scale and provide lots of added value? Then, you might want to look into buying a solution.&lt;/p&gt;

&lt;p&gt;This means purchasing a ready-made out-of-the-box distributed tracing solution. You can either choose a managed solution, i.e a solution that manages open source (like Jaeger) for you. Or, an unmanaged solution, i.e a distributed tracing solution from a vendor, with unique features and capabilities. &lt;/p&gt;

&lt;p&gt;Whichever you choose, buying a solution enables immediate implementation, observability, and insights. &lt;/p&gt;

&lt;p&gt;Let’s dive deeper into how these two types of solutions compare.&lt;/p&gt;

&lt;h1&gt;
  
  
  Building vs. Buying Distributed Tracing – a Comparison
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1. Maintenance and Scalability
&lt;/h2&gt;

&lt;p&gt;You’re on the quest for data. Luckily, you’ve come to the right place. However, maintaining and storing all that data isn’t always easy, or easy on your resources. Scaling your tracing operation is another huge challenge.&lt;/p&gt;

&lt;p&gt;Ingesting and storing large amounts of data from many services requires a reliable infrastructure. This infrastructure should enable scaling the amount of traces collected, processing them, and storing them in a database like &lt;a href="https://www.elastic.co/"&gt;ElasticSearch&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Building a solution requires constant operation and maintenance as you scale your data collections. Buying a solution ensures your data is scalable and available, no matter the load or requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Features and Capabilities
&lt;/h2&gt;

&lt;p&gt;Built and bought solutions offer different features and capabilities. With built solutions, you can design the system according to your specific needs and requirements. On the other hand, buying solutions provide out-of-the-box capabilities that are meant to give you value beyond your basic needs. There is usually a wide variety, and they are built on industry best practices and requirements from multiple companies.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Cost
&lt;/h2&gt;

&lt;p&gt;Built solutions require many resources for configuring, designing, managing, maintaining, and operating. While this might seem more cost-effective, the truth is that when you build your own, you know where you start – but not where you’ll end up. You have to factor in the time developers need to learn and build the solution, as well as the cloud resources cost.&lt;/p&gt;

&lt;p&gt;Buying a solution, on the other hand, has a fixed cost. The price might seem higher, but everything is taken care of. You can also always start out with a free trial or free tier to make sure the solution answers your needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Security
&lt;/h2&gt;

&lt;p&gt;Security is shifting left, making it more and more the responsibility of developers. When building a distributed tracing solution, you also have to take security controls into consideration. Data privacy, reducing the attack surface and mitigating vulnerabilities have to be designed. Purchased solutions, on the other hand, do the security heavy lifting. They take security concerns off your plate and offer it out-of-the-box.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Business Focus
&lt;/h2&gt;

&lt;p&gt;Sure, your super developer powers can build anything. But should they? When developers build a distributed tracing solution internally, they’re diverted from the core business. Why not let other developers, who work at companies where distributed tracing is the core business, build the solution for you?&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Democratization and Accessibility
&lt;/h2&gt;

&lt;p&gt;When distributed tracing solutions are built internally, the engineers who built them control the knowledge and accessibility to the product. Buying a solution enables more democratization since access and training is available for all.&lt;/p&gt;

&lt;h1&gt;
  
  
  Buy vs. Build Distributed Tracing Comparison Table
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cF_1Ss8H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nsotxu1tt5fpvf2yq5zc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cF_1Ss8H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nsotxu1tt5fpvf2yq5zc.png" alt="Build vs. Built Distributed Tracing Comparison Table"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Distributed Tracing Solutions
&lt;/h1&gt;

&lt;p&gt;Unless you are a large company with available resources and unique architecture, or an individual looking to test the water of distributed tracing (in which case maybe a hybrid model would also work for you), we believe buying is the way to go. &lt;/p&gt;

&lt;p&gt;Here are some of the tools you can choose from for both building and buying a distributed tracing solution:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Open Source: Jaeger
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.jaegertracing.io/"&gt;Jaeger&lt;/a&gt; is an open-source, distributed tracing tool that you can use as the basis for building your own distributed tracing solution. You can use Jaeger for transaction monitoring, latency optimization, root cause analysis, dependency analysis, and context propagation. Jaeger can run on any infrastructure and leverage OpenTelemetry regardless of the code language.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---66FObX4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tk56upjq0xbsoyff4uan.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---66FObX4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tk56upjq0xbsoyff4uan.png" alt="Jaeger UI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Managed Distributed Tracing Solution
&lt;/h2&gt;

&lt;p&gt;If you’re into Jaeger but not into getting your hands dirty with it, managed solutions will bring the hand sanitizer and do it for you. With such solutions, you get Jaeger’s capabilities, but the vendor packs them up for you in dashboards that already provide you with insights and observability.&lt;/p&gt;

&lt;p&gt;It’s also easier to get started than having to download Jaeger and building your infrastructure from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Aspecto
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.aspecto.io/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=distributed-tracing-build-vs-buy"&gt;Aspecto&lt;/a&gt; is a distributed tracing platform with plenty of new features and a unique UI. Aspecto helps developers find, fix, and prevent distributed application issues across the entire development cycle.&lt;/p&gt;

&lt;p&gt;It’s the Chrome DevTools for your distributed applications.&lt;/p&gt;

&lt;p&gt;OpenTelemetry based, Aspecto allows developers to prevent issues before they reach production by implementing telemetry data that learns the system, then compares what they do locally to the production, staging, or other locals baseline data. You can easily &lt;a href="https://docs.aspecto.io/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=distributed-tracing-build-vs-buy"&gt;install&lt;/a&gt; Aspecto, for free, with a one-liner SDK, or give the &lt;a href="https://www.aspecto.io/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=distributed-tracing-build-vs-buy"&gt;Live Playground&lt;/a&gt; a spin.&lt;/p&gt;

&lt;p&gt;And if you’re already using the OpenTelemetry collector, you can use it to export traces to the Aspecto OTEL collector.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jIM2E51n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y86txjdcuynnr46vsvfh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jIM2E51n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y86txjdcuynnr46vsvfh.png" alt="Aspecto UI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Building your distributed tracing solution might sound like a nice challenge. Unless you’re a huge company with endless resources and unique architecture – you might be embarking on a very costly and ineffective adventure. &lt;/p&gt;

&lt;p&gt;Buying a tracing solution offers you all the capabilities and observability you need right when you get started. You can buy Jaeger-based managed solutions or get more feedback and visibility with tons of additional features through solutions like Aspecto. &lt;/p&gt;

&lt;p&gt;We recommend getting started with distributed tracing as soon as you can, no matter your choice. Make your microservices management and troubleshooting a lot more effective with tracing.&lt;/p&gt;

</description>
      <category>microservices</category>
      <category>webdev</category>
      <category>architecture</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>Logging vs Tracing: Why Logs Aren’t Enough to Debug Your Microservices</title>
      <dc:creator>Michael Haberman</dc:creator>
      <pubDate>Thu, 24 Jun 2021 15:10:51 +0000</pubDate>
      <link>https://dev.to/aspecto/logging-vs-tracing-why-logs-aren-t-enough-to-debug-your-microservices-4jgi</link>
      <guid>https://dev.to/aspecto/logging-vs-tracing-why-logs-aren-t-enough-to-debug-your-microservices-4jgi</guid>
      <description>&lt;p&gt;When debugging microservices, it can be challenging for developers to identify the root cause of issues. Not to mention how frustrating it is to search through endless logs across multiple services and the time it takes them.&lt;/p&gt;

&lt;p&gt;With all these challenges, however, there is a silver lining — distributed tracing.&lt;/p&gt;

&lt;p&gt;Distributed tracing can help your developers with tracking requests across services (but more on that later).&lt;/p&gt;

&lt;p&gt;Let’s dive into what is distributed tracing, its benefits, and the role it plays in your teams’ system. Then we’ll cover which tools developers can use to implement distributed tracing in a cloud native environment.&lt;/p&gt;

&lt;p&gt;But first, to understand where tracing fits in your microservices debugging process and why you might even need them in the first place, let’s identify the challenges that debugging with logs pose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Log Debugging Challenges
&lt;/h2&gt;

&lt;p&gt;Logs can be very useful when we are trying to understand an unexpected response or a production failure. However, logs don’t have unlimited capabilities. Here are some of the challenges they pose for your developers when they are debugging microservices:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Logging Is a Manual Time-Consuming Process
&lt;/h3&gt;

&lt;p&gt;Adding logs is not an automatic process, and it requires a lot of meticulous, manual work. Identifying all the potential information that will be needed for debugging, adding the logs, removing them if necessary – these all take a long time and require a lot of effort. Also, the process is error-prone. Developers might be spending a lot of time adding logs but will still miss the exact information they need in production.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. It’s Hard to Find the Right Balance
&lt;/h3&gt;

&lt;p&gt;Developers need to ensure they have enough logs for debugging, but not too many logs so that the code is too heavy and they waste too much time on adding and analyzing them. It’s hard to create this balance. If they haven’t logged enough information, they’ll miss data for debugging. If they logged too much, the process becomes resource-intensive and makes log analysis much more difficult.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Tracking Logs across Services Is Difficult
&lt;/h3&gt;

&lt;p&gt;Tracking and analyzing log entries across multiple services, containers, and processes is challenging. The developer has to be able to make sense of the relationship between all the different logs, which requires understanding the code flow in different services and correlating them to logs. They have to go through the process of transforming raw text (logs) into visualization in their minds.&lt;/p&gt;

&lt;p&gt;This takes a very, very long time.&lt;/p&gt;

&lt;p&gt;Even companies that have added unique identifiers to their instrumentation to enable tracking have difficulties maintaining and updating them. Not to mention ensuring all developers are up to speed about their homegrown identifier conventions.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Logs Aren’t Standardized
&lt;/h3&gt;

&lt;p&gt;Logs do not have a structured format, meaning that any developer can create messages and events according to their style. While this provides flexibility and freedom, it can be challenging and counter-productive for your team to try to understand someone else’s logs or to explain them.&lt;/p&gt;

&lt;p&gt;Also, lack of standardization leaves more room for human error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Log Debugging Fail
&lt;/h2&gt;

&lt;p&gt;As a result, logs won’t always provide the required information to solve performance and regressions. There are many solutions out there that try to overcome these challenges. These include standardization conventions, best practices, analysis tools, and more. But, maybe we need to realize that logging has its limitations and that your team needs another solution for debugging microservices.&lt;/p&gt;

&lt;p&gt;And that solution is tracing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Distributed Tracing?
&lt;/h2&gt;

&lt;p&gt;Traces complement logs. While logs provide information about what happened inside the service, distributed tracing tells you what happened between services/components and their relationships. This is extremely important for microservices, where many issues are caused due to the failed integration between components.&lt;/p&gt;

&lt;p&gt;Also, logs are a manual developer tool and can be used for any level of activity – a specific low-level detail, or a high-level action. This is also why there are many logging best practices available for developers to learn from. On the other hand, traces are generated automatically, providing the most complete understanding of the architecture.&lt;/p&gt;

&lt;p&gt;Distributed tracing is tracing that is adapted to a microservices architecture. Distributed tracing is designed to enable request tracking across autonomous services and modules, providing observability into cloud native systems.&lt;/p&gt;

&lt;p&gt;Distributed Tracing Advantages&lt;br&gt;
Where logging is bounded, distributed tracing thrives. Let’s see how distributed tracing answers logging limitations when it comes to debugging microservices.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Visualization
&lt;/h3&gt;

&lt;p&gt;Traces are visual instrumentation. As opposed to text logs, with traces, developers don’t have to imagine the communication flows and make up an image in their minds. Instead, they can see it right before their eyes. This makes it easier for developers to understand the relationships between services and to resolve issues, like performance bottlenecks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgoneupqx848snajlmsoy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgoneupqx848snajlmsoy.png" alt="Logging vs Tracing Aspecto Platform"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Automation
&lt;/h3&gt;

&lt;p&gt;Unlike logs, traces are automatic. Developers don’t have to make the manual effort of adding logs to get the complete picture. Instead, they automatically get a visualization of what happened. This also solved the standardization problem. With automated traces, the standardization is hard-coded in.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Accelerate Time-to-Market
&lt;/h3&gt;

&lt;p&gt;Distributed tracing provides observability and a clear picture of the services. This improves productivity because it enables developers to spend less time trying to locate errors and debugging them, as the answers are more clearly presented to them. As a result, productivity is increased, and developers can spend more time developing features, (or taking a break), while you accelerate time-to-market.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Tracking Requests Across Services
&lt;/h3&gt;

&lt;p&gt;Microservices interactions span multiple services. Distributed tracing enables understanding the system and the relationships between components. This is done by tracking and recording all these requests through unique IDs that are passed to the services handling them. As a result, developers can see the flow and progression of the request across the entire architecture, which is often the hardest to understand when debugging. Your team’s code quality will improve immensely.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Easy to Use and Implement
&lt;/h3&gt;

&lt;p&gt;With the right setup, developers can work with multiple applications and across different programming languages. This is unique for distributed tracing and saves your team a lot of time and headaches, by not restricting you to one language or certain apps.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Insightful
&lt;/h3&gt;

&lt;p&gt;Distributed tracing provides the developer with a lot of insightful information. This includes request time, information about components, latency, application health, and more. All this info can be useful when debugging and during root cause analysis, for improving code quality and resolving customer issues quickly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbhgwornas8q8dyvqmkr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbhgwornas8q8dyvqmkr.png" alt="Distributed tracing timeline and diagram view on Aspecto"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  When Should We Use Distributed Tracing?
&lt;/h2&gt;

&lt;p&gt;Great question! Here are the three main use cases in which distributed tracing can be helpful for you and your team.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. For a Distributed-Application Architecture
&lt;/h3&gt;

&lt;p&gt;If your department is using a distributed infrastructure, we highly recommend implementing distributed tracing. As you can see, this is the best method for tracking requests across services, with many teams involved and when you have complex processes in place.&lt;/p&gt;

&lt;p&gt;It makes sure you don’t waste your time trying to investigate issues across machines or, search through endless logs.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. When You Don’t Know Which Problem to Look for
&lt;/h3&gt;

&lt;p&gt;One of the reasons developers end up with too many logs is that they want to cover themselves and make sure they have information for all and any scenario that could go wrong. But that’s the wrong approach. This is exactly what traces are for. Traces provide you with all the heaps of information you need to analyze yourself, without the disadvantages of logs. So if you don’t know what the problem is, you can analyze until you do.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. When You Need Observability
&lt;/h3&gt;

&lt;p&gt;Distributed traces provide you with visibility into the system and across all services and the relationships between them. You can see the journey requests went through, how long they took, insights into system health, and more. You can use distributed tracing not only for identifying why a problem occurred, but also to avoid problems with ongoing observability and tracking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Distributed Tracing Tools
&lt;/h2&gt;

&lt;p&gt;Hopefully, by now you’re convinced that distributed tracing can make your life easier, or at least shorten your debugging time. To get you started, here are three tools for your team to look into. These tools use an open-source called &lt;a href="https://opentelemetry.io/" rel="noopener noreferrer"&gt;OpenTelemetry&lt;/a&gt;, an observability framework for microservices and a member of the &lt;a href="https://cncf.io/?utm_content=inline-mention" rel="noopener noreferrer"&gt;Cloud Native Computing Foundation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are the tracing tools that will complement your logging efforts, especially in a microservices architecture:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Jaeger
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.jaegertracing.io/" rel="noopener noreferrer"&gt;Jaeger&lt;/a&gt; is an open-source, distributed tracing tool. It enables transaction monitoring, latency optimization, and advanced data analysis. Jaeger supports most common languages and requires running Kubernetes. &lt;a href="https://www.jaegertracing.io/download/" rel="noopener noreferrer"&gt;You can check it out here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F44jmbbjpbu8qt09ikko8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F44jmbbjpbu8qt09ikko8.png" alt="Jaeger UI view"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Zipkin
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://zipkin.io/" rel="noopener noreferrer"&gt;Zipkin&lt;/a&gt;, an open-source tool very similar to Jaeger, and also provides all distributed tracing capabilities. For implementation, Zipkin doesn’t require containers. You can use Docker, &lt;a href="https://zipkin.io/pages/quickstart.html" rel="noopener noreferrer"&gt;but you don’t have to&lt;/a&gt;. The difference between the two is minor, and in the end, it comes to personal preferences and specific technology stack needs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fii8en4qugm0stsnp1v1y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fii8en4qugm0stsnp1v1y.png" alt="Zipkin UI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Aspecto
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.aspecto.io/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=logging-vs-tracing-why-logs-arent-enough-to-debug-your-microservices"&gt;Aspecto&lt;/a&gt; is like the Chrome DevTools for your distributed applications, helping developers find, fix, and prevent distributed application issues across the entire development cycle. Starting with their local dev environment all the way to production.&lt;/p&gt;

&lt;p&gt;Aspecto is OpenTelemetry based, and the way it allows developers to prevent issues before they reach production is by implementing telemetry data that learns the system, then compares what they do locally to the production, staging, or other locals baseline data.&lt;/p&gt;

&lt;p&gt;This helps you to validate changes and prevent issues, live, while you develop.&lt;/p&gt;

&lt;p&gt;It’s super easy to &lt;a href="https://docs.aspecto.io/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=logging-vs-tracing-why-logs-arent-enough-to-debug-your-microservices"&gt;install&lt;/a&gt; with a one-liner SDK, and you can give the &lt;a href="https://www.aspecto.io/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=logging-vs-tracing-why-logs-arent-enough-to-debug-your-microservices"&gt;Live Playground&lt;/a&gt; a spin.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjrpirzyol4qbpr4427dd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjrpirzyol4qbpr4427dd.png" alt="Aspecto Observability Platform"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Debugging with logs can only get you so far. By implementing distributed tracing, you can see your requests and services, and spend less time debugging. Try distributed tracing with an open-source tool, like &lt;a href="https://www.jaegertracing.io/" rel="noopener noreferrer"&gt;Jaeger&lt;/a&gt; or &lt;a href="https://zipkin.io/" rel="noopener noreferrer"&gt;Zipkin&lt;/a&gt; and if you’re looking for that extra boost of predicting the effects of your changes, give &lt;a href="https://www.aspecto.io/?utm_source=dev.to&amp;amp;utm_medium=post&amp;amp;utm_campaign=logging-vs-tracing-why-logs-arent-enough-to-debug-your-microservices"&gt;Aspecto&lt;/a&gt; a try, for faster feedback and more visibility.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>microservices</category>
      <category>debugging</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Choosing a Message Broker: Kafka vs RabbitMQ vs AWS SQS/SNS</title>
      <dc:creator>Michael Haberman</dc:creator>
      <pubDate>Tue, 01 Jun 2021 15:05:10 +0000</pubDate>
      <link>https://dev.to/aspecto/choosing-a-message-broker-kafka-vs-rabbitmq-vs-aws-sqs-sns-20na</link>
      <guid>https://dev.to/aspecto/choosing-a-message-broker-kafka-vs-rabbitmq-vs-aws-sqs-sns-20na</guid>
      <description>&lt;p&gt;Microservice applications rely heavily on messaging and asynchronous communications to keep everything working smoothly. &lt;/p&gt;

&lt;p&gt;Choosing the right message broker is one of the first critical choices you must make when developing the services that need to communicate with each other. &lt;/p&gt;

&lt;p&gt;Making the “right” choice can be a battle of features and edge cases that can be challenging to differentiate between. &lt;/p&gt;

&lt;p&gt;In this article, I’ll help provide a bit of guiding light by providing an overview of a few of the more well-known message brokers – Kafka, RabbitMQ, and AWS SQS/SNS. &lt;/p&gt;

&lt;p&gt;I’ll explore the driving forces behind them, the general messaging model they follow and do my best to provide some guidance on selecting the broker that is right for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apache Kafka
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://kafka.apache.org/" rel="noopener noreferrer"&gt;Kafka&lt;/a&gt; is an open-source message broker developed and maintained primarily by the &lt;a href="https://www.apache.org/foundation/" rel="noopener noreferrer"&gt;Apache Software Foundation&lt;/a&gt;, with the assistance of the open-source community.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Focus on streamable content, working with large data streams&lt;/li&gt;
&lt;li&gt;Message persistence and reprocessing capabilities are core features&lt;/li&gt;
&lt;li&gt;On-site hosting with third party options&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kafka provides optimized stream-based processing of events, with a publish/subscribe model driving the communications between consumers. &lt;/p&gt;

&lt;p&gt;These events can be subdivided into topics, allowing for greater organization of your distributed application’s communication patterns, and are partitioned onto multiple servers within a cluster, allowing for a resilient and highly performant message delivery system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technical Details and Deployment
&lt;/h3&gt;

&lt;p&gt;Apache &lt;a href="https://kafka.apache.org/documentation/streams/" rel="noopener noreferrer"&gt;provides SDKs&lt;/a&gt; in several different languages.&lt;/p&gt;

&lt;p&gt;Kafka is designed to be deployed on-site, within your own application’s architecture. This can be on a set of stand-alone servers, a virtual machine, or a Docker container.&lt;/p&gt;

&lt;p&gt;Multiple vendors offer Kafka hosting as a service, such as &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/kafka-hosting.html" rel="noopener noreferrer"&gt;AWS&lt;/a&gt;, &lt;a href="https://www.cloudkarafka.com/" rel="noopener noreferrer"&gt;CloudKarafka&lt;/a&gt;, and &lt;a href="https://aiven.io/kafka" rel="noopener noreferrer"&gt;Aiven&lt;/a&gt;, or in virtual machines.&lt;/p&gt;

&lt;p&gt;Below is some sample JS code for &lt;a href="https://kafka.js.org/docs/getting-started" rel="noopener noreferrer"&gt;getting started&lt;/a&gt; with Apache Kafka events:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { Kafka } = require('kafkajs')
const kafka = new Kafka({
 clientId: 'my-app',
 brokers: ['localhost:9092']
})

// this produces a message
async function produce() {
 const producer = kafka.producer()
 await producer.connect()
 await producer.send({
   topic: 'TOPIC_NAME',
   messages: [
     { key: 'key1', value: 'hello world' },
   ],
 })
}

async function consume() {
 const consumer = kafka.consumer({ groupId: 'my-group' })
 await consumer.connect()
 await consumer.subscribe({ topic: 'TOPIC_NAME' })
 await consumer.run({
   eachMessage: async ({ topic, partition, message }) =&amp;gt; {
     console.log({
       key: message.key.toString(),
       value: message.value.toString(),
       headers: message.headers,
     })
   },
 })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Strengths &amp;amp; Weaknesses
&lt;/h3&gt;

&lt;p&gt;Kafka has a high focus on data stream throughput, something that shows in their &lt;a href="https://www.confluent.io/blog/kafka-fastest-messaging-system/" rel="noopener noreferrer"&gt;performance statistics&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This focus on processing streams of data results in a system with high throughput, allowing for complex processing of large data streams. &lt;/p&gt;

&lt;p&gt;Kafka’s routing capabilities for those streams of data are relatively limited when compared to other message brokers – a gap that is continually getting smaller as these products improve. &lt;/p&gt;

&lt;p&gt;To summarize, Kafka is a powerful solution that can provide robust and fault-tolerant high-performance message streaming, letting you confidently drive your application’s behavior.&lt;/p&gt;

&lt;p&gt;Depending on your bandwidth and resources, you can abstract away as much or as little of the hosting as you feel comfortable, making Kafka a solid choice that will scale with your traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  RabbitMQ
&lt;/h2&gt;

&lt;p&gt;Like Kafka, &lt;a href="https://www.rabbitmq.com/" rel="noopener noreferrer"&gt;RabbitMQ&lt;/a&gt; is another open-source message broker. Originally developed by Rabbit Technologies, the technology has through a series of acquisitions ended up under the ownership of VMWare.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Focus on messaging-based communication, with support for large data streams&lt;/li&gt;
&lt;li&gt;Complex routing capabilities provided as a core feature&lt;/li&gt;
&lt;li&gt;On-site hosting with third party options&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RabbitMQ uses the publish/subscribe model as well, sending message objects in their binary form to different named queues, which can be dynamically created and destroyed. &lt;/p&gt;

&lt;p&gt;RabbitMQ is designed to operate both in isolation and as part of a cluster, providing enough configurable power to drive any set of redundancy or data safety needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technical Details and Deployment
&lt;/h3&gt;

&lt;p&gt;RabbitMQ provides several &lt;a href="https://www.rabbitmq.com/devtools.html" rel="noopener noreferrer"&gt;client libraries&lt;/a&gt; in a wide variety of languages.&lt;/p&gt;

&lt;p&gt;It can be deployed on-site, on anything from a full server to a container, or on one of several cloud providers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.stackhero.io/en/services/RabbitMQ" rel="noopener noreferrer"&gt;Stackhero&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cloudamqp.com/" rel="noopener noreferrer"&gt;CloudAMQP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.compose.com/databases/rabbitmq" rel="noopener noreferrer"&gt;Compose&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following sample code, written in Node.js using the AMQPLIB package, should provide a small sample of what it might be like to work with RabbitMQ:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const amqp = require('amqplib/callback_api');
amqp.connect('amqp://localhost', function(error0, connection) {
 if (error0) {
   throw error0;
 }
 connection.createChannel(function(error1, channel) {
   if (error1) {
     throw error1;
   }
   const queue = 'hello-queue';
   const msg = 'Hello world!';

   channel.assertQueue(queue, {
     durable: false
   });

   // Sending message to queue
   channel.sendToQueue(queue, Buffer.from(msg));
   console.log("Sent message", msg);

   // Consuming messages
   channel.consume(queue, function(msg) {
     console.log("Received message", msg.content.toString());
   }, { noAck: true });
 });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Strengths &amp;amp; Weaknesses
&lt;/h3&gt;

&lt;p&gt;RabbitMQ has the power to handle workloads of nearly any size, and can effectively scale alongside your application as your user base grows.&lt;/p&gt;

&lt;p&gt;With a  focus on messaging-based delivery and complex routing scenarios, RabbitMQ is extremely adaptable to any application architecture. &lt;/p&gt;

&lt;p&gt;While originally there was no great support for data stream processing, and messages were generally only processed once without the capacity to reprocess a stream, both of these gaps have closed as RabbitMQ has continued to grow. &lt;/p&gt;

&lt;p&gt;With the ability to take ownership of the things you want and outsource the rest, RabbitMQ can fit into any appropriate role in your application’s infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Amazon Web Services SQS/SNS
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/sns/" rel="noopener noreferrer"&gt;SNS&lt;/a&gt; and &lt;a href="https://aws.amazon.com/sqs/" rel="noopener noreferrer"&gt;SQS&lt;/a&gt; represent two different ways of looking at distributed messaging.&lt;/p&gt;

&lt;p&gt;SNS is highly focused on message delivery, offering a publish-subscribe model to quickly distribute a message to an array of clients (e.g., mobile devices, HTTPS endpoints, other AWS services).&lt;/p&gt;

&lt;p&gt;SQS, by comparison, is focused on the successful delivery and processing of messages by individual clients.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Two products allowing for both broadcast messaging and pub/sub&lt;/li&gt;
&lt;li&gt;Quick setup and configuration using AWS&lt;/li&gt;
&lt;li&gt;No hosting available outside of AWS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While SNS will broadcast the same message to an array of recipients, SQS will distribute queue messages to single subscribers for processing.&lt;/p&gt;

&lt;p&gt;SNS takes a push-based approach to notifications, allowing for automated responses to notification activity, while SQS tends to focus more on a polling-style mechanism with some additional event-driven functionality supported.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technical Details
&lt;/h3&gt;

&lt;p&gt;AWS provides a general SDK with access to most AWS services (SQS and SNS included) in &lt;a href="https://aws.amazon.com/tools/" rel="noopener noreferrer"&gt;several popular languages&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The below sample code uses the AWS SDK to demonstrate the process of working with SNS and SQS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SNS - publish
const AWS = require('aws-sdk');
AWS.config.update({ region: 'REGION' });

const publishParams = {
 Message: 'MESSAGE_TEXT',
 TopicArn: 'TOPIC_ARN'
};

const publishTextPromise = new AWS.SNS({ apiVersion: '2010-03-31' }).publish(publishParams).promise();

publishTextPromise.then(
 function(data) {
   console.log(`Message ${publishParams.Message} sent to topic ${publishParams.TopicArn}`);
 }).catch(
 function(err) {
   console.error(err, err.stack);
 });

// SNS - Subscribe
const subscribeParams = {
 TopicArn : 'TOPIC_ARN'
}
const subscribePromise = new AWS.SNS({ apiVersion: '2010-03-31' }).listSubscriptionsByTopic(subscribeParams).promise();
subscribePromise.then(
 function(data) {
   console.log(data);
 }).catch(
 function(err) {
   console.error(err, err.stack);
 }
);

// SQS - send
const sqs = new AWS.SQS({ apiVersion: '2012-11-05' });
const queueURL = "SQS_QUEUE_URL";

const sendParams = {
 DelaySeconds: 10,
 MessageAttributes: {
   "Title": {
     DataType: "String",
     StringValue: "Some String"
   }
 },
 MessageBody: "Something!",
 QueueUrl: queueURL
};

sqs.sendMessage(sendParams, function(err, data) {
 if (err) {
   console.log("Error sending to SQS", err);
 } else {
   console.log("Success sending to SQS", data.MessageId);
 }
});

// SQS - receive
const receiveParams = {
 AttributeNames: [
   "SentTimestamp"
 ],
 MaxNumberOfMessages: 10,
 MessageAttributeNames: [
   "All"
 ],
 QueueUrl: queueURL,
 VisibilityTimeout: 20,
 WaitTimeSeconds: 0
};

sqs.receiveMessage(receiveParams, function(err, data) {
 if (err) {
   console.log("Receive Error", err);
 } else if (data.Messages) {
   console.log("Received messages:", JSON.stringify(data.Messages))
 }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Strengths &amp;amp; Weaknesses
&lt;/h3&gt;

&lt;p&gt;AWS SQS and SNS, together, can be used to build the backbone of a highly scalable, highly resilient distributed application.&lt;/p&gt;

&lt;p&gt;With ties into many other AWS services (such as AWS Lambda), these two tools can help you easily grow your application’s communications, while providing you with all the tools you need to manage the underlying complexities of the service interactions. &lt;/p&gt;

&lt;p&gt;If your web application is already running on AWS, set-up time is effectively zero, with much less complexity than many of the other systems. This does potentially come at the expense of a larger AWS bill as the number of messages grows. &lt;/p&gt;

&lt;p&gt;While Kafka and RabbitMQ don’t provide a default message size limit, AWS provides some limits around SQS and SNS messages – converting the messages into S3 objects after they reach a certain size.&lt;/p&gt;

&lt;p&gt;We published a &lt;a href="https://www.aspecto.io/blog/how-to-send-large-sqs-sns-messages-with-node-js/" rel="noopener noreferrer"&gt;detailed article on how you can overcome this size limit&lt;/a&gt; – I highly recommend browsing it to get a feel for how SQS in particular manages large messages.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;[You can find our SQS/SNS Producer/Consumer Library inside. It provides an ability to pass payloads through s3]&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;SQS and SNS being cloud-first do add the additional complication of being vendor-locked to a specific service, whereas other message brokers resolve this by providing local installation and maintenance capabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing The Right Message Broker
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb5osbyu9grpfnvcy9psr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb5osbyu9grpfnvcy9psr.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Generally speaking, there are two considerations you should be thinking about when choosing a broker:&lt;/p&gt;

&lt;h3&gt;
  
  
  Consideration #1: The Type of Messages You Send
&lt;/h3&gt;

&lt;p&gt;The first step to choosing a message broker is determining what messages you’ll be sending, and what their general format will be. &lt;/p&gt;

&lt;p&gt;The characteristics of these messages will drive the questions that need to be asked about each platform’s offering, though most will be roughly equivalent in terms of feature set – meaning that at a general level, every solution listed above supports the functionality needed to serve as a message broker for a scalable distributed application. &lt;/p&gt;

&lt;p&gt;From a pure feature perspective, one solution is as good as another.&lt;/p&gt;

&lt;h3&gt;
  
  
  Consideration #2: Your Daily Work and Applications’ Infrastructure
&lt;/h3&gt;

&lt;p&gt;This is where secondary considerations come into play. Think about your day-to-day work and systems and ask yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are you building an application exclusively in AWS? Maybe SQS and SNS make the most sense for establishing your inter-service communication&lt;/li&gt;
&lt;li&gt;Are you more interested in writing your application than maintaining the piped data between its components? Then a third-party hosted solution might be the best option for allowing you to focus on your strengths while growing your codebase&lt;/li&gt;
&lt;li&gt;Are you highly focused on speed of delivery and minimal latency? Then Kinesis might be right up your alley (we’ll go over Kinesis on another article, so stay tuned), while an application more focused on verified delivery and redundancy might choose a different technology.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The requirements of your application’s infrastructure and behavioral patterns will drive the choice at this level.&lt;/p&gt;

&lt;p&gt;With the above taken into the account, and with the caveat that it is hard – and somewhat unfair – to reduce these large tech products down to a couple of lines of recommendations, &lt;strong&gt;here are some guidelines on choosing the right message broker:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you care about message retention and being able to easily re-process data, Kafka is likely your best option&lt;/li&gt;
&lt;li&gt;If you are more concerned with being able to maintain and implement a complex set of routing rules, RabbitMQ is likely your best choice&lt;/li&gt;
&lt;li&gt;If you’re a small startup looking to get up and running quickly, with minimal overhead, AWS SQS/SNS is a great option given its quick setup and cost structure&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting End-to-end Visibility Into a Message Journey
&lt;/h2&gt;

&lt;p&gt;One element you’ll want to evaluate is how to best maintain the final product. Once your application is sending messages, how do we track things down when they go wrong? &lt;/p&gt;

&lt;p&gt;&lt;a href="https://opentelemetry.io/" rel="noopener noreferrer"&gt;OpenTelemetry&lt;/a&gt; is a set of SDKs and tools that can be used to set up observability for your distributed application, providing you the means to troubleshoot the distributed messaging driving your application when things go wrong.&lt;/p&gt;

&lt;p&gt;Here’s a quick &lt;a href="https://www.aspecto.io/blog/how-to-achieve-end-to-end-microservices-visibility-in-asyn-messaging-with-opentelemetry/" rel="noopener noreferrer"&gt;step-by-step guide on implementing OpenTelemetry in your distributed applications&lt;/a&gt;, allowing you to achieve end-to-end visibility into a message journey. The guide demonstrates how to do so with Kafka as the message broker.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;[PS: grab your OpenTelemetry Kafkajs Instrumentation for Node.js inside.]&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;If you’re building an application that is “distributed” in any way, odds are you’ll need to handle asynchronous communications between components of your application at some point. &lt;/p&gt;

&lt;p&gt;Messages – and the brokers that deliver them – will play a critical role in the infrastructure driving your application. &lt;/p&gt;

&lt;p&gt;The above summary is by no means exhaustive – I would probably need another thousand words just to make a solid start at capturing the complete message broker landscape – but should hopefully provide some quality information that you can use when making your decision. &lt;/p&gt;

&lt;p&gt;The key is to fully understand the requirements for your application, and how those requirements fit into the capabilities of the message brokers you are evaluating.&lt;/p&gt;

&lt;p&gt;There is ultimately no “wrong” answer to which message broker you choose, but hopefully, the above information helps point you in the right direction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;About Aspecto

Aspecto is an OpenTelemetry-based troubleshooting platform 
that helps developers prevent distributed application 
issues from their local dev environment, 
and across the entire development cycle.

You can think of it as the 
Chrome DevTools for your distributed applications.

Aspecto is used for detecting and 
troubleshooting microservices-based distributed systems, 
and preventing software failures before deployment.

Visit us at Aspecto.io
for more microservices tutorials
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>tutorial</category>
      <category>webdev</category>
      <category>aws</category>
      <category>microservices</category>
    </item>
  </channel>
</rss>
