<?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: austin</title>
    <description>The latest articles on DEV Community by austin (@austinlparker).</description>
    <link>https://dev.to/austinlparker</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%2F28770%2Ff823abbc-7e83-4a41-bd97-5658d2acf6e7.png</url>
      <title>DEV Community: austin</title>
      <link>https://dev.to/austinlparker</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/austinlparker"/>
    <language>en</language>
    <item>
      <title>Ask Austin: Putting The IR into ObseRvabIlity</title>
      <dc:creator>austin</dc:creator>
      <pubDate>Fri, 29 Apr 2022 14:17:48 +0000</pubDate>
      <link>https://dev.to/lightstep/ask-austin-putting-the-ir-into-observability-h9b</link>
      <guid>https://dev.to/lightstep/ask-austin-putting-the-ir-into-observability-h9b</guid>
      <description>&lt;p&gt;If you've listened to me on a podcast, or spoken to me online, you might have heard me claim that 'observability is the foundation that you build on.' What does that mean, though? In this edition of 'Ask Austin,' I'll share some insights I’ve picked up talking to our customers about how observability is a foundational component of DevOps practices, such as incident response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Schrodinger's U+1F408
&lt;/h2&gt;

&lt;p&gt;The most crucial currency of a startup is time — you're under immense pressure to ship features and write reliable, resilient code. Into every life a little rain must fall, though, and failure seems inevitable. Some people might tell you that failure can be boxed away where it won't ever happen, but it really is impossible to create something perfect. We have to understand, tolerate, and ameliorate failure rather than shy away from it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://lightstep.com/observability-101"&gt;Observability&lt;/a&gt; is absolutely critical to this effort. Without observability, you can't quantify failure and know how to prioritize it. More specifically, without observability, you aren't able to connect system state to business outcomes using SLO's, which means you lack the ability to understand failure in the context of what you're actually trying to &lt;em&gt;do&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This is where things get tricky, though — [your SLO's tell you when performance is impacting users (&lt;a href="https://go.lightstep.com/register-the-secret-to-high-quality-slos-on-demand-webinar.html"&gt;https://go.lightstep.com/register-the-secret-to-high-quality-slos-on-demand-webinar.html&lt;/a&gt;), but how do you start to tolerate, and ameliorate that failure? This is where observability alone can't help — you need to adopt best practices and tools that can aid in communication and recovery efforts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a Culture of Resiliency
&lt;/h2&gt;

&lt;p&gt;In order to respond and mitigate failure in your system, it's not enough to be aware of it. Your ability to respond starts before failures even happen, though — before you even write the first line of code in a service. You need to start out the way you intend to keep going. I'm going to list three strategies you should adopt in order to build a culture of resiliency in your engineering team:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Monitoring and observability should be 'day zero' problems.  Ensure that every new feature or service has appropriate tracing, metrics, and logging telemetry being output and captured through &lt;a href="https://opentelemetry.io"&gt;OpenTelemetry&lt;/a&gt; tools, and test these telemetry outputs as part of your CI/CD process.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Effective incident response relies on documentation and communication. Create a defined process for building playbooks that include links to dashboards, explanations of alerts and SLOs, and service ownership. Keep them up to date as part of your recurring engineering workload — especially as the team grows or contracts. If you're writing everything down as you go along, you're more resilient to unexpected changes in team composition.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Understand alerts before you build them. Ideally, you should only be alerting off of SLOs for application behavior! I've seen too many people get burnt out by blindly setting up alerts for their infrastructure resources and spend hours or days chasing down failures that didn't actually matter that much to their customers. Alerts need tending and grooming, just like anything else — you can try it yourself with your existing alerts. Spend a week on-call noting if a triggered alert actually impacted the customer experience in some way.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Incidents Happen
&lt;/h2&gt;

&lt;p&gt;It's inevitable that things will break, so we need to build systems that tolerate failure. Observability and incident response tools are helpful in this quest. Observability tells you where, and why, a failure occurred. Incident response tools help you manage and respond to those failures. The real question you need to ask yourself is how much time you want to spend on the management of your observability and incident response platforms.&lt;/p&gt;

&lt;p&gt;While tools such as &lt;a href="https://prometheus.io"&gt;Prometheus&lt;/a&gt; and &lt;a href="https://upptime.js.org"&gt;Upptime&lt;/a&gt; will let you monitor, alert, and communicate incidents to your users, there's a lot more than just tracking metrics and pinging your page to really understand your system. The cost of stitching together your own bespoke solution in terms of time alone can be weeks or months, which can strain already over-extended engineers. Moreover, wouldn't you rather be working on things that provide value to your end-users rather than spinning up &lt;a href="https://grafana.com"&gt;Grafana&lt;/a&gt; again?&lt;/p&gt;

&lt;p&gt;Traditional paid services are an option, but many of them incur a management cost. Seat-based pricing that controls how many people can access the tool is a pain, especially since most companies make single-sign on an 'enterprise' feature. This isn't universally true — Lightstep has no limits on the amount of users of our tools, because your data is useful to all of your engineers, not just a few. If you’re already feeling underwater on managing alert fatigue, &lt;a href="https://lightstep.com/incident-response/signup"&gt;try us out&lt;/a&gt; and see the difference for yourself.&lt;/p&gt;

&lt;p&gt;If this all seems overwhelming, I understand — especially if you're at a smaller company or on a smaller team, some of this probably feels like stuff you haven't started to really deal with in earnest yet. The best advice I can give you is to find your peers and build community with them. All of the advice in the world is great, but finding someone that's been where you are and can help direct you along the way is even better. If you're interested in being a part of ours, you can join us on our &lt;a href="https://ltstp.run/discord"&gt;Discord&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hope this answered a few of y'alls questions about observability and incident response. If you've got more burning questions, feel free to hit me up on our Discord server or on &lt;a href="https://twitter.com/austinlparker"&gt;Twitter&lt;/a&gt; and I'll answer them next time!&lt;/p&gt;

</description>
      <category>observability</category>
      <category>oncall</category>
      <category>devops</category>
      <category>tooling</category>
    </item>
    <item>
      <title>OpenTelemetry Python: All you need to know</title>
      <dc:creator>austin</dc:creator>
      <pubDate>Thu, 03 Dec 2020 14:13:48 +0000</pubDate>
      <link>https://dev.to/lightstep/opentelemetry-python-all-you-need-to-know-3p67</link>
      <guid>https://dev.to/lightstep/opentelemetry-python-all-you-need-to-know-3p67</guid>
      <description>&lt;p&gt;Hi all, &lt;a href="https://github.com/tedsuo/"&gt;tedsuo&lt;/a&gt; back again, dropping a knowledge bomb and a bunch of stale-yet-crunchy pop culture references. Last week we covered Node; this week we are going to dive into Python.&lt;br&gt;
If you crack open OpenTelemetry, you’ll quickly discover that &lt;em&gt;there’s a lot there&lt;/em&gt;. But, as a developer applying OpenTelemtry to your application, 99% of what’s in there doesn’t matter.&lt;/p&gt;
&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;All you need to know is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Initialization:&lt;/strong&gt; How to start and shutdown cleanly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tracer methods:&lt;/strong&gt; get_tracer, get_current_span, startSpan, and withSpan.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Span methods:&lt;/strong&gt; setAttribute, addEvent, recordException, setStatus, and end.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Seriously, that’s it. If you want to try it out, follow the guide below. A heavily commented version of the finished tutorial can be found at &lt;a href="https://github.com/tedsuo/otel-node-basics"&gt;https://github.com/tedsuo/otel-python-basics&lt;/a&gt;, please use it as a reference when you get started with instrumenting your own application.&lt;/p&gt;
&lt;h2&gt;
  
  
  Python: Off to Docker
&lt;/h2&gt;

&lt;p&gt;(If you already have a python setup you’re fine with, just skip this bit).&lt;/p&gt;

&lt;p&gt;This time, let’s do our local development in docker. Managing Python installations can be a bit of a snake’s nest, especially on a mac, where the current default python3 installation has a bit of an issue with &lt;a href="https://github.com/giampaolo/psutil/issues/1832"&gt;psutil&lt;/a&gt;, which we depend on. &lt;/p&gt;

&lt;p&gt;First, make a directory for your application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;otel-py-demo &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;otel-py-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://docs.docker.com/get-docker/"&gt;Install docker&lt;/a&gt;, then grab a python image and start a container that mounts your application directly. If you’re in your app directory, the following starts a python container with your current directory mounted at &lt;code&gt;/app&lt;/code&gt;, and logs you into a bash shell within the container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;:/app python:3.8 bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whenever you need to log into a new terminal, find the container ID, and then use it to exec into a bash shell.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;docker&lt;/span&gt; &lt;span class="n"&gt;ps&lt;/span&gt;
&lt;span class="n"&gt;CONTAINER&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt;   &lt;span class="n"&gt;IMAGE&lt;/span&gt;         &lt;span class="n"&gt;COMMAND&lt;/span&gt;    &lt;span class="n"&gt;ETC&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;bc1e3c27f4d0&lt;/span&gt;   &lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;3.7&lt;/span&gt;   &lt;span class="s"&gt;"python3"&lt;/span&gt;   &lt;span class="n"&gt;More&lt;/span&gt; &lt;span class="n"&gt;columns&lt;/span&gt;
&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;docker&lt;/span&gt; &lt;span class="k"&gt;exec&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;bc1e3c27f4d0&lt;/span&gt; &lt;span class="n"&gt;bash&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s “all you need to know” about docker. 😁&lt;/p&gt;

&lt;h2&gt;
  
  
  Hello, World
&lt;/h2&gt;

&lt;p&gt;Once again, it is time to say Hello to this cruel World.&lt;br&gt;
First, exec into your docker container and install the required dependencies. For this basic app, we’re going to use &lt;code&gt;flask&lt;/code&gt; for the server, and &lt;code&gt;requests&lt;/code&gt; for the client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cd&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;
&lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt;
&lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a file named &lt;code&gt;server.py&lt;/code&gt; and make the world’s simplest app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sleep&lt;/span&gt;

&lt;span class="n"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
   &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"hello world&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
   &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Amazing. Add a client at &lt;code&gt;client.py&lt;/code&gt; which  makes five requests in a loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&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;requests&lt;/span&gt;&lt;span class="p"&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;"http://localhost:8000/hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In one docker terminal, start the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;export&lt;/span&gt; &lt;span class="n"&gt;FLASK_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;development&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
 &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Serving&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="s"&gt;"server"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lazy&lt;/span&gt; &lt;span class="n"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;development&lt;/span&gt;
 &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Debug&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;
 &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Running&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Press&lt;/span&gt; &lt;span class="n"&gt;CTRL&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In another, run the client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;python&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;py&lt;/span&gt;
&lt;span class="n"&gt;Hello&lt;/span&gt; &lt;span class="n"&gt;World&lt;/span&gt;
&lt;span class="n"&gt;Hello&lt;/span&gt; &lt;span class="n"&gt;World&lt;/span&gt;
&lt;span class="n"&gt;Hello&lt;/span&gt; &lt;span class="n"&gt;World&lt;/span&gt;
&lt;span class="n"&gt;Hello&lt;/span&gt; &lt;span class="n"&gt;World&lt;/span&gt;
&lt;span class="n"&gt;Hello&lt;/span&gt; &lt;span class="n"&gt;World&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works! &lt;/p&gt;

&lt;h2&gt;
  
  
  Install OpenTelemetry
&lt;/h2&gt;

&lt;p&gt;Ok so if you know python that was all very boring. Here’s the stuff you came for: installing opentelemetry.&lt;/p&gt;

&lt;p&gt;First, you need to pick the analysis tool you want to target. I work on Lightstep, and we have a &lt;a href="https://app.lightstep.com/signup/developer?signup_source=otelpythonblog"&gt;free Community account&lt;/a&gt; specifically for trying out OpenTelemetry like this. These instructions assume you have one of those. If you’d like to set up Jaeger instead, you can find installation instructions &lt;a href="https://opentelemetry-python.readthedocs.io/en/stable/exporter/jaeger/jaeger.html"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;To connect to Lightstep, install the Lightstep &lt;a href="https://medium.com/@tedsuo/opentelemetry-launchers-what-they-solve-and-why-we-need-them-15fc38d022a"&gt;distro&lt;/a&gt; for OpenTelemetry, the OpenTelemetry launcher. Lightstep is OpenTelemetry native, all the launcher does is install the relevant packages and make the configuration simpler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;use&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;feature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2020&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;resolver&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;launcher&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The launcher will install the core opentelemetry components, plus the currently available instrumentation. Just to unpack it a bit, there are three critical packages, beyond the launcher itself, which are worth understanding as they explain how OpenTelemetry is structured.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pypi.org/project/opentelemetry-api/"&gt;opentelemetry-api&lt;/a&gt;: the API package contains the opentelemetry instrumentation API. This package only contains interfaces, no implementation. It is safe to bring into any package without concern that a large dependency chain may follow it. &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pypi.org/project/opentelemetry-sdk/"&gt;opentelemetry-sdk&lt;/a&gt;: the SDK package contains the standard implementation for opentelemetry. This implementation is a framework written in python, allowing for various exporters, samplers, and lifecycle hooks to be plugged in, so that a wide variety of analysis tools can be supported.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pypi.org/project/opentelemetry-instrumentation/"&gt;opentelemetry-instrumentation&lt;/a&gt;: this package contains two command line tools for automatically instrumenting your application: &lt;code&gt;opentelemetry-bootstrap&lt;/code&gt; and &lt;code&gt;opentelemetry-instrument&lt;/code&gt;. We’re going to use both of them now.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Install Automatic Instrumentation
&lt;/h2&gt;

&lt;p&gt;The first command to learn is &lt;code&gt;opentelemetry-bootstrap&lt;/code&gt;. This will inspect the currently installed site-packages, and detect any packages we have instrumentation available for. By default, it prints out the packages to be copied into a requirements file, but it can also install them for you. For this example, let’s run it in installation mode.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;opentelemetry&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;bootstrap&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s it for installation!&lt;/p&gt;

&lt;h2&gt;
  
  
  Run with OpenTelemetry
&lt;/h2&gt;

&lt;p&gt;The easiest way to run OpenTelemetry is via the &lt;code&gt;opentelemetry-instrument&lt;/code&gt; command, using env vars for configuration. You can find a list of available configurations &lt;a href="https://github.com/lightstep/otel-launcher-python#configuration-options"&gt;here&lt;/a&gt;, but there are only two which are required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;LS_SERVICE_NAME&lt;/code&gt; - The name for this type of service. We’ll use &lt;code&gt;hello-sever&lt;/code&gt; and &lt;code&gt;hello-client&lt;/code&gt;, respectively.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LS_ACCESS_TOKEN&lt;/code&gt; - You can find this one by first logging into your Lightstep account (or &lt;a href="https://app.lightstep.com/signup/developer?signup_source=otelpythonblog"&gt;create one&lt;/a&gt;), then going to the settings page. Use the clipboard button to copy the access token. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To run opentelemetry with Lightstep, first log into your account  and find your access token on the Settings page. Use the clipboard button to copy the access token.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;export&lt;/span&gt; &lt;span class="n"&gt;FLASK_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;development&lt;/span&gt;
&lt;span class="n"&gt;export&lt;/span&gt; &lt;span class="n"&gt;LS_SERVICE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;
&lt;span class="n"&gt;export&lt;/span&gt; &lt;span class="n"&gt;LS_ACCESS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ACCESS&lt;/span&gt; &lt;span class="n"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;&amp;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;instrument&lt;/span&gt; &lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do the same for the client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;export&lt;/span&gt; &lt;span class="n"&gt;LS_SERVICE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;
&lt;span class="n"&gt;export&lt;/span&gt; &lt;span class="n"&gt;LS_ACCESS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ACCESS&lt;/span&gt; &lt;span class="n"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;&amp;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;instrument&lt;/span&gt; &lt;span class="n"&gt;python&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;py&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check for data by clicking on the explorer:&lt;br&gt;
&lt;a href="//images.ctfassets.net/d3bkzhxwv8fv/1AbfTW8nMjigqjweAhwp0p/319dd03c8ddff7bf281b13ffab9ae5ed/Python__1_.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d3bkzhxwv8fv/1AbfTW8nMjigqjweAhwp0p/319dd03c8ddff7bf281b13ffab9ae5ed/Python__1_.png" alt="OpenTelemetry Python: Lightstep"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Huzzah! We see some spans. Click into one and check out the trace. &lt;br&gt;
&lt;a href="//images.ctfassets.net/d3bkzhxwv8fv/7epIUzYIoQFNVDwmUyNc9v/9fd2ddbfcaedebcf494c5ce8ad281930/Python__2_.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d3bkzhxwv8fv/7epIUzYIoQFNVDwmUyNc9v/9fd2ddbfcaedebcf494c5ce8ad281930/Python__2_.png" alt="OpenTelemetry Python in Lightstep - Trace view"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s pause for a second and review the data we are looking at. There are two spans, one from the requests package on the client, and one from the flask package on the server.  Clicking on a span, you can see that it is already rich with data. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;http.*&lt;/code&gt; and &lt;code&gt;net.*&lt;/code&gt; – these conventions describe everything about the request.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;instrumentation.name&lt;/code&gt;  – this describes the instrumentation package which generated the span. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;span.kind&lt;/code&gt; - either client, server, or internal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The representation of common concepts like HTTP are standardized across languages. so that analysis tools can automatically interpret the data they are looking at. We refer to these standardized attributes as Semantic Conventions. The complete list can be found &lt;a href="https://github.com/open-telemetry/opentelemetry-specification/tree/master/specification/trace/semantic_conventions"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  No code required
&lt;/h2&gt;

&lt;p&gt;The biggest, most important note is that we added OpenTelemetry to our service, but didn’t write any code. Everything could be done from the command line. This means that OpenTelemetry can potentially be added to a service by an operator, with a simple modification to deployment. &lt;/p&gt;

&lt;p&gt;I highly recommend this approach as a first pass, before adding any additional detail. OpenTelemetry needs to be installed in every service in order for distributed tracing to work. It is more important to get every service instrumented at a high level than it is to dig in and deeply instrument the application code in a particular service. Library level instrumentation (flask, requests, redis, etc) will give you enough information to set up alerting root causing issues. &lt;/p&gt;

&lt;p&gt;If this is the first time you’ve added distributed tracing to your system, don’t be surprised if a number of latency-related issues immediately become visible! After you’ve done a wide-scale rollout, you can dig in selectively and add detail where needed. Converting your existing logs to span events is another great way to add detail without having to write a lot of code.&lt;/p&gt;
&lt;h2&gt;
  
  
  Using the OpenTelemery Python API
&lt;/h2&gt;

&lt;p&gt;Okay, automation is great, but eventually, you are going to want to add detail. Spans are already decorated with standardized attributes, but once you’re settled in, you will want to start adding more detail.&lt;/p&gt;

&lt;p&gt;The most important details to add are &lt;strong&gt;application-level attributes&lt;/strong&gt; critical to segmenting your data. For example, a projectID allows you to differentiate between errors that are affecting everyone connecting to a service, vs errors that are localized to a handful of accounts. Those would be two very different scenarios, and you would probably start looking in different places based on that feedback.&lt;/p&gt;

&lt;p&gt;Also, logs. They are a thing. OpenTelemetry has a structured logging facility, we just call it &lt;strong&gt;events&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Adding data to the current span
&lt;/h3&gt;

&lt;p&gt;To add additional data to your trace, you need access to the currently active span. Since context propagation is already set up, thanks to the automatic instrumentation, there is already a span available to you. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attributes&lt;/strong&gt; are simply key value pairs. &lt;strong&gt;Events&lt;/strong&gt; consist of a message and a dictionary of attributes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;opentelemetry&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
   &lt;span class="c1"&gt;# get the current span, created by flask
&lt;/span&gt;   &lt;span class="n"&gt;span&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_current_span&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="c1"&gt;# add more attributes to the server span
&lt;/span&gt;   &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http.route"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"some_route"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="c1"&gt;# add events (AKA structured logging)
&lt;/span&gt;   &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"event message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"event_attributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

   &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"hello"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s best to add data to existing spans, rather than create child spans. This keeps all of the attributes grouped together, which makes for better indexing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a child span
&lt;/h3&gt;

&lt;p&gt;Of course, you are going to want to create child spans on some occasions. A span represents a distinct operation - not an individual function, but an entire operation, such as a database query. Generally, this means you shouldn't be creating spans in your application code, they should be managed as part of the framework or library you are using.&lt;/p&gt;

&lt;p&gt;But, that said, here is how you do it. First, create a tracer. A tracer is just a namespace - it lets you know which package created the span, via the &lt;code&gt;instrumentation.name&lt;/code&gt; attribute (you can also add a version as a second parameter).&lt;/p&gt;

&lt;p&gt;Span management has two parts - the span lifetime and the span context. The lifetime is managed by starting the span with a tracer and adding it to a trace by assigning it a parent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Start the span with a name and a parent span
&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my_operation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
   &lt;span class="c1"&gt;# pass the span around as a parameter
&lt;/span&gt;   &lt;span class="n"&gt;do_work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
   &lt;span class="c1"&gt;# End the span, which measures the span duration and
&lt;/span&gt;   &lt;span class="c1"&gt;# triggers the span data to be exported.
&lt;/span&gt;   &lt;span class="c1"&gt;# WARNING: failing to end a span will create a leak.
&lt;/span&gt;   &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using spans directly like this is cumbersome. Instead, we want to create a new context where the span is active so that it can be accessed by &lt;code&gt;get_current_span&lt;/code&gt; instead of passing it around. In almost all cases,  the easiest way to manage a span is by calling &lt;code&gt;start_as_current_span&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;opentelemetry&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;

&lt;span class="c1"&gt;# create a tracer and name it after your package
&lt;/span&gt;&lt;span class="n"&gt;tracer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_tracer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
   &lt;span class="c1"&gt;# add latency to the parent span
&lt;/span&gt;   &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="c1"&gt;# always create a new context when starting a span
&lt;/span&gt;   &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"server_span"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="c1"&gt;# add an event to the child span
&lt;/span&gt;     &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"event message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"event_attributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
     &lt;span class="c1"&gt;# get_current_span will now return the same span
&lt;/span&gt;     &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;propagation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_current_span&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;add_attribute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
     &lt;span class="c1"&gt;# add latency to the child span
&lt;/span&gt;     &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"hello"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you ever need to create a span in your application code, I strongly recommend using the above pattern.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recording errors
&lt;/h3&gt;

&lt;p&gt;Ok, one final bit. We’ve covered spans, attributes, and events. But what about exceptions? Exceptions are reported as events, but they should be properly formatted. As a convenience, OpenTelemetry provides a record_exception method for capturing them correctly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;opentelemetry.trace.status&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StatusCode&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
   &lt;span class="n"&gt;span&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;propagation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_current_span&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
   &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ZeroDivisionError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="c1"&gt;# record an exception
&lt;/span&gt;       &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;record_exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="c1"&gt;# fail the operation
&lt;/span&gt;       &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ERROR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"caught zero division error"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Uncaught exceptions are automatically recorded as errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;And that is that. All you need to know to get started with tracing in Python. &lt;/p&gt;

&lt;p&gt;Hopefully, it’s clear that If you stick with the above patterns, you can get a great deal of visibility with very little work. Of course, there are many more details and options; you can check out the &lt;a href="https://opentelemetry-python.readthedocs.io/"&gt;API documentation&lt;/a&gt; for more information. I also have a more involved &lt;a href="https://opentelemetry.lightstep.com/python"&gt;getting started guide&lt;/a&gt;; it works as a handy reference for all of the procedures described above.&lt;/p&gt;

&lt;p&gt;OpenTelemetry is still in beta due to API changes, but it is also already in production across many organizations. If you stick to a &lt;a href="https://github.com/lightstep/otel-launcher-python"&gt;Distro&lt;/a&gt; and automated instrumentation, you can use OpenTelemetry today without much fear of a breaking change affecting you.&lt;/p&gt;

&lt;p&gt;Also: consider joining our community! There are plenty of libraries left to instrument. You can find us on &lt;a href="https://github.com/open-telemetry/opentelemetry-js"&gt;GitHub&lt;/a&gt;, or say hi on &lt;a href="https://gitter.im/open-telemetry/opentelemetry-node"&gt;gitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>python</category>
      <category>performance</category>
      <category>observability</category>
    </item>
    <item>
      <title>OpenTelemetry NodeJS: All you need to know</title>
      <dc:creator>austin</dc:creator>
      <pubDate>Thu, 03 Dec 2020 14:13:15 +0000</pubDate>
      <link>https://dev.to/lightstep/opentelemetry-nodejs-all-you-need-to-know-1ngd</link>
      <guid>https://dev.to/lightstep/opentelemetry-nodejs-all-you-need-to-know-1ngd</guid>
      <description>&lt;p&gt;Hi all, &lt;a href="https://github.com/tedsuo/"&gt;tedsuo&lt;/a&gt; here. We’re passing an important milestone on OpenTelemetry: the &lt;a href="https://github.com/open-telemetry/opentelemetry-specification/tree/master/specification/trace"&gt;tracing specification&lt;/a&gt; is about to be frozen, and release candidates for OpenTelemetry tracing implementations will be coming soon, with metrics following in the next couple of months. &lt;/p&gt;

&lt;p&gt;While we are putting our core documentation together, I thought now would be a good time to point out how simple it is to actually use distributed tracing in JavaScript. OpenTelemetry is a large framework, it has a lot of options and a lot of surface area. But, as an end user, you don’t have to worry about all of that. So, forget the details: this walkthrough contains all you need to know to actually use OpenTelemetry in Node. Apply this walkthrough to your application, and you are good to go.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;All you need to know is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initialization: How to start and shutdown cleanly.&lt;/li&gt;
&lt;li&gt;Tracer methods: getTracer, getCurrentSpan, startSpan, and withSpan.&lt;/li&gt;
&lt;li&gt;Span methods: setAttribute, addEvent, recordException, setStatus, and end.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Seriously, that’s it. If you want to try it out, follow the guide below. A heavily commented version of the finished tutorial can be found at &lt;a href="https://github.com/tedsuo/otel-node-basics"&gt;https://github.com/tedsuo/otel-node-basics&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hello, world
&lt;/h2&gt;

&lt;p&gt;For this tutorial, we’re going to make a very, very simple application: an express service that responds to &lt;a href="http://localhost:9000/hello"&gt;http://localhost:9000/hello&lt;/a&gt; with “Hello World.” It’s as basic as it is original! &lt;/p&gt;

&lt;p&gt;First, make a directory to contain your project, and install express:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i express
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we have that, let’s get to coding.  Make a  file called  &lt;code&gt;server.js&lt;/code&gt; and serve up some hello world:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/hello&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello World&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, make a file called &lt;code&gt;client.js&lt;/code&gt; which sends 5 requests to the server and then exits.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;makeRequest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
     &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;9000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/hello&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;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
     &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&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="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
     &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&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="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&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;makeRequest&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;Boot up server and check that it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; node server.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the server running, test the client in another tab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; node client.js
Hello World
Hello World
Hello World
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  OpenTelemetry Architecture in 30 seconds
&lt;/h2&gt;

&lt;p&gt;Ok, I said no details, but here is one that is actually helpful. OpenTelemetry clients have two major components: the SDK and the API. The SDK is the actual framework, the API is what you use to instrument your code. &lt;/p&gt;

&lt;p&gt;This separation provides loose coupling: your application code only depends on the API, which has virtually no dependencies and acts like a no-op when the SDK is not installed. This allows packages to add instrumentation without automatically pulling in the implementation’s dependency chain (think grpc, etc). This separation of concerns is especially helpful for OSS libraries that want to bake in instrumentation, but don’t want to create overhead or dependency conflicts when OpenTelemetry is not being used.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tip: Never reference any SDK package outside of installation and setup. All other packages and application code should only depend on the API.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In case you were wondering, while there are two Javascript SDKs - one for NodeJS and one for the browser - there is only one Javascript API. Instrumented code remains portable between both environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pick an OpenTelemetry backend
&lt;/h2&gt;

&lt;p&gt;Ok, let’s add OpenTelemetry to this application. To test our tracing, you’ll need a place to send the data. &lt;/p&gt;

&lt;p&gt;At Lightstep, we created &lt;a href="https://app.lightstep.com/signup/developer?signup_source=nodejsblog"&gt;free-for-life community accounts&lt;/a&gt; specifically for making OpenTelemetry easy to experiment with. If you don’t already have one, please grab an account.&lt;/p&gt;

&lt;p&gt;If you’d like to use Zipkin or Jaeger instead, this &lt;a href="https://github.com/open-telemetry/opentelemetry-js/blob/master/getting-started/README.md"&gt;getting started guide&lt;/a&gt; will walk you through the setup. Once you’re set-up, you can come back here and follow the rest of the tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install the NodeJS OpenTelemetry Launcher
&lt;/h2&gt;

&lt;p&gt;Since we’re connecting to Lightstep, we’ll also be using the Lightstep Distro of OpenTelemetry, the &lt;a href="https://github.com/lightstep/otel-launcher-node"&gt;OpenTelemetry Launchers&lt;/a&gt;. Distros package up any plugins and configuration needed to talk to a particular backend. At the moment, we’re still fleshing out the full definition of a Distro (what is allowed, and what isn’t), but the basic point is to make getting started easier by reducing configuration boilerplate. If you want more detail, you can check out &lt;a href="https://lightstep.com/blog/opentelemetry-launchers-what-they-solve-and-why-we-need-them"&gt;this blog post&lt;/a&gt; where I initially proposed the concept.&lt;/p&gt;

&lt;p&gt;Installing the OpenTelemetry Launcher package will also install OpenTelemetry, plus all currently available &lt;a href="https://github.com/open-telemetry/opentelemetry-js#plugins"&gt;instrumentation&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i lightstep-opentelemetry-launcher-node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create an OpenTelemetry initialization file
&lt;/h2&gt;

&lt;p&gt;To instrument your server, you need to start the OpenTelemetry SDK before loading your application. As of v0.12, OpenTelemetry NodeJS loads asynchronously, This is actually the trickiest bit of OpenTelemetry right now, and in future versions will move to a simpler, synchronous startup.  However, for now you can copy and paste the approach below, and it will work for any application.&lt;/p&gt;

&lt;p&gt;Create a file called server_init.js. This will serve as your new entry point. You can copy and paste the below code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;lightstep&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nx"&gt;opentelemetry&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="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lightstep-opentelemetry-launcher-node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sdk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lightstep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configureOpenTelemetry&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
 &lt;span class="na"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;ACCESS_TOKEN&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello-server-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;serviceVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;v1.2.3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;propagators&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tracecontext,b3&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="nx"&gt;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./server&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;()&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;shutdown&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SDK shut down successfully&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="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error shutting down SDK&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SIGINT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SIGTERM&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure the launcher with your &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens"&gt;Lightstep Access Token&lt;/a&gt; (You can find your Access Token on the settings page). Create a client_init.js file in the same manner, only change the serviceName to ‘hello-client’ and the required startup file to  ‘./client’.&lt;/p&gt;

&lt;p&gt;Use the launcher to start the  SDK. Once the SDK has completed its setup, require your original entry point to start your application. &lt;/p&gt;

&lt;p&gt;Why load your application in two phases like this? If your application begins requiring packages (or running) before OpenTelemetry is set up, it can create issues. By initializing OpenTelemetry in a separate file, and only requiring the rest of your application after the SDK is started,  OpenTelemetry has an opportunity to automatically apply any available instrumentation, as well as &lt;a href="https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-resource-detector-aws"&gt;auto-detect&lt;/a&gt; any available &lt;a href="https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-resource-detector-gcp"&gt;system resources&lt;/a&gt; before your application starts to run. It also ensures that your application loads normally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run your application with OpenTelemetry
&lt;/h2&gt;

&lt;p&gt;Start your newly auto-instrumented server and client. Let’s also turn the debug logs on, so we can see what OpenTelemetry is doing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_LOG_LEVEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;debug
node server_init.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_LOG_LEVEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;debug
node client_init.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At startup, the debug logs will print out the configuration, and list every successfully loaded  instrumentation library. Every time the tracer flushes data, all of the spans which have been exported are printed out.  This can be really helpful for debugging when you are setting up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check out what automatic instrumentation gives you
&lt;/h2&gt;

&lt;p&gt;Switch over to Lightstep, or your backend of choice, and  confirm the spans were received:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d3bkzhxwv8fv/1npMWCLBsAt2mNLvJZIHUL/77559e62778d1bbcaf333a3e20da3716/OTel_Node__1_.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d3bkzhxwv8fv/1npMWCLBsAt2mNLvJZIHUL/77559e62778d1bbcaf333a3e20da3716/OTel_Node__1_.png" alt="OTel Node (1)"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;
Yup, we see spans. Click through and look at a trace:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d3bkzhxwv8fv/52JQnLaMBmEaCWqfvV7ZJX/61b2b53bedd22567f39e7b7d301cecae/Example_trace.jpg" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d3bkzhxwv8fv/52JQnLaMBmEaCWqfvV7ZJX/61b2b53bedd22567f39e7b7d301cecae/Example_trace.jpg" alt="Example trace in Lightstep"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;
Notice that we see a client span from hello-client, a server span from hello-server, and several internal spans representing built-in express components. Also, notice that the client and server spans are already populated with HTTP, network, and other attributes. &lt;/p&gt;

&lt;p&gt;All of this common information is standardized across instrumentation as &lt;a href="https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md"&gt;semantic conventions&lt;/a&gt;. An HTTP request will always be described with the same keys and values, regardless of what language or package it comes from.&lt;/p&gt;

&lt;p&gt;This is a lot of really useful information. We already have a complete trace, with a lot of detail, and we haven’t written any instrumentation yet. When rolling out OpenTelemetry, this is the approach I recommend. Get OpenTelemetry installed into every service and ensure that context is propagating correctly, before adding any further detail. This will be enough information to set up error monitoring and identify latency issues.&lt;/p&gt;
&lt;h2&gt;
  
  
  The OpenTelemetry Javascript API
&lt;/h2&gt;

&lt;p&gt;Ok, so the out-of-the-box experience will get you a long way, but of course, you will eventually want to add additional application data. &lt;br&gt;
Spans should ideally be managed by your application framework. In this case, the express framework manages the span for you. In your application code, you can continue to decorate these spans with more information. There are two primary types of data you will want to add: attributes and events.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Span attributes&lt;/strong&gt; are indexes for segmenting your data. For example, you may want to add &lt;code&gt;project.id&lt;/code&gt; or &lt;code&gt;account.id&lt;/code&gt; in order to understand if slow requests and errors are specific to a certain set of accounts, or affecting everyone.&lt;/p&gt;

&lt;p&gt;Fine grain logging can be added as &lt;strong&gt;span events&lt;/strong&gt;. Events are a form of structured logging - use them like you would logs. The advantage with span events is that you can automatically find all of the logs associated with a particular transaction, rather than having to go hunting with a bunch of searches and filters. As you scale up, this becomes a lifesaver (or, at least, a big time saver).&lt;/p&gt;

&lt;p&gt;First, require the OpenTelemetry API. At the package level, create a tracer and name it after your package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;opentelemetry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@opentelemetry/api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// create a tracer and name it after your package&lt;/span&gt;
&lt;span class="kd"&gt;const&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;opentelemetry&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;getTracer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@otel-node-basics/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The name of the tracer appears on every span as the  &lt;code&gt;instrumentation.name&lt;/code&gt; attribute. This is useful for investigating instrumentation issues.&lt;/p&gt;

&lt;p&gt;Once you have a tracer, you can use it to access the server span created by the express instrumentation. Calling &lt;code&gt;tracer.getCurrentSpan()&lt;/code&gt; will return the span for the current context. Once you have access to the span, you can add attributes and events.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/hello&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="c1"&gt;// access the span created by express instrumentation&lt;/span&gt;
 &lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="o"&gt;=&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;getCurrentSpan&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="c1"&gt;// add an attribute to segment your data by projectID&lt;/span&gt;
 &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;projectID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="c1"&gt;// log an event and include some structured data.&lt;/span&gt;
 &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;setting timeout&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="na"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

 &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;responding&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello World&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="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also chain these methods, which can be  a little more concise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/hello&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&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;getCurrentSpan&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
       &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;projectID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123&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="nx"&gt;addEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;setting timeout&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="na"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

 &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&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;getCurrentSpan&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;addEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sending response&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello World&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="mi"&gt;300&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;Run your server and client again, and you will see these new attributes and events show up on the same spans.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating your own spans
&lt;/h2&gt;

&lt;p&gt;You can also create your own spans. These spans will automatically become children of the current span and added to the trace. &lt;/p&gt;

&lt;p&gt;Span management involves three steps: starting the span, setting it as the current span, and ending the span.&lt;/p&gt;

&lt;p&gt;To start a child span, grab the tracer again, and call &lt;code&gt;tracer.startSpan( name )&lt;/code&gt;. Name the span after the operation you are measuring. Advice on naming can be found in the tracing &lt;a href="https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/api.md#span"&gt;specification&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;IMPORTANT:&lt;/strong&gt; make sure to end the span when your operation finishes, or you will have a leak!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After &lt;code&gt;span.end()&lt;/code&gt; is called, Spans are queued up to be exported in the next flush. Calls to &lt;code&gt;setAttribute&lt;/code&gt; and &lt;code&gt;addEvent&lt;/code&gt; become no-ops after &lt;code&gt;span.end()&lt;/code&gt; is called.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/hello&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// start a new span named “sleeper”&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;childSpan&lt;/span&gt; &lt;span class="o"&gt;=&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;startSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sleeper&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

 &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// childSpan works normally when referenced&lt;/span&gt;
   &lt;span class="nx"&gt;childSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;finished sleeping&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="c1"&gt;// However, starting a span does not automatically&lt;/span&gt;
   &lt;span class="c1"&gt;// set it to the current span. getCurrentSpan still &lt;/span&gt;
   &lt;span class="c1"&gt;// returns the parent span.&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;getCurrentSpan&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello World&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="c1"&gt;// Ending the span is a requirement. It measures the duration &lt;/span&gt;
   &lt;span class="c1"&gt;// of the operation, and then sends the span to the exporter.&lt;/span&gt;
   &lt;span class="nx"&gt;childSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;300&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;So, the above “works,” except the child span has not been set as the current span. In almost all circumstances, this is critical. You want the rest of your code to be able to access the span without handing it around as a parameter. And unless you set the new span as current, &lt;code&gt;getCurrentSpan&lt;/code&gt; will return the parent span, which would be incorrect.&lt;/p&gt;

&lt;p&gt;So, after you start a span, create a closure in which the span is active by calling &lt;code&gt;tracer.withSpan(span, cb)&lt;/code&gt;. Within the callback, the new span will now be active.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/hello&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// start a new span named “sleeper”&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;childSpan&lt;/span&gt; &lt;span class="o"&gt;=&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;startSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sleeper&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

 &lt;span class="c1"&gt;// use withSpan to create a new context&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;withSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;childSpan&lt;/span&gt;&lt;span class="p"&gt;,()&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="c1"&gt;// getCurrentSpan now correctly returns childSpan&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="o"&gt;=&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;getCurrentSpan&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
     &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sending response&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello World&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;300&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My advice is to avoid creating child spans, except when you truly require a new context - seprating out a dabatase operations from application code, for example. Ideally, span management should happen in some kind of framework, rather than scattered about your application code. Favor adding events over creating child spans. If you pool all of your attributes on to the same span, you will get better indexing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error Handling
&lt;/h2&gt;

&lt;p&gt;There is one final type of event that deserves special attention: exceptions. In OpenTelemetry, exceptions are recorded as events. But, to ensure that the exception is properly formatted, the &lt;code&gt;span.recordException(error)&lt;/code&gt; method should be used instead of &lt;code&gt;addEvent&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/hello&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ooops&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="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// Add the exception as a properly formatted event.&lt;/span&gt;
   &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recordException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="c1"&gt;// Set the status code to make the exception count &lt;/span&gt;
   &lt;span class="c1"&gt;// as an error.&lt;/span&gt;
   &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setStatus&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&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;CanonicalCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UNKNOWN&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;By default, exceptions do not count as errors. In OpenTelemetry, an error means that the overall operation did not complete. Plenty of exceptions are expected, and a handled exception does not automatically mean the entire operation failed to complete. In other cases, an operation could fail without an exception being thrown. &lt;/p&gt;

&lt;p&gt;In order to declare an operation a failure, call &lt;code&gt;span.setStatus()&lt;/code&gt; and pass in an error code.  Status codes are used by analysis tools to automatically trigger alerting, measure error rates, etc.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: status codes will be simplified in the next version of OpenTelemetry.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  That’s all, folks!
&lt;/h2&gt;

&lt;p&gt;And that is that. All you need to know to get started with tracing in NodeJS. Hopefully, that was pretty straight forwards, and clears up any mysteries about how to  use OpenTelemetry. &lt;/p&gt;

&lt;p&gt;If you stick with the above patterns, you can get a great deal of visibility with very little work. Of course, there are many more details and options; you can check out the &lt;a href="https://open-telemetry.github.io/opentelemetry-js/"&gt;API documentation&lt;/a&gt; for more information. I also have a more involved &lt;a href="https://opentelemetry.lightstep.com/js/nodejs-setup/"&gt;getting started guide&lt;/a&gt;; it works as a handy reference for all of the procedures described above. &lt;/p&gt;

&lt;p&gt;OpenTelemetry is still in beta due to API changes, but it is also already in production across many organizations. If you stick to a &lt;a href="https://github.com/lightstep/otel-launcher-node"&gt;Distro&lt;/a&gt; and automated instrumentation, you can use OpenTelemetry today without much fear of a breaking change, as those changes will most likely involve the API. &lt;/p&gt;

&lt;p&gt;If you are writing manual instrumentation during the beta, consider creating helper functions that simplify the API for your use cases, and give you a centralized place to manage any potential breakage.&lt;/p&gt;

&lt;p&gt;Also: consider joining our community! There are plenty of libraries left to instrument. You can find us on &lt;a href="https://github.com/open-telemetry/opentelemetry-js"&gt;GitHub&lt;/a&gt;, or say hi on &lt;a href="https://gitter.im/open-telemetry/opentelemetry-node"&gt;gitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>javascript</category>
      <category>performance</category>
      <category>observability</category>
    </item>
    <item>
      <title>Understanding Static Site Performance with OpenTelemetry</title>
      <dc:creator>austin</dc:creator>
      <pubDate>Wed, 24 Jun 2020 16:38:42 +0000</pubDate>
      <link>https://dev.to/lightstep/understanding-static-site-performance-with-opentelemetry-224j</link>
      <guid>https://dev.to/lightstep/understanding-static-site-performance-with-opentelemetry-224j</guid>
      <description>&lt;p&gt;When you're building a static website, performance can be hard to measure. There are hundreds of variables and confounding conditions that can impact page load speed, everything from mundane considerations like the size of a PNG to the complex vagaries of Content Delivery Networks. How are you supposed to figure out if your users, the people that are trying to load your page, are having a good experience? This becomes trickier when you're using popular managed hosting services, like Netlify, Contentful, or GitHub Pages. You need some sort of telemetry data from the perspective of your end-users in order to accurately gauge how long things take to load.&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%2Fi%2Fz4ky9832npsmikp5czft.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%2Fi%2Fz4ky9832npsmikp5czft.jpg" alt="Plotting the performance of your site over time is a useful way to see if things are getting better, or worse."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OpenTelemetry can help you solve this problem! OpenTelemetry is an open source project that promises to make high-quality telemetry a "built-in" feature of cloud-native software. To this end, the project maintains a variety of 'automatic instrumentation' plugins for popular frameworks, runtimes, and libraries that offer you the ability to drop OpenTelemetry into your existing code without major changes in order to profile the performance of your software in production. In simpler terms, OpenTelemetry collects data on the performance of your software or web site from your &lt;em&gt;end-user's perspective&lt;/em&gt;, and send that telemetry data to one of many open source or proprietary tools that allow you to persist and analyze it.&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll go through the entire process of setting up OpenTelemetry on a static site using Hugo and Webpack, then configuring a deployment of the OpenTelemetry collector in Kubernetes to receive data from our site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;In order to add OpenTelemetry to a Hugo site, you'll need a Hugo site - tautological, I know, but them's the breaks. If you already have one, then you can use that -- if you're trying to follow along without an existing site, I'd check out the &lt;a href="https://github.com/netlify-templates/victor-hugo" rel="noopener noreferrer"&gt;Victor Hugo&lt;/a&gt; boilerplate generator, as it'll get you started on the right foot with Hugo and Webpack. You'll also need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/en/download/" rel="noopener noreferrer"&gt;Node.JS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/get-npm" rel="noopener noreferrer"&gt;NPM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll continue this tutorial assuming you're using Victor Hugo, with some notes from my experience instrumenting the &lt;a href="https://opentelemetry.io" rel="noopener noreferrer"&gt;opentelemetry.io&lt;/a&gt; site. I'll also assume that you're familiar with the basics of using Git and GitHub, and the basics of HTML, CSS, and JavaScript.&lt;/p&gt;

&lt;p&gt;If you'd like to follow along with deploying an OpenTelemetry Collector, you'll need a Kubernetes cluster as well - we'll use Google Kubernetes Engine in this tutorial, but the steps should work on any Kubernetes cluster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;First, create a fork (or simply clone) the Victor Hugo boilerplate, and checkout your source code. We'll start by adding OpenTelemetry to our project -&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ npm install --save @opentelemetry/core @opentelemetry/tracing @opentelemetry/web @opentelemetry/plugin-document-load @opentelemetry/exporter-collector&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will install and save several OpenTelemetry components, including the core API and SDK components, automatic browser instrumentation and plugins, and an exporter to the OpenTelemetry Collector. Let's now add a new file to our repository where we'll import OpenTelemetry at &lt;code&gt;/src/tracing.js&lt;/code&gt;. You don't need to add anything here for now, but before we forget, let's import it in the main JS file. Open &lt;code&gt;/src/index.js&lt;/code&gt; and modify it as so&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// JS Goes here - ES6 supported&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./tracing.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./css/main.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Say hello&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&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! Edit me in src/index.js&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;Now that we've got the skeleton of our project set up, it's time to add OpenTelemetry itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding OpenTelemetry-Web
&lt;/h2&gt;

&lt;p&gt;In the previous step, we created a new file, called &lt;code&gt;tracing.js&lt;/code&gt; to hold our OpenTelemetry code. Switch to that in your editor, and you're ready to install and configure OpenTelemetry. First, we'll need to import a few packages that we installed earlier. Add the following --&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SimpleSpanProcessor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ConsoleSpanExporter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@opentelemetry/tracing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;WebTracerProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@opentelemetry/web&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DocumentLoad&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@opentelemetry/plugin-document-load&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;Let's briefly touch on what our imports are doing here. First, we're importing a &lt;em&gt;Span Processor&lt;/em&gt; which is registered to our &lt;em&gt;Tracer Provider&lt;/em&gt;. This component is responsible for handling span data that's generated by the tracer, customarily by &lt;em&gt;exporting&lt;/em&gt; it. Our &lt;code&gt;ConsoleSpanExporter&lt;/code&gt; will write span data to the browser console for now. Finally, the &lt;code&gt;DocumentLoad&lt;/code&gt; plugin extends the capabilities of our tracer provider, allowing it to automatically instrument (read: generate spans for) our page load.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Spans are the building blocks of traces. They represent work being done by your process, or page.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Complete the setup of OpenTelemetry by adding the following code to this file --&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebTracerProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DocumentLoad&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;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addSpanProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SimpleSpanProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConsoleSpanExporter&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code will create our provider and plugin, register the span &lt;br&gt;
processor to the provider, and start the provider. Amazingly enough, this is all you need to do for now! In your terminal, start your page preview with &lt;code&gt;npm run preview&lt;/code&gt; and open &lt;code&gt;http://localhost:3000&lt;/code&gt; in a web browser. Open your JavaScript console, and refresh the page, you should see output similar to the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;HMR&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Waiting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;update&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;signal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;WDS...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;log.js:&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;🦊&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Hello!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Edit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;me&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;src/index.js&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;index.js:&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;traceId:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"16b18f5cef76bc6c4fd1578bd0df53d9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;parentId:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"741587dc317632f9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;name:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"documentFetch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;id:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"53ea6e17e3389a01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;kind:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;timestamp:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1592582737016000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;duration:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;57000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;attributes:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;status:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;events:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;ConsoleSpanExporter.js:&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;traceId:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"16b18f5cef76bc6c4fd1578bd0df53d9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;parentId:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"741587dc317632f9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;name:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:3000/main.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;id:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ffd85307d05068f5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;kind:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;timestamp:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1592582737140000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;duration:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;17000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;attributes:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;status:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;events:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;ConsoleSpanExporter.js:&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;traceId:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"16b18f5cef76bc6c4fd1578bd0df53d9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;parentId:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"741587dc317632f9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;name:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:3000/main.css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;id:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"278b38cfa637b67c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;kind:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;timestamp:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1592582737140000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;duration:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;19000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;attributes:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;status:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;events:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;ConsoleSpanExporter.js:&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;traceId:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"16b18f5cef76bc6c4fd1578bd0df53d9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;parentId:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;name:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"documentLoad"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;id:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"741587dc317632f9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;kind:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;timestamp:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1592582737016000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;duration:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;252000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;attributes:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;status:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;events:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;ConsoleSpanExporter.js:&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Briefly, let's take a look at one of the objects we see here --&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"traceId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"16b18f5cef76bc6c4fd1578bd0df53d9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"documentLoad"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"741587dc317632f9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1592582737016000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;252000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"attributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"component"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"document-load"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"events"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fetchStart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="mi"&gt;1592582737&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="mi"&gt;16000105&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;more&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;events...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a JSON representation of a &lt;em&gt;span&lt;/em&gt;, which is what OpenTelemetry is creating for you using the &lt;code&gt;DocumentLoad&lt;/code&gt; plugin. Spans include more information than you see here, but this is most of the important parts: a name, a trace identifier, a span identifier, timestamp, and duration. We can also see &lt;em&gt;attributes&lt;/em&gt; and &lt;em&gt;events&lt;/em&gt; -- these are, respectively, properties that help categorize the span, and events that occurred during the span's lifetime.&lt;/p&gt;

&lt;p&gt;Let's add some more attributes to our spans in order to make them a bit more useful. Since our ultimate goal is to understand the performance of our page loads, there's two things I can immediately think of that would be useful -- the language of the user's browser, and the path that our users are accessing. We can add both of these properties to our traces by creating some &lt;em&gt;default attributes&lt;/em&gt;. In &lt;code&gt;tracing.js&lt;/code&gt;, add a new object and modify your provider initialization as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;locale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;browser.language&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;browser.path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebTracerProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DocumentLoad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;defaultAttributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;locale&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our &lt;code&gt;locale&lt;/code&gt; object reads a few values from the browser runtime (namely, the language that the browser is set to, and the current path) and assigns them to our provider as default attributes, which means they'll be applied to all spans created by our tracer. If you refresh your page, you can prove this to yourself by looking at the attribute key in the console output. We'll use these later to get an idea of what pages people are looking at, and roughly where they're from in the world (or at least, we'll be able to use the browser language as a rough proxy for where in the world they are).&lt;/p&gt;

&lt;p&gt;Now that we've added OpenTelemetry, we need to actually get the data somewhere other than the browser console. There's a few wrinkles to attend to here as well. First, on modern browsers, OpenTelemetry uses the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Beacon_API" rel="noopener noreferrer"&gt;Beacon API&lt;/a&gt; to forward telemetry to a collector service in order to reduce latency for end-users. We also need a place to send that data to. You can either directly export telemetry data to a backend service, or send it to a collector to be aggregated and forwarded.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can discover the wide range of exporters available to OpenTelemetry at the &lt;a href="https://opentelemetry.io/registry/" rel="noopener noreferrer"&gt;OpenTelemetry Registry&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There's pros and cons to each of these methods, ones that we won't fully elaborate on due to space considerations, but for the purposes of this tutorial we'll be setting up an OpenTelemetry collector in order to receive our telemetry data. This provides a useful separation of concerns between the generation of telemetry, and the dispensation of that telemetry - for example, if we want to send our telemetry data elsewhere, we can do so by modifying our collector, without having to redeploy our site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying the OpenTelemetry Collector
&lt;/h2&gt;

&lt;p&gt;The collector itself is a fairly straightforward piece of software with a few moving parts to understand. Generally, though, it allows you to define one or more &lt;em&gt;Receivers&lt;/em&gt;, which are endpoints that can receive telemetry data in a specific format. This telemetry data is then sent to another system for analysis and storage through an &lt;em&gt;Exporter&lt;/em&gt;. Receivers and Exporters are part of one or more &lt;em&gt;Pipelines&lt;/em&gt;, which also allow for the configuration of &lt;em&gt;Processors&lt;/em&gt; that can modify the telemetry data in some way. Finally, the collector supports several &lt;em&gt;Extensions&lt;/em&gt; which add new features and functionality.&lt;/p&gt;

&lt;p&gt;In our case, we don't need anything terribly complicated in terms of collector configuration. We're going to receive data in the OpenTelemetry Format (henceforth referred to as &lt;em&gt;OTLP&lt;/em&gt;), and export it to Lightstep using OTLP as well. We'll add some processors to control the amount of memory our collector instances use, and to allow for batching and retrying of exports. One other consideration we must address, however, is TLS (Transport Layer Security). If we deploy our site using HTTPS (and you &lt;em&gt;are&lt;/em&gt; using HTTPS in 2020, aren't you?) then our collector also needs to be served over HTTPS. Since we're using Kubernetes, we can take advantage of the Ingress resource to handle this for us -- we'll be using &lt;code&gt;nginx-ingress&lt;/code&gt; along with &lt;code&gt;cert-manager&lt;/code&gt; to automate the process of creating and provisioning SSL certificates.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that the OTLP receiver can handle TLS on its own, but for ease of certificate maintenence, we'll be using our Kubernetes Ingress to terminate TLS and send data to collector instances in plaintext.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm going to split this next part into a few discrete steps because there's a lot going on. I'm going to assume that your cluster is basically pristine - here's what mine looked like when I started.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Master version: 1.16.8-gke.15.&lt;/li&gt;
&lt;li&gt;3 nodes, type &lt;code&gt;n1-standard-1&lt;/code&gt; with autoscaling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Set your &lt;code&gt;kubectl&lt;/code&gt; context to your Kubernetes cluster before continuing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preparing our Kubernetes Cluster
&lt;/h3&gt;

&lt;p&gt;Without getting into a ton of extraneous details, we're going to be using &lt;code&gt;nginx-ingress&lt;/code&gt; as our Ingress resource provider in lieu of the GKE Ingress. This is mostly because of how health checks work on GKE Ingress and how the OTLP Receiver in the controller functions (in short, GKE expects the &lt;code&gt;/&lt;/code&gt; route to return &lt;code&gt;HTTP 200 OK&lt;/code&gt; on &lt;code&gt;GET&lt;/code&gt; even if your container readiness probe specifies something else entirely), so we'll start by installing &lt;code&gt;nginx-ingress&lt;/code&gt; and &lt;code&gt;cert-manager&lt;/code&gt; to our cluster. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Not using GKE? Find more installation instructions for NGINX Ingress &lt;a href="https://kubernetes.github.io/ingress-nginx/deploy/" rel="noopener noreferrer"&gt;here&lt;/a&gt; and for cert-manager &lt;a href="https://cert-manager.io/docs/installation/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You'll need to first initialize your user as a cluster administrator by running the following command.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ kubectl create clusterrolebinding cluster-admin-binding \&lt;br&gt;
  --clusterrole cluster-admin \&lt;br&gt;
  --user $(gcloud config get-value account)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After this, install &lt;code&gt;nginx-ingress&lt;/code&gt; by running this command.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/cloud/deploy.yaml&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You should see a variety of resources being configured and created on your cluster. You can validate that the installation worked by executing &lt;code&gt;$ kubectl get pods -n ingress-nginx&lt;/code&gt;, you should see something similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;NAME                                        READY   STATUS      RESTARTS   AGE
ingress-nginx-admission-create-9hv54        0/1     Completed   0          22h
ingress-nginx-admission-patch-ddjfp         0/1     Completed   0          22h
ingress-nginx-controller-579fddb54f-zjhq7   1/1     Running     0          22h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, lets install &lt;code&gt;cert-manager&lt;/code&gt;. Run the following command.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v0.15.1/cert-manager.yaml&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Again, you'll see a lot of output as resources are created on your cluster. Validate the installation by running &lt;code&gt;$ kubectl get pods -n cert-manager&lt;/code&gt;, your result should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;NAME                                       READY   STATUS    RESTARTS   AGE
cert-manager-9b8969d86-zrgpg               1/1     Running   0          22h
cert-manager-cainjector-8545fdf87c-pfvxd   1/1     Running   0          22h
cert-manager-webhook-8c5db9fb6-4bdpq       1/1     Running   0          22h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're now ready to configure our deployment of the OpenTelemetry Collector.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a Collector Configuration
&lt;/h3&gt;

&lt;p&gt;Our first order of business will be to configure the collector itself. We'll store our configuration as a Kubernetes &lt;code&gt;ConfigMap&lt;/code&gt; which will be mounted into each pod, and the collector will read this file at startup to configure itself. This makes reconfiguring our collector as simple as updating the ConfigMap, then restarting the pods.&lt;/p&gt;

&lt;p&gt;In our case, we expect fairly light load on the collectors, so we're not going to go overboard in resourcing them. Here's the ConfigMap I used, I'll explain some of the more esoteric parts below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfigMap&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web-collector-conf&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;opentelemetry-collector&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web-collector-conf&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web-collector-config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;receivers:&lt;/span&gt;
      &lt;span class="s"&gt;otlp:&lt;/span&gt;
        &lt;span class="s"&gt;endpoint: "0.0.0.0:55680"&lt;/span&gt;
    &lt;span class="s"&gt;processors:&lt;/span&gt;
      &lt;span class="s"&gt;batch:&lt;/span&gt;
      &lt;span class="s"&gt;memory_limiter:&lt;/span&gt;
        &lt;span class="s"&gt;ballast_size_mib: 700&lt;/span&gt;
        &lt;span class="s"&gt;limit_mib: 1500&lt;/span&gt;
        &lt;span class="s"&gt;spike_limit_mib: 100&lt;/span&gt;
        &lt;span class="s"&gt;check_interval: 5s&lt;/span&gt;
      &lt;span class="s"&gt;queued_retry:&lt;/span&gt;
    &lt;span class="s"&gt;extensions:&lt;/span&gt;
      &lt;span class="s"&gt;health_check: {}&lt;/span&gt;
    &lt;span class="s"&gt;exporters:&lt;/span&gt;
      &lt;span class="s"&gt;otlp:&lt;/span&gt;
        &lt;span class="s"&gt;endpoint: "ingest.lightstep.com:443"&lt;/span&gt;
        &lt;span class="s"&gt;headers:&lt;/span&gt;
          &lt;span class="s"&gt;"lightstep-access-token": &amp;lt;insert access token&amp;gt;&lt;/span&gt;
    &lt;span class="s"&gt;service:&lt;/span&gt;
      &lt;span class="s"&gt;extensions: [health_check]&lt;/span&gt;
      &lt;span class="s"&gt;pipelines:&lt;/span&gt;
        &lt;span class="s"&gt;traces:&lt;/span&gt;
          &lt;span class="s"&gt;receivers: [otlp]&lt;/span&gt;
          &lt;span class="s"&gt;processors: [memory_limiter, batch, queued_retry]&lt;/span&gt;
          &lt;span class="s"&gt;exporters: [otlp]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The configuration file for the collector is also a YAML file, which makes it compose neatly with Kubernetes YAML syntax. Of note in this are really two things - first, the &lt;code&gt;memory_limiter&lt;/code&gt; processor and the &lt;code&gt;otlp&lt;/code&gt; exporter. I'm going to &lt;a href="https://github.com/open-telemetry/opentelemetry-collector/tree/master/processor/memorylimiter" rel="noopener noreferrer"&gt;link to the documentation on the memory limiter&lt;/a&gt; but in short, these options assist us in managing the memory usage of the collector process in order to prevent it running out of memory. On the exporter, I've set the endpoint to forward traces to Lightstep, and I'm passing in an access token (you'd find this in your Lightstep project under 'Settings') as a header.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Not already a Lightstep user? &lt;a href="https://app.lightstep.com/signup?signup_source=devtier" rel="noopener noreferrer"&gt;Get a free developer account here&lt;/a&gt; and follow along!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If we wanted to add another exporter to this pipeline, it'd be very simple -- create a new exporter, and add it to the array of exporters in the pipelines section of our configuration. We could also define a metrics pipeline and send that data to Prometheus or any other desired system. This is, really, one of the advantages of using the collector - you can manage where stuff goes, completely independent of how its generated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set up the Kubernetes Deployment and Service
&lt;/h3&gt;

&lt;p&gt;Now that our configuration is settled, it's time to deploy the collector to our Kubernetes cluster and expose it so that it's reachable by our Ingress. At this point, I'd suggest you reference &lt;a href="https://github.com/austinlparker/otel-web-collector" rel="noopener noreferrer"&gt;this GitHub repository&lt;/a&gt; as a source for the Kubernetes YAML, as I'm going to just point out things you should be aware of -- we're not doing anything too different from a bog-standard deployment. First, let's check out &lt;code&gt;deployment.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One very important thing to note is the &lt;code&gt;command&lt;/code&gt; being passed to the container. The flag &lt;code&gt;--mem-ballast-size-mib&lt;/code&gt; &lt;strong&gt;must&lt;/strong&gt; match the &lt;code&gt;ballast_size_mib&lt;/code&gt; value defined in the ConfigMap. Other than this, it's a fairly straightforward deployment. The &lt;code&gt;livenessProbe&lt;/code&gt; and &lt;code&gt;readinessProbe&lt;/code&gt; are accessing port 13133 because that's the default (you enable this by adding the &lt;code&gt;health_check&lt;/code&gt; extension in the collector configuration). Finally, take note of the &lt;code&gt;image&lt;/code&gt; -- in this case, we're using a development build of the collector, but you may wish to use a stable release &lt;em&gt;or&lt;/em&gt; the &lt;code&gt;opentelemetry-collector-contrib&lt;/code&gt; container. See &lt;a href="https://github.com/open-telemetry/opentelemetry-collector-contrib" rel="noopener noreferrer"&gt;this link&lt;/a&gt; for information about what's contained in the contrib collector -- it's usually exporters and plugins that aren't in the "main line" collector. On to &lt;code&gt;service.yaml&lt;/code&gt;. We're simply mapping port 55680 to port 80 on a ClusterIP, which is how our Ingress will connect to it.&lt;/p&gt;

&lt;p&gt;At this point, you're ready to start deploying this to your cluster. Execute &lt;code&gt;$ kubectl apply -f configmap.yaml -f deployment.yaml -f service.yaml&lt;/code&gt;, and the cluster will be on its way. After a few moments (slightly more if you've never pulled these containers before), running &lt;code&gt;$ kubectl get pods&lt;/code&gt; should display something similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;NAME                                        READY   STATUS    RESTARTS   AGE
web-collector-deployment-79cfc8797c-7vvln   1/1     Running   0          23h
web-collector-deployment-79cfc8797c-vzslm   1/1     Running   0          23h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating your Ingress and Certificates
&lt;/h3&gt;

&lt;p&gt;You're halfway there, and if you were only going to be sending telemetry data to your collector from inside this cluster, you'd be ready to roll. However, we want to send data from outside the cluster, so we need to expose this service to the world.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important Note!&lt;/strong&gt; You'll need a domain name that you control in order to create a certificate using ACME, so be ready.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;First, we're going to deploy our Ingress service in order to determine the external IP address we need to assign to our domain name. We'll actually be deploying this ingress in two steps, so rather than simply applying the existing &lt;code&gt;ingress.yaml&lt;/code&gt;, take a look at this version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web-collector-ingress&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kubernetes.io/ingress.class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nginx"&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;tls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;YOUR FQDN&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web-collector-tls&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;YOUR FQDN&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
        &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web-collector-svc&lt;/span&gt;
          &lt;span class="na"&gt;servicePort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;code&gt;&amp;lt;YOUR FQDN&amp;gt;&lt;/code&gt;, you would want to use whatever domain name will point to your collector (in my case, I used 'otelwebtelemetry.com', but you could use a subdomain, such as 'collector.mysite.com'). Save this file and apply it using &lt;code&gt;kubectl&lt;/code&gt;, and wait several minutes. Run &lt;code&gt;$ kubectl get ingress&lt;/code&gt; and you should see something similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;NAME                    HOSTS                  ADDRESS           PORTS     AGE
web-collector-ingress   otelwebtelemetry.com   104.198.132.223   80, 443   22h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your DNS management, set your host to the ADDRESS you see in your kubectl output. Note that DNS changes can take some time to propagate around the internet, so you may need to wait up to 30 minutes (or longer) - a good way to see if it's ready is to run &lt;code&gt;$ dig @8.8.8.8 &amp;lt;your domain&amp;gt;&lt;/code&gt; and see if the answer section has correctly associated your domain name with the IP address of your ingress controller.&lt;/p&gt;

&lt;p&gt;Meanwhile, you should verify that the Ingress controller is functioning properly. The easiest way to do so, for the collector, is to run &lt;code&gt;curl&lt;/code&gt; against the OTLP receiver path.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ curl -kivL -X POST -H 'Host: &amp;lt;YOUR FQDN&amp;gt;' 'http://&amp;lt;YOUR IP&amp;gt;/v1/trace'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This command will provide verbose output, follow redirects, show TLS headers, and not give an error on an insecure certificate as it makes a POST request to the OTLP endpoint. If you get a &lt;code&gt;200 OK&lt;/code&gt; response, everything is working, and we can set up certificate management through Let's Encrypt.&lt;/p&gt;

&lt;p&gt;Refer to the &lt;code&gt;le-staging-issuer.yaml&lt;/code&gt; and &lt;code&gt;le-prod-issuer.yaml&lt;/code&gt; files in the repository. You should start with the staging one, as Let's Encrypt aggressively rate limits connections - once everything works, you'll switch to the production (prod) issuer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cert-manager.io/v1alpha2&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIssuer&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;letsencrypt-staging&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;acme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://acme-staging-v02.api.letsencrypt.org/directory&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your@email.com&lt;/span&gt;
    &lt;span class="na"&gt;privateKeySecretRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;letsencrypt-staging&lt;/span&gt;
    &lt;span class="na"&gt;solvers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;http01&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;ingress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;nginx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In both this and the production issuer, make sure to change the &lt;code&gt;email&lt;/code&gt; field to one that you control. You can then apply this to your cluster with &lt;code&gt;$ kubectl apply -f le-staging-issuer.yaml&lt;/code&gt;. Verify that the issuer was successfully created and registered by running &lt;code&gt;$ kubectl describe clusterissuer letsencrypt-staging&lt;/code&gt; and verify that the &lt;code&gt;Type&lt;/code&gt; field is set to &lt;code&gt;Ready&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In your &lt;code&gt;ingress.yaml&lt;/code&gt;, add two new annotations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kubernetes.io/ingress.class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nginx"&lt;/span&gt;
    &lt;span class="na"&gt;cert-manager.io/cluster-issuer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;letsencrypt-staging"&lt;/span&gt;
    &lt;span class="na"&gt;acme.cert-manager.io/http01-ingress-class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nginx"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, run &lt;code&gt;$ kubectl apply -f ingress.yaml&lt;/code&gt; once again. After a few moments, run &lt;code&gt;$ kubectl get certificate&lt;/code&gt;, and you should see a certificate that's set to the value of &lt;code&gt;secretName&lt;/code&gt; in your ingress (in my case, it's &lt;code&gt;web-collector-tls&lt;/code&gt;). Run &lt;code&gt;$ kubectl describe certificate &amp;lt;name&amp;gt;&lt;/code&gt; and you should see 'Ready' under Type, as well as several events (one of which should say 'Certificate issued successfully').&lt;/p&gt;

&lt;p&gt;The last step, then, is to switch from the staging Let's Encrypt issuer to the production one. In your ingress, change the &lt;code&gt;"cert-manager.io/cluster-issuer"&lt;/code&gt; annotation value to &lt;code&gt;"letsencrypt-prod"&lt;/code&gt; and the &lt;code&gt;secretName&lt;/code&gt; so that it won't conflict with the staging secret (you can just add &lt;code&gt;-prod&lt;/code&gt;). Deploy the production issuer by running &lt;code&gt;$ kubectl apply -f le-prod-issuer.yaml&lt;/code&gt;, and then redeploy your ingress again. You should now have an OpenTelemetry Collector deployed to the public internet! Verify this with &lt;code&gt;$ curl -vL -X POST https://&amp;lt;your domain&amp;gt;/v1/trace&lt;/code&gt;, if you see a 200 response code with empty braces as the body, then you're good to go!&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure OpenTelemetry Web with the Collector Exporter
&lt;/h2&gt;

&lt;p&gt;That was a lot, I know - but we're back to something more straightforward now. Only one more step to go! Back to our &lt;code&gt;tracing.js&lt;/code&gt; file, add a new import and configure the Collector Exporter. The following is what your file should look like after we're done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SimpleSpanProcessor&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@opentelemetry/tracing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;WebTracerProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@opentelemetry/web&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DocumentLoad&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@opentelemetry/plugin-document-load&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CollectorExporter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@opentelemetry/exporter-collector&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;exporter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CollectorExporter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;your website name&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://&amp;lt;your domain name&amp;gt;/v1/trace&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;locale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;browser.language&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;browser.path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebTracerProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DocumentLoad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;defaultAttributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;locale&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addSpanProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SimpleSpanProcessor&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;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if you've configured everything correctly up to this point, you should be able to refresh your page a few times in order to generate data and then open up Lightstep. You should see some data in the Explorer, corresponding to page loads from your site!&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%2Fi%2F0dxlm7znmiolhv05mr4z.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%2Fi%2F0dxlm7znmiolhv05mr4z.png" alt="Static Site Performance - Explorer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From here, you can simply deploy your site to the internet using Netlify, GitHub Pages, or your own personal hosting and start to see exactly how people are using your site in new and interesting ways. Want to know how page load speeds are for users with the Russian language, grouped by the pages they're viewing? Lightstep makes that easy!&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%2Fi%2F8wk1su82d4c8qxbu0okc.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%2Fi%2F8wk1su82d4c8qxbu0okc.png" alt="Understanding Static Site Performance - Explorer Detail"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;We've been through a lot in this tutorial, so I think it's best to do a quick recap of everything we learned today.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Integrating OpenTelemetry into your static site is as straightforward as adding some packages and configuring a tracing module. You don't have to change any of your existing code, just make sure that you import the tracing module first!&lt;/li&gt;
&lt;li&gt;Setting up an OpenTelemetry Collector is a great way to collect trace and metric data from your services, be they front-end or back-end, and can be done through Kubernetes.&lt;/li&gt;
&lt;li&gt;Once you're using OpenTelemetry, Lightstep is a great way to get started analyzing your trace data, but by no means are you locked in. You can use OpenTelemetry to export data to a variety of open source and proprietary analysis tools!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for sticking with me through this, I know it's a lot to take in - but I know that once you try it, you'll find something to love. I firmly believe that OpenTelemetry solves one of the biggest problems that plague people who run and build software for a living, the eternal question, "What the $#@* is it doing?" As someone that's asked that question many times over the years, usually in the middle of the night, I'm really excited to see the progress it's made towards providing easier and better answers to that question.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>hugo</category>
      <category>observability</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How-To: Tracing React + Node in under 50 lines of code!</title>
      <dc:creator>austin</dc:creator>
      <pubDate>Tue, 23 Jun 2020 16:19:10 +0000</pubDate>
      <link>https://dev.to/lightstep/how-to-tracing-react-node-in-under-50-lines-of-code-3ie8</link>
      <guid>https://dev.to/lightstep/how-to-tracing-react-node-in-under-50-lines-of-code-3ie8</guid>
      <description>&lt;p&gt;When you're building a React app, it can be difficult to know what's going on behind the scenes in terms of performance. When something's slow, how do you figure out why? There could be a hundred things at fault -- a bug in your reducer, a slow API, problems with the database, a network failure -- and tracking down these problems can be extremely time-consuming and frustrating.&lt;/p&gt;

&lt;p&gt;What if there was an easier way to get answers? OpenTelemetry to the rescue! In less than 50 lines of code, you can trace every step in a request - from your React code, down to your database - in production with little additional overhead. In this video, I'll show you how to integrate OpenTelemetry into a React SPA and a Node.JS server built on Express, how to deal with common configuration problems, and how to verify that everything is working.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/utp-sOkFswY"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Next time, we'll look at some deployment options for our application and explore building custom metrics in React. Join me then!&lt;/p&gt;

</description>
      <category>react</category>
      <category>node</category>
      <category>javascript</category>
      <category>observability</category>
    </item>
    <item>
      <title>Hugo + OpenTelemetry - How fast is my site? (Video, Pt. 2)</title>
      <dc:creator>austin</dc:creator>
      <pubDate>Fri, 19 Jun 2020 13:13:01 +0000</pubDate>
      <link>https://dev.to/lightstep/hugo-opentelemetry-how-fast-is-my-site-video-pt-2-1jc8</link>
      <guid>https://dev.to/lightstep/hugo-opentelemetry-how-fast-is-my-site-video-pt-2-1jc8</guid>
      <description>&lt;p&gt;We love Hugo for building blazing-fast static sites, but it can be tricky to understand how well they perform for everybody. There can be hundreds or thousands of variables from an end-user's browser, to their ISP, to the vagaries of routing and content delivery networks around the world. Wouldn't it be nice if there was a simple way to quickly understand the performance of your static site? OpenTelemetry makes that easy! You can see how to add OpenTelemetry in my previous video - &lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/austinlparker" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--C7klopXq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--q7MUyTM---/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/28770/f823abbc-7e83-4a41-bd97-5658d2acf6e7.png" alt="austinlparker image"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/lightstep/hugo-opentelemetry-how-fast-is-my-site-video-pt-1-2dl4" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Hugo + OpenTelemetry - How fast is my site? (Video, Pt. 1)&lt;/h2&gt;
      &lt;h3&gt;austin ・ Jun 16 ・ 1 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#opentelemetry&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tutorial&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Once you've integrated OpenTelemetry into your site, how do you actually get the data somewhere that you can analyze and review it? In this video, we'll walk through the configuration and deployment of the OpenTelemetry Collector in a Kubernetes cluster, and expose it to the world via HTTP. I'll show you how to add some interesting attributes to the automatic instrumentation that you can analyze later, and we'll see how you can easily connect your OpenTelemetry Collector to Lightstep in order to visualize the traces being emitted.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ovMM3-GYd4o"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;We're almost there - the last step is getting ready for production by configuring HTTPS on our Collector. Keep an eye out for the wrap-up post where I'll write up the entire journey, and offer some suggestions on how to add OpenTelemetry to your site!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links and References&lt;/strong&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/open-telemetry"&gt;
        open-telemetry
      &lt;/a&gt; / &lt;a href="https://github.com/open-telemetry/opentelemetry-js"&gt;
        opentelemetry-js
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      OpenTelemetry JavaScript API and SDK
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/open-telemetry"&gt;
        open-telemetry
      &lt;/a&gt; / &lt;a href="https://github.com/open-telemetry/opentelemetry-collector"&gt;
        opentelemetry-collector
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      OpenTelemetry Collector
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Questions? Comments? Want to watch the next show live?&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://twitter.com/austinlparker"&gt;Follow me on Twitter for all the latest!&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitch.tv/oncallmemaybe"&gt;Follow On-Call Me Maybe on Twitch!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opentelemetry</category>
      <category>tutorial</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Hugo + OpenTelemetry - How fast is my site? (Video, Pt. 1)</title>
      <dc:creator>austin</dc:creator>
      <pubDate>Tue, 16 Jun 2020 15:41:07 +0000</pubDate>
      <link>https://dev.to/lightstep/hugo-opentelemetry-how-fast-is-my-site-video-pt-1-2dl4</link>
      <guid>https://dev.to/lightstep/hugo-opentelemetry-how-fast-is-my-site-video-pt-1-2dl4</guid>
      <description>&lt;p&gt;We love Hugo for building blazing-fast static sites, but it can be tricky to understand how well they perform for everybody. There can be hundreds or thousands of variables from an end-user's browser, to their ISP, to the vagaries of routing and content delivery networks around the world. Wouldn't it be nice if there was a simple way to quickly understand the performance of your static site?&lt;/p&gt;

&lt;p&gt;OpenTelemetry can help! In this video series, we'll walk through how you can start instrumenting a static web page generated with Hugo (or the static site generator of your choice) using OpenTelemetry's automatic instrumentation, send that telemetry data somewhere, and then use it to analyze the performance of your page and debug in production.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/p4pqj8Vj3nQ"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Stay tuned for part two, where we'll get our telemetry out of the browser and into an analysis system like Lightstep in order to visualize the performance of our site.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links and References&lt;/strong&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/open-telemetry"&gt;
        open-telemetry
      &lt;/a&gt; / &lt;a href="https://github.com/open-telemetry/opentelemetry-js"&gt;
        opentelemetry-js
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      OpenTelemetry JavaScript API and SDK
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/open-telemetry"&gt;
        open-telemetry
      &lt;/a&gt; / &lt;a href="https://github.com/open-telemetry/opentelemetry.io"&gt;
        opentelemetry.io
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      The OpenTelemetry website and documentation
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/gohugoio"&gt;
        gohugoio
      &lt;/a&gt; / &lt;a href="https://github.com/gohugoio/hugo"&gt;
        hugo
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      The world’s fastest framework for building websites.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/jgthms"&gt;
        jgthms
      &lt;/a&gt; / &lt;a href="https://github.com/jgthms/bulma"&gt;
        bulma
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Modern CSS framework based on Flexbox
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Questions? Comments? Want to watch the next show live?&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://twitter.com/austinlparker"&gt;Follow me on Twitter for all the latest!&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitch.tv/oncallmemaybe"&gt;Follow On-Call Me Maybe on Twitch!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opentelemetry</category>
      <category>tutorial</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Your System is Deeper Than You Think</title>
      <dc:creator>austin</dc:creator>
      <pubDate>Wed, 08 Jan 2020 20:07:56 +0000</pubDate>
      <link>https://dev.to/austinlparker/your-system-is-deeper-than-you-think-1oo5</link>
      <guid>https://dev.to/austinlparker/your-system-is-deeper-than-you-think-1oo5</guid>
      <description>&lt;p&gt;Trying to parse my career trajectory from reading my resume is like relying on a pirate’s riddle to find treasure. Instead of “walk four paces from the crooked tree, west of skull rock,” though, it’s “spend eight months dropping frozen chicken tenders into a deep fryer, then move eight hundred miles to write automated QA tests for software.” &lt;/p&gt;

&lt;p&gt;But there’s one constant in all the varied jobs I’ve had: they are defined by systems that are largely outside of our control, but that we’re ultimately responsible for. &lt;/p&gt;

&lt;p&gt;The person who prepares your food has about as much control over the process that delivered them the ingredients as the on-call developer has — getting paged at 3 a.m. because some tangentially related system on the Internet has broken. It’s the people, though, who are ultimately responsible for the immediate crisis, and who bear the burden of fixing the immediate problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Even Simple Systems Are Complex&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The thing that these systems all have in common is that they’re largely more complex than it seems from the outside. Similar to an iceberg, where a small precipice breaking through the waves can hide a massive chunk of floating ice below the waterline (at least until climate change gets really going and we’re all living in Gas Town waiting for our turn in Thunderdome Plus), the systems that we’re responsible for in our professional lives are often more massive and cumbersome than they appear at first glance. Why is this? Some of it is easily explained, some more difficult, but the usual answer is quite simply ‘inertia’. This organizational and technical inertia isn’t simply something that affects large technical companies, however — it’s something that touches nearly everyone working with software today.&lt;/p&gt;

&lt;p&gt;One form of this hidden depth is, of course, other people’s computers, better known as ‘the cloud’. Cloud services have revolutionized the software industry, giving us access to infinitely scalable hardware resources, durable managed services, and a whole host of convenient ways to accidentally delete your entire cloud stack because someone fat-fingered a terraform apply. I’ve seen that last one happen: a new team member at a prior job accidentally deleted every resource in our AWS account due to a combination of poor internal documentation, flawed pairing practices, and overly permissive IAM roles. &lt;/p&gt;

&lt;p&gt;But, even if you’ve built an internal system that’s resilient to human beings, how confident are you that every external service you rely on is also so circumspect? You may have a small application with only a handful of services (or even only one service),  but every external API you rely on, be it from a cloud provider, or some other SaaS product, is a potential point of complexity, failure, and hair-rending frustration when it goes down and takes your application with it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your Dependencies, Their Dependencies, and (Lest We Forget) What’s Dependent on Them&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You probably didn’t write your own HTTP stack, or networking stack, or even string comparison library. While it’s easy (and cheap) to go after left-pad or other, similar stories (such as Docker migrating their Go packages to a new GitHub organization, before gomod), the biggest threat to the performance and security of your application may simply be bugs in your third-party dependencies. &lt;/p&gt;

&lt;p&gt;Either through benign logic errors, or malicious intent, every module you import is a potential landmine and a source of complexity that you have little control over. &lt;/p&gt;

&lt;p&gt;As open source becomes more integral to the art of software development, the potential impact becomes even more widespread — you may vet all of your dependencies, after all, but are the authors of your dependencies taking that same level of care with their dependencies? This, of course, isn’t simply something you need to consider with direct code dependencies — your CI system, your package and container repositories, your deployment pipeline — these are all uncontrolled sources of entropy and complexity that you need to contend with.&lt;/p&gt;

&lt;p&gt;While we often think about our software systems strictly in terms of technical depth and complexity, it’s important to remember the organizational and human systems that underpin them. &lt;/p&gt;

&lt;p&gt;I think most people can relate to the feeling of helplessness that comes from fighting endless battles against organizational dynamics that don’t seem to make a lot of sense or have misaligned priorities. Maybe your organization rewards new features, while maintenance work is seen as “less important”. Perhaps you’re trapped in a ‘sales-driven development’ pattern where your work shifts from project to project, relentlessly adding new checkboxes to a system without a lot of concern for the overall scalability or maintainability of the application? &lt;/p&gt;

&lt;p&gt;Long-term vendor contracts can tie us to particular pieces of technology, forcing hacks and workarounds to development. There are a million other pieces of organizational detritus that float around our teams, regardless of the size or complexity of the actual software we work on. This hidden depth is possibly the most pernicious, as it’s difficult to understand how you can even begin to tackle it, but it’s still a source of complexity as you build and maintain software.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Burning(Out) Man: We’ve All Been There, Some of Us Are Just More Vocal About It&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, we don’t have the luxury of sitting back and saying “eh, we’ll fix it tomorrow” when it comes to addressing these issues. You may have a brilliant team of developers, designers, PMs, and more — but you can’t afford the human costs associated with unreliable software. Posts about burnout litter technical communities across the web and recent studies indicate three in five employees feel burnt out by their job at least once a month. &lt;/p&gt;

&lt;p&gt;The stress-induced by trying to debug and analyze failures, especially those that aren’t in services or external systems under your direct control, can contribute to burnout. Teams that are suffering from burnout find themselves in situations that can rapidly escalate out of control, and the consequences can be dire — as failures pile on and multiply, more and more time is spent firefighting rather than dealing with the root cause of failures, which leads to more failure and late-night pages. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Truth About Deep Systems&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We’ve been talking a lot about deep systems recently here at LightStep, and I’d encourage you to read some of that material and think about it in the context I’ve presented here. &lt;/p&gt;

&lt;p&gt;In short, deep systems are architectures where there are at least four layers of stacked, independently operated services, including cloud or SaaS dependencies. Perhaps a better way to think about deep systems is not so much an explicit definition, but rather what they “sound like”:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“I’m too busy to update the runbook.”&lt;/li&gt;
&lt;li&gt;“Where’s Chris? I’m dealing with a P0, and they’re the only one who knows how to debug this.”&lt;/li&gt;
&lt;li&gt;“We have too many dashboards!”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve spoken with a lot of developers who think that their system isn’t ‘deep’ because they’ve only got a handful of services, or because they’re a small team. I’d argue that this isn’t the case at all — as demonstrated above, there’s an awful lot of ways your system can have hidden depth that contributes to stress, burnout, and unreliable software. The solution isn’t to despair, but it’s to embrace observability as a core practice of how you build and run software.&lt;/p&gt;

&lt;p&gt;This way, when something breaks, you’ll have the context you need to understand what happened, who is responsible, and how to best resolve the issue — even if the regression or error is deep in the stack or the result of a third-party dependency.&lt;/p&gt;

</description>
      <category>observability</category>
      <category>distributedsystems</category>
      <category>tracing</category>
      <category>apm</category>
    </item>
    <item>
      <title>OpenTelemetry 101: What is an Exporter?</title>
      <dc:creator>austin</dc:creator>
      <pubDate>Mon, 28 Oct 2019 14:57:26 +0000</pubDate>
      <link>https://dev.to/lightstep/opentelemetry-101-what-is-an-exporter-5h4i</link>
      <guid>https://dev.to/lightstep/opentelemetry-101-what-is-an-exporter-5h4i</guid>
      <description>&lt;p&gt;&lt;a href="https://opentelemetry.io" rel="noopener noreferrer"&gt;OpenTelemetry&lt;/a&gt; is an open-source observability framework for generating, capturing, and collecting telemetry data for cloud-native software. Prior posts in this series have covered the definition of observability, as it applies to OpenTelemetry, and a dive into the tracing and metrics API. There’s a third critical component, though, that you’ll want to understand before you get started using OpenTelemetry, and that’s how to actually get data out of it! In this post, we’ll talk about the OpenTelemetry exporter model and the OpenTelemetry Collector, along with several basic deployment strategies.&lt;/p&gt;

&lt;p&gt;Note - Some of the information in this post is subject to change as the &lt;a href="https://github.com/open-telemetry/opentelemetry-specification" rel="noopener noreferrer"&gt;specification for OpenTelemetry&lt;/a&gt; continues to mature.&lt;/p&gt;

&lt;p&gt;To understand how OpenTelemetry’s exporter model works, it is useful to generally understand a little bit about how instrumentation is integrated to service code. Generally, you can have instrumentation at three different points: Your service, its library dependencies, and its platform dependencies. Integrating at the service level is fairly straightforward, as you would declare a dependency in your code on the appropriate OpenTelemetry package and deploy it with your code. Library dependencies are similar, except that your libraries would generally only declare a dependency on the OpenTelemetry API. Platform dependencies are a more unusual case. When I say ‘platform dependency’, what I mean are the pieces of software you run to provide services to your service, things like &lt;a href="https://www.envoyproxy.io/" rel="noopener noreferrer"&gt;Envoy&lt;/a&gt; and &lt;a href="https://istio.io/" rel="noopener noreferrer"&gt;Istio&lt;/a&gt;. These will deploy their own copy of OpenTelemetry, independent of your actions, but will also generally emit trace context that your service will want to be a part of.&lt;/p&gt;

&lt;p&gt;In every case, the trace and metric data that your service or its dependencies emit are of limited use unless you can actually collect that data somewhere for analysis and alerting. The OpenTelemetry component responsible for batching and transporting telemetry data to a backend system is known as an &lt;em&gt;&lt;a href="https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/sdk-tracing.md#span-exporter" rel="noopener noreferrer"&gt;exporter&lt;/a&gt;&lt;/em&gt;. The exporter interface is implemented by the OpenTelemetry SDKs, and uses a simple plug-in model that allows for telemetry data to be translated to whatever format a backend system requires, and transmit it to that backend system. Exporters can be composed and chained together, allowing for common functionality to be shared (like tagging data before export, or providing a queue to ensure consistent performance) across multiple protocols. &lt;/p&gt;

&lt;p&gt;To put this in more concrete terms, let’s compare OpenTelemetry to OpenTracing. In OpenTracing, if you wanted to switch what system you were reporting data to, you’d need to replace the entire tracer component with another -- for example, swapping out the Jaeger client library with the LightStep client library. In OpenTelemetry, you simply need to change the export component, or even just add the new one and export to multiple backend systems simultaneously. This makes it a lot easier to try out new analysis tools or send your telemetry data to different analysis tools in different environments.&lt;/p&gt;

&lt;p&gt;While the exporter model is very convenient, there are instances when you don’t have the ability to actually redeploy a service in order to add a new exporter. In some organizations, there’s a disconnect between the people writing the instrumented code and the people running the observability platform, which can impair the velocity of rolling out changes to where data goes. In addition, some teams may prefer to abstract the entire exporter model out from their code, and into a separate service. This is where the &lt;a href="https://github.com/open-telemetry/opentelemetry-collector" rel="noopener noreferrer"&gt;OpenTelemetry Collector&lt;/a&gt; comes in useful. The collector is a separate process that is designed to be a ‘sink’ for telemetry data emitted by many processes, which can then export that data to backend systems. The Collector has two different deployment strategies -- either running as an agent alongside a service, or as a remote application. You’d generally think about using both: the agent would be deployed with your service and run as a separate process, or in a sidecar. The collector would be deployed separately, as its own application running in a container or virtual machine. Each agent would forward telemetry data to the collector, which could then export it to a variety of backend systems such as LightStep, Jaeger, Prometheus, and more.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fl8aywbrxf46kpdhimdy8.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fl8aywbrxf46kpdhimdy8.png" alt="A diagram of the OpenTelemetry Exporter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Regardless of how you choose to instrument or deploy OpenTelemetry, exporters provide a lot of powerful ways to report telemetry data. You can directly export from your service, you can proxy through the collector, or you can aggregate into standalone collectors -- or even a mix of these! Ultimately, what’s important is that you’re getting that telemetry data into an observability platform that can help you analyze and understand what’s going on in your system.&lt;/p&gt;

</description>
      <category>observability</category>
      <category>opensource</category>
      <category>devops</category>
    </item>
    <item>
      <title>OpenTelemetry 101: What Are Metrics?</title>
      <dc:creator>austin</dc:creator>
      <pubDate>Tue, 15 Oct 2019 00:21:24 +0000</pubDate>
      <link>https://dev.to/lightstep/opentelemetry-101-what-are-metrics-d9e</link>
      <guid>https://dev.to/lightstep/opentelemetry-101-what-are-metrics-d9e</guid>
      <description>&lt;p&gt;&lt;a href="https://opentelemetry.io"&gt;OpenTelemetry&lt;/a&gt; is an open-source observability framework for generating, capturing, and collecting telemetry data for cloud-native software. In the previous posts in this series, I discussed what observability is, as it relates to OpenTelemetry, and what tracing is. Today, I’d like to cover the second major area of the OpenTelemetry API: metrics. &lt;/p&gt;

&lt;p&gt;OpenTelemetry’s metrics API supports reporting diagnostic measurements from a service using the three basic kinds of instruments. These instruments are commonly known as counters, gauges, and measures. Developers use these objects in order to gain visibility into operational metrics about their service.&lt;/p&gt;

&lt;p&gt;Most developers are familiar with metrics in some fashion. It’s extremely common, for instance, to monitor metric values such as process memory utilization or error rate, and to create alerts to indicate when a service is violating a predetermined threshold. In addition to these common measurements, metrics events streaming to these instruments can be applied in other unique ways, including by being aggregated and recorded by tracing or logging systems. With that in mind, let’s look at the instruments available through the OpenTelemetry Metrics API and discuss how they can be used.&lt;/p&gt;

&lt;p&gt;The API distinguishes between the metric instruments available through their semantic meaning rather than the eventual type of the value they export. This is somewhat unconventional, and it stems from the design of OpenTelemetry itself -- the separation between the API and the SDK forces the SDK to ultimately determine what should happen with any specific metric event, and could potentially implement a given instrument in an non-obvious or non-standard way. If you’re familiar with existing metrics APIs (such as the Prometheus API), this explains why there’s no method to export a histogram or summary distribution. These are considered to be measures, and the SDK can be configured to export a histogram or a summary from a measure. &lt;/p&gt;

&lt;p&gt;From a developer’s point of view, the exact backing instrument is somewhat opaque by design, as the API is designed for portability. Developers use the Add, Set, and Record methods with optional declarations to set restrictions on the specific measure (for example, to allow a counter to support positive and negative values) and the SDK takes care of the rest. That said, which of these should you use for any given scenario?&lt;/p&gt;

&lt;p&gt;If you’re trying to record a count of something -- such as the sum of errors over time -- then you should use the Add method, which is supported by a counter. A counter is, by default, monotonic, which means it only expects positive values. This property allows a counter by default to be interpreted as a rate measurement. Rates are a comparison between two quantities -- bytes transferred per second, for example. Non-monotonic counters are also supported, which can be useful for reporting changes in a quantity (like the number of elements in a set as they’re added and removed). &lt;/p&gt;

&lt;p&gt;What about circumstances where you need a count, but not a quantity? For example, if you’re measuring something over an arbitrary interval or you don’t care about the rate of change, just the current value? This is when you would use a gauge, by calling Set. You can think about a gauge like a thermostat, or a ratio (such as memory consumption vs. total memory available). As you may expect, the default for these instruments is non-monotonic, but a monotonic option is available as well. Why? To report a change, versus a sum. If you’re trying to report a sum you should use a gauge, but if you’re reporting increments then you should use a counter. &lt;/p&gt;

&lt;p&gt;Finally, what should you use when you care about individual measurements? Consider a circumstance where you want to know both the count and sum of a given event, or when you’re interested in grouping events into quantiles? You would use Record for these values, as it expresses a measure. A common application of measures is to create a histogram or summary of values. You could use these to calculate averages, like the average size of a particular response, or the average duration of HTTP requests.&lt;/p&gt;

&lt;p&gt;This is getting rather long, so let’s do a quick review. There are three instruments you can use in OpenTelemetry, each defined by the method you call to send a metric event. They are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Counters, which you &lt;em&gt;Add&lt;/em&gt; a value to. These are good for values that you’d like to think of as a rate, or changes in a quantity.&lt;/li&gt;
&lt;li&gt;Gauges, which you &lt;em&gt;Set&lt;/em&gt; the value of. You can think of these as either a car’s odometer (a monotonic gauge, it never decreases) or a car’s speedometer (a non-monotonic gauge, as it can go up and down.)&lt;/li&gt;
&lt;li&gt;Measures, to which you &lt;em&gt;Record&lt;/em&gt; a value. These are useful to build histograms or summaries, metric projections that let you calculate averages of many values.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The exact mechanism by which you’ll use each of these instruments is a bit language-specific -- OpenTelemetry, by design, is trying to allow each language SIG to implement the API in a way that is conventional for the language it’s being implemented in. This means the exact details of creating a new metric event may not match the specification precisely, you should consult the documentation for your particular OpenTelemetry implementation for more information. At a high level, however, here’s how it works.&lt;/p&gt;

&lt;p&gt;First, you’ll need to create an instrument of the appropriate kind, and give it a descriptive name. Each instrument name needs to be unique inside its process. You can also provide label keys, which are optional key values that are used to optimize the metric export pipeline. You’ll also need to initialize a LabelSet, which is a set of labels (both keys and values) that correspond to attributes being set on your metric events. What does this look like?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// in our init or in middleware...
requestKeys = ["path", "host"] // label keys

// now we initialize instruments for our keys, a Count and a Measure
requestBytes = NewIntCounter("request.bytes", WithKeys(requestKeys), withUnit(unit.Bytes))
requestLatency = NewFloatMeasure("request.latency", WithKeys(requestKeys), withUnit(unit.Second))

// then, define the labels for each key (and add more if required)
labels = meter.DefineLabels({“path”: “/api/getFoo/{id}”, “host”: “host.name”})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Again, the specifics are going to be slightly different for each language, but the basic gist is the same -- early on, you create actual metric instruments by giving them a name, and telling them what keys they’ll be seeing. After that, either explicitly add labels for your instruments (or get a blank set of labels, if appropriate). Once you’ve accomplished this, actually recording metric events is fairly straightforward.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;requestBytes.Add(labels, req.bytes)
requestLatency.Record(labels, req.latency)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Another option, for high-performance scenarios, is to utilize a handle. This effectively skips a step, by precomputing the instrument and labels (after all, you can add arbitrary labels to a label set, and calling an instrument through the label set will add that event to each combination of label + instrument), useful if you’re in a performance-critical section of code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;requestBytesHandle = requestBytes.GetHandle(labels)
requestLatencyHandle = requestLatency.GetHandle(labels)

for req in requestBatch {
    ...
    requestBytesHandle.Add(req.bytes)
    requestLatencyHandle.Record(req.latency)
    ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;One thing to remember with handles, however, is that you’re responsible for ‘cleaning up’ after you’re done by freeing the handle and deallocating it!&lt;/p&gt;

&lt;p&gt;So, to summarize one more time: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Metric events are recorded through instruments.&lt;/li&gt;
&lt;li&gt;You need to create a unique instrument for each metric you want to record, and give it a unique name.&lt;/li&gt;
&lt;li&gt;You can apply additional metadata to your instrument through labels.&lt;/li&gt;
&lt;li&gt;Recording metric events is performed either by calling the appropriate method on the instrument itself, or by getting a precomputed handle and calling the method there.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hopefully this has given you a better understanding of how the OpenTelemetry metrics API functions. It’s a lot to take in! The real power of OpenTelemetry, however, isn’t just that it provides a tracing and metrics API for you to use. In my next post, I’ll cover the exporter model, and the OpenTelemetry Collector, which are the ways you’ll get data out of OpenTelemetry and into analysis systems such as LightStep.&lt;/p&gt;

</description>
      <category>observability</category>
      <category>devops</category>
      <category>sre</category>
    </item>
    <item>
      <title>OpenTelemetry 101: How to Start Contributing</title>
      <dc:creator>austin</dc:creator>
      <pubDate>Thu, 10 Oct 2019 17:25:43 +0000</pubDate>
      <link>https://dev.to/lightstep/opentelemetry-101-how-to-start-contributing-537l</link>
      <guid>https://dev.to/lightstep/opentelemetry-101-how-to-start-contributing-537l</guid>
      <description>&lt;p&gt;The strength and durability of any open source project is, quite often, measured by its contributor base. Projects with a large and active community – not just users, but also individuals who give back by opening issues, fixing bugs, writing documentation, and so forth – are generally ones that produce higher quality software. If you’ve been reading about &lt;a href="https://opentelemetry.io"&gt;OpenTelemetry&lt;/a&gt; then you may be asking yourself how you can get involved. It’s a great question, and it’s also a great time to dig in and start making pull requests during &lt;a href="https://hacktoberfest.digitalocean.com/"&gt;Hacktoberfest&lt;/a&gt; (you can get a free t-shirt!), so let me tell you the best way to get started.&lt;/p&gt;

&lt;h1&gt;
  
  
  Join The Discussion
&lt;/h1&gt;

&lt;p&gt;Nearly all communication related to OpenTelemetry is public, and if you want to get started contributing, then it’s easy to join in the discussion. Every SIG (Special Interest Group) has regular meetings, all of which are published on a public calendar (links to which are as follows: &lt;a href="https://calendar.google.com/calendar?cid=Z29vZ2xlLmNvbV9iNzllM2U5MGo3YmJzYTJuMnA1YW41bGY2MEBncm91cC5jYWxlbmRhci5nb29nbGUuY29t"&gt;gCal&lt;/a&gt;, &lt;a href="https://calendar.google.com/calendar/ical/google.com_b79e3e90j7bbsa2n2p5an5lf60%40group.calendar.google.com/public/basic.ics"&gt;iCal&lt;/a&gt;, &lt;a href="https://calendar.google.com/calendar/embed?src=google.com_b79e3e90j7bbsa2n2p5an5lf60%40group.calendar.google.com&amp;amp;ctz=America%2FLos_Angeles"&gt;web&lt;/a&gt;). Recordings of past SIG meetings can be found on our &lt;a href="https://www.youtube.com/channel/UCHZDBZTIfdy94xMjMKz-_MA"&gt;YouTube channel&lt;/a&gt;. Want to just dip your toe in on the community in general? Everyone is encouraged to join in the monthly meetings, held on the second Wednesday of every month. You can also join the &lt;a href="https://lists.cncf.io/g/cncf-opentelemetry-contributors"&gt;mailing list&lt;/a&gt; to keep track of discussions and questions. The mailing list will publish announcements, project updates, and other important news in an easy-to-follow format. If you just want to chat, check out our &lt;a href="https://gitter.im/open-telemetry/community"&gt;Gitter&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Honestly, if all you feel like you can contribute is your awareness, then that’s great! Being aware of what’s happening in the project and making your voice heard about decisions like the API and design of the SDK are valuable – we love hearing from people with experience instrumenting their code for telemetry, or running observability systems. Your expertise not only matters, it can be used to help push the state of the art forward.&lt;/p&gt;

&lt;h1&gt;
  
  
  Join A SIG
&lt;/h1&gt;

&lt;p&gt;If you’re interested in participating more concretely, then you should think about joining a SIG. We organize our community members that want to work on specific aspects of the project into SIGs by language, or area of interest. For example, if you wanted to help implement the OpenTelemetry API specification into code, you could join the Java or .NET SIG. If you want to debate the finer points of the cross-language specification, then the Specification SIG might be more your speed. If you want to support the community by maintaining the website, then there’s a SIG for that too!&lt;/p&gt;

&lt;p&gt;There’s nothing special that you need to do to join a SIG – just drop in to a weekly meeting and go from there!&lt;/p&gt;

&lt;h1&gt;
  
  
  Your First Contribution
&lt;/h1&gt;

&lt;p&gt;If you’re looking to contribute, then the easiest way to jump in is by doing. Find a repository in the OpenTelemetry organization that you’d like to make a commit to, then make a &lt;a href="https://help.github.com/en/articles/fork-a-repo"&gt;fork&lt;/a&gt; of it into your account. Want to know what to work on? Check the issues tab and filter the list by labels such as “up for grabs” or “good first issue”. These will be some places you can start contributing immediately! Be sure to post in the issue and let a maintainer know that you’re taking the issue for yourself so they can update the issue accordingly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q07yC1N9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/xty0wesx0xby3tfzlmhp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q07yC1N9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/xty0wesx0xby3tfzlmhp.png" alt="A sample issue from the OpenTelemetry repositories."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to add new functionality, it’s generally a good idea to create an issue and discuss it with that SIG’s maintainers first, to make sure that your idea is in line with the overall project goals. You can also create issues in order to report bugs, make suggestions, or give feedback to the SIG maintainers.&lt;/p&gt;

&lt;p&gt;You don’t have to limit yourself to contributing code, though. The project needs more examples, sample code, documentation, and other resources to make it as easy to use as possible. You can find issues for building documentation in the &lt;a href="https://github.com/open-telemetry/opentelemetry.io/issues"&gt;website repository&lt;/a&gt; that are ready-to-go for Hacktoberfest, with more to come. Finally, you can contribute by communicating about OpenTelemetry itself. Tell your friends! Tell your coworkers! We’re extremely excited about our alpha releases that are coming out over the next few weeks, and would love to get more users to look at them and give their impressions.&lt;/p&gt;

&lt;p&gt;So, again, if you want to contribute to OpenTelemetry, there’s three things I’d suggest:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Subscribe to the mailing list, join the Gitter, and follow/star the project on GitHub.&lt;/li&gt;
&lt;li&gt;Check out the public calendar and attend SIG meetings, and/or the monthly community meeting.&lt;/li&gt;
&lt;li&gt;Open an issue, make a PR, write a tweet, or create some samples that demonstrate how to use OpenTelemetry – anything, and everything, is appreciated!&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>observability</category>
      <category>contributorswanted</category>
      <category>hacktoberfest</category>
    </item>
    <item>
      <title>OpenTelemetry 101: What Is Tracing?</title>
      <dc:creator>austin</dc:creator>
      <pubDate>Thu, 03 Oct 2019 17:58:08 +0000</pubDate>
      <link>https://dev.to/lightstep/opentelemetry-101-what-is-tracing-1605</link>
      <guid>https://dev.to/lightstep/opentelemetry-101-what-is-tracing-1605</guid>
      <description>&lt;p&gt;&lt;a href="https://opentelemetry.io/" rel="noopener noreferrer"&gt;OpenTelemetry&lt;/a&gt; is an open-source observability framework for generating, capturing, and collecting telemetry data for cloud-native software. In my previous post, I covered what &lt;a href="https://lightstep.com/blog/opentelemetry-101-what-is-tracing/" rel="noopener noreferrer"&gt;observability means for software&lt;/a&gt;, and talked about the three types of telemetry data that comprise observability signals -- tracing, metrics, and logs. Today, I’ll be taking a deeper look at the first of these, tracing.&lt;/p&gt;

&lt;p&gt;When we refer to tracing in OpenTelemetry, we’re generally referring to distributed tracing (or distributed request tracing). Traditionally, tracing is a low-level practice used to profile and analyze application code by developers through a combination of specialized debugging tools (such as dtrace on Linux or ETW on Windows) and programming techniques. By contrast, distributed tracing is an application of these techniques to modern, microservice-based architectures. &lt;/p&gt;

&lt;p&gt;Microservices introduce significant challenges to tracing a request through an application, thanks to the distributed nature of microservices deployments. Consider a traditional monolithic application: since your code is centralized onto a single host, diagnosing a failure can be as simple as following a stack trace. When your application consists of tens, hundreds, or thousands of services running across many hosts, you can’t rely on a single stack trace -- you need something that represents the entire request as it moves from service to service, component to component. Distributed tracing solves this problem, providing powerful capabilities such as anomaly detection, distributed profiling, workload modeling, and diagnosis of steady-state problems.&lt;/p&gt;

&lt;p&gt;Much of the terminology and mental models that we use to describe distributed tracing can trace their origin to systems such as &lt;a href="https://www.usenix.org/legacy/publications/library/proceedings/hotos03/tech/full_papers/barham/barham_html/paper.html" rel="noopener noreferrer"&gt;Magpie&lt;/a&gt;, &lt;a href="https://www.usenix.org/legacy/event/nsdi07/tech/full_papers/fonseca/fonseca.pdf" rel="noopener noreferrer"&gt;X-Trace&lt;/a&gt;, and &lt;a href="https://ai.google/research/pubs/pub36356" rel="noopener noreferrer"&gt;Dapper&lt;/a&gt;. Dapper, particularly, has been highly influential to modern distributed tracing, and many of the mental models and terminology that OpenTelemetry uses can trace their origin to there. The goal of these distributed tracing systems was to profile requests that moved across processes and hosts, and generate data about those requests suitable for analysis.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F7h01gjbcn2kjaxighxq4.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F7h01gjbcn2kjaxighxq4.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The above diagram represents a sample trace. A trace is a collection of linked spans, which are named and timed operations that represent a unit of work in the request. A span that isn’t the child of any other span is the parent span, or root span, of the trace. The root span, typically, describes the end-to-end latency of the entire trace, with child spans representing sub-operations.&lt;/p&gt;

&lt;p&gt;To put this in more concrete terms, let’s consider the request flow of a system that you might encounter in the real world, such as a ride sharing app. When a user requests a ride, multiple actions begin to take place -- information is passed between services in order to authenticate and authorize the user, validate their payment information, locate nearby drivers, and dispatch one of them to pick up the rider. A simplified diagram of this system, and a trace of a request through it, appears in the following figure. As you can see, each operation generates a span to represent the work being done during its execution. These spans have implicit relationships (parent-child) both from the beginning of the entire request at the client, but also from individual services in the trace. Traces are composable in this way, where a valid trace is comprised of valid sub-traces.&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%2F39i6d92xpmr4nr1ee2bvv7a1-wpengine.netdna-ssl.com%2Fwp-content%2Fuploads%2F2019%2F10%2FApp-Trace-Illustration.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%2F39i6d92xpmr4nr1ee2bvv7a1-wpengine.netdna-ssl.com%2Fwp-content%2Fuploads%2F2019%2F10%2FApp-Trace-Illustration.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each span in OpenTelemetry encapsulates several pieces of information, such as the name of the operation it represents, a start and end timestamp, events and attributes that occurred during the span, links to other spans, and the status of the operation. In the above diagram, the dashed lines connecting spans represent the context of the trace. The context (or trace context) contains several pieces of information that can be passed between functions inside a process or between processes over an RPC. In addition to the span context, identifiers that represent the parent trace and span, the context can contain other information about the process or request, like custom labels. &lt;/p&gt;

&lt;p&gt;One important feature of spans, as mentioned before, is that they encapsulate other information. Much of this information is required -- the operation name, the start and stop timestamps, for example -- but some is optional. OpenTelemetry offers two data types, &lt;code&gt;Attribute&lt;/code&gt; and &lt;code&gt;Event&lt;/code&gt; which are incredibly valuable as they help to contextualize what happened during the execution measured by a single span. &lt;code&gt;Attribute&lt;/code&gt;s (known as &lt;code&gt;tags&lt;/code&gt; in OpenTracing) are key-value pairs that can be freely added to a span to help in analysis of the trace data. You can think of attributes as data that you’d like to eventually aggregate or use to filter your trace data, like a customer identifier, process hostname, or anything else you can imagine. &lt;code&gt;Event&lt;/code&gt;s (known as &lt;code&gt;logs&lt;/code&gt; in OpenTracing) are time-stamped strings that can be attached to a span, with an optional set of &lt;code&gt;Attribute&lt;/code&gt;s that further describe it. OpenTelemetry additionally provides a set of &lt;a href="https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md" rel="noopener noreferrer"&gt;semantic conventions&lt;/a&gt; of reserved attributes and events for operation or protocol specific information.&lt;/p&gt;

&lt;p&gt;Spans in OpenTelemetry are generated by the &lt;code&gt;Tracer&lt;/code&gt;, an object that tracks the currently active span and allows you to create (or activate) new spans. Tracers are configured with &lt;code&gt;Propagator&lt;/code&gt; objects that support transferring the context across process boundaries. The exact mechanism of creating and registering a tracer is dependent on your implementation and language, but you can generally expect there to be a global &lt;code&gt;Tracer&lt;/code&gt; capable of providing a default tracer for your spans, and/or a &lt;code&gt;Tracer&lt;/code&gt; provider capable of granting access to the tracer for your component. As spans are created and completed, the tracer dispatches them to the OpenTelemetry SDK’s &lt;code&gt;Exporter&lt;/code&gt;, which is responsible for sending your spans to a backend system for analysis.&lt;/p&gt;

&lt;p&gt;To recap, let’s summarize:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A span is the basic building block of a trace. A trace is a collection of linked spans.&lt;/li&gt;
&lt;li&gt;Spans are objects that represent a unit of work, which is a named operation such as the execution of a microservice or a function call.&lt;/li&gt;
&lt;li&gt;A parentless span is known as the root span or parent span of a trace.&lt;/li&gt;
&lt;li&gt;Spans contain attributes and events, which describe and contextualize the work being done under a span.&lt;/li&gt;
&lt;li&gt;A tracer is used to create and manage spans inside a process, and across process boundaries through propagators.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my next post in this series, I plan to discuss the OpenTelemetry metrics data source, and how it interacts with the traces. Stay tuned!&lt;/p&gt;

</description>
      <category>observability</category>
      <category>devops</category>
      <category>opentelemetry</category>
    </item>
  </channel>
</rss>
