<?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>Forem: Lightstep</title>
    <description>The latest articles on Forem by Lightstep (@lightstep).</description>
    <link>https://forem.com/lightstep</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%2Forganization%2Fprofile_image%2F1250%2Fd7396685-f48f-4ea2-bea3-729b4c5c31ce.png</url>
      <title>Forem: Lightstep</title>
      <link>https://forem.com/lightstep</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/lightstep"/>
    <language>en</language>
    <item>
      <title>Tracetest in Action: Running Trace-Based Tests on the OpenTelemetry Demo App with Nomad</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Tue, 28 Feb 2023 15:00:00 +0000</pubDate>
      <link>https://forem.com/lightstep/tracetest-in-action-running-trace-based-tests-on-the-opentelemetry-demo-app-with-nomad-5dg0</link>
      <guid>https://forem.com/lightstep/tracetest-in-action-running-trace-based-tests-on-the-opentelemetry-demo-app-with-nomad-5dg0</guid>
      <description>&lt;p&gt;The concept of Trace-based testing (TBT) was first presented by my awesome colleague, &lt;a href="https://twitter.com/tedsuo" rel="noopener noreferrer"&gt;Ted Young&lt;/a&gt;, at &lt;a href="https://www.youtube.com/watch?v=NU-fTr-udZg" rel="noopener noreferrer"&gt;KubeCon North America 2018&lt;/a&gt;. The idea behind it was simple, yet elegant: if you’re already creating distributed &lt;a href="https://opentelemetry.io/docs/concepts/signals/traces/" rel="noopener noreferrer"&gt;Traces&lt;/a&gt; in your application code, why not use this same Trace data to create test assertions to validate your end-to-end system flow?&lt;/p&gt;

&lt;p&gt;Back in 2018, Trace-based testing was just an idea. Fast-forward to today: TBT is now a reality, thanks to Trace standardization à la &lt;a href="https://opentelemetry.io" rel="noopener noreferrer"&gt;OpenTelemetry&lt;/a&gt; (OTel) and Trace-based testing tools like &lt;a href="https://tracetest.io" rel="noopener noreferrer"&gt;Tracetest&lt;/a&gt;, &lt;a href="https://gethelios.dev" rel="noopener noreferrer"&gt;Helios&lt;/a&gt;, and &lt;a href="https://github.com/aspecto-io/malabi" rel="noopener noreferrer"&gt;Malabi&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I first heard about TBT in the summer of 2022, when I was writing a piece on how &lt;a href="https://lightstep.com/blog/observability-mythbusters-not-only-for-sre" rel="noopener noreferrer"&gt;Observability is useful for QAs&lt;/a&gt;. After writing the piece, I wanted to get my hands dirty and try this Trace-based testing thing for myself. The tool that I chose for my initial TBT explorations was Tracetest, which is open-source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tracetest Overview
&lt;/h2&gt;

&lt;p&gt;Tracetest came out in May 2022, and I first got my hands on Tracetest &lt;a href="https://github.com/lightstep/tracetest-nomad/tree/main/jobspec" rel="noopener noreferrer"&gt;back in June 2022&lt;/a&gt;, so it was still pretty fresh! At the time, it integrated only with Jaeger. More specifically, it used the Jaeger API to pull OTel Traces from Jaeger to register them into Tracetest. You then used the UI to create trace-based tests.&lt;/p&gt;

&lt;p&gt;Tracetest has come a long way since those early days! It now has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://docs.tracetest.io/cli/configuring-your-cli" rel="noopener noreferrer"&gt;CLI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A quickstart installer so that you can get a full-fledged example up and running on an existing Kubernetes cluster (Very slick and easy to get going, I might add!)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://tracetest.io/integrations" rel="noopener noreferrer"&gt;Integrations&lt;/a&gt; with a number of Observability back-ends and other tools, including &lt;a href="https://app.lightstep.com" rel="noopener noreferrer"&gt;Lightstep&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A declarative approach to defining Trace-based tests via YAML&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I got to play around with these newer features last December, after a months-long hiatus, and it was really cool to see the evolution of the product. If you follow &lt;a href="https://linktr.ee/adriana_villela" rel="noopener noreferrer"&gt;my work&lt;/a&gt;, you’ll know that I play in both the Kubernetes and Nomad worlds. Today, I’ll be taking you on a quick little guided tour of Tracetest, using Traces from the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo" rel="noopener noreferrer"&gt;OpenTelemetry Demo App&lt;/a&gt; to give you a feel for how it works. The whole setup will be running on HashiCorp Nomad. \&lt;/p&gt;

&lt;h2&gt;
  
  
  Tutorial
&lt;/h2&gt;

&lt;p&gt;In this tutorial, I will show you how to install Tracetest (v0.9.1) and the OTel Demo App (v1.1.0) on Nomad running locally using &lt;a href="https://github.com/servian/hashiqube" rel="noopener noreferrer"&gt;HashiQube&lt;/a&gt;. I will then show you how to create and run a simple test with Tracetest, using a couple of Traces from the OTel Demo App.&lt;/p&gt;

&lt;p&gt;Although the versions of both Tracetest and the OTel Demo App used in this tutorial are slightly older than what’s out there, you should still get a decent idea of how everything works. The OTel Demo App is configured to send Traces and Metrics to Lightstep via the &lt;a href="https://opentelemetry.io/docs/collector/" rel="noopener noreferrer"&gt;OTel Collector&lt;/a&gt;. The Collector is also configured to send Traces to Tracetest. This configuration is based on guidance from the &lt;a href="https://docs.tracetest.io/configuration/connecting-to-data-stores/lightstep/" rel="noopener noreferrer"&gt;official Tracetest docs&lt;/a&gt;. You can see the full Collector configuration used in this example &lt;a href="https://github.com/avillela/nomad-conversions/blob/ed7b383ab743e279e4b658d3b6bdf036be3dc46f/tracetest/jobspec/otel-collector.nomad#L93-L163" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Please note that there are no official Nomad jobspecs for either Tracetest or the OTel Demo App, so I went ahead and did the conversions myself from Kubernetes manifests to Nomad jobspecs. You can check out the jobspecs in &lt;a href="https://github.com/avillela/nomad-conversions" rel="noopener noreferrer"&gt;this repo&lt;/a&gt;. If you’re curious as to how I went about the Kubernetes-to-Nomad conversion, you can &lt;a href="https://medium.com/dev-genius/how-to-convert-kubernetes-manifests-into-nomad-jobspecs-7a58d2fa07a0" rel="noopener noreferrer"&gt;check out my blog post on this topic&lt;/a&gt;. I also have a blog post dedicated to running the OTel Demo App on Nomad. If this tickles your fancy, you can check it out &lt;a href="https://medium.com/@adri-v/running-the-opentelemetry-demo-app-on-hashicorp-nomad-a3e21e35369d" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Assumptions
&lt;/h3&gt;

&lt;p&gt;Before we move on, I am assuming that you have a basic understanding of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nomad. If not, mosey on over to my &lt;a href="https://adri-v.medium.com/just-in-time-nomad-80f57cd403ca" rel="noopener noreferrer"&gt;Nomad intro post&lt;/a&gt;. &lt;a href="https://danielabaron.me/blog/nomad-tips-and-tricks/" rel="noopener noreferrer"&gt;This blog post&lt;/a&gt; by &lt;a href="https://danielabaron.me/" rel="noopener noreferrer"&gt;Daniela Baron&lt;/a&gt; is also great.&lt;/li&gt;
&lt;li&gt;Observability (o11y) and &lt;a href="https://opentelemetry.io/" rel="noopener noreferrer"&gt;OpenTelemetry&lt;/a&gt; (OTel). If not, mosey on over to my &lt;a href="https://storiesfromtheherd.com/unpacking-observability-the-observability-stack-93d4733e2a72" rel="noopener noreferrer"&gt;Observability &amp;amp; OTel post&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pre-Requisites
&lt;/h3&gt;

&lt;p&gt;In order to run the example in this tutorial, you’ll need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://vagrantup.com/" rel="noopener noreferrer"&gt;Vagrant&lt;/a&gt; (version 2.3.1 at the time of this writing) - to provision HashiQube&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; (version 20.10.21 at the time of this writing) - we’ll be running HashiQube on Docker, using the &lt;a href="https://developer.hashicorp.com/vagrant/docs/providers/docker" rel="noopener noreferrer"&gt;Vagrant Docker Provider&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll also need to make sure that your Docker Desktop resource settings are set to the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPUs: 3&lt;/li&gt;
&lt;li&gt;Memory: 9.5GB&lt;/li&gt;
&lt;li&gt;Swap: 3GB&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tutorial Repos
&lt;/h3&gt;

&lt;p&gt;Below are the repos that we’ll be using for today’s tutorial:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/avillela/hashiqube" rel="noopener noreferrer"&gt;My modified HashiQube Repo&lt;/a&gt; (fork of &lt;a href="https://github.com/servian/hashiqube" rel="noopener noreferrer"&gt;servian/hashiqube&lt;/a&gt;). If you’re curious, you can see what modifications I’ve made &lt;a href="https://github.com/avillela/hashiqube#about" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;My &lt;a href="https://github.com/avillela/nomad-conversions" rel="noopener noreferrer"&gt;Nomad Conversions&lt;/a&gt; repo&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Part 1: Provision Nomad, Consul, and Vault using HashiQube
&lt;/h3&gt;

&lt;p&gt;In this section, we’ll be provisioning a local Hashi environment (including Nomad, Consul, and Vault) instance using &lt;a href="https://github.com/avillela/hashiqube" rel="noopener noreferrer"&gt;HashiQube&lt;/a&gt;. Then, we’ll install &lt;a href="https://tracetest.io" rel="noopener noreferrer"&gt;Tracetest&lt;/a&gt; and the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo" rel="noopener noreferrer"&gt;OTel Demo App&lt;/a&gt; on &lt;a href="https://nomadproject.io" rel="noopener noreferrer"&gt;Nomad&lt;/a&gt;. Feel free to skip this section if you already have a working Hashi environment with Nomad, Consul, and Vault.&lt;/p&gt;

&lt;h4&gt;
  
  
  1- Update /etc/hosts
&lt;/h4&gt;

&lt;p&gt;We use the &lt;a href="https://traefik.io" rel="noopener noreferrer"&gt;Traefik&lt;/a&gt; load-balancer to expose our services, which we access as subdomains of &lt;code&gt;localhost&lt;/code&gt;. In order ensure that we can access our Traefik-exposed services (and also the Traefik dashboard itself, you’ll need to add the following entries to &lt;code&gt;/etc/hosts&lt;/code&gt; on your host machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1 traefik.localhost
127.0.0.1 otel-demo.localhost
127.0.0.1 tracetest.localhost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more on Traefik load-balancing on Nomad, check out &lt;a href="https://medium.com/dev-genius/running-hashiqube-using-the-vagrant-docker-provider-3e551c0eca97" rel="noopener noreferrer"&gt;this post&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  2- Provision a Local Hashi Environment with HashiQube
&lt;/h4&gt;

&lt;p&gt;Start HashiQube by following the detailed instructions &lt;a href="https://github.com/avillela/hashiqube#quickstart" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can now access the apps below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vault: &lt;a href="http://localhost:8200" rel="noopener noreferrer"&gt;http://localhost:8200&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Nomad: &lt;a href="http://localhost:4646" rel="noopener noreferrer"&gt;http://localhost:4646&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Consul: &lt;a href="http://localhost:8500" rel="noopener noreferrer"&gt;http://localhost:8500&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Traefik: &lt;a href="http://traefik.localhost" rel="noopener noreferrer"&gt;http://traefik.localhost&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Part 2: Deploying Tracetest and the OTel Demo App on Nomad
&lt;/h3&gt;

&lt;p&gt;We’re ready to deploy the OTel Demo App and Tracetest on Nomad!&lt;/p&gt;

&lt;h4&gt;
  
  
  1- Add Lightstep Access Token to Vault
&lt;/h4&gt;

&lt;p&gt;As I mentioned earlier, we’ll be sending our Traces to both Lightstep and Tracetest. In order to send Traces to Lightstep, you’ll need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get a &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens#create-an-access-token" rel="noopener noreferrer"&gt;Lightstep Access Token&lt;/a&gt;. (Make sure that you &lt;a href="https://app.lightstep.com/signup" rel="noopener noreferrer"&gt;sign up&lt;/a&gt; for a &lt;a href="https://app.lightstep.com/" rel="noopener noreferrer"&gt;Lightstep&lt;/a&gt; account first, if you don’t already have one.)&lt;/li&gt;
&lt;li&gt;Configure Vault by following the instructions &lt;a href="https://github.com/avillela/hashiqube#vault-setup" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Add your Lightstep Access Token to Vault by running the command:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vault kv put kv/otel/o11y/lightstep &lt;span class="nv"&gt;ls_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;LS_TOKEN&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;&amp;lt;LS_TOKEN&amp;gt;&lt;/code&gt; is your &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens#create-an-access-token" rel="noopener noreferrer"&gt;Lightstep Access Token&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  2- Deploy the OTel Demo App and Tracetest
&lt;/h4&gt;

&lt;p&gt;First, let’s clone the repo, and go to our working directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/avillela/nomad-conversions.git
&lt;span class="nb"&gt;cd &lt;/span&gt;nomad-conversions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let’s deploy the services:&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="c"&gt;# Enable memory over-subscription&lt;/span&gt;
nomad operator scheduler set-config &lt;span class="nt"&gt;-memory-oversubscription&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="c"&gt;# Tracetest deploy&lt;/span&gt;
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; tracetest/jobspec/traefik.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; tracetest/jobspec/postgres.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; tracetest/jobspec/tracetest.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; tracetest/jobspec/otel-collector.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; tracetest/jobspec/go-server.nomad
&lt;span class="c"&gt;# OTel Demo App deploy&lt;/span&gt;
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/redis.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/ffspostgres.nomad

nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/adservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/cartservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/currencyservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/emailservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/featureflagservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/paymentservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/productcatalogservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/quoteservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/shippingservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/checkoutservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/recommendationservice.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/frontend.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/loadgenerator.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/frontendproxy.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/grafana.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/jaeger.nomad
nomad job run &lt;span class="nt"&gt;-detach&lt;/span&gt; otel-demo-app/jobspec/prometheus.nomad
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; We’ve enabled memory &lt;a href="https://developer.hashicorp.com/nomad/docs/commands/operator/scheduler/set-config#memory-oversubscription" rel="noopener noreferrer"&gt;oversubscription&lt;/a&gt; in Nomad. This is a one-time setting.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since we’re running the jobs in &lt;a href="https://developer.hashicorp.com/nomad/docs/commands/job/run#detach" rel="noopener noreferrer"&gt;detached mode&lt;/a&gt;, Nomad won’t wait to start the next job until the current one has deployed successfully. This means that your output will look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Job registration successful
Evaluation ID: d3eaa396-954e-241f-148d-6720c35f34bf
Job registration successful
Evaluation ID: 6bba875d-f415-36b7-bfeb-2ca4b9982acb
Job registration successful
Evaluation ID: 16dc8ef8-5e26-68f4-89b6-3d96b348775b
Job registration successful
Evaluation ID: 34de0532-a3b5-8691-bf18-51c0cc030573
Job registration successful
Evaluation ID: 7310e6a2-9945-710b-1505-c01bd58ccd35
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Friendly reminder that the &lt;code&gt;Evaluation ID&lt;/code&gt; values will be different on your machine.&lt;/p&gt;

&lt;h4&gt;
  
  
  3- See it in Nomad
&lt;/h4&gt;

&lt;p&gt;As things are deploying, you can mosey on over to the Nomad UI at &lt;a href="http://localhost:4646" rel="noopener noreferrer"&gt;http://localhost:4646&lt;/a&gt; to see how things are coming along:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqnnviyj9nx5c0envpzxl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqnnviyj9nx5c0envpzxl.png" alt="Screen capture of the HashiCorp Nomad UI showing the OTel Demo App and Tracetest services starting up" width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It will take some time for all of the services to come up (sometimes up to 30 minutes), depending on network speed (especially since Nomad needs to download the images and initialize the services) and system resources, so be patient! Since some services depend on other services in order to run, you may see services in limbo or some going up and down for a while, per the above screen capture. DON’T PANIC! IT WILL ALL BE OKAY!!&lt;/p&gt;

&lt;p&gt;Once all of the jobs are up and running, you’ll see everything look green, like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flbhqncw8z44gzkl3qe5x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flbhqncw8z44gzkl3qe5x.png" alt="Screen capture of the HashiCorp Nomad UI showing the OTel Demo App and Tracetest services started" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also head on over to Consul at &lt;a href="http://localhost:8500" rel="noopener noreferrer"&gt;http://localhost:8500&lt;/a&gt; to see the health of the services:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ne4u1pqf6o6ic1qdlyc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ne4u1pqf6o6ic1qdlyc.png" alt="Screen capture of services running in Consul" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By default, unhealthy services show up at the top, with a red “x” next to them. Since we don’t see any nasty red “x”s in the above screen shot, we know that our services are lookin’ good!&lt;/p&gt;

&lt;h4&gt;
  
  
  4- Access the OTel Demo App
&lt;/h4&gt;

&lt;p&gt;To make sure that the OTel Demo App is up and running, you can access it at: &lt;a href="http://otel-demo.localhost" rel="noopener noreferrer"&gt;http://otel-demo.localhost&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftf96aebpmgmehcypcygx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftf96aebpmgmehcypcygx.png" alt="Screen capture of the OTel Demo app running at http://otel-demo.localhost" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  5- Access Tracetest
&lt;/h4&gt;

&lt;p&gt;You should also be able to access the Tracetest UI at: &lt;a href="http://tracetest.localhost" rel="noopener noreferrer"&gt;http://tracetest.localhost&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpwp2gxv70td3eruob9ra.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpwp2gxv70td3eruob9ra.png" alt="Tracetest user interface running at http://tracetest.localhost" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 3: Creating &amp;amp; Running Tests in Tracetest
&lt;/h3&gt;

&lt;p&gt;Now that we have the OTel Demo App and Tracetest up and running, we can finally create our tests in Tracetest!&lt;/p&gt;

&lt;h4&gt;
  
  
  1- Install the Tracetest CLI
&lt;/h4&gt;

&lt;p&gt;The Tracetest CLI allows you to interact with Tracetest programmatically. You can install the Tracetest CLI on Mac via Homebrew:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;kubeshop/tracetest/tracetest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Windows and Linux, check out the instructions &lt;a href="https://docs.tracetest.io/getting-started/installation#install-the-tracetest-cli" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s make sure that the CLI is installed properly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tracetest version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample output:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  2- Configure the Tracetest CLI
&lt;/h4&gt;

&lt;p&gt;Now that the CLI is installed, let’s configure the Tracetest CLI so that it knows what Tracetest installation it needs to talk to. Make sure that you’re still in the &lt;a href="https://github.com/avillela/nomad-conversions" rel="noopener noreferrer"&gt;nomad-conversions&lt;/a&gt; root directory, and then run:&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;cd &lt;/span&gt;tracetest
tracetest configure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a file called &lt;code&gt;config.yaml&lt;/code&gt; in the directory from which you ran &lt;code&gt;tracetest configure&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When you run the above command, you will be prompted for the Tracetest server URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Enter your Tracetest server URL [http://tracetest.localhost]:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that it’s already pre-populated with &lt;code&gt;http://tracetest.localhost&lt;/code&gt;. This is because there’s already a &lt;code&gt;config.yaml&lt;/code&gt; file in our &lt;code&gt;tracetest&lt;/code&gt; directory. If you don’t type anything in and hit return, the CLI will just use the value already in &lt;code&gt;config.yaml&lt;/code&gt;. If you want to use a different URL, make sure that you prefix that URL with &lt;code&gt;http://&lt;/code&gt; (or &lt;code&gt;https://&lt;/code&gt; if you’re hosting a secure instance).&lt;/p&gt;

&lt;p&gt;Next, you’ll be prompted on whether or not you wish to enable analytics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Enable analytics? [Y/n]:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you hit return here, it will enable analytics by default. Type &lt;code&gt;n&lt;/code&gt; to disable analytics.&lt;/p&gt;

&lt;p&gt;Now, let’s open up &lt;code&gt;config.yaml&lt;/code&gt;:&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;cat &lt;/span&gt;config.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scheme: http
endpoint: tracetest.localhost
analyticsEnabled: false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3- Create a Trace-based test in Tracetest
&lt;/h4&gt;

&lt;p&gt;Now we’re ready to create a test! We can do this in one of two ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using the UI (check out the Tracetest docs &lt;a href="https://docs.tracetest.io/web-ui/creating-tests" rel="noopener noreferrer"&gt;here&lt;/a&gt; for details)&lt;/li&gt;
&lt;li&gt;From a YAML file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m a big fan of doing things programmatically, so let’s create our test from a YAML file. &lt;a href="https://docs.tracetest.io/web-ui/creating-tests" rel="noopener noreferrer"&gt;Per the Tracetest docs&lt;/a&gt;, you can create tests from HTTP requests, &lt;a href="https://grpc.io" rel="noopener noreferrer"&gt;gRPC&lt;/a&gt; requests, &lt;a href="https://curl.se" rel="noopener noreferrer"&gt;cURL&lt;/a&gt; commands, and more. In our case, we’re creating our tests from a gRPC request, so our Tracetest test definition file YAML looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type: Test
spec:
 id: &amp;lt;test_id&amp;gt;
 name: &amp;lt;test_name&amp;gt;
 description: &amp;lt;test_description&amp;gt;
 trigger:
   type: grpc
   grpc:
     protobufFile: |
        &amp;lt;protobuf_file_contents_here&amp;gt;
     address: &amp;lt;grpc_endpoint_address&amp;gt;
     method: &amp;lt;grpc_method_name&amp;gt;
 specs:
 - selector: span[tracetest.span.type="general" name="&amp;lt;operation_name&amp;gt;"]
   assertions:
   - attr:&amp;lt;attribute_name&amp;gt; &amp;lt;operator&amp;gt; &amp;lt;value&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;test_id&amp;gt;&lt;/code&gt; is a unique test identifier. If you create your test via the UI, a unique ID is assigned. If you create a test from a YAML file, you can assign your own ID. For this example, let’s name our ID &lt;code&gt;​tt-recsvc-01&lt;/code&gt;. As long as it’s unique, you should be good.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;test_name&amp;gt;&lt;/code&gt; is the name of your test. Use something short and meaningful.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;test_description&amp;gt;&lt;/code&gt; is short description of your test.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;trigger.type&lt;/code&gt; is &lt;code&gt;grpc&lt;/code&gt;, since we’re creating a test from a gRPC request.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;trigger.grpc.protobufFile&lt;/code&gt; should be the &lt;strong&gt;&lt;em&gt;complete contents&lt;/em&gt;&lt;/strong&gt; of your &lt;a href="https://en.wikipedia.org/wiki/Protocol_Buffers" rel="noopener noreferrer"&gt;protobuf file&lt;/a&gt;. In our case, we’re going to embed the contents of &lt;a href="https://github.com/open-telemetry/opentelemetry-demo" rel="noopener noreferrer"&gt;OTel Demo App&lt;/a&gt;’s &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/blob/v1.1.0/pb/demo.proto" rel="noopener noreferrer"&gt;demo.proto&lt;/a&gt; file in this field.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;grpc_endpoint_address&amp;gt;&lt;/code&gt; is the address at which the gRPC endpoint is served. In this case, we’re using &lt;a href="https://developer.hashicorp.com/consul/docs/discovery/dns" rel="noopener noreferrer"&gt;Consul DNS&lt;/a&gt; to expose the service’s address. If we look at the &lt;a href="https://github.com/avillela/nomad-conversions/blob/main/otel-demo-app/jobspec/recommendationservice.nomad" rel="noopener noreferrer"&gt;Recommendation Service’s Nomad jobspec&lt;/a&gt;, you’ll see that the name of the gRPC service is &lt;code&gt;recommendationservice&lt;/code&gt;. So when we query it in &lt;a href="https://consul.io" rel="noopener noreferrer"&gt;Consul&lt;/a&gt;, it should be accessible at this address &lt;code&gt;recommendationservice.service.consul&lt;/code&gt;. We can test this by logging into the HashiQube image. Do this by going to the root directory of HashiQube repo, and typing &lt;code&gt;vagrant ssh&lt;/code&gt;. Once you’re in the HashiQube Vagrant box, run this command:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dig +short recommendationservice.service.consul
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected result:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;We get the port number from the Recommendation Service jobspec’s &lt;a href="https://github.com/avillela/nomad-conversions/blob/ed7b383ab743e279e4b658d3b6bdf036be3dc46f/otel-demo-app/jobspec/recommendationservice.nomad#L18" rel="noopener noreferrer"&gt;port definition stanza&lt;/a&gt;. In our case, the port number is &lt;a href="https://github.com/avillela/nomad-conversions/blob/ed7b383ab743e279e4b658d3b6bdf036be3dc46f/otel-demo-app/jobspec/recommendationservice.nomad#L18" rel="noopener noreferrer"&gt;9001&lt;/a&gt;. Note that we need to use a static port number in order for this setup to work.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;grpc_method_name&amp;gt;&lt;/code&gt; is the name of the gRPC method that we’re testing. It follows the naming convention &lt;code&gt;&amp;lt;package_name&amp;gt;.&amp;lt;service_name&amp;gt;.&amp;lt;method_name&amp;gt;&lt;/code&gt;. Where &lt;code&gt;&amp;lt;package_name&amp;gt;&lt;/code&gt; is &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/blob/9a66c3a7e490dc0a30c738b92d931d04ce42f479/pb/demo.proto#L19" rel="noopener noreferrer"&gt;hipstershop&lt;/a&gt;, &lt;code&gt;&amp;lt;service_name&amp;gt;&lt;/code&gt; is &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/blob/9a66c3a7e490dc0a30c738b92d931d04ce42f479/pb/demo.proto#L58" rel="noopener noreferrer"&gt;RecommendationService&lt;/a&gt;, and &lt;code&gt;&amp;lt;method_name&amp;gt;&lt;/code&gt; is &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/blob/9a66c3a7e490dc0a30c738b92d931d04ce42f479/pb/demo.proto#L59" rel="noopener noreferrer"&gt;ListRecommendations&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;spec&lt;/code&gt; is where we define our test assertions. We need to first select our target Span(s), and then we define what assertions to apply to the Span(s).  In our case, we are selecting a span named &lt;code&gt;get_product_list&lt;/code&gt;, and we’re asserting that the value of the attribute &lt;code&gt;app.filtered_products.count&lt;/code&gt; is set to &lt;code&gt;9&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With all that in mind, our YAML should now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type: Test
spec:
id: ​tt-recsvc-01
name: RecommendationService test
description: Sample test file for the OTel Demo App RecommendationService
trigger:
  type: grpc
  grpc:
    protobufFile: |
      // Copyright 2020 Google LLC
      //
      // Licensed under the Apache License, Version 2.0 (the "License");
      // you may not use this file except in compliance with the License.
      // You may obtain a copy of the License at
      //
      //      http://www.apache.org/licenses/LICENSE-2.0
      //
      // Unless required by applicable law or agreed to in writing, software
      // distributed under the License is distributed on an "AS IS" BASIS,
      // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      // See the License for the specific language governing permissions and
      // limitations under the License.

      syntax = "proto3";

      import "google/protobuf/timestamp.proto";

      package hipstershop;

      option go_package = "genproto/hipstershop";

      // -----------------Cart service-----------------

      service CartService {
          rpc AddItem(AddItemRequest) returns (Empty) {}
          rpc GetCart(GetCartRequest) returns (Cart) {}
          rpc EmptyCart(EmptyCartRequest) returns (Empty) {}
      }

      message CartItem {
          string product_id = 1;
          int32  quantity = 2;
      }

      message AddItemRequest {
          string user_id = 1;
          CartItem item = 2;
      }

      message EmptyCartRequest {
          string user_id = 1;
      }

      message GetCartRequest {
          string user_id = 1;
      }

      message Cart {
          string user_id = 1;
          repeated CartItem items = 2;
      }

      message Empty {}

      // ---------------Recommendation service----------

      service RecommendationService {
        rpc ListRecommendations(ListRecommendationsRequest) returns (ListRecommendationsResponse){}
      }

      message ListRecommendationsRequest {
          string user_id = 1;
          repeated string product_ids = 2;
      }

      message ListRecommendationsResponse {
          repeated string product_ids = 1;
      }

      // ---------------Product Catalog----------------

      service ProductCatalogService {
          rpc ListProducts(Empty) returns (ListProductsResponse) {}
          rpc GetProduct(GetProductRequest) returns (Product) {}
          rpc SearchProducts(SearchProductsRequest) returns (SearchProductsResponse) {}
      }

      message Product {
          string id = 1;
          string name = 2;
          string description = 3;
          string picture = 4;
          Money price_usd = 5;

          // Categories such as "clothing" or "kitchen" that can be used to look up
          // other related products.
          repeated string categories = 6;
      }

      message ListProductsResponse {
          repeated Product products = 1;
      }

      message GetProductRequest {
          string id = 1;
      }

      message SearchProductsRequest {
          string query = 1;
      }

      message SearchProductsResponse {
          repeated Product results = 1;
      }

      // ---------------Shipping Service----------

      service ShippingService {
          rpc GetQuote(GetQuoteRequest) returns (GetQuoteResponse) {}
          rpc ShipOrder(ShipOrderRequest) returns (ShipOrderResponse) {}
      }

      message GetQuoteRequest {
          Address address = 1;
          repeated CartItem items = 2;
      }

      message GetQuoteResponse {
          Money cost_usd = 1;
      }

      message ShipOrderRequest {
          Address address = 1;
          repeated CartItem items = 2;
      }

      message ShipOrderResponse {
          string tracking_id = 1;
      }

      message Address {
          string street_address = 1;
          string city = 2;
          string state = 3;
          string country = 4;
          string zip_code = 5;
      }

      // -----------------Currency service-----------------

      service CurrencyService {
          rpc GetSupportedCurrencies(Empty) returns (GetSupportedCurrenciesResponse) {}
          rpc Convert(CurrencyConversionRequest) returns (Money) {}
      }

      // Represents an amount of money with its currency type.
      message Money {
          // The 3-letter currency code defined in ISO 4217.
          string currency_code = 1;

          // The whole units of the amount.
          // For example if `currencyCode` is `"USD"`, then 1 unit is one US dollar.
          int64 units = 2;

          // Number of nano (10^-9) units of the amount.
          // The value must be between -999,999,999 and +999,999,999 inclusive.
          // If `units` is positive, `nanos` must be positive or zero.
          // If `units` is zero, `nanos` can be positive, zero, or negative.
          // If `units` is negative, `nanos` must be negative or zero.
          // For example $-1.75 is represented as `units`=-1 and `nanos`=-750,000,000.
          int32 nanos = 3;
      }

      message GetSupportedCurrenciesResponse {
          // The 3-letter currency code defined in ISO 4217.
          repeated string currency_codes = 1;
      }

      message CurrencyConversionRequest {
          Money from = 1;

          // The 3-letter currency code defined in ISO 4217.
          string to_code = 2;
      }

      // -------------Payment service-----------------

      service PaymentService {
          rpc Charge(ChargeRequest) returns (ChargeResponse) {}
      }

      message CreditCardInfo {
          string credit_card_number = 1;
          int32 credit_card_cvv = 2;
          int32 credit_card_expiration_year = 3;
          int32 credit_card_expiration_month = 4;
      }

      message ChargeRequest {
          Money amount = 1;
          CreditCardInfo credit_card = 2;
      }

      message ChargeResponse {
          string transaction_id = 1;
      }

      // -------------Email service-----------------

      service EmailService {
          rpc SendOrderConfirmation(SendOrderConfirmationRequest) returns (Empty) {}
      }

      message OrderItem {
          CartItem item = 1;
          Money cost = 2;
      }

      message OrderResult {
          string   order_id = 1;
          string   shipping_tracking_id = 2;
          Money shipping_cost = 3;
          Address  shipping_address = 4;
          repeated OrderItem items = 5;
      }

      message SendOrderConfirmationRequest {
          string email = 1;
          OrderResult order = 2;
      }


      // -------------Checkout service-----------------

      service CheckoutService {
          rpc PlaceOrder(PlaceOrderRequest) returns (PlaceOrderResponse) {}
      }

      message PlaceOrderRequest {
          string user_id = 1;
          string user_currency = 2;

          Address address = 3;
          string email = 5;
          CreditCardInfo credit_card = 6;
      }

      message PlaceOrderResponse {
          OrderResult order = 1;
      }

      // ------------Ad service------------------

      service AdService {
          rpc GetAds(AdRequest) returns (AdResponse) {}
      }

      message AdRequest {
          // List of important key words from the current page describing the context.
          repeated string context_keys = 1;
      }

      message AdResponse {
          repeated Ad ads = 1;
      }

      message Ad {
          // url to redirect to when an ad is clicked.
          string redirect_url = 1;

          // short advertisement text to display.
          string text = 2;
      }

      // ------------Feature flag service------------------

      service FeatureFlagService {
        rpc GetFlag(GetFlagRequest) returns (GetFlagResponse) {}
        rpc CreateFlag(CreateFlagRequest) returns (CreateFlagResponse) {}
        rpc UpdateFlag(UpdateFlagRequest) returns (UpdateFlagResponse) {}
        rpc ListFlags(ListFlagsRequest) returns (ListFlagsResponse) {}
        rpc DeleteFlag(DeleteFlagRequest) returns (DeleteFlagResponse) {}
      }

      message Flag {
        string name = 1;
        string description = 2;
        bool enabled = 3;
        google.protobuf.Timestamp created_at = 4;
        google.protobuf.Timestamp updated_at = 5;
      }

      message GetFlagRequest {
        string name = 1;
      }

      message GetFlagResponse {
        Flag flag = 1;
      }

      message CreateFlagRequest {
        string name = 1;
        string description = 2;
        bool enabled = 3;
      }

      message CreateFlagResponse {
        Flag flag = 1;
      }

      message UpdateFlagRequest {
        string name = 1;
        bool enabled = 2;
      }

      message UpdateFlagResponse {}

      message ListFlagsRequest {}

      message ListFlagsResponse {
        repeated Flag flag = 1;
      }

      message DeleteFlagRequest {
        string name = 1;
      }

      message DeleteFlagResponse {}
    address: recommendationservice.service.consul:9001
    method: hipstershop.RecommendationService.ListRecommendations
specs:
 - selector: span[name="get_product_list"]
   assertions:
   - attr:app.filtered_products.count  =  9
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save this file as &lt;code&gt;recsvc-test.yml&lt;/code&gt; in the &lt;a href="https://github.com/avillela/nomad-conversions/tree/main/tracetest/tests" rel="noopener noreferrer"&gt;tracetest/tests&lt;/a&gt; directory of &lt;a href="https://github.com/avillela/nomad-conversions" rel="noopener noreferrer"&gt;nomad-conversions&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  4- Run the Trace-based test
&lt;/h4&gt;

&lt;p&gt;With our test YAML in hand, we can run our test! Make sure that you’re running this from &lt;a href="https://github.com/avillela/nomad-conversions/tree/main/tracetest" rel="noopener noreferrer"&gt;tracetest&lt;/a&gt; directory of &lt;a href="https://github.com/avillela/nomad-conversions" rel="noopener noreferrer"&gt;nomad-conversions&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tracetest test run --definition tests/recsvc-test.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✔ RecommendationService test (http://tracetest.localhost/test/avtest4567/run/1/test)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s check out our test in the Tracetest UI by going to &lt;a href="http://tracetest.localhost" rel="noopener noreferrer"&gt;http://tracetest.localhost&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm0n4b1z43aoue48cv3n7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm0n4b1z43aoue48cv3n7.png" alt="Screen capture of the results of running the RecommendationService Test in the Tracetest UI" width="800" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s click on the &lt;code&gt;&amp;gt;&lt;/code&gt; next to the test definition to expand it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqs2mdxf5mcnywfbh2p6b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqs2mdxf5mcnywfbh2p6b.png" alt="Screen capture of expanding the RecommendationService Test in the Tracetest UI" width="800" height="179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that the UI shows that it ran v1 of the test. If we make any changes to the test definition, Tracetest bumps the test version; however, if we were to change only the test ID and re-run the file, it would show up as an entirely different test in Tracetest, and not a different version of the same test.&lt;/p&gt;

&lt;p&gt;Let’s explore our test definition in the UI, by clicking in the area highlighted below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvyqpcguldftc93fj1616.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvyqpcguldftc93fj1616.png" alt="Selecting your test from the Tracetest UI" width="800" height="179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Will take us to the test trigger definition screen:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff1nxkxbgy56st71rxnqy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff1nxkxbgy56st71rxnqy.png" alt="Tracetest test trigger definition screen" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This might seem familiar - it’s the information that we entered in our test YAML. Tracetest runs the API endpoint and then checks to see if there are any Traces associated with this with this endpoint, and lets you create tests against the traces. Test specs are defined in the  &lt;code&gt;Test&lt;/code&gt; tab, located at the top middle section of the screen:&lt;/p&gt;

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

&lt;p&gt;Clicking on it brings us to the test spec definition screen:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwnjlsr9cyypsfkfmagdi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwnjlsr9cyypsfkfmagdi.png" alt="Tracetest test spec definition screen" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, let’s head to the top left-hand side of the screen, and click on the purple circle with &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fygkj38ae8bsnivb3zlua.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fygkj38ae8bsnivb3zlua.png" alt="Expanding the test spec definition screen to reveal span details" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This reveals a side pane with all sorts of information about the selected Span. In our case, we’ve selected the &lt;code&gt;Tracetest trigger&lt;/code&gt; Span (top box), so our screen looks something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsdnyz841wx9vttlh52c7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsdnyz841wx9vttlh52c7.png" alt="Tracetest test definition screen with the expanded span info" width="800" height="942"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice how the &lt;code&gt;Tracetest trigger&lt;/code&gt; box has a slight blue highlight. But we’re not interested in this &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#spans" rel="noopener noreferrer"&gt;Span&lt;/a&gt;. We’re actually interested in the &lt;code&gt;get_product_list&lt;/code&gt; &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#spans" rel="noopener noreferrer"&gt;Span&lt;/a&gt;, so let’s click on that Span:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foidcpbnnv25w64nye19p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foidcpbnnv25w64nye19p.png" alt="Tracetest test definition screen get_product_list test" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice how it has an attribute called &lt;code&gt;app.filtered_products.count&lt;/code&gt;, and that its value is &lt;code&gt;9&lt;/code&gt;…which is the value that we’re checking for in our assertion! Ta-da! 🎉&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 4: See it Lightstep!
&lt;/h3&gt;

&lt;p&gt;Remember that our OTel Collector is configured to send Traces to both Tracetest and Lightstep, so we should see the same Trace data in both.&lt;/p&gt;

&lt;p&gt;I went into my Lightstep account and created a &lt;a href="https://docs.lightstep.com/docs/use-notebooks" rel="noopener noreferrer"&gt;Notebook&lt;/a&gt; that looked like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ops0n17vbspn9yzb8uo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ops0n17vbspn9yzb8uo.png" alt="Lightstep Notebook screen for Tracetest" width="800" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And clicked on one of the matching traces returned, which led me to the Trace diagram below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzw3sifq7yjwnlbmztrv0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzw3sifq7yjwnlbmztrv0.png" alt="Flame graph of get_product_list trace" width="800" height="493"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, we have our &lt;code&gt;get_product_list&lt;/code&gt; trace, which has an attribute called &lt;code&gt;app.filtered_products.count&lt;/code&gt; with a value of &lt;code&gt;9&lt;/code&gt;. Yay!&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Tracetest is a great tool for unlocking the potential of Trace-based testing. Although the example in this tutorial was fairly simple, I hope that it gives you an appreciation for some of the cool stuff that Tracetest can do. I definitely plan to keep my eye out as this tool improves, and &lt;a href="https://thenewstack.io/our-2023-site-reliability-engineering-wish-list/" rel="noopener noreferrer"&gt;I am excited to see where Trace-based testing takes us in 2023&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;For more on the ins and outs of Tracetest, be sure to check out the official docs &lt;a href="https://docs.tracetest.io" rel="noopener noreferrer"&gt;here&lt;/a&gt;. For you visual learners, I’ve put together a quick little four-minute video highlighting the main things covered in this post. Be sure to check it out &lt;a href="https://youtu.be/EBWf2aaR9wk" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That was definitely a lot to take in, so give yourself a pat on the back for getting through this! Now please enjoy this lovely photo of Mookie the rat being cradled in my husband’s arms.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa56lh0yna3f1dhue6kuc.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa56lh0yna3f1dhue6kuc.JPG" alt="Hello from Mookie the rat!" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Peace, love, and code!&lt;/p&gt;

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




&lt;p&gt;This post was originally published on the &lt;a href="https://tracetest.io/blog/tracetest-in-action-running-trace-based-tests-on-the-opentelemetry-demo-app-with-nomad" rel="noopener noreferrer"&gt;Tracetest blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>bug</category>
      <category>n8nbrightdatachallenge</category>
      <category>challenge</category>
    </item>
    <item>
      <title>Three Terraform Mistakes, and How to Avoid Them</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Mon, 21 Nov 2022 15:56:17 +0000</pubDate>
      <link>https://forem.com/lightstep/three-terraform-mistakes-and-how-to-avoid-them-88a</link>
      <guid>https://forem.com/lightstep/three-terraform-mistakes-and-how-to-avoid-them-88a</guid>
      <description>&lt;p&gt;In my &lt;a href="https://lightstep.com/blog/observability-as-code-with-kubernetes-and-lightstep"&gt;last blog post&lt;/a&gt;, I talked about how &lt;a href="https://lightstep.com/blog/authors/ana-margarita-medina"&gt;Ana Margarita Medina&lt;/a&gt; and I used &lt;a href="https://terraform.io"&gt;Terraform&lt;/a&gt; to show off &lt;a href="https://lightstep.com/blog/observability-mythbusters-observability-landscape-as-code"&gt;Observability-Landscape-as-Code&lt;/a&gt; in practice, leveraging the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo App&lt;/a&gt; to do so. The Demo App showcases instrumentation of &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#understanding-distributed-tracing"&gt;Traces&lt;/a&gt; and &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#reliability--metrics"&gt;Metrics&lt;/a&gt; of different services written in different languages using &lt;a href="https://opentelemetry.io"&gt;OpenTelemetry&lt;/a&gt; (OTel). Our Terraform code did the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Created a Kubernetes cluster&lt;/li&gt;
&lt;li&gt;Deployed the Demo App to &lt;a href="https://kubernetes.io"&gt;Kubernetes&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Deployed &lt;a href="https://opentelemetry.io/docs/collector/"&gt;OpenTelemetry Collector&lt;/a&gt; to &lt;a href="https://kubernetes.io"&gt;Kubernetes&lt;/a&gt;, and configured it to send Traces and Metrics to &lt;a href="https://app.lightstep.com"&gt;Lightstep&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Created dashboards in Lightstep&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, I’m a fan of beautiful code, so we organized our code using &lt;a href="https://developer.hashicorp.com/terraform/language/modules"&gt;Terraform Modules&lt;/a&gt;. We used a module for provisioning the Kubernetes cluster, one for deploying the OTel Demo App and the OTel Collector, and one for creating the Lightstep dashboards.&lt;/p&gt;

&lt;p&gt;We also leveraged the following Terraform &lt;a href="https://developer.hashicorp.com/terraform/language/providers"&gt;Providers&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://registry.terraform.io/providers/hashicorp/google/latest/docs"&gt;Google Cloud Provider&lt;/a&gt; for spinning up a Kubernetes Cluster in &lt;a href="https://cloud.google.com/"&gt;Google Cloud Platform (GCP)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs"&gt;Kubernetes Provider&lt;/a&gt; and the &lt;a href="https://registry.terraform.io/providers/hashicorp/helm/latest/docs"&gt;Helm Provider&lt;/a&gt; for deploying the app to Kubernetes&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://registry.terraform.io/providers/lightstep/lightstep/latest/docs"&gt;Lightstep Provider&lt;/a&gt; for creating the dashboards in Lightstep&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All good, right? Except for one teeeensy little problem...the last time I’d touched Terraform was in early 2021, and even then, I was just tweaking code. So I kinda had to teach myself Terraform all over again. And I hit up a few snags along the way. Cue. The. Panic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyhfmitip26xra66pnx08.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyhfmitip26xra66pnx08.jpg" alt='Home Alone meme: Kevin with hands to his face screaming. Text: "OMG Terraform is mad at me"' title="Home Alone meme: Kevin with hands to his face screaming. Text: 'OMG Terraform is mad at me'. URL: https://imgflip.com/i/709pup" width="500" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fortunately, Google came through, and we were able to resolve the issues. In today’s blog post, I will cover THREE Terraform gotchas that Ana and I hit, and how we solved them, so that you will hopefully be spared our utter despair and panic. 😅&lt;/p&gt;

&lt;p&gt;Let’s do this!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt;  If you want to follow along to see the full Terraform source code, you can check it out &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/tree/main/gke-otel-demo"&gt;here&lt;/a&gt;. Even though the source code is specific to the Observability-Landscape-as-Code use case, the main Terraform concepts in this blog post can be ported over to other scenarios.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Gotcha #1: The Chicken-and-Egg Scenario
&lt;/h2&gt;

&lt;p&gt;After creating a Kubernetes cluster, we needed to create a Kubernetes resource before we could apply the Helm chart to install the OpenTelemetry demo app. The &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/tree/main/charts/opentelemetry-demo"&gt;Demo App’s Helm Chart&lt;/a&gt; deploys an OpenTelemetry Collector. We wanted to configure the Collector to send OTel data to Lightstep. To do so, you need to add a &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens"&gt;Lightstep Access Token&lt;/a&gt;, which is stored as a &lt;a href="https://kubernetes.io/docs/concepts/configuration/secret/"&gt;Kubernetes secret&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;You can learn more about the specifics of this setup &lt;a href="https://medium.com/dev-genius/running-opentelemetry-demo-app-in-kubernetes-95dccd613e0b"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To create the secret in Kubernetes before running the Helm Chart, we used the &lt;a href="https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs"&gt;Kubernetes Provider&lt;/a&gt;. In order to use this provider, Terraform needs to know information about your cluster, so that it knows what cluster to apply the manifest to. To do this, I needed to store the cluster  information in the &lt;code&gt;data&lt;/code&gt; stanza, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"google_client_config"&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"google_container_cluster"&lt;/span&gt; &lt;span class="s2"&gt;"primary"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_name&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;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"kubernetes"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;host&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://${data.google_container_cluster.primary.endpoint}"&lt;/span&gt;
 &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;google_client_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;default&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_token&lt;/span&gt;
 &lt;span class="nx"&gt;cluster_ca_certificate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;google_container_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;master_auth&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="nx"&gt;cluster_ca_certificate&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;Easy peasey, right? Unfortunately, when I ran &lt;code&gt;terraform apply&lt;/code&gt;, I kept getting the following errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Invalid template interpolation value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Attempt to index null value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically, Terraform was trying to evaluate the contents of the &lt;code&gt;data&lt;/code&gt; stanza (which were null) before it had any information about the Kubernetes cluster. Which of course it didn’t, because the cluster didn’t yet exist!! Hence the null contents. &lt;/p&gt;

&lt;p&gt;I frantically Googled this one for a while, spinning my wheels. And then, the “aha” moment  hit me, when I saw somewhere in one of my searches that I could use the  &lt;code&gt;depends_on&lt;/code&gt; attribute in the &lt;code&gt;data&lt;/code&gt; stanza. So I added &lt;code&gt;depends_on = [module.k8s_cluster_create]&lt;/code&gt; to both my &lt;code&gt;data&lt;/code&gt; stanzas, which basically says, “Hey buddy, don’t try to evaluate this until AFTER the &lt;code&gt;k8s_cluster_create module&lt;/code&gt; (i.e. the module in which the Kubernetes cluster is created) is run. So now, after adding &lt;code&gt;depends_on&lt;/code&gt;, my &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/blob/450333c0132e1a0ee99f4633af2d48a06c4ad8dd/gke-otel-demo/terraform/providers.tf#L32-L49"&gt;providers.tf (lines 32-49)&lt;/a&gt; code looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"google_client_config"&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;k8s_cluster_create&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"google_container_cluster"&lt;/span&gt; &lt;span class="s2"&gt;"primary"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;k8s_cluster_create&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="nx"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_name&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;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"kubernetes"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;host&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://${data.google_container_cluster.primary.endpoint}"&lt;/span&gt;
 &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;google_client_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;default&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_token&lt;/span&gt;
 &lt;span class="nx"&gt;cluster_ca_certificate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;google_container_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;master_auth&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="nx"&gt;cluster_ca_certificate&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;And after making that change, all was well with the world. Huzzah!&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotcha #2: Using Modules with depends_on
&lt;/h2&gt;

&lt;p&gt;While the above problem went away, I then found myself face-to-face with yet another conundrum. When I initially wrote my Terraform code, everything was in one big file, and it worked just fine. So OF COURSE I just assumed that when I prettified my code and moved things into modules, I could just get away defining my &lt;a href="https://developer.hashicorp.com/terraform/language/providers"&gt;Providers&lt;/a&gt; in the Modules themselves. Well, you can. That is…if you don’t use the &lt;code&gt;depends_on&lt;/code&gt; attribute in your Module call.&lt;/p&gt;

&lt;p&gt;So basically, when I tried to say that the Module &lt;code&gt;lightstep_dashboards&lt;/code&gt; depended on &lt;code&gt;k8s_cluster_create&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"k8s_cluster_create"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./modules/k8s"&lt;/span&gt;

   &lt;span class="nx"&gt;cluster_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_name&lt;/span&gt;
   &lt;span class="nx"&gt;project_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project_id&lt;/span&gt;
   &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
   &lt;span class="nx"&gt;network&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;network&lt;/span&gt;
   &lt;span class="nx"&gt;subnet&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subnet&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"deploy_otel_demo_app"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./modules/otel_demo_app"&lt;/span&gt;

   &lt;span class="nx"&gt;otel_demo_namespace&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;otel_demo_namespace&lt;/span&gt;
   &lt;span class="nx"&gt;ls_access_token&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ls_access_token&lt;/span&gt;
   &lt;span class="nx"&gt;cluster_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_name&lt;/span&gt;
   &lt;span class="nx"&gt;project_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project_id&lt;/span&gt;
   &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
   &lt;span class="nx"&gt;network&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;network&lt;/span&gt;
   &lt;span class="nx"&gt;subnet&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subnet&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"lightstep_dashboards"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./modules/lightstep"&lt;/span&gt;
   &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;k8s_cluster_create&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

   &lt;span class="nx"&gt;lightstep_project&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ls_project&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I kept getting this error when I ran &lt;code&gt;terraform apply&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Module is incompatible with count, for_each and depends_on
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This error happens when the &lt;a href="https://developer.hashicorp.com/terraform/language/modules#child-modules"&gt;Child Module&lt;/a&gt; contains a &lt;code&gt;provider&lt;/code&gt; block and the Module that you’re trying to call is using &lt;code&gt;count&lt;/code&gt;, &lt;code&gt;depends_on&lt;/code&gt;, and/or &lt;code&gt;for_each&lt;/code&gt;. Why? Because &lt;code&gt;provider&lt;/code&gt; blocks inside a &lt;a href="https://developer.hashicorp.com/terraform/language/modules#child-modules"&gt;Child Module&lt;/a&gt; are not allowed when your Module call is using &lt;code&gt;count&lt;/code&gt;, &lt;code&gt;depends_on&lt;/code&gt;, and/or &lt;code&gt;for_each&lt;/code&gt;. You can read up more on this &lt;a href="https://github.com/hashicorp/terraform/issues/31081#issuecomment-1131908824"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Well, it turns out that correct practice is to define your &lt;code&gt;provider&lt;/code&gt; block in the &lt;a href="https://developer.hashicorp.com/terraform/language/modules#the-root-module"&gt;Root Module&lt;/a&gt;, as Providers are automagically passed down to the &lt;a href="https://developer.hashicorp.com/terraform/language/modules#child-modules"&gt;Child Modules&lt;/a&gt;. So to make the above error go away, I &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/blob/main/gke-otel-demo/terraform/providers.tf"&gt;moved all of my Provider definitions to the Root Module&lt;/a&gt;, and was able to &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/blob/main/gke-otel-demo/terraform/main.tf"&gt;keep depends_on in my Module call&lt;/a&gt;. If I didn’t have any dependencies, I could’ve left out the &lt;code&gt;depends_on&lt;/code&gt; block, but I wouldn’t really be following the recommended  practice.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE&lt;/em&gt;&lt;/strong&gt;: You can learn more about Providers and Modules &lt;a href="https://developer.hashicorp.com/terraform/language/modules/develop/providers"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Gotcha #3: Referencing a non-TF provider in a module
&lt;/h2&gt;

&lt;p&gt;Two problems down. Awesome! Unfortunately, my problems were not over. I continued to anger the Module Gods, because I encountered yet another issue when I moved my non-modularized code into Modules. This time, it had to do with using the &lt;a href="https://registry.terraform.io/providers/lightstep/lightstep/latest/docs"&gt;Lightstep Provider&lt;/a&gt;. You see, this Provider comes from a third-party (i.e. not HashiCorp), which in this case is Lightstep. Lightstep is what is known as a &lt;a href="https://developer.hashicorp.com/terraform/registry/providers#provider-tiers-namespaces"&gt;Partner Provider&lt;/a&gt;.  This means that in the &lt;a href="https://registry.terraform.io/browse/providers"&gt;Provider Registry&lt;/a&gt;, the Provider is named &lt;code&gt;lightstep/lightstep&lt;/code&gt;, where the first &lt;code&gt;lightstep&lt;/code&gt; means that the Provider is created and maintained by Lightstep, and the second &lt;code&gt;lightstep&lt;/code&gt; is the actual Provider name. For comparison, the &lt;code&gt;hashicorp/google&lt;/code&gt; provider is an &lt;a href="https://developer.hashicorp.com/terraform/registry/providers#provider-tiers-namespaces"&gt;Official Provider&lt;/a&gt;, because it is created and maintained by HashiCorp.&lt;/p&gt;

&lt;p&gt;Now here’s the odd part. When I tried to run &lt;code&gt;terraform init&lt;/code&gt;, I was graced with this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Failed to query available provider packages

Could not retrieve the list of available versions for provider hashicorp/lightstep 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Um...what? This did not compute, because in my &lt;code&gt;providers.tf&lt;/code&gt; file, I &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/blob/450333c0132e1a0ee99f4633af2d48a06c4ad8dd/gke-otel-demo/terraform/providers.tf#L12-L15"&gt;CLEARLY said that the Provider name was lightstep/lightstep&lt;/a&gt;, so where oh where was it getting this &lt;code&gt;hashicorp/lightstep&lt;/code&gt; business from?? LOOK ⬇️⬇️⬇️&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&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="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lightstep/lightstep"&lt;/span&gt;
     &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;=1.70.0"&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;O Google gods, help meeeeee!!&lt;/p&gt;

&lt;p&gt;Well, it turns out that when using a Partner Provider in a Module, Terraform assumes the Provider is an Official Provider, and is therefore automagically given a &lt;code&gt;hashicorp&lt;/code&gt; suffix when passing it down to the Module. So Terraform basically thought that the Provider was called &lt;code&gt;hashicorp/lightstep&lt;/code&gt;, even though I &lt;em&gt;clearly&lt;/em&gt; defined it correctly in the &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/blob/450333c0132e1a0ee99f4633af2d48a06c4ad8dd/gke-otel-demo/terraform/providers.tf#L12-L15"&gt;Providers section of the Root Module&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To fix this issue, I ended up having to define a &lt;code&gt;required_providers&lt;/code&gt; stanza in the Root Module, as I had already done, AND I also had to add a &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/blob/main/gke-otel-demo/terraform/modules/lightstep/providers.tf"&gt;required_providers stanza to my Child Module&lt;/a&gt;, as per the snippet  below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;required_providers&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="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lightstep/lightstep"&lt;/span&gt;
     &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;=1.70.0"&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;After that, my &lt;code&gt;terraform init&lt;/code&gt; stopped screaming at me!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq5m8nvnqxduyciiazolf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq5m8nvnqxduyciiazolf.jpg" alt='Jimmy Fallon in front of blue curtain saying "Phew!"' title="Jimmy Fallon in front of blue curtain saying 'Phew!' Image source: https://images.app.goo.gl/7XyDJJ88DHzcXhrJ7" width="479" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Today we learned that Terraform can be a wee finicky. We learned that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding &lt;code&gt;depends_on&lt;/code&gt; to the &lt;code&gt;data&lt;/code&gt; stanza used to capture your Kubernetes cluster configuration data ensures that Terraform doesn’t try to evaluate the &lt;code&gt;data&lt;/code&gt; stanza until AFTER the cluster is created, thereby avoiding some serious Terraform Anger™.&lt;/li&gt;
&lt;li&gt;If you want to use &lt;code&gt;depends_on&lt;/code&gt; in a Module call, the Provider configuration must be done in the Root Module. Also, it’s the recommended practice even if you don’t want to use &lt;code&gt;depends_on&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If you have a Module that references a Partner Provider, you need to define a &lt;code&gt;required_providers&lt;/code&gt; stanza in both the Root Module and the Child Module.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope that these tips prevent you from experiencing Terraform Anguish™  next time you find yourself Terraformin’. And now, I shall reward you with a picture of my rat Mookie, who is seen below peering out of an authentic &lt;a href="https://cheesehead.com"&gt;Wisconsin Cheese Head&lt;/a&gt; hat.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--h_glAxog--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://images.ctfassets.net/ne67e1btg495/4WjpdboNgYiF77IJZvfCn5/7a6221a74ab020611ccb138fac30aa87/mookie-cheese-hat.JPG%3Fw%3D3520%26h%3D1980%26q%3D50%26fm%3Dwebp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h_glAxog--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://images.ctfassets.net/ne67e1btg495/4WjpdboNgYiF77IJZvfCn5/7a6221a74ab020611ccb138fac30aa87/mookie-cheese-hat.JPG%3Fw%3D3520%26h%3D1980%26q%3D50%26fm%3Dwebp" alt="Mookie the rat peering out of a Cheese Head hat. Image by Adriana Villela" title="Mookie the rat peering out of a Cheese Head hat. Image by Adriana Villela" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Peace, love, and code. 🦄 🌈 💫&lt;/p&gt;




&lt;p&gt;Got questions about Terraform or Observability-Landscape-as-Code? Talk to me! Feel free to connect through &lt;a href="//mailto:devrel@lightstep.com"&gt;e-mail&lt;/a&gt;, or hit me up on &lt;a href="https://twitter.com/adrianamvillela"&gt;Twitter&lt;/a&gt;, &lt;a href="https://hachyderm.io/web/@adrianamvillela#"&gt;Mastodon&lt;/a&gt;, or &lt;a href="https://www.linkedin.com/in/adrianavillela/"&gt;LinkedIn&lt;/a&gt;. Hope to hear from y’all!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>terraform</category>
      <category>tech</category>
      <category>hashicorp</category>
    </item>
    <item>
      <title>Observability-Landscape-as-Code in Practice</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Tue, 15 Nov 2022 15:47:02 +0000</pubDate>
      <link>https://forem.com/lightstep/observability-landscape-as-code-in-practice-4co3</link>
      <guid>https://forem.com/lightstep/observability-landscape-as-code-in-practice-4co3</guid>
      <description>&lt;p&gt;&lt;strong&gt;&lt;em&gt;with &lt;a href="https://lightstep.com/blog/authors/ana-margarita-medina"&gt;Ana Margarita Medina&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you follow &lt;a href="https://lightstep.com/blog/authors/adriana-villela"&gt;Adriana's writings on Observability&lt;/a&gt;, you may recall a post from back in June introducing the concept of &lt;a href="https://lightstep.com/blog/observability-mythbusters-observability-landscape-as-code"&gt;Observability-Landscape-as-Code (OLaC)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;An Observability Landscape is made up of the following pieces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application instrumentation&lt;/li&gt;
&lt;li&gt;Collecting and storing application telemetry&lt;/li&gt;
&lt;li&gt;An Observability back-end&lt;/li&gt;
&lt;li&gt;A set of meaningful SLOs&lt;/li&gt;
&lt;li&gt;Alerts for on-call Engineers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/4ZXBvcXtkWVjEBnYNP1pEf/d2c9262fe98996e4ae9f875a43e1514b/o11y-landscape.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/4ZXBvcXtkWVjEBnYNP1pEf/d2c9262fe98996e4ae9f875a43e1514b/o11y-landscape.png" alt="The Observability landscape is made up of Collecting and storing application telemetry, An Observability back-end, A set of meaningful SLOs, Alerts for on-call Engineers" title="The Observability landscape is made up of Collecting and storing application telemetry, An Observability back-end, A set of meaningful SLOs, Alerts for on-call Engineers. Image by Adriana Villela."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Keeping that in mind, OLaC is simply the codification of your Observability Landscape, thereby ensuring consistency, maintainability, and reproducibility.&lt;/p&gt;

&lt;p&gt;That’s all well and good, but how about seeing this thing in action? Well, my friend, you’ve come to the right place, because today, you get to see a tutorial featuring a number of OLaC practices in action!&lt;/p&gt;

&lt;p&gt;Today, &lt;a href="https://lightstep.com/blog/authors/ana-margarita-medina"&gt;Ana Margarita Medina&lt;/a&gt; (my fellow DevRel and partner in crime) and I will be highlighting the following aspects of OLaC:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://lightstep.com/blog/observability-mythbusters-observability-landscape-as-code#instrument-your-code"&gt;Application instrumentation&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How?&lt;/strong&gt; À la &lt;a href="https://opentelemetry.io"&gt;OpenTelemetry&lt;/a&gt;, via the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo App&lt;/a&gt;, which contains examples of &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#understanding-distributed-tracing"&gt;Traces&lt;/a&gt; and &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#reliability--metrics"&gt;Metrics&lt;/a&gt; instrumentation.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://lightstep.com/blog/observability-mythbusters-observability-landscape-as-code#use-the-otel-collector--codify-its-deployment"&gt;Collecting &amp;amp; storing application telemetry&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How:&lt;/strong&gt; &lt;a href="https://docs.lightstep.com/otel/quick-start-collector"&gt;OpenTelemetry Collector&lt;/a&gt; is deployed via code (Helm chart), alongside the various services that make up the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo App&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://lightstep.com/blog/observability-mythbusters-observability-landscape-as-code#codify-observability-back-end-configuration"&gt;Codifying your Observability back-end configuration&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How:&lt;/strong&gt; Using the &lt;a href="https://registry.terraform.io/providers/lightstep/lightstep/latest/docs"&gt;Lightstep Terraform Provider&lt;/a&gt; to create dashboards in &lt;a href="https://app.lightstep.com"&gt;Lightstep&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We wanted to showcase OLaC principles with a real-life example using modern cloud-native tooling...Which means using &lt;a href="https://kubernetes.io"&gt;Kubernetes&lt;/a&gt; for our cloud infrastructure with &lt;a href="https://cloud.google.com/gcp"&gt;Google Cloud&lt;/a&gt;’s Kubernetes offering, &lt;a href="https://cloud.google.com/kubernetes-engine"&gt;GKE&lt;/a&gt;. Now, since we are good practitioners of OLaC and SRE, we won’t just be setting things up through the clickity click of a UI. No sirreee. Instead, we’ll be #automatingAllTheThings using &lt;a href="https://hashicorp.com"&gt;HashiCorp&lt;/a&gt; &lt;a href="https://terraform.io"&gt;Terraform&lt;/a&gt;. Terraform allows us to do infrastructure-as-code (IaC), and gives us tons of added benefits like better control over our resources and standardization. These are key principles in OLaC and IaC.&lt;/p&gt;

&lt;p&gt;We will be deploying &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo App&lt;/a&gt; to our cluster. The Demo App has been instrumented using &lt;a href="https://opentelemetry.io"&gt;OpenTelemetry&lt;/a&gt;, and will send &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#understanding-distributed-tracing"&gt;Traces&lt;/a&gt; and &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#reliability--metrics"&gt;Metrics&lt;/a&gt; through the &lt;a href="https://docs.lightstep.com/otel/quick-start-collector"&gt;OpenTelemetry Collector&lt;/a&gt; to Lightstep.&lt;/p&gt;

&lt;p&gt;Are you ready??? Let’s get started!&lt;/p&gt;

&lt;h1&gt;
  
  
  Tutorial
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Pre-Requisites
&lt;/h2&gt;

&lt;p&gt;Before you begin, you will need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://app.lightstep.com/signup/developer?signup_source=docs"&gt;Lightstep account&lt;/a&gt; so you can see application Traces, and Metrics dashboards&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens#create-an-access-token"&gt;Lightstep Access Token&lt;/a&gt; for the &lt;a href="https://app.lightstep.com"&gt;Lightstep&lt;/a&gt; project you would like to use&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://docs.lightstep.com/docs/create-and-manage-api-keys"&gt;Lightstep API key&lt;/a&gt; for creating dashboards in &lt;a href="https://app.lightstep.com"&gt;Lightstep&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.terraform.io/downloads"&gt;Terraform CLI&lt;/a&gt; to run the Terraform scripts&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://cloud.google.com/gcp"&gt;Google Cloud&lt;/a&gt; account, so you can create a Kubernetes cluster (&lt;a href="https://cloud.google.com/kubernetes-engine"&gt;GKE&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cloud.google.com/sdk/docs/install-sdk"&gt;gcloud CLI&lt;/a&gt; to interact with Google Cloud&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/"&gt;kubectl&lt;/a&gt; to interact with Kubernetes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Steps
&lt;/h2&gt;

&lt;p&gt;In today’s tutorial, we’ll be running Terraform code to showcase OLaC in action. For convenience, the main components have been broken up into separate &lt;a href="https://www.terraform.io/language/modules"&gt;Terraform modules&lt;/a&gt; that will do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a Kubernetes cluster (GKE) in Google Cloud using the &lt;a href="https://registry.terraform.io/providers/hashicorp/google/latest/docs"&gt;Google Terraform Provider&lt;/a&gt;. This is defined in the &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/tree/main/gke-otel-demo/terraform/modules/k8s"&gt;k8s&lt;/a&gt; module in our repo.&lt;/li&gt;
&lt;li&gt;Deploy the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo App&lt;/a&gt; to the cluster using the &lt;a href="https://registry.terraform.io/providers/hashicorp/helm/latest/docs"&gt;Helm Terraform Provider&lt;/a&gt;. This is defined &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/tree/main/gke-otel-demo/terraform/modules/otel_demo_app"&gt;otel_demo_app&lt;/a&gt; module in our repo.&lt;/li&gt;
&lt;li&gt;Create Metrics dashboards in &lt;a href="https://registry.terraform.io/providers/lightstep/lightstep/latest/docs"&gt;Lightstep Terraform Provider&lt;/a&gt;. This is defined in the &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/tree/main/gke-otel-demo/terraform/modules/lightstep"&gt;lightstep&lt;/a&gt; module in our repo.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The full code listing for this tutorial can be found &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/tree/main/k8s-cluster-with-otel-demo"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1- Clone the example repo
&lt;/h3&gt;

&lt;p&gt;Let's start by cloning the example repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/lightstep/unified-observability-k8s-kubecon.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2- Initialize Sub-Modules
&lt;/h3&gt;

&lt;p&gt;This project makes use of a few &lt;a href="https://git-scm.com/book/en/v2/Git-Tools-Submodules"&gt;Git submodules&lt;/a&gt;, so in order to ensure that things work nicely,  you’ll need to pull them in:&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;cd &lt;/span&gt;unified-observability-k8s-kubecon
git submodule init &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git submodule update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3- Google Cloud Login
&lt;/h3&gt;

&lt;p&gt;Before we can create a GKE cluster you must authenticate your Google Cloud account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud auth application-default login &lt;span class="nt"&gt;--no-launch-browser&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will be presented with a link which you need to open up in a browser, to authenticate your Google ID. Once you are authenticated, the browser will display an authorization token for you to paste in the command line, as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/7aJFKi4LN8L4N6fgcDbauS/5fd36e469285896c26ab5624de73151d/gcloud-auth-prompt.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/7aJFKi4LN8L4N6fgcDbauS/5fd36e469285896c26ab5624de73151d/gcloud-auth-prompt.png" alt="gcloud auth prompt" title="gcloud auth prompt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4- Create terraform.tfvars
&lt;/h3&gt;

&lt;p&gt;Now that you’re authenticated, let’s get ready to Terraform! Before you can do that, we need to create a &lt;code&gt;terraform.tfvars&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Lucky for you, we have a handy-dandy template that you can use to get started:&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;cd &lt;/span&gt;k8s-cluster-with-otel-demo/terraform
&lt;span class="nb"&gt;cp &lt;/span&gt;terraform.tfvars.template terraform.tfvars
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next populate the following values in the file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;your_gcp_project&amp;gt;&lt;/code&gt;: The name of your Google Cloud project. Don’t know your project name? No problem! Just run &lt;code&gt;gcloud config get-value project&lt;/code&gt; to find out what it is!&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;your_gke_cluster_name&amp;gt;&lt;/code&gt;: The name you wish to give your GKE cluster. Make sure it follows Kubernetes cluster naming conventions (i.e. no underscores &lt;code&gt;_&lt;/code&gt; or special characters).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;your_lightstep_access_token&amp;gt;&lt;/code&gt;: Your &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens#create-an-access-token"&gt;Lightstep Access Token&lt;/a&gt;. This is used to send Traces to your &lt;a href="https://docs.lightstep.com/docs/create-projects-for-your-environments"&gt;Lightstep Project&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;your_lightstep_api_key&amp;gt;&lt;/code&gt;: Your &lt;a href="https://docs.lightstep.com/docs/create-and-manage-api-keys"&gt;Lightstep API key&lt;/a&gt;. This is used to create our Metrics dashboards.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;your_lightstep_org_name&amp;gt;&lt;/code&gt;: Your Lightstep organization name. Not sure what your organization is called? No problem! Log into Lightstep,and click on the person icon on the bottom left of your screen. This will pop up a little menu. The organization name can be found under the “Account Management” heading, like this:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/62bgzto893vBqq06so4A24/38ae8e8820b66d76df610cb9335888c7/ls_org_name.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/62bgzto893vBqq06so4A24/38ae8e8820b66d76df610cb9335888c7/ls_org_name.png" alt="Finding your Lightstep organization name" title="Finding your Lightstep organization name"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that my organization is called “LightStep”. Yours will be different. Note also that Organization names are case-sensitive.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;your_lightsep_project_name&amp;gt;&lt;/code&gt;: The name of the &lt;a href="https://docs.lightstep.com/docs/create-projects-for-your-environments"&gt;Lightstep Project&lt;/a&gt; where the Metrics dashboards will be displayed.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;/strong&gt; &lt;code&gt;terraform.tfvars&lt;/code&gt; is in &lt;code&gt;.gitignore&lt;/code&gt; and won't be put into version control.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  5- Run Terraform
&lt;/h3&gt;

&lt;p&gt;This step will initialize Terraform (install providers locally), and then will apply the Terraform plan. &lt;/p&gt;

&lt;p&gt;It will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a Kubernetes cluster&lt;/li&gt;
&lt;li&gt;Deploy the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo App&lt;/a&gt; using the &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/tree/main/charts/opentelemetry-demo"&gt;OpenTelemetry Demo Helm Chart&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create dashboards in Lightstep&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before running the commands below, make sure that you’re already in the &lt;code&gt;k8s-cluster-with-otel-demo/terraform&lt;/code&gt; folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform init
terraform apply &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note that this step may take up to 30 minutes, depending on GKE’s disposition. Be patient. 😄&lt;/p&gt;

&lt;h3&gt;
  
  
  6- Update your kubeconfig
&lt;/h3&gt;

&lt;p&gt;Now that the cluster is created, you can add it to your &lt;code&gt;kubeconfig&lt;/code&gt; file! By default, the file is saved at &lt;code&gt;$HOME/.kube/config&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before you can update your &lt;code&gt;kubeconfig&lt;/code&gt;, you first need to make sure that you have the &lt;a href="https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl#install_plugin"&gt;gke-gcloud-auth-plugin&lt;/a&gt; installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud components &lt;span class="nb"&gt;install &lt;/span&gt;gke-gcloud-auth-plugin
gke-gcloud-auth-plugin &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"export USE_GKE_GCLOUD_AUTH_PLUGIN=True"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can add the cluster to &lt;code&gt;kubeconfig&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud container clusters get-credentials &lt;span class="si"&gt;$(&lt;/span&gt;terraform output &lt;span class="nt"&gt;-raw&lt;/span&gt; kubernetes_cluster_name&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;terraform output &lt;span class="nt"&gt;-raw&lt;/span&gt; region&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gets the &lt;code&gt;kubernetes_cluster_name&lt;/code&gt; and &lt;code&gt;region&lt;/code&gt; output values from Terraform (that’s the &lt;code&gt;terraform output -raw&lt;/code&gt; stuff), and plunks those into your &lt;code&gt;gcloud container clusters get-credentials&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Or, if you closed the terminal in which you were running Terraform and lost your output values, you can also do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud container clusters get-credentials &amp;lt;cluster_name&amp;gt; &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;region&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;&amp;lt;cluster_name&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;region&amp;gt;&lt;/code&gt; correspond to the values you entered in Step 3 in your &lt;code&gt;terraform.tfvars&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  7- Check out the OTel Demo app
&lt;/h3&gt;

&lt;p&gt;Now that you’ve got the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo App&lt;/a&gt; deployed to your cluster, let’s take a look at it! First, let’s peek into Kubernetes to see what’s up.&lt;/p&gt;

&lt;p&gt;If you run &lt;code&gt;kubectl get ns&lt;/code&gt;, you’ll notice that there’s now a new namespace called &lt;code&gt;otel-demo&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/7Amz3cvi7YGA9sW4ZyWDID/9d040359f783953cdec7443c6b9d6a3c/kubectl-get-ns.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/7Amz3cvi7YGA9sW4ZyWDID/9d040359f783953cdec7443c6b9d6a3c/kubectl-get-ns.png" alt='Results of running "kubectl get ns"' title="Results of running 'kubectl get ns'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is where we deployed the OTel Demo app. Let’s look into this namespace to see what we’ve created. First, let’s look at the pods with &lt;code&gt;kubectl get pods -ns otel-demo&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/3Vjy8eS8TcD7DJzIO1BMCm/c99bb85e247233c9b70d74afa7a7dd3c/kubectl-get-pods.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/3Vjy8eS8TcD7DJzIO1BMCm/c99bb85e247233c9b70d74afa7a7dd3c/kubectl-get-pods.png" alt='Results of running "kubectl get pods"' title="Results of running 'kubectl get pods'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice how we deployed a bunch of different services that make up the OTel Demo App, including &lt;code&gt;adservice&lt;/code&gt;, &lt;code&gt;cartservice&lt;/code&gt;, &lt;code&gt;recommendationservice&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;We also deployed an &lt;a href="https://docs.lightstep.com/otel/quick-start-collector"&gt;OTel Collector&lt;/a&gt;. Its configuration YAML is stored in a configmap. We can take a peek by running &lt;code&gt;kubectl describe configmap otel-demo-app-otelcol -n otel-demo&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/7nzEZ5yat0sF2rgIpS4lGT/aa3b72dd3b8d32a023854f3a3512468b/kubectl-get-configmap.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/7nzEZ5yat0sF2rgIpS4lGT/aa3b72dd3b8d32a023854f3a3512468b/kubectl-get-configmap.png" alt='Results of running "kubectl describe configmap"' title="Results of running 'kubectl describe configmap'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see that we also reference a variable called &lt;code&gt;${LS_TOKEN}&lt;/code&gt; which represents your &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens#create-an-access-token"&gt;Lightstep Access Token&lt;/a&gt;, which you set in &lt;code&gt;terraform.tfvars&lt;/code&gt;. But where is it? The secret is mounted to the OTel Collector container instance as a secret called &lt;code&gt;otel-collector-secret&lt;/code&gt;. Let’s take a look at the secret by running &lt;code&gt;kubectl describe secret otel-collector-secret -n otel-demo&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/3t5WRDOBgaGumEOr7q6xqj/1444778683e1cb6b418e7fcae350ff54/kubectl-get-secret.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/3t5WRDOBgaGumEOr7q6xqj/1444778683e1cb6b418e7fcae350ff54/kubectl-get-secret.png" alt='Results of running "kubectl describe secret"' title="Results of running 'kubectl describe secret'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All this magic happens in &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/blob/main/gke-otel-demo/terraform/configs/otel-demo-app-values.yaml"&gt;otel-demo-app-values-ls.yaml&lt;/a&gt;. This is a version of &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/main/charts/opentelemetry-demo/values.yaml"&gt;values.yaml&lt;/a&gt; from the &lt;a href="https://github.com/open-telemetry/opentelemetry-helm-charts/blob/main/charts/opentelemetry-demo"&gt;OTel Demo App Helm Chart&lt;/a&gt; with updates to the Collector configs so that we can configure the OTel Collector to send Traces to Lightstep.&lt;/p&gt;

&lt;h3&gt;
  
  
  8 - Run the OTel Demo App
&lt;/h3&gt;

&lt;p&gt;Okay...enough Kubernetes talk. Let’s look at the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo App&lt;/a&gt;! You can access the Demo App by Kubernetes port-forward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward &lt;span class="nt"&gt;-n&lt;/span&gt; otel-demo svc/otel-demo-app-frontend 8080:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To access the front-end, go to &lt;a href="http://localhost:8080"&gt;http://localhost:8080&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/3SnG3kY8p1OZ6UW31ZqXNz/dde580e4af8afc15518880721fc9c30a/otel-demo-ui.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/3SnG3kY8p1OZ6UW31ZqXNz/dde580e4af8afc15518880721fc9c30a/otel-demo-ui.png" alt="OTel Demo App UI running on http://localhost:8080" title="OTel Demo App UI running on http://localhost:8080"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go ahead and explore the amazing selection of telescopes and accessories, and buy a few. 😉🔭&lt;/p&gt;

&lt;h3&gt;
  
  
  9- See Traces in Lightstep
&lt;/h3&gt;

&lt;p&gt;We can now pop over to Lightstep and check out some &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#understanding-distributed-tracing"&gt;Traces&lt;/a&gt;. Let’s do this by creating a &lt;a href="https://docs.lightstep.com/docs/use-notebooks"&gt;Notebook&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, click on the little page icon on the left nav bar (highlighted in blue, below). That will bring up this page:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/5Nckr9lNvszGFsgzjwOqog/89d5b91a62ed12dda09354a3232b914b/ls-create-notebook.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/5Nckr9lNvszGFsgzjwOqog/89d5b91a62ed12dda09354a3232b914b/ls-create-notebook.png" alt="Lightstep create blank notebook page" title="Lightstep create blank notebook page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we build our query for our Traces. Let’s look at the traces from the &lt;code&gt;recommendationservice&lt;/code&gt;. We’ll do by entering &lt;code&gt;recommendationservice&lt;/code&gt; in the field next to “All telemetry”. Because this is a service, select the second value from the drop-down, which says, “Use ‘recommendationservice’ as service value”, as per below:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/2wfmzZ62GwRMUfnnTsHRky/a81cd0f4c8eaf7d44239091d455b95a2/ls-create-notebook-2.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/2wfmzZ62GwRMUfnnTsHRky/a81cd0f4c8eaf7d44239091d455b95a2/ls-create-notebook-2.png" alt="Creating a notebook for the OTel Demo App's recommendationservice" title="Creating a notebook for the OTel Demo App's recommendationservice"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you select that value, you’ll see a chart like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/2ii2nk7J8eoJpyB2Ehhbt1/a868be73b4bd237f66675a675ff9266d/ls-create-notebook-3.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/2ii2nk7J8eoJpyB2Ehhbt1/a868be73b4bd237f66675a675ff9266d/ls-create-notebook-3.png" alt="Results of recommendationservice Trace query" title="Results of recommendationservice Trace query"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The little green dots represent trace exemplars from that Service. Hover over one of them to see for yourself!&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/1raoM2PdyFQDe1jmMa5Hoj/58a060ee6085cfd3ad6d2ef134b8170f/ls-create-notebook-4.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/1raoM2PdyFQDe1jmMa5Hoj/58a060ee6085cfd3ad6d2ef134b8170f/ls-create-notebook-4.png" alt="Trace exemplars for recommendationservice in Lightstep notebook" title="Trace exemplars for recommendationservice in Lightstep notebook"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you click on one of these dots, you’ll get taken to the Trace view. Before you click, be sure to save your Notebook first (don’t worry, you’ll get a reminder before you navigate away from the page)!&lt;/p&gt;

&lt;p&gt;Here’s the Trace view we see when we click on the &lt;code&gt;get_product_list&lt;/code&gt; dot (Operation) above::&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/5qsIxT75tcxS1urLNExPSb/4abac571d5aa0924f0d2b9be9b4a691a/ls-trace-view.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/5qsIxT75tcxS1urLNExPSb/4abac571d5aa0924f0d2b9be9b4a691a/ls-trace-view.png" alt="OTel Demo App's recommendationservice Trace view" title="OTel Demo App's recommendationservice Trace view"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pretty cool, amirite?&lt;/p&gt;

&lt;h3&gt;
  
  
  10- See Kubernetes Metrics in Lightstep
&lt;/h3&gt;

&lt;p&gt;But wait...there’s more! Not only can we see Traces in Lightstep, we can also see &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#reliability--metrics"&gt;Metrics&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Remember when you ran &lt;code&gt;terraform apply&lt;/code&gt;? Well, not only did it create a Kubernetes cluster, deploy the OTel Demo App (and OTel Collector), it also created some handy-dandy Metrics dashboards for us.&lt;/p&gt;

&lt;p&gt;You can check out the newly-created Metrics dashboards by going to the Dashboards icon (the icon with 4 little squares) on the left navigation bar:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/SgVDi14wZKPYKinnQ0RCa/a39152fc5a03666d47df849d36b2d5bc/lightstep-dashboard-list.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/SgVDi14wZKPYKinnQ0RCa/a39152fc5a03666d47df849d36b2d5bc/lightstep-dashboard-list.png" alt="Lightstep dashboard list for OTel Demo App" title="Lightstep dashboard list for OTel Demo App"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, let’s check out the &lt;strong&gt;Kubernetes / Compute Resources / Cluster&lt;/strong&gt; dashboard. This dashboard lets you see the state of your cluster.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/55fX1xA5ytWrDTx7iLsT5H/1e6492c8869770997524eab23c60ac15/kubernetes-cluster-dashboards.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/55fX1xA5ytWrDTx7iLsT5H/1e6492c8869770997524eab23c60ac15/kubernetes-cluster-dashboards.png" alt="Kubernetes Compute Resources Cluster Dashboard" title="Kubernetes Compute Resources Cluster Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We then have various other Metrics called &lt;strong&gt;Kubernetes Workload Metrics&lt;/strong&gt;. These are the dashboards with names that start with “&lt;strong&gt;Kubernetes / Compute Resources / Workload&lt;/strong&gt;”. These dashboards are specific to the services you are running. They take into account the Kubernetes Workloads in your various namespaces, using &lt;a href="https://github.com/kubernetes/kube-state-metrics"&gt;kube-state-metrics&lt;/a&gt;. For a closer look, check out &lt;a href="https://github.com/lightstep/unified-observability-k8s-kubecon/blob/main/gke-otel-demo/terraform/modules/lightstep/otel_demo_app_k8s_dashboard.tf"&gt;otel_demo_app_k8s_dashboard.tf&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We used &lt;a href="https://github.com/lightstep/prometheus-k8s-opentelemetry-collector"&gt;Lightstep’s Prometheus Kubernetes OpenTelemetry Collector&lt;/a&gt; to get these Metrics into Lightstep. This Helm chart is inspired by &lt;a href="https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack"&gt;kube-prometheus-stack&lt;/a&gt;, but with one crucial difference -- no Prometheus! We’re able to use recent enhancements to the &lt;a href="https://github.com/open-telemetry/opentelemetry-operator"&gt;OpenTelemetry Operator for Kubernetes&lt;/a&gt; such as support for Service Monitors in order to scrape Prometheus metrics from pods, system components, and more.&lt;/p&gt;

&lt;p&gt;The best part is that you don’t need to install and maintain a &lt;a href="https://prometheus.io"&gt;Prometheus&lt;/a&gt; instance to be able to run it! But wait…there’s more! You also don’t need to use Lightstep as your Observability back-end in order to take advantage of this special Collector! How cool is that??&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You can learn more about the &lt;a href="https://github.com/lightstep/prometheus-k8s-opentelemetry-collector"&gt;Prometheus Kubernetes OpenTelemetry Collector&lt;/a&gt; by checking out the docs &lt;a href="https://docs.lightstep.com/docs/otel-collector-k8s-quick-start"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, the &lt;strong&gt;Kubernetes / Compute Resources / Workload / otel-demo-app-cartservice&lt;/strong&gt; dashboard displays metrics for the OTel Demo App’s cartservice. In it we can see how our containers and pods are doing based on Metrics such as those for CPU and Memory.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/57ppDqZqiXLZ6r0MeaoVmg/cd717d5ed0603df5adcf2eb5c1811c83/workload-cart-service-dashboard.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/57ppDqZqiXLZ6r0MeaoVmg/cd717d5ed0603df5adcf2eb5c1811c83/workload-cart-service-dashboard.png" alt="Workload dashboard for OTel Demo App's cartservice" title="Workload dashboard for OTel Demo App's cartservice"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  11- See Application Metrics in Lightstep
&lt;/h3&gt;

&lt;p&gt;Ah...but we’re not done with Metrics just yet! If you go back to the dashboard view and scroll to the very end of the list, you’ll see the &lt;strong&gt;OTel Demo App - Application Metrics&lt;/strong&gt; dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/SgVDi14wZKPYKinnQ0RCa/a39152fc5a03666d47df849d36b2d5bc/lightstep-dashboard-list.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/SgVDi14wZKPYKinnQ0RCa/a39152fc5a03666d47df849d36b2d5bc/lightstep-dashboard-list.png" alt="Lightstep dashboard list for OTel Demo App" title="Lightstep dashboard list for OTel Demo App"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s click on it to take a quick little peek!&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/3FwKSbXFAuij68d3UinVqV/443fed4e4d4129d5e91ebc41ca5a02e0/otel-demo-app-dashboard.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/3FwKSbXFAuij68d3UinVqV/443fed4e4d4129d5e91ebc41ca5a02e0/otel-demo-app-dashboard.png" alt="OTel Demo App Metrics Dashboard" title="OTel Demo App Metrics Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The latest version of the OTel Demo App emits both auto-instrumented and manually-instrumented Metrics. In today’s demo, we wanted to highlight some of the &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#reliability--metrics"&gt;Metrics&lt;/a&gt; from the &lt;code&gt;recommendationservice&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;First, we have the auto-instrumented Python Metrics, which are captured from the Python runtime:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;runtime.cpython.cpu_time&lt;/code&gt;: Track the amount of time being spent in different states of the CPU. This includes user (time running application code) and system (time spent in the operating system). This metric is represented as total elapsed time in seconds.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;runtime.cpython.memory&lt;/code&gt;: Memory utilization &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;runtime.cpython.gc_count&lt;/code&gt;: Number of times the garbage collector has been called.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We also have one manually-instrumented Metric:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;app_recommendations_counter&lt;/code&gt;: Cumulative count of the number of recommended products per service call&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more on the &lt;code&gt;recommendationservice&lt;/code&gt; &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#reliability--metrics"&gt;Metrics&lt;/a&gt;, check out &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/blob/main/docs/services/recommendationservice.md"&gt;this doc&lt;/a&gt;. For more on &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#reliability--metrics"&gt;Metrics&lt;/a&gt; captured by other services, check out the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo/tree/main/docs/services"&gt;OTel Demo App service docs&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  12- Teardown
&lt;/h3&gt;

&lt;p&gt;If you’re no longer using this environment, don’t forget to tear down its resources, to avoid running up a huge cloud bill. You’re welcome. 😉&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform destroy &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This step can take up to 30 minutes, so please be patient! Also, you’ll probably notice that on first run, you’ll see the following error:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/iWJTisAFXDNxnrlyf16n5/b9da920442c86ddca0ae7bad01b5b82b/tf-destroy-error.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/iWJTisAFXDNxnrlyf16n5/b9da920442c86ddca0ae7bad01b5b82b/tf-destroy-error.png" alt="Terraform destroy error" title="Terraform destroy error"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Don’t panic!&lt;/em&gt;&lt;/strong&gt; If you run &lt;code&gt;terraform destroy -auto-approve&lt;/code&gt; again, it will finish nukifying all the things.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Today we got to see some aspects of Observability-Landscape-as-Code (OLaC) in practice! Specifically, we looked at the following elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application instrumentation with &lt;a href="https://opentelemetry.io"&gt;OpenTelemetry&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Collecting and storing application telemetry via the OTel Collector&lt;/li&gt;
&lt;li&gt;Configuring an Observability back-end (i.e. &lt;a href="https://app.lightstep.com"&gt;Lightstep&lt;/a&gt;) through code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We showcased this by using Terraform to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deploy the OpenTelemetry Demo App to Kubernetes.&lt;/strong&gt; The Otel Demo App showcases the &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#understanding-distributed-tracing"&gt;Traces&lt;/a&gt; and &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#reliability--metrics"&gt;Metrics&lt;/a&gt; instrumentation of different services in different languages using OpenTelemetry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy an OpenTelemetry Collector to Kubernetes (part of the Demo App deployment).&lt;/strong&gt; The Collector is used to send application Traces and Metrics to Lightstep.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configure Lightstep dashboards.&lt;/strong&gt; The Lightstep Terraform provider allowed us to codify this.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Codifying our Observability Landscape means that we can tear down and recreate our application, Collector, and dashboards as needed, knowing that we’ll have consistency across the board every single time. Plus, it means that we can version control it, so that it’s not lost in the ether somewhere, or sitting in a secret server under Bob’s desk. Bonus!&lt;/p&gt;

&lt;p&gt;Hopefully this gives you a nice little flavour of the power of OLaC, and will inspire you to go out there and start OLaC-ing too! (I just made up a new verb. You’re welcome.)&lt;/p&gt;

&lt;p&gt;Whew! That was a lot to think about and take in! Give yourself a pat on the back, because we’ve covered a LOT! Now, please enjoy this picture of Adriana’s rat, Bunny, enjoying an almond!&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/2xH9Iu8LOCbVRvTudg80Zo/37dc2c3d97529ed22ca16739467d05ec/bunny-the-rat.JPEG" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/2xH9Iu8LOCbVRvTudg80Zo/37dc2c3d97529ed22ca16739467d05ec/bunny-the-rat.JPEG" alt="Bunny the rat enjoying an almond treat" title="Bunny the rat enjoying an almond treat"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Peace, love, and code. 🦄 🌈 💫&lt;/p&gt;




&lt;p&gt;The &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo App&lt;/a&gt; is always looking for feedback and contributors. Please consider joining the &lt;a href="https://github.com/open-telemetry/community/blob/main/community-membership.md#member"&gt;OTel Community&lt;/a&gt; to help make OpenTelemetry AWESOME!&lt;/p&gt;




&lt;p&gt;Got questions about Observability-Landscape-as-Code? Talk to us! Feel free to connect with us through &lt;a href="//mailto:devrel@lightstep.com"&gt;e-mail&lt;/a&gt;, or:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connect with Adriana on &lt;a href="https://twitter.com/adrianamvillela"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/adrianavillela/"&gt;LinkedIn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Connect with Ana on &lt;a href="https://twitter.com/Ana_M_Medina"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/anammedina/"&gt;LinkedIn&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hope to hear from y’all!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>kubernetes</category>
      <category>terraform</category>
      <category>observability</category>
    </item>
    <item>
      <title>KubeCon North America 2022: A Retrospective</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Wed, 09 Nov 2022 20:15:53 +0000</pubDate>
      <link>https://forem.com/lightstep/kubecon-north-america-2022-a-retrospective-4i4d</link>
      <guid>https://forem.com/lightstep/kubecon-north-america-2022-a-retrospective-4i4d</guid>
      <description>&lt;p&gt;&lt;strong&gt;with &lt;a href="https://www.linkedin.com/in/anammedina/"&gt;Ana Margarita Medina&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hello fellow Kubernetes nerds! 🤓 KubeCon North America is now behind us, so it’s time for a conference retrospective! Now, y’all must be thinking, “Awww, not another one! Noooo...” Ah, but ‘tis not just any KubeCon wrap-up, because both &lt;a href="https://lightstep.com/blog/authors/ana-margarita-medina"&gt;Ana&lt;/a&gt; and I will be sharing our perspectives, and we’re awesome and y’all love us. Amirite? Course I am. Without further ado...&lt;/p&gt;

&lt;h2&gt;
  
  
  Adriana’s Point-of-View
&lt;/h2&gt;

&lt;p&gt;If there is any conference that is on My Conference Bucket List™, it is most definitely KubeCon. I have a love-hate relationship with &lt;a href="https://kubernetes.io"&gt;Kubernetes&lt;/a&gt; (don’t we all, though?), and &lt;a href="https://adri-v.medium.com/list/kubernetes-090db256e52b"&gt;have spent hours trying to understand its wiley ways&lt;/a&gt;, and cursing at my terminal at yet another &lt;a href="https://stackoverflow.com/questions/41604499/my-kubernetes-pods-keep-crashing-with-crashloopbackoff-but-i-cant-find-any-lo"&gt;CrashLoopBackOff&lt;/a&gt;. So to go to KubeCon and nerd out on Kubernetes just sounded awesome to me. &lt;/p&gt;

&lt;p&gt;I finally got my chance by attending &lt;a href="https://events.linuxfoundation.org/kubecon-cloudnativecon-north-america/"&gt;KubeCon North America 2022&lt;/a&gt;, which took place in Detroit, MI.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/3jXTMhBsdJ7xyKsKMLUQsd/129dd3bb67c4fce33e65259c928f0082/detroit-collage.JPG" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/3jXTMhBsdJ7xyKsKMLUQsd/129dd3bb67c4fce33e65259c928f0082/detroit-collage.JPG" alt="Collage of various downtown Detroit landmarks" title="Collage of various downtown Detroit landmarks"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I was super excited to attend the conference for a few reasons.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It would be the first time I’d be getting to meet my Lightstep DevRel team, and find out if I was the shortest of the bunch. Spoiler alert: Ana and I are the shortest at around 5’3 (161cm for my Metric peeps). Latina genes. I roll with it.&lt;/li&gt;
&lt;li&gt;It was my conference début as a DevRel. Y’all, this is my first DevRel role, if you can believe it - 6 months in and lovin’ it!&lt;/li&gt;
&lt;li&gt;Last time I attended a conference was &lt;a href="https://devopsdays.org/events/2018-toronto/welcome/"&gt;DevOps Days Toronto, in 2018&lt;/a&gt;. The highlight of that was getting &lt;a href="https://nicolefv.com/"&gt;Dr. Nicole Forsgren&lt;/a&gt; and &lt;a href="https://research.google/people/106958/"&gt;Jez Humble&lt;/a&gt; (both of &lt;a href="https://www.devops-research.com/research.html"&gt;DORA&lt;/a&gt; fame) to sign a hard copy of their book, &lt;a href="https://www.amazon.ca/Accelerate-Software-Performing-Technology-Organizations/dp/1942788339/ref=asc_df_1942788339/?tag=googleshopc0c-20&amp;amp;linkCode=df0&amp;amp;hvadid=293004119900&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=13450460705037761726&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9000918&amp;amp;hvtargid=pla-446149606248&amp;amp;psc=1"&gt;Accelerate&lt;/a&gt;, so not too shabby. PS: Get the book. Bonus: if you get the audiobook, it’s read by Nicole. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, enough rambling. Here’s my KubeCon recap, newbie edition! &lt;/p&gt;

&lt;p&gt;As a COVID-cautious gal, I was pleased to see that the &lt;a href="https://www.cncf.io/"&gt;CNCF&lt;/a&gt; had various COVID precautions in place including vaccine requirement or a negative COVID test to pick your badge and masking was required and enforced. various COVID precautions in place. For starters, you needed proof of vaccination or proof of a negative COVID test in order to pick your badge. Inside the venue, masking was required and enforced. I always mask indoors (no exceptions), so I definitely felt more at ease among the droves of humans, knowing that we had those protections in place. The other thing that I very much appreciated is that the CNCF provided freebie &lt;a href="https://www.globalpointofcare.abbott/en/product-details/navica-binaxnow-covid-19-us.html"&gt;rapid COVID tests&lt;/a&gt; proctored through &lt;a href="https://www.emed.com"&gt;eMed&lt;/a&gt;, so I was able to test daily while I was there, for extra peace of mind.&lt;/p&gt;

&lt;p&gt;Although KubeCon itself didn’t start until Wednesday, October 26th, I flew in on Sunday night so that I could catch &lt;a href="https://events.linuxfoundation.org/open-observability-day-north-america/program/schedule/"&gt;Open Observability Day&lt;/a&gt; on Monday, and &lt;a href="https://www.eventbrite.com/e/otel-unplugged-kubeconcloudnativecon-detroit-2022-tickets-427595037267?utm_source=o11y.news&amp;amp;utm_medium=email"&gt;OTel Unplugged&lt;/a&gt; on Tuesday.&lt;/p&gt;

&lt;h3&gt;
  
  
  Day 1: Open Observability Day
&lt;/h3&gt;

&lt;p&gt;I popped in and out of &lt;a href="https://events.linuxfoundation.org/open-observability-day-north-america/program/schedule/"&gt;Open Observability Day&lt;/a&gt;, and of the talks that I caught, there were two talks that I really enjoyed. One was &lt;a href="https://www.youtube.com/watch?v=p0oHZl3M51A&amp;amp;list=PLj6h78yzYM2MXp3asRZmwDIBPXfJIJn4F&amp;amp;index=8"&gt;OTel Me How to Build a Data Pipeline for Observability, by Daniel Kim and Reese Lee&lt;/a&gt;. Even though I was already familiar with the content, I really appreciated Daniel and Reese’s super energetic presentation style. It was definitely a welcome pick-me-up to help fight post-lunch food coma. The other talk I really enjoyed was &lt;a href="https://www.youtube.com/watch?v=ieDvAiWxlt0&amp;amp;list=PLj6h78yzYM2MXp3asRZmwDIBPXfJIJn4F&amp;amp;index=9"&gt;What Can eBPF Actually do for Modern-Day Observability? by Ori Shussman&lt;/a&gt;. In it, he talks about how &lt;a href="https://www.tigera.io/learn/guides/ebpf/"&gt;eBPF&lt;/a&gt; lets us see data that is otherwise not visible. For example, eBPF is useful for providing greater insight into &lt;a href="https://grpc.io"&gt;gRPC&lt;/a&gt; calls, which are notoriously difficult to observe. 🤯I also wanted to give a special shout-out to &lt;a href="https://www.youtube.com/watch?v=1v8aL9Jxce8&amp;amp;list=PLj6h78yzYM2MXp3asRZmwDIBPXfJIJn4F&amp;amp;index=13"&gt;Opening the Door to Observability, by Libby Meren&lt;/a&gt;. Although I missed this talk, this topic is very near and dear to my heart, because in my previous role, I had to spend a chunk of time trying to get buy-in on doing Observability The Right Way ™.&lt;/p&gt;

&lt;p&gt;Interested in checking out the other talks? Y’all can check out videos for them &lt;a href="https://www.youtube.com/watch?v=Q5Vf8bpTDlI&amp;amp;list=PLj6h78yzYM2MXp3asRZmwDIBPXfJIJn4F"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/5toOUhVqcmAjFAE5eAqpOv/28020dadb6092456f957ac958d391e23/open-o11y-day.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/5toOUhVqcmAjFAE5eAqpOv/28020dadb6092456f957ac958d391e23/open-o11y-day.png" alt="A presentation in progress at Open Observability Day" title="A presentation in progress at Open Observability Day"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Later that afternoon, I got to meet my Lightstep DevRel fam in real life, and it was AWESOME! Unfortunately, we never took a group selfie. 😿&lt;/p&gt;

&lt;h3&gt;
  
  
  Day 2: OTel Unplugged
&lt;/h3&gt;

&lt;p&gt;I spent Day 2 at the &lt;a href="https://www.weddingwire.com/biz/colony-club-detroit-detroit/68e394fe233e346c.html"&gt;Colony Club&lt;/a&gt; to attend &lt;a href="https://www.eventbrite.com/e/otel-unplugged-kubeconcloudnativecon-detroit-2022-tickets-427595037267?utm_source=o11y.news&amp;amp;utm_medium=email"&gt;OTel Unplugged&lt;/a&gt;. This event was sponsored by &lt;a href="https://lightstep.com"&gt;Lightstep&lt;/a&gt;, &lt;a href="https://honeycomb.io"&gt;Honeycomb&lt;/a&gt;, &lt;a href="https://newrelic.com"&gt;New Relic&lt;/a&gt;, &lt;a href="https://www.splunk.com"&gt;Splunk&lt;/a&gt;, &lt;a href="https://www.dynatrace.com"&gt;Dynatrace&lt;/a&gt;, &lt;a href="https://www.crowdstrike.com"&gt;Crowdstrike&lt;/a&gt;, and &lt;a href="https://www.nginx.com"&gt;NGINX&lt;/a&gt;. I came into the event not knowing what to expect. I can sometimes clamp up when I’m around folks that I don’t know, but because I was helping with the event check-in, I got to say hello to a number of the attendees, which helped break the ice. And it turns out that there were a lot of names that I recognized from my work in the OTel community, and it was nice to connect in person with folks whom I’d only previously met through Slack or Zoom.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/6eQFxw1380BEjAClwSSoMK/620fb02724bfe3101f50cc00dcce50ed/otel-unplugged-collage.JPG" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/6eQFxw1380BEjAClwSSoMK/620fb02724bfe3101f50cc00dcce50ed/otel-unplugged-collage.JPG" alt="Collage of OTel Unplugged highlights" title="Collage of OTel Unplugged highlights"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PS: Big shout-out to the venue, which was gorgeous, and the staff were super friendly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Day 3: KubeCon!
&lt;/h3&gt;

&lt;p&gt;Finally, the main event! I’d been to the conference center on Monday to pick up my badge, but the volume of people that day didn’t even compare to how busy things got on Wednesday. It was a whirlwind of a day, and I spent some of my time at the Lightstep booth. The Lightstep crew did demos of the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OTel Demo App&lt;/a&gt; to illustrate &lt;a href="https://lightstep.com/blog/observability-as-code-with-kubernetes-and-lightstep"&gt;Observability-Landscape-as-Code in action&lt;/a&gt;, which &lt;a href="https://twitter.com/Ana_M_Medina"&gt;Ana&lt;/a&gt; and I poured our blood, terror, terror, sweat, and (happy?) tears into it before KubeCon. It’s a great little demo, if I do say so myself, and I definitely recommend that you check it out. (Shameless plug, I know. Sorrynotsorry.) &lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/5lI6chZQiVWri0NpfC5Ok2/5e96c01e39b9818c35327defbbbcbf56/ls-booth-collage.JPG" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/5lI6chZQiVWri0NpfC5Ok2/5e96c01e39b9818c35327defbbbcbf56/ls-booth-collage.JPG" alt="Collage of the Lightstep booth at KubeCon" title="Collage of the Lightstep booth at KubeCon"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The life-sized game of &lt;a href="https://en.wikipedia.org/wiki/Jenga"&gt;Jenga&lt;/a&gt; at our booth was a huge draw, and there were a lot of adventurous folks who tried their hand at it. The pic of Ana and me below shows how tall the tower got. What you don’t see is that, shortly after the pic was taken, one of us knocked the tower down. Oops. 😳&lt;/p&gt;

&lt;p&gt;Our team also had KubeCon-Detroit-themed stickers made especially for this event. Ana took things to the next level and did her nails to match our stickers. And I don’t know if you can see it in any of the photos below, but she also wore Kubernetes-themed earrings too. She’s very extra, and I love it! The stickers were part of a scavenger hunt that revealed the location of a pizza party hosted by Lightstep on Thursday night.&lt;/p&gt;

&lt;p&gt;Ana and I also spent some time roaming the exhibitors’ area, grabbing some sweet swag (I nabbed a sweet OTel t-shirt and the cutest little &lt;a href="https://tracetest.io"&gt;Tracetest&lt;/a&gt; plushie, whom I named Tracey), meeting new and interesting people, and meeting some familiar faces in real life. (Hi &lt;a href="https://twitter.com/a_bangser"&gt;Abby&lt;/a&gt;!!) Ah, the life of a DevRel. So. Much. Fun. 💜💜💜&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/1f3a4StmUJTosYBaEK1nbI/7ce98a53eda032fedb80d33c461fcf2d/kubecon-collage.JPG" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/1f3a4StmUJTosYBaEK1nbI/7ce98a53eda032fedb80d33c461fcf2d/kubecon-collage.JPG" alt="Collage of KubeCon highlights" title="Collage of KubeCon highlights"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, I wasn’t able to stay for the full conference, but from what I did experience, it was lots of fun. This most definitely will not be my last KubeCon, and I most definitely can’t wait for the next KubeCon! Bring it!&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/6fr8XpNHdr8uZ9mCqDopYu/a63aa189b672fae018eab03b410858ab/bring-it.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/6fr8XpNHdr8uZ9mCqDopYu/a63aa189b672fae018eab03b410858ab/bring-it.png" alt="Bring it On meme" title="Bring it On meme"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ana’s Point-of-View
&lt;/h2&gt;

&lt;p&gt;Ana here! I wanted to provide my perspective from attending four KubeCons in the past. This is my second KubeCon through COVID, and I appreciate that the CNCF has enforced COVID protocols for their events. As someone who caught COVID during KubeCon EU València, I was extra careful this time around, and tested daily. I’m still testing and COVID negative, so thanks, CNCF! &lt;/p&gt;

&lt;p&gt;On Monday, I was scheduled to go to the Contributor Summit, but I lost my voice the week before on Thursday, and I still hadn’t recovered it by Monday morning.🥲 I had talks and booth duty during the coming week, so instead I did something more easygoing and I just helped my fellow Lightsteppers get ready for KubeCon and did final touches on my slides. &lt;/p&gt;

&lt;p&gt;One thing that was really hard this year was that there were too many (or what felt to me like too many) co-located/Day 0 events. It’s great to see the ecosystem continuing to grow, but it definitely makes it very hard to decide which events to go to, for an opportunity to learn from the community. I know I struggled with this, so I do hope that we try to unify topics a bit more next time. &lt;/p&gt;

&lt;p&gt;On Tuesday, I split my time between &lt;a href="https://kccncna2022.sched.com/event/1AOma/chaos-day-hosted-by-harness-additional-in-person-registration-fee-100"&gt;Chaos Day&lt;/a&gt;, &lt;a href="https://community.cncf.io/events/details/cncf-keptn-community-presents-keptn-community-day-kubecon-cloudnativecon-detroit/"&gt;Keptn Community Day&lt;/a&gt;, and of course, the hallway track of the co-located events. From working in the Chaos Engineering community the last 4+ years, I loved to see the &lt;a href="https://twitter.com/Ana_M_Medina/status/1584958979767402496"&gt;Cloud Native Chaos Engineering community growth&lt;/a&gt;, I also loved talking to folks about the future of &lt;a href="https://keptn.sh/"&gt;Keptn&lt;/a&gt;, and giving a talk on how to achieve more reliability with this CNCF sandbox project and the other OSS tools. &lt;/p&gt;

&lt;p&gt;As Wednesday kicked off, it was great to feel the buzz and excitement of folks so eager to learn and connect. From Day 1’s keynote, I really enjoyed the reminder that companies benefiting from Kubernetes and the CNCF ecosystem should be getting involved, giving back, and mentoring others. There was a lot of love to maintainers and contributors in the ecosystem, and as someone involved in the &lt;a href="https://github.com/kubernetes/sig-release"&gt;Kubernetes Release Team&lt;/a&gt; for Kubernetes v1.25 and v1.26, it was a huge honor to see my face up in there with the rest of the team! 💙&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/676f9AHqWgYElocXjzquV6/b9ed8b78c35061d8b6085257554dad6f/release_team.jpg" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/676f9AHqWgYElocXjzquV6/b9ed8b78c35061d8b6085257554dad6f/release_team.jpg" alt="Slide of the Kubernetes release team" title="Slide of the Kubernetes release team. Photo credit: https://twitter.com/LeonardPahlke/status/1585273031680823298/photo/1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also took some time to walk the show floor, buy some CNCF swag and check out the vendor swag too. &lt;/p&gt;

&lt;p&gt;Thursday was another busy day of running around with the community. I also got to record some Cloud Native in Spanish content with some other Latinxs in the space. In addition to that, Lightstep hosted two events, one of which was our secret pizza party. It was fun to take over Detroit’s famous alley, &lt;a href="https://www.thebelt.org/about-the-belt"&gt;The Belt&lt;/a&gt;, to hang out and talk about SRE. Thanks to everyone who stopped by! #DetroitSRECity 🤘&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/11r0SAW3PyUozU9xiazURY/a2d50c4df2b0ca244f33a2eb9f1282c9/the_belt_collage.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/11r0SAW3PyUozU9xiazURY/a2d50c4df2b0ca244f33a2eb9f1282c9/the_belt_collage.png" alt="Collage of highlights from The Belt" title="Collage of highlights from The Belt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the other places I spent a lot of time at at KubeCon NA was the CNCF Project Pavillion. I was very happy to see that it was a bit larger than the area we had during KubeCon EU, but I still wish that it was bigger and wasn’t so tucked away in the corner. A number of booths were showcasing their projects with demos through the week, hosted Q&amp;amp;A time, and gave away swag. If you are still trying to understand the Cloud Native Ecosystem, you can look at this &lt;a href="https://landscape.cncf.io/"&gt;very extensive map&lt;/a&gt; of the landscape and projects under the CNCF, some of which are more advanced than others. Of course I’m biased, but I’m really excited for the work that &lt;a href="https://keptn.sh/"&gt;Keptn&lt;/a&gt; is doing in helping developers have more control over their application lifecycle. I’m also very excited to see where &lt;a href="https://backstage.io/"&gt;Backstage&lt;/a&gt; goes and how other CNCF projects can integrate with their service catalog. &lt;/p&gt;

&lt;p&gt;On Friday, I got a chance to hang out some more with my community friends and give a talk on the future of Keptn with our friends at &lt;a href="https://www.dynatrace.com"&gt;Dynatrace&lt;/a&gt;. I am very excited to see where this project goes and what work we continue to do with OpenTelemetry to make it easier to observe our deployments and their reliability. &lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/4siIu0JPRwrKyVvUUEa31/4035b01d421b4d2897f2e234640e8c06/keptn_friday_talk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/4siIu0JPRwrKyVvUUEa31/4035b01d421b4d2897f2e234640e8c06/keptn_friday_talk.jpg" alt="Ana at the Keptin Friday talk with some of the Keptin crew" title="Ana at the Keptin Friday talk with some of the Keptin crew"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Aaaand...that’s a wrap! KubeCon was a ton of fun for both Ana and me. We loved having the  Lightstep DevRel team in person for the first time, we loved connecting with our various CNCF communities, Lightstep customers, and it was great fun to see all the cool tech out there. Whether you’re a KubeCon newbie like me, or KubeCon veteran like Ana, it was great to see the CNCF community continuing to come together, grow, and prosper. We are definitely looking forward to KubeCon EU in 2023!&lt;/p&gt;

&lt;p&gt;Now, please enjoy this photo of Adriana’s rat, Phoebe, keeping her company as she does her KubeCon expense report.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/4wTOgMiV7u4a1XLElOM483/47fefc06e936287fb2c86cacae2b8c11/phoebe-computer.jpg" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/4wTOgMiV7u4a1XLElOM483/47fefc06e936287fb2c86cacae2b8c11/phoebe-computer.jpg" alt="Phoebe the rat helps Adriana with KubeCon expenses" title="Phoebe the rat helps Adriana with KubeCon expenses"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Peace, love, and code. 🦄 🌈 💫&lt;/p&gt;




&lt;p&gt;Got questions about Observability? Talk to us! Feel free to connect with us through &lt;a href="//mailto:devrel@lightstep.com"&gt;e-mail&lt;/a&gt;, or:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connect with Adriana up on &lt;a href="https://twitter.com/adrianamvillela"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/adrianavillela/"&gt;LinkedIn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Connect with Ana on &lt;a href="https://twitter.com/Ana_M_Medina"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/anammedina/"&gt;LinkedIn&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hope to hear from y’all!&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>kubecon</category>
      <category>devrel</category>
      <category>conference</category>
    </item>
    <item>
      <title>Observability Mythbusters: Observability Anti-Patterns</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Wed, 19 Oct 2022 19:57:23 +0000</pubDate>
      <link>https://forem.com/lightstep/observability-mythbusters-observability-anti-patterns-9c4</link>
      <guid>https://forem.com/lightstep/observability-mythbusters-observability-anti-patterns-9c4</guid>
      <description>&lt;p&gt;Have you ever been at a place that claimed to be all in on Observability, only for you to realize that well, they’re not really following Observability practices? Yeah. Me neither. Just kidding. I’ve been in this space long enough to witness my fair share of anti-patterns, or what I like to refer to as, “Crimes Against Observability”. In today’s post, I’ll be calling these out, in the hopes that you can avoid these crimes and be on your merry way towards unlocking Observability’s powers. &lt;/p&gt;

&lt;p&gt;Let’s get started!&lt;/p&gt;

&lt;h3&gt;
  
  
  1- Traces not treated as a first-class citizen
&lt;/h3&gt;

&lt;p&gt;Too many organizations put far too much emphasis on metrics and logs, while either completely disregarding or downplaying traces. Yes, &lt;a href="https://lightstep.com/blog/observability-mythbusters-logs-and-metrics-arent-enough"&gt;metrics and logs are useful…to a point&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Metrics&lt;/strong&gt; can give us information about things like CPU levels and the amount of time that it takes to complete a transaction. But they can only provide aggregate information that you can’t drill down into to understand what’s going on with your system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Logs&lt;/strong&gt; provide useful point-in-time information; however, by themselves, logs make it pretty damn difficult to troubleshoot. They’re a wall of text that you have to parse through so you can kinda sorta maybe piece together what’s up with your code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://lightstep.com/blog/observability-mythbusters-logs-and-metrics-arent-enough"&gt;Neither metrics nor logs give you enough context to understand what’s happening with your system at a high level&lt;/a&gt;. Thus, the biggest crime against Observability is committed when metrics and logs are treated as the main actors of your Observability story, when in fact they take more of a supporting role. Spoiler alert: traces are the true stars of the show. &lt;/p&gt;

&lt;p&gt;So how do we fix this? Take a trace-first approach. Traces give you that end-to-end system wide view. They show you not only what’s going on within services, but also across services. How do logs and metrics fit into this? &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make logs more useful by making them a part of our overall story—i.e. traces—embedded as Span Events. &lt;/li&gt;
&lt;li&gt;Correlate metrics to traces via a linking attribute. For example, a VM with a given IP address can be correlated to a Trace if we capture IP address as a Span attribute.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Moral of the story:&lt;/strong&gt; take a trace-first approach with your &lt;a href="https://lightstep.com/blog/observability-mythbusters-observability-landscape-as-code"&gt;Observability landscape&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  2- The Wall ’o Dashboards
&lt;/h3&gt;

&lt;p&gt;Y’all, this one’s nails on chalkboard for me. I once worked at a place where leadership thought that their production woes would be solved by dashboarding all the things. “I helped set up a wall of dashboards when I worked at XYZ company and it helped us so much!” Yeah. Like 10 years ago, when there wasn’t much else to work with. Time to get with the times and rethink that wall ‘o dashboards, my friends. &lt;/p&gt;

&lt;p&gt;Does that mean that dashboards go away entirely? No. Instead, rethink your dashboard situation. Use fewer dashboards. Don’t rely on the Wall ‘o Dashboards to guide your Observability journey. A better alternative to Metrics dashboards would be to use &lt;a href="https://sre.google/sre-book/service-level-objectives/"&gt;Service-Level Objectives (SLOs)&lt;/a&gt;. SLOs are actionable. For example, suppose you have an SLO that states that the response time for Service X must be 95% of the time. If the service is not meeting that SLO, it triggers an alert to notify by Slack, phone, pager, passenger pigeon, or whatever, to tell your on-call engineers that your system is not behaving within the expected parameters, and that you've gotta take a closer look at things.&lt;/p&gt;

&lt;p&gt;For more hot takes on dashboards, check out this &lt;a href="https://charity.wtf/2021/08/09/notes-on-the-perfidy-of-dashboards/"&gt;great piece by Charity Majors&lt;/a&gt; and this &lt;a href="https://youtu.be/5maHQiElGgY"&gt;short, fun video by Austin Parker&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  3- Getting someone else to instrument your code
&lt;/h3&gt;

&lt;p&gt;Say you’re a developer. Would you get someone else to comment your code or write your unit tests? I didn’t think so. Now, say that your team is getting into Observability. This means that you need to instrument your code à la &lt;a href="//opentelemetry.io"&gt;OpenTelemetry&lt;/a&gt;. Would you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Instrument your own code&lt;/li&gt;
&lt;li&gt;Ask someone else (maybe your SREs?) to instrument your code&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you answered B, then, yeaaaaahhhh...“Houston, we have a problem”.&lt;/p&gt;

&lt;p&gt;What’s wrong with this picture? Well, for starters, the SREs didn’t write the code. You did. Just like it would be super weird to have someone comment &lt;strong&gt;YOUR&lt;/strong&gt; code and write unit tests for &lt;strong&gt;YOUR&lt;/strong&gt; code, then why would it be okay to have someone else instrument &lt;strong&gt;YOUR&lt;/strong&gt; code? How in Space do you expect them to know &lt;strong&gt;WHAT&lt;/strong&gt; to instrument?&lt;/p&gt;

&lt;p&gt;Look, there’s no shame in not having instrumented your code before. If you’re just getting started with Observability on an existing code base, you bet your pants that you’ll have to go through your code and instrument it. But you can also ensure that you instrument new code as you write it, going forward. Moral of the story, instrument your own code. More specifically, if you focus on &lt;a href="https://lightstep.com/blog/observability-mythbusters-observability-landscape-as-code#instrument-your-code"&gt;instrumenting your home-grown frameworks and libraries&lt;/a&gt;, then you have all the coverage you need, as far as tracing is concerned. Whatever you do, please, don't get someone else to do your dirty work for you. &lt;/p&gt;

&lt;p&gt;But hey, don’t take just my word for it. I’ll let &lt;a href="https://twitter.com/oncallmemaybe/status/1570069781210107906?s=21&amp;amp;t=nhP2h1KNYopD7leVTT4XmQ"&gt;Liz Fong-Jones have the last word&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;“You’re a full grown-up software engineer. Write your own damn tests. Write your own damn comments. Write your own damn Observability annotations. This will help YOU understand your code later."&lt;/p&gt;

&lt;h3&gt;
  
  
  4- Belief that Observability Tooling == Observability
&lt;/h3&gt;

&lt;p&gt;Oh, mes amis, this couldn’t be further from the truth. Observability is a set of practices supported by tools. These include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instrumenting your &lt;a href="https://lightstep.com/blog/observability-mythbusters-not-only-for-sre"&gt;code properly so that you have enough info to troubleshoot when issues arise&lt;/a&gt;, and thereby avoiding calling the same group of “experts” to troubleshoot &lt;/li&gt;
&lt;li&gt;Treating traces as first-class citizens&lt;/li&gt;
&lt;li&gt;Keeping an eye on the health of your system after you release to prod&lt;/li&gt;
&lt;li&gt;Creating meaningful, SLO-based alerts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Put that into place, and you’ve got yourself some Observability.&lt;/p&gt;

&lt;h3&gt;
  
  
  5- Observability theatre
&lt;/h3&gt;

&lt;p&gt;We’ve seen it before. Companies going all-in on so-called “&lt;a href="https://medium.com/dzerolabs/how-to-nail-your-digital-transformation-584630c3fbde"&gt;digital transformations&lt;/a&gt;”. Which, of course, includes going Agile and creating a DevOps practice. All noble causes. And the intentions are good…maybe? In reality, nobody actually believed it would work, or wanted it to work. In short, it was all theatre. And I’ve seen that with Observability too. “Hey! We need Observability!” Woo hoo! 🎉 But then it turns out to be a front for Land o’ Logs and Walls ‘o Metrics Dashboards. Because that’s what folks grew up with. And that’s all they want to know. &lt;/p&gt;

&lt;p&gt;Moral of the story: if you don’t have buy-in from your org, both from the exec ranks and the engineers doing the work. Can you get said buy-in? Absolutely! It won’t be an easy journey, but the worthwhile things are never easy to achieve, are they?&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Observability ain’t easy, but keeping the following things in mind will help guide you in your Observability journey:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make Traces your first-class citizen in your Observability Landscape. ✅&lt;/li&gt;
&lt;li&gt;Say “byeeeee” to the wall ’o dashboards, and hello to SLOs. ✅&lt;/li&gt;
&lt;li&gt;Instrument your own damn code. Future You will thank you for it. ✅&lt;/li&gt;
&lt;li&gt;Observability is a set of practices supported by tools. Sort your practices out before reaching out for the shiny new thing. ✅&lt;/li&gt;
&lt;li&gt;Just say no to Observability Theatre.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And now, I shall reward you with a picture of an Octopus decked out for fall, drawn by my &lt;a href="https://instagram.com/old_fashion_glazed"&gt;superly-talented 14-year-old daughter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/2YvMMhcr4p5tLINcKzq10F/066fa53499149b0486fc9678d6b2cf7d/octopus.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/2YvMMhcr4p5tLINcKzq10F/066fa53499149b0486fc9678d6b2cf7d/octopus.png" alt="Octopus going shopping in autumn gear. Drawing by Hannah Maxwell (https://instagram.com/old_fashion_glazed)" title="Octopus going shopping in autumn gear. Drawing by Hannah Maxwell (https://instagram.com/old_fashion_glazed)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Peace, love, and code. 🦄 🌈 💫&lt;/p&gt;




&lt;p&gt;Got questions about Observability and/or OpenTelemetry? Talk to me! Feel free to connect through &lt;a href="//mailto:devrel@lightstep.com"&gt;e-mail&lt;/a&gt;, or hit me up on &lt;a href="https://twitter.com/adrianamvillela"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/adrianavillela/"&gt;LinkedIn&lt;/a&gt;. Hope to hear from y’all!&lt;/p&gt;

</description>
      <category>observability</category>
      <category>thoughtleadership</category>
      <category>architecture</category>
      <category>opentelemetry</category>
    </item>
    <item>
      <title>OpenTelemetry for Python: Manual Configuration &amp; Context Propagation</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Tue, 20 Sep 2022 20:11:24 +0000</pubDate>
      <link>https://forem.com/lightstep/opentelemetry-for-python-the-hard-way-2hb8</link>
      <guid>https://forem.com/lightstep/opentelemetry-for-python-the-hard-way-2hb8</guid>
      <description>&lt;p&gt;In my &lt;a href="https://lightstep.com/blog/auto-instrumentation-is-magic-using-opentelemetry-python-with-lightstep"&gt;last blog post&lt;/a&gt;, I showed y’all how to instrument Python code with &lt;a href="//opentelemetry.io"&gt;OpenTelemetry (OTel)&lt;/a&gt;, à la &lt;a href="https://lightstep.com/blog/auto-instrumentation-is-magic-using-opentelemetry-python-with-lightstep#automatic-instrumentation--python"&gt;auto-instrumentation&lt;/a&gt;. You may also recall from that post that I &lt;a href="https://lightstep.com/blog/auto-instrumentation-is-magic-using-opentelemetry-python-with-lightstep#should-i-always-use-the-auto-instrumentation-agent"&gt;recommended using the Python auto-instrumentation binary&lt;/a&gt; even for non-auto-instrumented libraries, because it abstracts all that pesky OTel config stuff so nicely. When you use it, along with any applicable Python auto&lt;a href="https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation"&gt;-instrumentation libraries&lt;/a&gt; (installed courtesy of &lt;a href="https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/opentelemetry-instrumentation#opentelemetry-bootstrap"&gt;opentelemetry-bootstrap&lt;/a&gt;), it takes care of context propagation across related services for you.&lt;/p&gt;

&lt;p&gt;All in all, it makes life nice ‘n easy for us!&lt;/p&gt;

&lt;p&gt;Well, today, my friends, we’re going to torture ourselves a weeeee bit, because we’re going to put that auto-instrumentation binary aside, and will instead dig into super-duper manual OpenTelemetry instrumentation for Python. Since we don’t have auto-instrumentation as our security blanket, we will have to learn how to do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configure OpenTelemetry for Python to send instrumentation data to an Observability back-end that supports &lt;a href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md"&gt;OTLP&lt;/a&gt;. Spoiler alert: we’ll be using &lt;a href="http://app.lightstep.com"&gt;Lightstep&lt;/a&gt; as our Observability back-end. ✅&lt;/li&gt;
&lt;li&gt;Propagate context across related services so that they show up as part of the same trace ✅&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;/strong&gt; I won’t go into how to create Spans with OTel for Python, since the &lt;a href="https://opentelemetry.io/docs/instrumentation/python/"&gt;official OTel docs&lt;/a&gt; do a mighty fine job of it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Are you scared? Well don’t be, because I’ve figured it all out so that you don’t have to!&lt;/p&gt;

&lt;p&gt;Are you readyyyyy? Let’s do this!!&lt;/p&gt;

&lt;h2&gt;
  
  
  Pre-Requisites
&lt;/h2&gt;

&lt;p&gt;Before we start our tutorial, here are some things that you’ll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A basic understanding of &lt;a href="https://www.python.org/"&gt;Python&lt;/a&gt; and &lt;a href="https://realpython.com/python-virtual-environments-a-primer/"&gt;Python virtual environments&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A basic understanding of &lt;a href="//opentelemetry.io"&gt;OpenTelemetry&lt;/a&gt;. I suggest checking out the &lt;a href="http://opentelemetry.io/docs"&gt;official OTel Docs&lt;/a&gt; for refresher, if you need one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’d like to run the full code examples in Part 2, you’ll also need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://app.lightstep.com/signup/developer?signup_source=docs"&gt;Lightstep Observability account&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens"&gt;Lightstep Access Token&lt;/a&gt; to tell Lightstep what project to send your traces to&lt;/li&gt;
&lt;li&gt;A basic understanding of how to use &lt;a href="http://app.lightstep.com"&gt;Lightstep Observability&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A working installation of &lt;a href="https://www.python.org/downloads/"&gt;Python&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 1: What’s Happening?
&lt;/h2&gt;

&lt;p&gt;We’ll be illustrating Python manual instrumentation with OpenTelemetry with a client and server app. The client will call a &lt;code&gt;/ping&lt;/code&gt; endpoint hosted by the server.&lt;/p&gt;

&lt;p&gt;The example in this tutorial can be found in the &lt;a href="https://github.com/lightstep/opentelemetry-examples/tree/main/python/opentelemetry/manual_instrumentation"&gt;lightstep/opentelemetry-examples&lt;/a&gt; repo. We will be working with three main files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/main/python/opentelemetry/manual_instrumentation/common.py"&gt;common.py&lt;/a&gt; - OTel configuration and connectivity (to connect to Lightstep)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/main/python/opentelemetry/manual_instrumentation/client.py"&gt;client.py&lt;/a&gt; - Connect to our server’s &lt;code&gt;/ping&lt;/code&gt; endpoint&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/main/python/opentelemetry/manual_instrumentation/server.py"&gt;server.py&lt;/a&gt; - Host the &lt;code&gt;/ping&lt;/code&gt; endpoint&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before we run the example code, we must first understand what it’s doing.&lt;/p&gt;

&lt;h3&gt;
  
  
  1- OTel Libraries
&lt;/h3&gt;

&lt;p&gt;In order to send OpenTelemetry data to an Observability back-end (e.g Lightstep), you need to install the following &lt;strong&gt;&lt;em&gt;OpenTelemetry packages&lt;/em&gt;&lt;/strong&gt;, which are included in &lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/main/python/opentelemetry/manual_instrumentation/requirements.txt"&gt;requirements.txt&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;opentelemetry-api
opentelemetry-sdk
opentelemetry-exporter-otlp-proto-grpc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we’re installing the OpenTelemetry API and SDK packages, along with &lt;code&gt;opentelemetry-exporter-otlp-proto-grpc&lt;/code&gt;, which is used to send OTel data to your Observability back-end (e.g. Lightstep) via &lt;a href="https://opentelemetry.io/docs/concepts/glossary/#grpc"&gt;gRPC&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2- OTel Setup and Configuration (common.py)
&lt;/h3&gt;

&lt;p&gt;In our example, OTel setup and configuration is done in &lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/main/python/opentelemetry/manual_instrumentation/common.py"&gt;common.py&lt;/a&gt;. We split things out into this separate file so that we don’t have to duplicate this code in &lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/main/python/opentelemetry/manual_instrumentation/client.py"&gt;client.py&lt;/a&gt; and &lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/main/python/opentelemetry/manual_instrumentation/server.py"&gt;server.py&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, we must import the required OTel packages:&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="n"&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;opentelemetry.exporter.otlp.proto.grpc.trace_exporter&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OTLPSpanExporter&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;opentelemetry.sdk.resources&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SERVICE_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Resource&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;opentelemetry.sdk.trace&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TracerProvider&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;opentelemetry.sdk.trace.export&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BatchSpanProcessor&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we must configure the Exporter. An &lt;strong&gt;Exporter&lt;/strong&gt; is how we send data to OpenTelemetry. As I mentioned earlier, Lightstep accepts data in the OTLP format, so we need to define an OTLP Exporter. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;/strong&gt; Some vendors don’t accept data in OTLP format, which means that you will need to use a &lt;a href="https://opentelemetry.io/registry/?language=go&amp;amp;component=exporter"&gt;vendor-specific exporter&lt;/a&gt; to send data to them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We configure our Exporter in Python like this:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_otlp_exporter&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
   &lt;span class="n"&gt;ls_access_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LS_ACCESS_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;OTLPSpanExporter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ingest.lightstep.com:443&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lightstep-access-token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ls_access_token&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;Some noteworthy items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;endpoint&lt;/code&gt; is set to &lt;code&gt;ingest.lightstep.com:443&lt;/code&gt;, which points to Lightstep’s public Microsatellite pool. If you are using an on-premise satellite pool, then check out &lt;a href="http://localhost:4000/docs/test-connectivity-to-microsatellites"&gt;these docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;You will need to set the &lt;code&gt;LS_ACCESS_TOKEN&lt;/code&gt; environment variable with your own &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens"&gt;Lightstep Access Token&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, we configure the Tracer Provider. A &lt;code&gt;TracerProvider&lt;/code&gt; serves as the entry point of the OpenTelemetry API. It provides access to &lt;code&gt;Tracer&lt;/code&gt;s. A &lt;code&gt;Tracer&lt;/code&gt; is responsible for creating a &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#spans"&gt;Span&lt;/a&gt; to trace the given operation.&lt;/p&gt;

&lt;p&gt;We configure our Tracer Provider in Python like this:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_tracer&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;span_exporter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_otlp_exporter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TracerProvider&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OTEL_RESOURCE_ATTRIBUTES&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;        
        &lt;span class="c1"&gt;# Service name is required for most backends
&lt;/span&gt;        &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;SERVICE_NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test-py-manual-otlp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TracerProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Using default service name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;processor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BatchSpanProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;span_exporter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_span_processor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;processor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_tracer_provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few noteworthy items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We define a &lt;a href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/sdk.md"&gt;Resource&lt;/a&gt; to provide OpenTelemetry with a bunch of information that identifies our service, including &lt;a href="https://github.com/open-telemetry/opentelemetry-python/blob/41b9e26d8324ae0496c85326b35e92bf617932d9/opentelemetry-semantic-conventions/src/opentelemetry/semconv/resource/__init__.py#L415"&gt;service name&lt;/a&gt; and &lt;a href="https://github.com/open-telemetry/opentelemetry-python/blob/41b9e26d8324ae0496c85326b35e92bf617932d9/opentelemetry-semantic-conventions/src/opentelemetry/semconv/resource/__init__.py#L433"&gt;service version&lt;/a&gt;. (You can see a full list of Resource attributes that you can set &lt;a href="https://github.com/open-telemetry/opentelemetry-python/blob/main/opentelemetry-semantic-conventions/src/opentelemetry/semconv/resource/__init__.py#L433"&gt;here&lt;/a&gt;.) As the name implies, &lt;em&gt;service name&lt;/em&gt; is the name of the microservice that you are instrumenting, and &lt;em&gt;service version&lt;/em&gt; is the version of the service that you are instrumenting. In this example, we get the service name and service version are passed in as key/value in the environment variable, &lt;a href="https://opentelemetry.io/docs/reference/specification/sdk-environment-variables/#general-sdk-configuration"&gt;OTEL_RESOURCE_ATTRIBUTES&lt;/a&gt; (we’ll see some example values in Part 2). If that environment variable is not present, we then set a default service name, &lt;code&gt;"test-py-manual-otlp"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We are using the &lt;a href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#batching-processor"&gt;BatchSpanProcessor&lt;/a&gt;, which means that we are telling OTel to export the data in batches. For the purposes of this example, we’re not doing anything beyond a basic configuration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3- Initialization (client.py and server.py)
&lt;/h3&gt;

&lt;p&gt;We’re finally ready to send data to Lightstep! All we need to do is call &lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/main/python/opentelemetry/manual_instrumentation/common.py"&gt;common.py&lt;/a&gt;’s &lt;code&gt;get_tracer&lt;/code&gt; function from &lt;code&gt;client.py&lt;/code&gt; (Lines &lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/49f018f2cb529a5c0def6109c7e0bfda791e1164/python/opentelemetry/manual_instrumentation/client.py#L17-L20"&gt;17-20&lt;/a&gt;) and &lt;code&gt;server.py&lt;/code&gt; (Lines &lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/49f018f2cb529a5c0def6109c7e0bfda791e1164/python/opentelemetry/manual_instrumentation/server.py#L17"&gt;17&lt;/a&gt; and &lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/49f018f2cb529a5c0def6109c7e0bfda791e1164/python/opentelemetry/manual_instrumentation/server.py#L29"&gt;29&lt;/a&gt;), like this:&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="n"&gt;common&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_tracer&lt;/span&gt;

&lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;tracer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_tracer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4- Instrumentation (client.py and server.py)
&lt;/h3&gt;

&lt;p&gt;With initialization done, we need to instrument our code, which means that we’ll need to create Spans. I won’t go into the specifics of Span creation here, since &lt;a href="https://opentelemetry.io/docs/instrumentation/python/"&gt;the OTel docs&lt;/a&gt; do a pretty good job of it, and as I mentioned in the intro, it’s outside of the scope of this post.&lt;/p&gt;

&lt;p&gt;I will, however, briefly mention that there are a couple of ways to instrument our code in Python, and you’ll see both ways of Span creation in the example code: &lt;a href="https://opentelemetry.io/docs/instrumentation/python/manual/#creating-spans"&gt;using the with statement&lt;/a&gt;, and &lt;a href="https://opentelemetry.io/docs/instrumentation/python/manual/#creating-spans-with-decorators"&gt;using function decorators&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can see an example of creating a Span using the &lt;a href="https://opentelemetry.io/docs/instrumentation/python/manual/#creating-spans"&gt;with statement&lt;/a&gt; in &lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/49f018f2cb529a5c0def6109c7e0bfda791e1164/python/opentelemetry/manual_instrumentation/client.py#L23-L32"&gt;client.py, Lines 23-32&lt;/a&gt;. Below is the full function listing:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_requests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&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="nf"&gt;start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;client operation&lt;/span&gt;&lt;span class="sh"&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="n"&gt;carrier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
            &lt;span class="nc"&gt;TraceContextTextMapPropagator&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;carrier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traceparent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;carrier&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traceparent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
            &lt;span class="n"&gt;res&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Request to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, got &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; bytes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Request to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; failed &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Span is initialized with the line, &lt;code&gt;with tracer.start_as_current_span("client operation"):&lt;/code&gt;, and everything below that line is within the scope of that Span.&lt;/p&gt;

&lt;p&gt;You can see an example of creating a Span using a &lt;a href="https://opentelemetry.io/docs/instrumentation/python/manual/#creating-spans-with-decorators"&gt;function decorator&lt;/a&gt; in &lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/49f018f2cb529a5c0def6109c7e0bfda791e1164/python/opentelemetry/manual_instrumentation/server.py#L78"&gt;server.py Line 78&lt;/a&gt;. Below is the full function listing:&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="nd"&gt;@tracer.start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pymongo_integration&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/pymongo/&amp;lt;length&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&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;pymongo_integration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;):&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="nf"&gt;start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;server pymongo operation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MongoClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mongo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;27017&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serverSelectionTimeoutMS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;opentelemetry-tests&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;collection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tests&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_one&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;_random_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few noteworthy items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The line &lt;code&gt;@tracer.start_as_current_span("pymongo_integration")&lt;/code&gt; starts the Span for the &lt;code&gt;pymongo_integration&lt;/code&gt; function. Everything in that function is within the scope of that Span.&lt;/li&gt;
&lt;li&gt;You may have also noticed that we initialize another span in there, with the line, &lt;code&gt;with tracer.start_as_current_span("server pymongo operation"):&lt;/code&gt;, (&lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/49f018f2cb529a5c0def6109c7e0bfda791e1164/python/opentelemetry/manual_instrumentation/server.py#L89"&gt;server.py, Line 89&lt;/a&gt;). This means that we end up with &lt;a href="https://opentelemetry.io/docs/instrumentation/python/manual/#creating-nested-spans"&gt;nested Spans&lt;/a&gt; (a Span within a Span).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5- Context Propagation
&lt;/h3&gt;

&lt;p&gt;As I mentioned in the intro, one of the advantages of using Python auto-instrumentation is that it takes care of context propagation across services for you. If you don’t use auto-instrumentation, however, you have to take care of context propagation yourself. Great. Just great.&lt;/p&gt;

&lt;p&gt;But before we dig into how to do that, we need to first understand context propagation.&lt;/p&gt;

&lt;p&gt;Definition time!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context&lt;/strong&gt; represents the information that correlates Spans across process boundaries. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Propagation&lt;/strong&gt; is the means by which context is bundled and transferred in and across services, often via HTTP headers.&lt;/p&gt;

&lt;p&gt;This means that when one service calls another, they will be linked together as part of the same &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#distributed-traces"&gt;Trace&lt;/a&gt;. If you go the pure manual instrumentation route (like we’re doing today), however, you have to make sure that your context is propagated across services that call each other, otherwise you’ll end up with separate, unrelated-even-though-they-should-be-related) Traces.&lt;/p&gt;

&lt;p&gt;I have to admit that I was wracking my brains trying to figure out this context propagation stuff. After much time spent Googling and asking folks around here for clarification, I finally got it, so I’m going to share this piece with you here to hopefully spare you some stress.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;/strong&gt; Although the OpenTelemetry documentation does provide some insight into how to do &lt;a href="https://opentelemetry.io/docs/instrumentation/python/cookbook/#manually-setting-span-context"&gt;manual context propagation in Python&lt;/a&gt;, the documentation needs a little work. I’m actually part of the &lt;a href="https://github.com/open-telemetry/opentelemetry.io"&gt;OpenTelemetry Comms SIG&lt;/a&gt;, so I am using this as motivation to improve the docs around this topic…stay tuned for updates to the OTel docs too! 😎&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Okay, so how do we do this manual context propagation? First, let’s remind ourselves of what’s happening in our example app. We have a &lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/49f018f2cb529a5c0def6109c7e0bfda791e1164/python/opentelemetry/manual_instrumentation/client.py"&gt;client&lt;/a&gt; service and a &lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/49f018f2cb529a5c0def6109c7e0bfda791e1164/python/opentelemetry/manual_instrumentation/server.py"&gt;server&lt;/a&gt; service. The client service calls the &lt;code&gt;/ping&lt;/code&gt; endpoint on the server service, which means that we expect them to be part of the same Trace. This in turn means that we have to ensure that they both have the same Trace ID in order to be seen by Lightstep (and other Observability back-ends) as being related.&lt;/p&gt;

&lt;p&gt;At a high level, we accomplish this by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Getting the Trace ID of the client&lt;/li&gt;
&lt;li&gt;Injecting the Trace ID into the HTTP header before the client calls the server&lt;/li&gt;
&lt;li&gt;Extracting the client’s Trace ID from the HTTP header on the server side&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Easy peasey! Now let’s look at the code that needs to make this happen.&lt;/p&gt;

&lt;p&gt;First, we need to start with something called a &lt;code&gt;carrier&lt;/code&gt;. A &lt;code&gt;carrier&lt;/code&gt; is just a key-value pair containing a Trace ID, and it looks something like this:&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="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traceparent&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;00-a9c3b99a95cc045e573e163c3ac80a77-d99d251a8caecd06-01&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;traceparent&lt;/code&gt; is the key, and the value is your Trace ID. Note that the above is just an example of what a Trace ID might look like. Obviously, your own Trace ID will be different (and will be different each time you run the code).&lt;/p&gt;

&lt;p&gt;Okay, great. Now how do we obtain said &lt;code&gt;carrier&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;First, we need to import a &lt;code&gt;TraceContextTextMapPropagator&lt;/code&gt; in &lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/main/python/opentelemetry/manual_instrumentation/client.py"&gt;client.py&lt;/a&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="n"&gt;opentelemetry.trace.propagation.tracecontext&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TraceContextTextMapPropagator&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we must populate the carrier:&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;carrier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="nc"&gt;TraceContextTextMapPropagator&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;carrier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you were to inspect the value of &lt;code&gt;carrier&lt;/code&gt; after this line, you would see that it would look something like this:&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="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;traceparent&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;00-a9c3b99a95cc045e573e163c3ac80a77-d99d251a8caecd06-01&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look familiar? 🤯&lt;/p&gt;

&lt;p&gt;Now that we have the &lt;code&gt;carrier&lt;/code&gt;, we need to put it into our HTTP header before we make a call to 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="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traceparent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;carrier&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traceparent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;span class="n"&gt;res&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And voilà! Your carrier is in the HTTP request!&lt;/p&gt;

&lt;p&gt;Now that we know what all of these snippets do, let’s put it all together. Here’s what our client code looks like:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_requests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&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="nf"&gt;start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;client operation&lt;/span&gt;&lt;span class="sh"&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="n"&gt;carrier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
            &lt;span class="nc"&gt;TraceContextTextMapPropagator&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;carrier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traceparent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;carrier&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traceparent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
            &lt;span class="n"&gt;res&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Request to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, got &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; bytes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Request to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; failed &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the full code listing, check out &lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/49f018f2cb529a5c0def6109c7e0bfda791e1164/python/opentelemetry/manual_instrumentation/client.py"&gt;client.py&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Okay…we’ve got things sorted out on the client side. Yay! Now let’s go to the server side and pluck our &lt;code&gt;carrier&lt;/code&gt; from the HTTP request.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/main/python/opentelemetry/manual_instrumentation/server.py"&gt;server.py&lt;/a&gt;, we pull the value of &lt;code&gt;traceparent&lt;/code&gt; from our header like this:&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;traceparent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_header_from_flask_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traceparent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where we define &lt;code&gt;get_header_from_flask_request&lt;/code&gt; as:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_header_from_flask_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can build our &lt;code&gt;carrier&lt;/code&gt; from this information:&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;carrier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traceparent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;traceparent&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use that to extract the context from this &lt;code&gt;carrier&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="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TraceContextTextMapPropagator&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;carrier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can create our Span with the context, &lt;code&gt;ctx&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="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="nf"&gt;start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/ping&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we are passing &lt;code&gt;ctx&lt;/code&gt; to a named parameter called &lt;code&gt;context&lt;/code&gt;. This ensures that our &lt;code&gt;"/ping"&lt;/code&gt; Span knows that it’s part of an existing Trace (the one originating from our client call).&lt;/p&gt;

&lt;p&gt;It is worth noting that any child Spans of the &lt;code&gt;"/ping"&lt;/code&gt; Span do not require us to pass in a context, since that’s passed in implicitly (see &lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/49f018f2cb529a5c0def6109c7e0bfda791e1164/python/opentelemetry/manual_instrumentation/server.py#L81"&gt;server.py, Line 81&lt;/a&gt;, for example).&lt;/p&gt;

&lt;p&gt;Now that we know what all of these snippets do, let’s put it all together. Here’s what our server code looks like:&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="bp"&gt;...&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;opentelemetry.trace.propagation.tracecontext&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TraceContextTextMapPropagator&lt;/span&gt;

&lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_header_from_flask_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/ping&lt;/span&gt;&lt;span class="sh"&gt;"&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;ping&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

   &lt;span class="n"&gt;traceparent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_header_from_flask_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traceparent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;carrier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traceparent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;traceparent&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="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TraceContextTextMapPropagator&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;carrier&lt;/span&gt;&lt;span class="p"&gt;)&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="nf"&gt;start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/ping&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

       &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randint&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="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="nf"&gt;redis_integration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="nf"&gt;pymongo_integration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="nf"&gt;sqlalchemy_integration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;_random_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the full code listing, check out &lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/49f018f2cb529a5c0def6109c7e0bfda791e1164/python/opentelemetry/manual_instrumentation/server.py"&gt;server.py&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: Try it!
&lt;/h2&gt;

&lt;p&gt;Now that we know the theory behind all of this, let’s run our example!&lt;/p&gt;

&lt;h3&gt;
  
  
  1- Clone the repo
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/lightstep/opentelemetry-examples.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2- Setup
&lt;/h3&gt;

&lt;p&gt;Let’s first start by setting up our Python virtual environment:&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;cd &lt;/span&gt;python/opentelemetry/manual_instrumentation

python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; ./bin/activate

&lt;span class="c"&gt;# Install requirements.txt&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3- Run the Server app
&lt;/h3&gt;

&lt;p&gt;We’re ready to run the server. Be sure to replace &lt;code&gt;&amp;lt;LS_ACCESS_TOKEN&amp;gt;&lt;/code&gt; with your own &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens"&gt;Lightstep Access Token&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export LS_ACCESS_TOKEN="&amp;lt;LS_ACCESS_TOKEN&amp;gt;"
export OTEL_RESOURCE_ATTRIBUTES=service.name=py-opentelemetry-manual-otlp-server,service.version=10.10.9

python server.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember how I told you that we’d see an example of values passed into &lt;a href="https://opentelemetry.io/docs/reference/specification/sdk-environment-variables/#general-sdk-configuration"&gt;OTEL_RESOURCE_ATTRIBUTES&lt;/a&gt;? Well, here it is! Here, we’re passing in the service name &lt;code&gt;py-opentelemetry-manual-otlp-server&lt;/code&gt;, and service version &lt;code&gt;10.10.9&lt;/code&gt;. The service name will show up in the Lightstep explorer.&lt;/p&gt;

&lt;p&gt;Your output will look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/3NaAtbY9oIF3S9146yB0Q7/d30604d8d671d58f3a815d0dced0f445/server-py-startup-output.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/3NaAtbY9oIF3S9146yB0Q7/d30604d8d671d58f3a815d0dced0f445/server-py-startup-output.png" alt="Python server.py startup sequence output" title="Python server.py startup sequence output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4- Run the Client app
&lt;/h3&gt;

&lt;p&gt;Open up a new terminal window, and run the client app. Be sure to replace &lt;code&gt;&amp;lt;LS_ACCESS_TOKEN&amp;gt;&lt;/code&gt; with your own &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens"&gt;Lightstep Access Token&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;PS: Make sure you’re in &lt;code&gt;python/opentelemetry/manual_instrumentation&lt;/code&gt; in the &lt;code&gt;opentelemetry-examples&lt;/code&gt; repo root.&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;LS_ACCESS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;LS_ACCESS_TOKEN&amp;gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_RESOURCE_ATTRIBUTES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;service.name&lt;span class="o"&gt;=&lt;/span&gt;py-opentelemetry-manual-otlp-client,service.version&lt;span class="o"&gt;=&lt;/span&gt;10.10.10

python client.py &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note how we’re passing in the service name &lt;code&gt;py-opentelemetry-manual-otlp-client&lt;/code&gt;, and service version &lt;code&gt;10.10.10&lt;/code&gt;. The service name will show up in the Lightstep explorer.&lt;/p&gt;

&lt;p&gt;When you run the client app, it will continuously call the &lt;code&gt;/ping&lt;/code&gt; endpoint. Let it run a few times (maybe 5-6 times-ish?), and kill it (à la &lt;code&gt;ctrl+c&lt;/code&gt;). Sample output:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/4uSvoyme1rUE4IFtMgA0xp/ecf1e291a28d1618456d4ea2421f18d6/client-py-output.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/4uSvoyme1rUE4IFtMgA0xp/ecf1e291a28d1618456d4ea2421f18d6/client-py-output.png" alt="Sample client.py output" title="Sample client.py output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you peek over at the terminal running &lt;code&gt;server.py&lt;/code&gt;, you will likely notice a super-ugly stack trace. DON’T PANIC! The &lt;code&gt;/ping&lt;/code&gt; service makes calls to &lt;a href="https://redis.com/"&gt;Redis&lt;/a&gt; and &lt;a href="https://www.mongodb.com/"&gt;MongoDB&lt;/a&gt;, and since neither of these services is running, you end up getting some nasty error messages like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/5IWuvbRFNRHiMe0QLXrV6i/993b7f441c33acd39205445bba712482/server-py-output-error.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/5IWuvbRFNRHiMe0QLXrV6i/993b7f441c33acd39205445bba712482/server-py-output-error.png" alt="Sample server.py program run output with error" title="Sample server.py program run output with error"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5- See it in Lightstep
&lt;/h3&gt;

&lt;p&gt;If you go to your trace view in Lightstep by selecting the &lt;code&gt;py-opentelemetry-manual-otlp-client&lt;/code&gt; service from the explorer (you could also see the same thing by going to the &lt;code&gt;py-opentelemetry-manual-otlp-server&lt;/code&gt; service), you’ll see the end-to-end trace showing the client calling the server, and the other functions called within the server.&lt;/p&gt;

&lt;p&gt;And remember that stack trace in Step 4? Well, it shows up as an error in your Trace. Which is cool, because it tells you that you have a problem, and pinpoints to where it’s happening! How cool is that??&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/1DSyzl4Vx01TwovVj9rsAc/49f84ba9316ba3e3e289dc59eb3ca33d/py-manual-instrumentation-ls-trace.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/1DSyzl4Vx01TwovVj9rsAc/49f84ba9316ba3e3e289dc59eb3ca33d/py-manual-instrumentation-ls-trace.png" alt="End-to-end trace sample of server.py and client.py in Lightstep" title="End-to-end trace sample of server.py and client.py in Lightstep"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And remember how we never passed our context to the &lt;code&gt;redis_integration&lt;/code&gt;  and &lt;code&gt;server redis operation&lt;/code&gt; Spans, you can see that &lt;code&gt;server redis operation&lt;/code&gt; rolls up to &lt;code&gt;redis_integration&lt;/code&gt;, which rolls up to &lt;code&gt;/ping&lt;/code&gt;, just like I said it would. Magic! 🪄&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Today we learned how to manually configure OpenTelemetry for Python to connect to Lightstep (this also works for any Observability back-end that ingests the &lt;a href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md"&gt;OTLP format&lt;/a&gt;). We also learned how to link related services together through manual context propagation.&lt;/p&gt;

&lt;p&gt;Now, if you ever find yourself in a situation whereby you need to either connect to your Observability back-end without the use of the Python auto-instrumentation binary and/or need to manually propagate context across services, you will know how to do it!&lt;/p&gt;

&lt;p&gt;Now, please enjoy this cuddly little pile of rats. From front to back: Phoebe, Bunny, and Mookie. They were nice enough to sit still for the camera while my husband held them.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/1m1ecSAYLDduaA76r4oDHM/dcad1b0ca80f87e93b5f7efd13552d76/pile-o-rats.JPG" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/1m1ecSAYLDduaA76r4oDHM/dcad1b0ca80f87e93b5f7efd13552d76/pile-o-rats.JPG" alt="Pile 'o rats! Featuring Phoebe, Bunny, and Mookie" title="Pile 'o rats! Featuring Phoebe, Bunny, and Mookie"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Peace, love, and code. 🌈 🦄 💫&lt;/p&gt;




&lt;p&gt;Got questions about OTel instrumentation with Python? Talk to me! Feel free to connect through &lt;a href="//mailto:devrel@lightstep.com"&gt;e-mail&lt;/a&gt;, or hit me up on &lt;a href="https://twitter.com/adrianamvillela"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/adrianavillela/"&gt;LinkedIn&lt;/a&gt;. Hope to hear from y’all!&lt;/p&gt;

</description>
      <category>python</category>
      <category>opentelemetry</category>
      <category>tutorial</category>
      <category>observability</category>
    </item>
    <item>
      <title>Introducing the On-Call Me Maybe Podcast!</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Mon, 12 Sep 2022 13:18:43 +0000</pubDate>
      <link>https://forem.com/lightstep/introducing-the-on-call-me-maybe-podcast-8jn</link>
      <guid>https://forem.com/lightstep/introducing-the-on-call-me-maybe-podcast-8jn</guid>
      <description>&lt;p&gt;Y’all, I am so excited to announce that &lt;a href="https://twitter.com/Ana_M_Medina"&gt;Ana Margarita Medina&lt;/a&gt; and I just launched the &lt;a href="//oncallmemaybe.com"&gt;On-Call Me Maybe podcast&lt;/a&gt;! This podcast is dedicated to nerding out on all things DevOps, SRE, Observability, On-Call, and everything in between. Want to learn more? Then read on, my friend!&lt;/p&gt;

&lt;h2&gt;
  
  
  Your most awesome hosts
&lt;/h2&gt;

&lt;p&gt;Ana and I met when we both joined Lightstep as Developer Advocates in April of this year, and though we haven’t known each other for very long, we are very much partners in crime. &lt;/p&gt;

&lt;p&gt;I’m super excited about this collaboration because Ana is a fellow Latina in tech. Being in a male-dominated industry, it’s been beyond super cool to work with a fellow tech gal from a similar cultural background. I was originally born in Rio de Janeiro, Brazil, and moved to Canada when I was 10. Ana hails from Costa Rica, and her parents are from Nicaragua. She moved to the US when she was in elementary school. We bring some of the flair of our respective backgrounds to this podcast. 😎&lt;/p&gt;

&lt;h2&gt;
  
  
  Our guests
&lt;/h2&gt;

&lt;p&gt;We want to use this podcast as a platform for nerdy tech discussions, sociotechnical systems, mental health, and diversity &amp;amp; inclusion…and our guests reflect that!  We want to connect our audience with folks from all walks of tech. You’ll hear from some familiar faces, and some not-so-familiar faces. Our conversations are fun and casual, with chill vibes all around. &lt;/p&gt;

&lt;h2&gt;
  
  
  Give us a listen!
&lt;/h2&gt;

&lt;p&gt;Our &lt;a href="https://oncallmemaybe.com/episodes/tech-origin-stories"&gt;first episode&lt;/a&gt; dropped on &lt;strong&gt;September 6th&lt;/strong&gt;. It features a conversation between Ana and me, as we talk about our beginnings in tech. We’ll be coming at you with new episodes on a weekly basis. You’ll be hearing from folks at &lt;a href="//honeycomb.io"&gt;Honeycomb&lt;/a&gt;, &lt;a href="//jeli.io"&gt;Jeli&lt;/a&gt;, &lt;a href="//thoughtworks.com"&gt;ThoughtWorks&lt;/a&gt;, &lt;a href="//hashicorp.com"&gt;HashiCorp&lt;/a&gt;, &lt;a href="//wavelo.com"&gt;Wavelo&lt;/a&gt;, and more! We’ve also got a couple of guest hosts, including &lt;a href="https://twitter.com/codeboten"&gt;Alex Boten&lt;/a&gt; and &lt;a href="https://twitter.com/tedsuo"&gt;Ted Young&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So what are you waiting for? Be sure to give us a listen, follow &lt;a href="//oncallmemaybe.com"&gt;On-Call Me Maybe&lt;/a&gt; on &lt;a href="//twitter.com/oncallmemaybe"&gt;Twitter&lt;/a&gt;, follow our &lt;a href="//linkedin.com/company/on-call-me-maybe-podcast"&gt;LinkedIn&lt;/a&gt; page, and subscribe via your favourite podcasting app!&lt;/p&gt;

&lt;p&gt;Peace, love, and code. 🌈 🦄 💫&lt;/p&gt;

</description>
      <category>observability</category>
      <category>oncall</category>
      <category>devops</category>
      <category>news</category>
    </item>
    <item>
      <title>Let's Talk About Psychological Safety</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Thu, 01 Sep 2022 17:29:44 +0000</pubDate>
      <link>https://forem.com/lightstep/lets-talk-about-psychological-safety-2bh1</link>
      <guid>https://forem.com/lightstep/lets-talk-about-psychological-safety-2bh1</guid>
      <description>&lt;h2&gt;
  
  
  There’s something askew…
&lt;/h2&gt;

&lt;p&gt;On the morning of Friday, July 8th, 2022, as I rolled out of bed, I instinctively reached over to my phone to check the weather for the day. Right away, I knew that something was off. My weather app wouldn’t load the weather. I toggled my wifi only to realize that I had no bars of cell signal on my phone. My husband’s phone also had no bars. No cell service and no internet. What was this? The ‘80s?!&lt;/p&gt;

&lt;p&gt;As remote workers, this meant that we could not work until this thing got resolved. Cut off from the outside world, we scrambled to find out what was up. How bad was this thing? Luckily we live near a subway station with free wifi, so we stood across the street from the station and bummed a wifi signal from there. &lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/BBzWm5Gtfju97m14Mmnot/85a256d5b8373f3da60b6d66891e7217/slack-status.PNG" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/BBzWm5Gtfju97m14Mmnot/85a256d5b8373f3da60b6d66891e7217/slack-status.PNG" alt="My Slack status on July 8th: No internet or cell. Free subway wifi is periodic lifeline. Send good internet vibes." title="My Slack status on July 8th. Screen shot by [Adriana Villela](http://adri-v.medium.com)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We were able to check in with our respective co-workers and scour the interwebs to find out what was up. There was a &lt;a href="https://www.rogers.com"&gt;Rogers&lt;/a&gt; service outage, and it affected cable, cell, and internet services. For those not in Canada, Rogers is one of our three large telecom providers. Our other two large providers, &lt;a href="https://www.telus.com/en/"&gt;Telus&lt;/a&gt; and &lt;a href="https://www.bell.ca"&gt;Bell&lt;/a&gt;, were unaffected.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/7p7lgFAAMCKq2Id8O0RVYx/1f6cb8cfa8c568201129494edfa6ef05/rogers-outage-message.PNG" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/7p7lgFAAMCKq2Id8O0RVYx/1f6cb8cfa8c568201129494edfa6ef05/rogers-outage-message.PNG" alt="Rogers service outage message on July 8th, 2022" title="Rogers service outage message on July 8th, 2022. Screen shot by [Adriana Villela](http://adri-v.medium.com)."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Psychological Safety
&lt;/h2&gt;

&lt;p&gt;To say that this situation sucked is an understatement. It definitely made me realize just how dependent we are on internet connectivity for our day-to-day lives. Blows my mind. 🤯 I wasn’t able to work at all on Friday, which meant I was a bit behind on my work. I know that it was far worse for others. I think of the Uber drivers who couldn’t make their living because they were on the affected network. I think of the store owners who couldn’t accept debit payments because the outage affected Interac. What about folks closing on a house that day who couldn’t transfer funds? Folks who needed to cash out investments to make a down payment for a house. Or folks who couldn’t get cash at the ATMs of the affected banks. So yeah. This definitely sucked for a LOT of folks. But do you know who this also sucked for? The folks in the thick of this outage, who were probably breaking out in cold sweats, shaking in their booties, losing sleep, skipping meals, and straight up having panic attacks as they tried to resolve this issue as quickly as possible. &lt;/p&gt;

&lt;p&gt;Before y’all come at me with your torches and pitchforks, let me remind you that the folks who were working to resolve Friday’s outage are HUMAN, and they were probably having the WORST. DAY. EVER.&lt;/p&gt;

&lt;p&gt;This got me thinking of all the times I was involved with major production issues, and just how friggin’ STRESSFUL it was. Imagine being called on to solve a production issue. You don’t know what’s going on. You need to get your bearings, and you need to do it FAST. To top it all off, you’re likely on a call with a ton of people, including project managers and execs who are breathing down your neck, asking you for updates. If you’re super lucky, you’ll get some cowboy exec who “used to code” 20 years ago and tries to tell you how to troubleshoot. Recipe for SO. MUCH. FUN. Not.&lt;/p&gt;

&lt;p&gt;Many folks who have worked in support or operations type roles have some serious PTSD from their experiences when they are called into fixing production issues. To the point where some just walk away from these types of roles, because it scars them for life. And the reason why they have PTSD over this is because they don’t have psychological safety. &lt;/p&gt;

&lt;p&gt;So, what is psychological safety? &lt;a href="https://en.wikipedia.org/wiki/Psychological_safety"&gt;Wikipedia&lt;/a&gt; actually gives a really good definition:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/61HGP67sxRc12VXfetmMdl/ee1abb5c7785a80dc152ebbaeb81e1ce/psychological-safety-definition.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/61HGP67sxRc12VXfetmMdl/ee1abb5c7785a80dc152ebbaeb81e1ce/psychological-safety-definition.png" alt="Psychological safety is being able to show and employ oneself without fear of negative consequences of self-image, status or career" title="Psychological safety is being able to show and employ oneself without fear of negative consequences of self-image, status or career"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/28n8Q1fqp9DzgV4IjHekYG/cb1ffb57533faf9d8490788674bdb1f1/Merp.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/28n8Q1fqp9DzgV4IjHekYG/cb1ffb57533faf9d8490788674bdb1f1/Merp.jpeg" alt="Merp" title="This is a Merp. It's cute. Image by [Hannah Maxwell](instagram.com/old_fashion_glazed)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Meh. Who cares?
&lt;/h2&gt;

&lt;p&gt;Why is psychological safety important? Because it gives folks room to fail. I’ve put in changes that broke a system before. It wasn’t intentional. And when I realized what happened, I was wracked with guilt, and was sweating buckets to find out the root cause. Unfortunately, like many folks in our industry, I didn’t have sympathetic management on my side to shield me from the fallout. If anything, I’ve had managers who were only too happy to point fingers at me for my failures.&lt;/p&gt;

&lt;p&gt;Unfortunately, even though I vowed to not do this myself as a manager, I’m ashamed to say that I’ve been part of the problem too. I was once managing a team that was responsible for a critical backbone system. They made a maintenance change during the day, assuming that it wouldn’t affect anything, because when they’d made the same change in pre-prod, everything had been  copacetic. Of course, things went south in prod. Because you know…Murphy. It brought the system down. Fortunately, my team was amazing and they were able to figure out the issue quickly, and brought things up within minutes. High fives all around. &lt;/p&gt;

&lt;p&gt;Then, upper management started messaging me. And texting me. They wanted answers. Why did this system go down? How could this happen? I assured them that the team didn’t realize that this would happen in prod, since it had worked in pre-prod. I promised that we would use off-hours maintenance windows. The team was not happy. We were trying to push the needle by following more SRE-like practices (like &lt;a href="https://docs.microsoft.com/en-us/devops/operate/safe-deployment-practices"&gt;deployments and updates during business hours&lt;/a&gt;), and this was not &lt;a href="https://sre.google/sre-book/part-III-practices/"&gt;SRE-like&lt;/a&gt; at all. We all knew it. But I was trying to keep upper management happy.&lt;/p&gt;

&lt;p&gt;We had &lt;em&gt;another&lt;/em&gt; outage a couple of weeks later. Similar, but different. Fixed quickly again. But upper management was livid. Why was this happening again? More messages, more texts, and video calls to the tone of “Explain yourself!”&lt;/p&gt;

&lt;p&gt;I let them get into my head, and I told the team how upper management was unhappy with these outages. Which of course stressed my team. They felt bad enough about the outage as it was. They’re good people. Smart people. Trying to do the right thing. But they’re also human. They make mistakes. And getting flack from me, even if it was minor flack, was distressing. And my team lead called me out on it. He sent me a private message saying that I wasn’t providing the team with psychological safety. &lt;/p&gt;

&lt;p&gt;Whoa. Did that ever hit me like a ton of bricks! My first reaction was anger. How rude! How dare he? I thought I went pretty easy on the team. My tone was chill. What’s his beef? They weren’t following protocol. Screw him. I was livid. &lt;/p&gt;

&lt;p&gt;But now that I think of it, HE. WAS. RIGHT.&lt;/p&gt;

&lt;p&gt;You see, there were two points of failure here. First, upper management was putting pressure on me because their major client had experienced so many outages (mostly not related to our team) that just one more outage, even a minor one that was fixed quickly, eroded their confidence in the company. But I think that perhaps they could’ve “trained” their client to understand that outages happen and that it’s about how quickly you recover from the outages that matters. Instead, they continued to feed the client’s notion that failure should never ever ever happen. If only…&lt;/p&gt;

&lt;p&gt;Then there was me. As a manager and spokesperson for my team, I should’ve done a better job of protecting them. Making it safe for them to fail. Telling them things like, “Upper management is on my butt over this and they want answers” just makes a stressful situation worse. I should’ve also pushed harder on the notion that we can only mature our SRE practices if we’re allowed to fail fast, recover quickly, and make changes during business hours. As a leader, I have to help make the change happen, and in this case I didn’t. &lt;/p&gt;

&lt;p&gt;Do I regret my actions? Definitely. Couldda wouldda shouldda, right? But let me tell you...I learned from my gaffe. I am so much more aware of psychological safety as both a manager and an individual contributor, and as part of my mission as a Developer Advocate, I want more people talking about psychological safety. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Path to Psychological Safety
&lt;/h2&gt;

&lt;p&gt;How do we achieve psychological safety? Though behavioural changes. That good ‘ole mindset change that we hear in the DevOps and Observability circles! Yes, it’s a real thing! And it starts with leadership.&lt;/p&gt;

&lt;p&gt;For leaders, here are some things y’all can do to help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Give engineers room to breathe.&lt;/strong&gt; I can assure you that asking the poor engineer when the issue will be fixed is incredibly aggravating. I can also assure you that when they have an update, the engineer will happily, gleefully, and excitedly be more than willing to report status.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don’t make your teams feel guilty for causing a failure or not fixing things quickly enough.&lt;/strong&gt; Remember that sometimes you have people who had nothing to do with the failure who are trying really to fix that system. Y’all need to cut them some slack!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Understand the root cause.&lt;/strong&gt; This means that you don’t point fingers at &lt;em&gt;who&lt;/em&gt; caused the change, but instead focus on the &lt;em&gt;what&lt;/em&gt; – i.e. understand the contributing factors that led to the failure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a safe space for your direct reports.&lt;/strong&gt; In doing so, they will feel comfortable in coming to you with concerns and even suggestions for improvement. To achieve this safe space, you will need to invest in relationship-building. Trust in them, and in return, they will trust you. If they feel that you have their back, they’ll have yours. I assure you. I’ve been on both sides.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Celebrate your engineers for the rockstars that they are. (Maybe give ‘em a hug too. #HugOps)&lt;/strong&gt; Really. As one friend of mine put it, 99% of the time, we as end-users are spoiled by relatively smooth-running systems, because there are engineers working their magic to minimize service disruptions. And yet folks are ready to pick on the 1% that went wrong, without even appreciating that there’s some seriously amazing work being done 99% of the time. How’s that good for morale, retention, and burnout prevention when your staff don’t even feel appreciated?&lt;/li&gt;
&lt;li&gt;**Embrace the failure. **Failure is inevitable, so the sooner you learn to deal with it, the better!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/7A9r4FmvCVucXWCoXfsaAS/2df1754cb470f980c155d3b086b698f0/blue-shirt-saves-the-day.jpg" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/7A9r4FmvCVucXWCoXfsaAS/2df1754cb470f980c155d3b086b698f0/blue-shirt-saves-the-day.jpg" alt="The guy in the blue shirt is under pressure during a production crisis" title="The guy under pressure during a production crisis."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So the above list is a great start. But wait…there’s more that can be done! Because we have some wonderful tools and practices at our disposal to help make this happen. &lt;/p&gt;

&lt;p&gt;First off, there’s &lt;a href="https://adri-v.medium.com/list/unpacking-observability-be1835c6dd23"&gt;Observability&lt;/a&gt;. (Come on…y’all didn’t think I could do a whole blog post without mentioning Observability, did you? 😉) Observability is your friend when major incidents arise, because it provides you a holistic view of your system. &lt;a href="https://lightstep.com/blog/observability-mythbusters-observability-landscape-as-code#instrument-your-code"&gt;Properly-instrumented code&lt;/a&gt;, along with a &lt;a href="https://medium.com/dzerolabs/unpacking-observability-how-to-choose-an-observability-vendor-aa0e6d80b71d"&gt;good Observability back-end&lt;/a&gt; help you see what’s going on in your systems so that you can troubleshoot things quickly and effectively. In doing so, troubleshooting goes from, “Ahhhh!! WTF is happening?!” to, “Oh, I can follow these breadcrumbs to see what’s going on in my system.” This puts support folks at ease because they know that they have the tools to confidently troubleshoot an issue. This puts management at ease, because they know that their support folks are empowered to troubleshoot quickly and effectively. In short, &lt;a href="https://twitter.com/LightstepHQ/status/1545149604764721154"&gt;Observability gives you psychological safety&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1545162437132820480-187" src="https://platform.twitter.com/embed/Tweet.html?id=1545162437132820480"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1545162437132820480-187');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1545162437132820480&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1545163665661362178-551" src="https://platform.twitter.com/embed/Tweet.html?id=1545163665661362178"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1545163665661362178-551');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1545163665661362178&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Secondly, there are &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#reliability--metrics"&gt;Service-Level Objectives&lt;/a&gt; (SLOs). SLOs help us identify what data ingested by your Observability back-end are important and alert-worthy. SLOs connect telemetry with specific customer experiences, so that the entire organization can understand the relationship between the software system and the business goals.&lt;/p&gt;

&lt;p&gt;Finally, there is &lt;a href="https://lightstep.com/blog/observability-mythbusters-observability-landscape-as-code#use-apis-for-incident-response-tool-configuration"&gt;Incident Response tooling&lt;/a&gt;, which, when properly configured and integrated with your Observability back-end, ensures that the right people are contacted at the right time, triggered by the right &lt;a href="https://lightstep.com/blog/observability-mythbusters-observability-landscape-as-code#get-your-slo-game-on-and-codify"&gt;SLO-based alerts&lt;/a&gt; to investigate an issue.&lt;/p&gt;

&lt;p&gt;In a nutshell, psychological safety is made possible by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Proper support from management&lt;/li&gt;
&lt;li&gt;Observability practices supported by tooling&lt;/li&gt;
&lt;li&gt;SLO-based alerts&lt;/li&gt;
&lt;li&gt;Incident response processes and tooling&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;I think we can all agree that the July 8th/9th outage sucked for those of us affected. This also exposes the fact that there’s a system resiliency and reliability issue that needs to be addressed in order to ensure customer retention and confidence. I don’t think that anyone would argue with that. But remember that our systems of today are far more complex than they were even 2 years ago. The systems and devices that we enjoy today are backed by impossibly complex systems, and the more complex our systems, the more risk is involved. &lt;/p&gt;

&lt;p&gt;Also, remember that this situation also TOTALLY SUCKED for the folks on the front lines who were desperately trying to resolve the Rogers outage. That said, if anything good came out of it, it’s that it once again opens up the conversation about psychological safety and how important it is in the tech world. Without psychological safety, you end up with burnout, and you end up with fewer and fewer people who are willing to take on these critical support roles that make our world run seamlessly.&lt;/p&gt;

&lt;p&gt;As a final thought, I’d like to leave you with these words from a good friend of mine:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/5GYWVqZty5ulAWLTCJXtks/5b4e7f4060fc977759becce20a869065/psychological-safety-quote.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/5GYWVqZty5ulAWLTCJXtks/5b4e7f4060fc977759becce20a869065/psychological-safety-quote.png" alt="psychological-safety-quote" title="See full quote at: https://adri-v.medium.com/lets-talk-about-psychological-safety-7cff102ce8be"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, please enjoy this picture of my rat Chrissy, who fancies herself a lioness today.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/2Yy5nKCqQqzqxBZVSNboLR/9cf9f4f892f652edba7eeb2706745585/chrissy-the-lion-rat.jpg" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/2Yy5nKCqQqzqxBZVSNboLR/9cf9f4f892f652edba7eeb2706745585/chrissy-the-lion-rat.jpg" alt="Chrissy the rat with hear head poking out through a piece of paper towel" title="Chrissy the rat fancies herself a lion. Photo by [Hannah Maxwell](https://www.instagram.com/old_fashion_glazed/)."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Peace, love, and code. 🦄 🌈 💫 &lt;/p&gt;




&lt;p&gt;Join the conversation! We’d love to hear your thoughts on psychological safety. Connect with us on the &lt;a href="https://ltstp.run/discord"&gt;Lightstep Community Discord&lt;/a&gt;, or get in touch by &lt;a href="//mailto:devrel@lightstep.com"&gt;e-mail&lt;/a&gt;. Hope to hear from y’all!&lt;/p&gt;

</description>
      <category>mentalhealth</category>
      <category>sre</category>
      <category>incidentresponse</category>
      <category>observability</category>
    </item>
    <item>
      <title>Observability Mythbusters: How hard is it to get started with OpenTelemetry?</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Mon, 29 Aug 2022 15:31:00 +0000</pubDate>
      <link>https://forem.com/lightstep/observability-mythbusters-how-hard-is-it-to-get-started-with-opentelemetry-3l2d</link>
      <guid>https://forem.com/lightstep/observability-mythbusters-how-hard-is-it-to-get-started-with-opentelemetry-3l2d</guid>
      <description>&lt;p&gt;So you're &lt;a href="https://medium.com/@adri-v/list/unpacking-observability-be1835c6dd23"&gt;new to Observability&lt;/a&gt; &lt;em&gt;and&lt;/em&gt; &lt;a href="//opentelemetry.io"&gt;OpenTelemetry&lt;/a&gt; &lt;em&gt;and&lt;/em&gt; Lightstep and you want to send traces to Lightstep to get a feel for the flow of things. But you're not sure how to get started. There's a LOT to learn and take in, and things can get pretty overwhelming, pretty fast, &lt;em&gt;amirite&lt;/em&gt;? I feel ya. I've been there before. That was me last year. Solidarity!!&lt;/p&gt;

&lt;p&gt;But, not to worry, my friend, because I am here to walk you through things. If you're looking to do a quickie setup to learn how to send OpenTelemetry data over to Lightstep, then you, my friend, have come to the right place!&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo&lt;/a&gt; repo was created to help lower the barrier to entry into OpenTelemetry and to illustrate its awesomeness. It features a fully-functioning online store made up of multiple micro-services, written in different languages that are supported by OpenTelemetry, that can send traces to an Observability back end, like Lightstep. (OMG that was a mouthful!) Most importantly, I found it superly duperly easy to set up and get going!&lt;/p&gt;

&lt;p&gt;In today's tutorial, I will show you how to get &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#distributed-traces"&gt;Traces&lt;/a&gt; produced by the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo&lt;/a&gt; into Lightstep. The example uses the &lt;a href="https://opentelemetry.io/docs/collector"&gt;OpenTelemetry (OTel) Collector&lt;/a&gt; to ingest the Traces and send them to an Observability back-end, which in our case, will be Lightstep. &lt;/p&gt;

&lt;p&gt;A few important notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I am assuming that you have a basic understanding of OpenTelemetry and Observability. If not, check out this great &lt;a href="https://opentelemetry.io/docs/concepts/what-is-opentelemetry/"&gt;OpenTelemetry overview&lt;/a&gt; and &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/"&gt;Observability oberview&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;I will not be covering application instrumentation with OpenTelemetry - our goal is merely to get &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#distributed-traces"&gt;Trace&lt;/a&gt; data into Lightstep via the OTel Collactor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before we start the tutorial, let's do a quick OTel Collector overview, so that you can understand &lt;em&gt;how&lt;/em&gt; you're getting data into Lightstep!&lt;/p&gt;

&lt;h3&gt;
  
  
  OTel Collector 101
&lt;/h3&gt;

&lt;p&gt;The OpenTelemetry (OTel) Collector is a vendor-neutral service that ingests, transforms, and exports data to one or more Observability back-ends.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/2GFtT5Q0LC71IBWa7YN9RV/b4326af7c0d99d0d89dcfbf5111f24f3/otel-collector.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/2GFtT5Q0LC71IBWa7YN9RV/b4326af7c0d99d0d89dcfbf5111f24f3/otel-collector.png" alt="OTel Collector" title="The OpenTelemetry Collector is made up of receivers, processtors, and exporters"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The OTel Collector consists of 3 main components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://opentelemetry.io/docs/collector/configuration/#receivers"&gt;Receivers&lt;/a&gt;&lt;/strong&gt; ingest data. Example data sources include: Kubernetes clusters, Prometheus, and application code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://opentelemetry.io/docs/collector/configuration/#processors"&gt;Processors&lt;/a&gt;&lt;/strong&gt; transform data. This can include adding/removing data, modifying data (e.g. data masking), and even filtering data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://opentelemetry.io/docs/collector/configuration/#exporters"&gt;Exporters&lt;/a&gt;&lt;/strong&gt; send data to an Observability back-end. In our case, Lightstep. Lightstep ingests OTel data natively, using the &lt;a href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md"&gt;OpenTelemetry Protocol (OTLP)&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://opentelemetry.io/docs/collector/configuration/#service"&gt;Pipelines&lt;/a&gt; are used to define how data flows in and out of the OTel Collector.&lt;/p&gt;

&lt;p&gt;For more on the OTel Collector, please see the &lt;a href="https://opentelemetry.io/docs/collector/"&gt;official OTel docs site&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; Although your instrumented code could &lt;em&gt;technically&lt;/em&gt; send data directly to an Observability back-end (e.g. Lightstep) without a Collector, it's not really the right way to go about it. You should always funnel all OTel data (regardless of the soure) to your back-end by way of the OTel Collector.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Running the OTel Collector
&lt;/h4&gt;

&lt;p&gt;There are &lt;a href="https://opentelemetry.io/docs/collector/getting-started"&gt;several ways to deploy the OTel Collector&lt;/a&gt;; however, we're keeping things simple and local today, using &lt;a href="https://docs.docker.com/get-started/08_using_compose"&gt;Docker Compose&lt;/a&gt;, which runs &lt;a href="https://www.docker.com/get-started/"&gt;Docker&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Configuration
&lt;/h4&gt;

&lt;p&gt;The OTel Collector is &lt;a href="https://opentelemetry.io/docs/collector/configuration/"&gt;configured via YAML&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The YAML file is used to define &lt;a href="https://opentelemetry.io/docs/collector/configuration/#receivers"&gt;receivers&lt;/a&gt;, &lt;a href="https://opentelemetry.io/docs/collector/configuration/#processors"&gt;processors&lt;/a&gt;, &lt;a href="https://opentelemetry.io/docs/collector/configuration/#exporters"&gt;exporters&lt;/a&gt;, and &lt;a href="https://opentelemetry.io/docs/collector/configuration/#service"&gt;pipelines&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tutorial
&lt;/h2&gt;

&lt;p&gt;Ah, now the moment y'all have been waiting for - the tutorial! Let's do this!&lt;/p&gt;

&lt;p&gt;For this tutorial, we will be using a &lt;a href="https://github.com/lightstep/opentelemetry-demo"&gt;modified version of the OpenTelemetry Demo&lt;/a&gt; repo. This repo has been modified to demonstrate how to send data to Lightstep using the OTel Collector. While the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;upstream repo&lt;/a&gt; contains configs to send data to a local installation of Jaeger, the modified version contains configs to send data to Lightstep. We will be keeping the Lightstep repo in sync with the upstream repo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pre-Requisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.docker.com/get-started/"&gt;Docker&lt;/a&gt; and &lt;a href="https://docs.docker.com/get-started/08_using_compose"&gt;Docker Compose&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://app.lightstep.com/signup"&gt;Lightstep account&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens#create-an-access-token"&gt;Lightstep Access Token&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Running the Demo App Locally
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1- Clone the repo
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/lightstep/opentelemetry-demo.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2- Edit the &lt;a href="https://github.com/lightstep/opentelemetry-demo/blob/main/src/otelcollector/otelcol-config-extras.yml"&gt;OTel Collector Config file&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;This file allows you to configure the OpenTelemetry Collector with your own Observability back-end. This case, otelcol-config-extras.yml is already configured to use Lightstep as the back-end. All you need to do is add your Lightstep Access Token to the file, to be able to send traces to Lightstep.&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;cd &lt;/span&gt;opentelemetry-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;a href="https://github.com/lightstep/opentelemetry-demo/blob/main/src/otelcollector/otelcol-config-extras.yml"&gt;&lt;code&gt;src/otelcollector/otelcol-config-extras.yml&lt;/code&gt;&lt;/a&gt; for editing using your favourite editor. The file looks like this:&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="c1"&gt;# extra settings to be merged into OpenTelemetry Collector configuration&lt;/span&gt;
&lt;span class="c1"&gt;# do not delete this file&lt;/span&gt;
&lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;logLevel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;debug&lt;/span&gt;
  &lt;span class="na"&gt;otlp/ls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ingest.lightstep.com:443"&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lightstep-access-token"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;lightstep_access_token&amp;gt;&lt;/span&gt;

&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="na"&gt;pipelines&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;traces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
       &lt;span class="na"&gt;processors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;batch&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
       &lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;otlp/ls&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
     &lt;span class="na"&gt;metrics&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
       &lt;span class="na"&gt;processors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;batch&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
       &lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;prometheus&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;otlp/ls&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;&amp;lt;lightstep_access_token&amp;gt;&lt;/code&gt; with your own &lt;a href="https://lightstep.com/docs/create-and-manage-access-tokens#create-an-access-token"&gt;Lightstep Access Token&lt;/a&gt;, and save the file. The Access Token tells what &lt;a href="https://docs.lightstep.com/docs/create-projects-for-your-environments"&gt;Lightstep project&lt;/a&gt; to send your telemetry data to.&lt;/p&gt;

&lt;p&gt;A few noteworthy items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lightstep ingests data in native &lt;a href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md"&gt;OpenTelemetry Protocol (OTLP)&lt;/a&gt; format, so we will use the &lt;a href="https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/otlpexporter"&gt;OTLP Exporter&lt;/a&gt;. The exporter can be called either &lt;code&gt;otlp&lt;/code&gt; or follow the naming format &lt;code&gt;otlp/&amp;lt;something&amp;gt;&lt;/code&gt;. We could call it &lt;code&gt;otlp/bob&lt;/code&gt; if we wanted to. We're calling our exporter &lt;code&gt;otlp/ls&lt;/code&gt; to signal to us that we are using the OTLP exporter to send the data to Lightstep.&lt;/li&gt;
&lt;li&gt;Though not mandatory, we are also using a &lt;a href="https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/loggingexporter"&gt;Logging Exporter&lt;/a&gt;. This is helpful, as it prints our traces to the Collector's &lt;code&gt;stdout&lt;/code&gt;. As you might imagine, this is great for debugging, especially if you see traces sent to &lt;code&gt;stdout&lt;/code&gt;, but not to your back-end.&lt;/li&gt;
&lt;li&gt;We must define a pipeline in the &lt;code&gt;service.pipelines&lt;/code&gt; section of the YAML config. Specifically, we need to define a pipeline for our taces. The pipeline tells the Collector:

&lt;ul&gt;
&lt;li&gt;Where it's getting Trace data from (it's being sent via OTLP)&lt;/li&gt;
&lt;li&gt;If there's any processing that needs to be done (this is optional)&lt;/li&gt;
&lt;li&gt;Where to send data to. In our case, it's to &lt;code&gt;stdout&lt;/code&gt; (via the &lt;a href="https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/loggingexporter"&gt;Logging Exporter&lt;/a&gt;) and to Lightstep (via &lt;a href="https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/otlpexporter"&gt;OTLP Exporter&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;The Collector can ingest data using both HTTP &lt;em&gt;and&lt;/em&gt; gRPC. The &lt;code&gt;receivers&lt;/code&gt; configuration may &lt;em&gt;appear&lt;/em&gt; to be empty; however, it actually means that we are using the defult values for the &lt;code&gt;receivers&lt;/code&gt; config. It is actually the same as saying:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="na"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;protocols&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;grpc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
         &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0:4317&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;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0:4318&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP:&lt;/strong&gt; If you find that you can't see data in Lightstep, make sure that you've defined a pipeline, and that the pipeline's exporter lists your &lt;code&gt;otlp/ls&lt;/code&gt; exporter. I can tell you that twice this week I forgot to add my exporter to the pipeline, and was sitting in front of my computer in sheer panic trying to figure out why I could see my traces in &lt;code&gt;stdout&lt;/code&gt; but not in Lightstep. 😱&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  3- Launch the sample app
&lt;/h4&gt;

&lt;p&gt;If you're not already in the repo root, make sure you go there:&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;cd &lt;/span&gt;opentelemetry-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run Docker compose. If you’re on an x86-64 machine (i.e. Intel or AMD-based, non-Apple Silicon), run the command below. If you’re running the Demo App for the first time, it will download all of the Docker images first. This may take several minutes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;--no-build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you’re on an Apple Silicon machine, you will need to build all images from source using the command below, as the images hosted in the GitHub Container Registry (GHCR) are built and optimized for x86-64 machines. This can take upwards of 20 minutes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose build
docker compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the app is up and running, you will see text continuously scrolling down the screen. &lt;em&gt;This is expected!!&lt;/em&gt; The sample output looks something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/3XnQjkivYR2sIikMELiK2y/7dcb7a0d091a1f5117caa90559d9cb94/otel-webstore-app-output.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/3XnQjkivYR2sIikMELiK2y/7dcb7a0d091a1f5117caa90559d9cb94/otel-webstore-app-output.png" alt="otel-webstore-app-output" title="OTel Demo App output - console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;ASIDE:&lt;/strong&gt; After the app started up, I kept waiting for the scrolling text to stop, only to realize that, "Oh, this thing is running. Guess I can access the URL now!" 🙄&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since we added our &lt;a href="https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/loggingexporter"&gt;Logging Exporter&lt;/a&gt; to our pipeline definition, you'll also see that zoom by past you in the terminal:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/WeufpJvJyGJIK60q3c81m/963972b703358b8ed489af70319c7664/otel-collector-stdout.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/WeufpJvJyGJIK60q3c81m/963972b703358b8ed489af70319c7664/otel-collector-stdout.png" alt="otel-collector-stdout" title="OTel Demo App output - OTel Collector logging exporter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; Obviously the Logging Exporter is more useful when it's not running with a bunch of other services competing for your attention on your terminal. This will be more useful if you're running it in &lt;a href="https://opentelemetry.io/docs/collector/getting-started/#kubernetes"&gt;Kubernetes&lt;/a&gt; or &lt;a href="https://opentelemetry.io/docs/collector/getting-started/#nomad"&gt;Nomad&lt;/a&gt;, or running standalone with &lt;a href="https://opentelemetry.io/docs/collector/getting-started/#docker"&gt;Docker&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can now access the Demo app: &lt;a href="http://localhost:8080"&gt;http://localhost:8080&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0g84msFq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://images.ctfassets.net/ne67e1btg495/-988184819/205a25bf691865b4bd73b9cf5a8a3a38/otel-demo-ui.png_en-US%3Fw%3D3024%26h%3D1876%26q%3D50%26fm%3Dwebp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0g84msFq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://images.ctfassets.net/ne67e1btg495/-988184819/205a25bf691865b4bd73b9cf5a8a3a38/otel-demo-ui.png_en-US%3Fw%3D3024%26h%3D1876%26q%3D50%26fm%3Dwebp" alt="Screen capture of the OTel Demo App UI" title="Screen capture of OTel Demo App UI" width="800" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Be sure to play around. Browse. Add items to your cart. Remove items from your cart. Checkout.&lt;/p&gt;

&lt;h4&gt;
  
  
  4- See the Traces in Lightstep
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fsAI_-tf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://images.ctfassets.net/ne67e1btg495/5qsIxT75tcxS1urLNExPSb/e208c54dd1ab636818024d693139e5d6/ls-trace-view.png%3Fw%3D3014%26h%3D1646%26q%3D50%26fm%3Dwebp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fsAI_-tf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://images.ctfassets.net/ne67e1btg495/5qsIxT75tcxS1urLNExPSb/e208c54dd1ab636818024d693139e5d6/ls-trace-view.png%3Fw%3D3014%26h%3D1646%26q%3D50%26fm%3Dwebp" alt="Screen capture of traces in Lightstep" title="Screen capture of Traces in Lightstep" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To view traces in your &lt;a href="https://app.lightstep.com"&gt;Lightstep Observability&lt;/a&gt; project, check out &lt;a href="http://localhost:4000/docs/use-notebooks"&gt;Lightstep Observability Notebooks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And that's it! Didn't I tell you? Pretty straightforward!&lt;/p&gt;

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

&lt;p&gt;Today we saw how easy it was to get Trace data into Lightstep using the &lt;a href="https://github.com/lightstep/opentelemetry-demo"&gt;modified version of the OpenTelemetry Demo Webstore&lt;/a&gt;, which uses our good friend, the OTel Collector.&lt;/p&gt;

&lt;p&gt;Now that you've been able to get Trace data into Lightstep, I encourage you to play around with it. If you have any questions about Observability or OTel, pop into the &lt;a href="https://ltstp.run/discord"&gt;Lightstep Community Discord&lt;/a&gt;, or &lt;a href="//mailto:devrel@lightstep.com"&gt;get in touch by e-mail&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And to reward you for your work today, please enjoy this cartoon drawn by my daughter.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/5rdyd3h2uXtYMd0zJRyQsq/f6cf6fe05d5919d5a7b588df18f97651/coffee-break-Hannah-maxwell.jpg" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/5rdyd3h2uXtYMd0zJRyQsq/f6cf6fe05d5919d5a7b588df18f97651/coffee-break-Hannah-maxwell.jpg" alt="The Coffee Break by Hannah Maxwell" title="The Coffee Break by Hannah Maxwell"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Peace, love, and code. 🦄 🌈  💫 &lt;/p&gt;




&lt;p&gt;The &lt;a href="https://github.com/open-telemetry/opentelemetry-demo"&gt;OpenTelemetry Demo Webstore App&lt;/a&gt; is always looking for feedback and contributors. Please consider &lt;a href="https://github.com/open-telemetry/community/blob/main/community-membership.md#member"&gt;joining the OTel Community&lt;/a&gt; to help make OpenTelemetry AWESOME!&lt;/p&gt;

</description>
      <category>opentelemetry</category>
      <category>opensource</category>
      <category>observability</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Auto-Instrumentation is Magic: Using OpenTelemetry Python with Lightstep</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Mon, 22 Aug 2022 15:04:00 +0000</pubDate>
      <link>https://forem.com/lightstep/auto-instrumentation-is-magic-using-opentelemetry-python-with-lightstep-1o5j</link>
      <guid>https://forem.com/lightstep/auto-instrumentation-is-magic-using-opentelemetry-python-with-lightstep-1o5j</guid>
      <description>&lt;p&gt;In my &lt;a href="https://dev.to/lightstep/observability-mythbusters-opentelemetry-to-lightstep-3-ways-in-go-is-possible-44p0"&gt;last OpenTelemetry blog post&lt;/a&gt;, I talked about how to send &lt;a href="//opentelemetry.io"&gt;OpenTelemetry (OTel)&lt;/a&gt; data to &lt;a href="//app.lightstep.com"&gt;Lightstep&lt;/a&gt; using Golang. That’s all well and good if you’re a Golang developer, but what if you use Python? Well, my friend, you’re in luck, because today, I’ll be looking at how to send OpenTelemetry data to Lightstep using Python.&lt;/p&gt;

&lt;p&gt;As with the OTel Golang post, we can send OTel data to Lightstep (or any other Observability tool that supports &lt;a href="https://dev.to/lightstep/observability-mythbusters-opentelemetry-to-lightstep-3-ways-in-go-is-possible-44p0"&gt;OpenTelemetry Protocol (OTLP)&lt;/a&gt;, for that matter) in one of 3 ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Direct from application&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opentelemetry.io/docs/collector/"&gt;OpenTelemetry Collector&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Launchers&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this post, I will dig into each of these three approaches in detail, with code snippets which explain how to get data into &lt;a href="http://app.lightstep.com"&gt;Lightstep Observability&lt;/a&gt;. Let’s do this!&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenTelemetry &amp;amp; Lightstep
&lt;/h2&gt;

&lt;p&gt;Lightstep Observability supports the native &lt;a href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md"&gt;OpenTelemetry Protocol (OTLP)&lt;/a&gt;. It can receive data in the OTLP format either via &lt;a href="https://opentelemetry.io/docs/concepts/glossary/#http"&gt;HTTP&lt;/a&gt; or &lt;a href="https://opentelemetry.io/docs/concepts/glossary/#grpc"&gt;gRPC&lt;/a&gt;. You will need to specify which method you wish to use in your code, as we’ll see in the upcoming code snippets.&lt;/p&gt;

&lt;p&gt;If you're curious about using gRPC vs HTTP for OpenTelemetry, check out &lt;a href="https://docs.lightstep.com/docs/send-otlp-over-http-to-lightstep"&gt;these docs&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;/strong&gt; Other Observability tools that support OTLP include &lt;a href="https://www.honeycomb.io/blog/all-in-on-opentelemetry/"&gt;Honeycomb&lt;/a&gt;, &lt;a href="https://grafana.com/blog/2021/04/13/how-to-send-traces-to-grafana-clouds-tempo-service-with-opentelemetry-collector/"&gt;Grafana&lt;/a&gt;, and &lt;a href="https://medium.com/jaegertracing/introducing-native-support-for-opentelemetry-in-jaeger-eb661be8183c"&gt;Jaeger&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Automatic Instrumentation &amp;amp; Python
&lt;/h2&gt;

&lt;p&gt;One thing that’s super cool about using OTel to instrument your Python code is that Python offers automatic (auto) instrumentation. What does this mean? At a high level, it means that you can run a Python OpenTelemetry binary (called &lt;a href="https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/opentelemetry-instrumentation"&gt;&lt;code&gt;opentelemetry-instrument&lt;/code&gt;&lt;/a&gt;) that wraps around your Python application, to automagically instrument it. 🪄&lt;/p&gt;

&lt;p&gt;More specifically, auto-instrumentation uses shims or bytecode instrumentation agents to intercept your code at runtime or at compile-time to add tracing and metrics instrumentation to the libraries and frameworks you depend on. The beauty of auto-instrumentation is that it requires a minimum amount of effort. Sit back, relax, and enjoy the show. A number of popular Python libraries are auto-instrumented, including &lt;a href="https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-flask"&gt;Flask&lt;/a&gt; and &lt;a href="https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-django"&gt;Django&lt;/a&gt;. You can find the full list &lt;a href="https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Manual instrumentation requires adding spans, context propagation, attributes, etc. to your code. It’s akin to commenting your code or writing tests.&lt;/p&gt;

&lt;p&gt;Does this mean that you shouldn’t manually instrument? Not at all! Start with auto-instrumentation if it’s available. If the auto-instrumentation isn’t sufficient for your use case (most often it’s not), then add in the manual instrumentation. For example, auto-instrumentation doesn’t know your business logic—it only knows about frameworks and languages—in which case you’ll want to manually instrument your business logic, so that you get that visibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pre-Requisites
&lt;/h2&gt;

&lt;p&gt;Before we start our tutorial, here are some things that you’ll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A basic understanding of &lt;a href="https://www.python.org/"&gt;Python&lt;/a&gt; and &lt;a href="https://realpython.com/python-virtual-environments-a-primer/"&gt;Python virtual environments&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A basic understanding of the &lt;a href="https://lightstep.com/blog/observability-mythbusters-how-hard-is-it-to-get-started-with-opentelemetry#otel-collector-101"&gt;OpenTelemetry Collector&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A basic understanding of how to use &lt;a href="//app.lightstep.com"&gt;Lightstep Observability&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’d like to run the full code examples, you’ll also need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://app.lightstep.com/signup/developer?signup_source=docs"&gt;Lightstep Observability account&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens"&gt;Lightstep Access Token&lt;/a&gt; to tell Lightstep what project to send your traces to&lt;/li&gt;
&lt;li&gt;A working installation of &lt;a href="https://www.python.org/downloads/"&gt;Python&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docker (to run the OTel Collector locally)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Direct from Application
&lt;/h2&gt;

&lt;p&gt;If you’re getting started with instrumenting your application with OpenTelemetry, this is probably the most common route taken by most beginners. As the name suggests, we are sending data to a given Observability back-end directly from our application code.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/4hTlOwHH8j36uF8kHUc0di/f0c47cdc218179f4a52b05a54e6d4a44/otel-to-ls-direct.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/4hTlOwHH8j36uF8kHUc0di/f0c47cdc218179f4a52b05a54e6d4a44/otel-to-ls-direct.png" alt="otel-to-ls-direct" title="Diagram illustrating sending OpenTelemetry data to Lightstep direct from application"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our sample application is a Flask application. We will be leveraging both automatic and manual instrumentation.&lt;/p&gt;

&lt;p&gt;Let’s look at this in greater detail below.&lt;/p&gt;

&lt;h4&gt;
  
  
  1- Set up your environment
&lt;/h4&gt;

&lt;p&gt;Let’s set up our working directory and our Python virtual environment&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_python
&lt;span class="nb"&gt;cd &lt;/span&gt;otel_python

python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; ./bin/activate
&lt;span class="nb"&gt;touch &lt;/span&gt;server.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;server.py&lt;/code&gt;, and paste the following:&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="n"&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="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;randint&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="nf"&gt;get_tracer_provider&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&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="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&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="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/rolldice&lt;/span&gt;&lt;span class="sh"&gt;"&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;roll_dice&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;do_roll&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="nd"&gt;@tracer.start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;do_roll&lt;/span&gt;&lt;span class="sh"&gt;"&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;do_roll&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;randint&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="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;current_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="nf"&gt;get_current_span&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;current_span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;roll.value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;current_span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;operation.name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Saying hello!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;current_span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;operation.other-stuff&lt;/span&gt;&lt;span class="sh"&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;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;res&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&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="nf"&gt;run&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="mi"&gt;8082&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;use_reloader&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2- Install the required OTel libraries
&lt;/h4&gt;

&lt;p&gt;These are the libraries that are required to send data to an &lt;strong&gt;&lt;em&gt;Observability back-end&lt;/em&gt;&lt;/strong&gt; (e.g Lightstep).&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="c"&gt;# OTel-specific&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;opentelemetry-distro
pip &lt;span class="nb"&gt;install &lt;/span&gt;opentelemetry-exporter-otlp

&lt;span class="c"&gt;# App-specific&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;flask
pip &lt;span class="nb"&gt;install &lt;/span&gt;requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few noteworthy items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installing &lt;code&gt;opentelemetry-distro&lt;/code&gt; will install a number of other dependent packages for instrumenting code, including &lt;code&gt;opentelemetry-api&lt;/code&gt; and &lt;code&gt;opentelemetry-sdk&lt;/code&gt;, and our auto-instrumentation wrapper binary, &lt;code&gt;opentelemetry-instrument&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;opentelemetry-exporter-otlp&lt;/code&gt; package is used to send OTel data to your Observability back-end (e.g. Lightstep). Installing it in turn installs &lt;code&gt;opentelemetry-exporter-otlp-proto-grpc&lt;/code&gt; (send data via gRPC) and &lt;code&gt;opentelemetry-exporter-otlp-proto-http&lt;/code&gt; (send data via HTTP).&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3- Install auto-instrumentation
&lt;/h4&gt;

&lt;p&gt;As you may recall from earlier in this post, Python auto-instrumentation includes a binary that wraps our Python application and automagically adds some high-level instrumentation for us. But that's only part of the picture. There are &lt;a href="https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation"&gt;Python auto-instrumentation libraries&lt;/a&gt; available for a number of popular Python libraries (e.g. Flask, requests). Using these auto-instrumentation libraries, along with &lt;code&gt;opentelemetry-instrument&lt;/code&gt;, gives us auto-instrumentation superpowers. 💪&lt;/p&gt;

&lt;p&gt;So how do we install these auto-instrumentation libraries? Well, there's a handy little tool for that, called &lt;code&gt;opentelemetry-bootstrap&lt;/code&gt;. It was installed as part of our installation of &lt;code&gt;opentelemetry-distro&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opentelemetry-bootstrap &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So what does this do? The above command will read through the packages installed in your active &lt;code&gt;site-packages&lt;/code&gt; folder, and will install the applicable auto-instrumentation libraries. For example, if you already installed the &lt;code&gt;flask&lt;/code&gt; and &lt;code&gt;requests&lt;/code&gt; packages (as we did in Step 2), running &lt;code&gt;opentelemetry-bootstrap -a install&lt;/code&gt; will install &lt;code&gt;opentelemetry-instrumentation-flask&lt;/code&gt; and &lt;code&gt;opentelemetry-instrumentation-requests&lt;/code&gt; for you. If you leave out &lt;code&gt;-a install&lt;/code&gt;, it will simply list out the recommended auto-instrumentation packages to be installed.&lt;/p&gt;

&lt;p&gt;For more information on &lt;code&gt;opentelemetry-bootstrap&lt;/code&gt;, check out the &lt;a href="https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/opentelemetry-instrumentation#opentelemetry-bootstrap"&gt;official OpenTelemetry docs&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  4- Run the app
&lt;/h4&gt;

&lt;p&gt;Here’s where it gets interesting! Normally to run this app, we’d run it 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;python server.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if we did that, we wouldn’t be sending any OTel data to Lightstep. So we must instead do this:&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_EXPORTER_OTLP_TRACES_HEADERS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"lightstep-access-token=&amp;lt;LS_ACCESS_TOKEN&amp;gt;"&lt;/span&gt;

opentelemetry-instrument &lt;span class="se"&gt;\&lt;/span&gt;
           &lt;span class="nt"&gt;--traces_exporter&lt;/span&gt; console,otlp_proto_grpc &lt;span class="se"&gt;\&lt;/span&gt;
           &lt;span class="nt"&gt;--metrics_exporter&lt;/span&gt; console,otlp_proto_grpc &lt;span class="se"&gt;\&lt;/span&gt;
           &lt;span class="nt"&gt;--service_name&lt;/span&gt; test-py-auto-otlp-grpc-server &lt;span class="se"&gt;\&lt;/span&gt;
           &lt;span class="nt"&gt;--exporter_otlp_endpoint&lt;/span&gt; &lt;span class="s2"&gt;"ingest.lightstep.com:443"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
           python server.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some noteworthy items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replace &lt;code&gt;&amp;lt;LS_ACCESS_TOKEN&amp;gt;&lt;/code&gt; with your own &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens"&gt;Lightstep Access Token&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;traces_exporter&lt;/code&gt; and &lt;code&gt;metrics_exporter&lt;/code&gt; specify which trace exporter and which metrics to use, respectively. In this case, traces and metrics are being exported to &lt;code&gt;console&lt;/code&gt; (stdout) and to &lt;code&gt;otlp_proto_grpc&lt;/code&gt;. The &lt;code&gt;otlp_proto_grpc&lt;/code&gt; option tells &lt;code&gt;opentelemetry-instrument&lt;/code&gt; to send it to an endpoint that accepts OTLP via gRPC. The full list of available options for &lt;code&gt;traces_exporter&lt;/code&gt; can be found &lt;a href="https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/opentelemetry-instrumentation"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;service_name&lt;/code&gt; sets the name of the service. This is the value that will show up in the &lt;a href="https://dev.to/docs/view-traces"&gt;Lightstep service explorer&lt;/a&gt;. Be sure to replace &lt;code&gt;&amp;lt;service_name&amp;gt;&lt;/code&gt; with your own service name.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;exporter_otlp_endpoint&lt;/code&gt; tells &lt;code&gt;opentelemetry-instrument&lt;/code&gt; to send the traces to gRPC endpoint &lt;code&gt;ingest.lightstep.com:443&lt;/code&gt; (i.e. Lightstep).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sample output:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/33H6uIrUld7fJmIw2mc7f2/df3e74ca607b215c9ba9a7565c9bf147/python-server-output-nogrpc-debug.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/33H6uIrUld7fJmIw2mc7f2/df3e74ca607b215c9ba9a7565c9bf147/python-server-output-nogrpc-debug.png" alt="Screen captuere of Python server app sample output" title="Screen captuere of Python server app sample output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Want to use HTTP instead of gRPC? First, you need to make sure that the &lt;code&gt;pip&lt;/code&gt; package &lt;code&gt;opentelemetry-exporter-otlp-proto-http&lt;/code&gt; is installed (should be automagically installed as part of installing &lt;code&gt;opentelemetry-exporter-otlp&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Next, your &lt;code&gt;opentelemetry-instrument&lt;/code&gt; command would look 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;opentelemetry-instrument &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--traces_exporter&lt;/span&gt; console,otlp_proto_http &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--metrics_exporter&lt;/span&gt; console &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service_name&lt;/span&gt; test-py-auto-otlp-server &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--exporter_otlp_traces_endpoint&lt;/span&gt; &lt;span class="s2"&gt;"https://ingest.lightstep.com:443/traces/otlp/v0.9"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  python server.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some noteworthy items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;traces_exporter&lt;/code&gt; uses &lt;code&gt;otlp_proto_http&lt;/code&gt; instead of &lt;code&gt;otlp_proto_grpc&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;exporter_otlp_traces_endpoint&lt;/code&gt; endpoint is &lt;code&gt;https://ingest.lightstep.com/traces/otlp/v0.9&lt;/code&gt; (see &lt;a href="http://localhost:4000/docs/test-connectivity-to-microsatellites"&gt;docs&lt;/a&gt;, instead of &lt;code&gt;ingest.lightstep.com:443&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;There is currently no metrics support for &lt;code&gt;otlp_proto_http&lt;/code&gt; and there is no &lt;code&gt;exporter_otlp_metrics_endpoint&lt;/code&gt; option, which is why metrics are being sent to &lt;code&gt;console&lt;/code&gt; only.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  5- Call the /rolldice service
&lt;/h4&gt;

&lt;p&gt;Open up a new terminal window, and run 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;curl http://localhost:8082/rolldice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the above line will return a random number between 1 and 6. Nothing too remarkable there. But if you look over at the terminal window for &lt;code&gt;server.py&lt;/code&gt;, you’ll notice something in the output:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/zCEwwQy5yguJyElrCs4tk/99289e8098e1fb6d41533b8f33f07875/python-server-client-output.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/zCEwwQy5yguJyElrCs4tk/99289e8098e1fb6d41533b8f33f07875/python-server-client-output.png" alt="Screen capture of Python server output after client call" title="Screen capture of Python server output after client call"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We see the trace from &lt;code&gt;server.py&lt;/code&gt;! Why are we seeing this here? Because we set the &lt;code&gt;--traces_exporter&lt;/code&gt; flag to &lt;code&gt;console,otlp_proto_grpc&lt;/code&gt;, which exports to Lightstep via OTLP &lt;em&gt;and&lt;/em&gt; to the console.&lt;/p&gt;

&lt;h4&gt;
  
  
  6- See it in Lightstep
&lt;/h4&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/29xrp2z3QT8LWQV5daHKDD/bbd535f6b8918e9e26f6485c3a5bf61d/python-server-otlp-ls.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/29xrp2z3QT8LWQV5daHKDD/bbd535f6b8918e9e26f6485c3a5bf61d/python-server-otlp-ls.png" alt="python-server-otlp-ls Screen capture of trace in Lightstep - OTLP direct" title="Screen capture of trace in Lightstep - OTLP direct"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;The next approach to sending data to an Observability back-end is by way of the &lt;a href="https://opentelemetry.io/docs/collector/"&gt;OpenTelemetry Collector&lt;/a&gt;. For non-development setups, this is the recommended approach to send OpenTelemetry data to your Observability back-end.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/3yCGLRDT0Efbq1F1sQO4Q5/a20e644bc17ec8b03047829a5b1c8699/otel-to-ls-via-collector.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/3yCGLRDT0Efbq1F1sQO4Q5/a20e644bc17ec8b03047829a5b1c8699/otel-to-ls-via-collector.png" alt="otel-to-ls-via-collector" title="Diagram showing sending OTel data to Lightstep via the Collector"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sending OTel data via the OTel Collector is &lt;em&gt;almost identical&lt;/em&gt; to what we did in the Direct from Application example above. The only difference is that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We need to run an OTel Collector&lt;/li&gt;
&lt;li&gt;When we run &lt;code&gt;opentelemetry-instrument&lt;/code&gt;, our options are slightly different&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s look at this in greater detail below.&lt;/p&gt;

&lt;h4&gt;
  
  
  1- Follow Steps 1-3 from the “&lt;em&gt;Direct from Application&lt;/em&gt;” example
&lt;/h4&gt;

&lt;h4&gt;
  
  
  2- Run the Collector
&lt;/h4&gt;

&lt;p&gt;First, we need to configure our Collector for sending data to Lightstep. We do this by grabbing &lt;code&gt;collector.yaml&lt;/code&gt; from Lightstep’s &lt;code&gt;opentelemetry-examples&lt;/code&gt; repo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone git@github.com:lightstep/opentelemetry-examples.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open up a new terminal window. First, you'll need to edit the &lt;code&gt;collector.yaml&lt;/code&gt; file. Be sure to replace &lt;code&gt;${LIGHTSTEP_ACCESS_TOKEN}&lt;/code&gt; with your own &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens"&gt;Lightstep Access Token&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now you can start up the Collector:&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;cd &lt;/span&gt;opentelemetry-examples/collector/vanilla
docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 4317:4317 &lt;span class="nt"&gt;-p&lt;/span&gt; 4318:4318 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/collector.yaml:/otel-config.yaml &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--name&lt;/span&gt; otelcol otel/opentelemetry-collector-contrib:0.53.0  &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"/otelcol-contrib"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"--config=otel-config.yaml"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample output:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/4jerLuY4TXd8DKooOmtGUN/cc73ce20b97ea3c83106f799b4396347/collector-startup.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/4jerLuY4TXd8DKooOmtGUN/cc73ce20b97ea3c83106f799b4396347/collector-startup.png" alt="Screen capture of OTel Collector startup sequence" title="Screen capture of OTel Collector startup sequence"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  3- Run the app
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opentelemetry-instrument &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--traces_exporter&lt;/span&gt; console,otlp &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--metrics_exporter&lt;/span&gt; console,otlp &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--service_name&lt;/span&gt; test-py-auto-collector-server &lt;span class="se"&gt;\&lt;/span&gt;
   python server.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that the endpoint isn't specified. That's because it assumes that you are using the default Collector gRPC endpoint, &lt;code&gt;0.0.0.0:4317&lt;/code&gt;. The above command is the equivalent of saying:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opentelemetry-instrument &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--traces_exporter&lt;/span&gt; console,otlp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--metrics_exporter&lt;/span&gt; console,otlp&lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service_name&lt;/span&gt; test-py-auto-collector-server &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--exporter_otlp_endpoint&lt;/span&gt; &lt;span class="s2"&gt;"0.0.0.0:4317"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--exporter_otlp_insecure&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  python server.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you specify the endpoint, you must also specify &lt;code&gt;--exporter_otlp_insecure true&lt;/code&gt; if a certificate isn't configured with your Collector.&lt;/p&gt;

&lt;p&gt;Some additional noteworthy items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;otlp&lt;/code&gt;, used in configuring &lt;code&gt;traces_exporter&lt;/code&gt; and &lt;code&gt;metrics_exporter&lt;/code&gt;, is equivalent to using &lt;code&gt;otlp_proto_grpc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To use a different Collector endpoint, simply replace it with your own. If you don't have a Certificate configured with your Collector, remember to add &lt;code&gt;--exporter_otlp_insecure true&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;You don't need to set &lt;code&gt;OTEL_EXPORTER_OTLP_TRACES_HEADERS&lt;/code&gt;, because that's already configured in the Collector's &lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/main/collector/vanilla/collector.yaml"&gt;config.yml&lt;/a&gt; file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you wish to use HTTP instead of gRPC, the command would then look 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;opentelemetry-instrument &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--traces_exporter&lt;/span&gt; console,otlp_proto_http &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--metrics_exporter&lt;/span&gt; console,otlp_proto_http &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service_name&lt;/span&gt; test-py-auto-collector-server &lt;span class="se"&gt;\&lt;/span&gt;
  python server.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which is the same as saying:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opentelemetry-instrument &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--traces_exporter&lt;/span&gt; console,otlp_proto_http &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--metrics_exporter&lt;/span&gt; console,otlp_proto_http &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service_name&lt;/span&gt; test-py-auto-collector-server &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--exporter_otlp_endpoint&lt;/span&gt; &lt;span class="s2"&gt;"http://0.0.0.0:4318"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--exporter_otlp_insecure&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  python server.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, if you wish to use your own Collector endpoint, simply replace the value in &lt;code&gt;exporter_otlp_endpoint&lt;/code&gt;, making sure that you prefix it with &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;. Remember to add &lt;code&gt;--exporter_otlp_insecure true&lt;/code&gt; if you don't have a Certificate configured with your Collector.&lt;/p&gt;

&lt;p&gt;Okay. Enough banter. Let's look at the sample output:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/33H6uIrUld7fJmIw2mc7f2/df3e74ca607b215c9ba9a7565c9bf147/python-server-output-nogrpc-debug.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/33H6uIrUld7fJmIw2mc7f2/df3e74ca607b215c9ba9a7565c9bf147/python-server-output-nogrpc-debug.png" alt="Screen captuere of Python server app sample output" title="Screen captuere of Python server app sample output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  4- Call the /rolldice service
&lt;/h4&gt;

&lt;p&gt;Open up a new terminal window, and run 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;curl http://localhost:8082/rolldice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample output:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/zCEwwQy5yguJyElrCs4tk/99289e8098e1fb6d41533b8f33f07875/python-server-client-output.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/zCEwwQy5yguJyElrCs4tk/99289e8098e1fb6d41533b8f33f07875/python-server-client-output.png" alt="Screen capture of Python server output after client call" title="Screen capture of Python server output after client call"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Again, we see the trace for &lt;code&gt;server.py&lt;/code&gt; because we set the --traces_exporter flag to console,otlp, which exports to the Collector via OTLP &lt;em&gt;and&lt;/em&gt; to the console.&lt;/p&gt;

&lt;h4&gt;
  
  
  5- See it in Lightstep
&lt;/h4&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/6Cm2D5uYU21Id4XSd9mcr6/45d9cd4754204d160bb54755ff61f802/python-server-collector-ls.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/6Cm2D5uYU21Id4XSd9mcr6/45d9cd4754204d160bb54755ff61f802/python-server-collector-ls.png" alt="Screen capture of trace in Lightstep - Collector" title="Screen capture of trace in Lightstep - Collector"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Launcher
&lt;/h2&gt;

&lt;p&gt;If you thought it was easy-peasey to send OTel data to Lightstep à la auto-instrumentation binary, then it’s even easier to do it via the &lt;a href="https://github.com/lightstep/otel-launcher-python"&gt;OTel Python Launcher&lt;/a&gt;! Think of it as an OTel wrapper to make it extra-easy to send data to Lightstep, by having a bunch of things pre-configured for you to lower that barrier to entry.&lt;/p&gt;

&lt;p&gt;Sending OTel data via the Launcher is &lt;em&gt;almost identical&lt;/em&gt; to what we did in the Direct from Application example above, with a few minor differences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We have fewer packages to (yay!)&lt;/li&gt;
&lt;li&gt;When we run &lt;code&gt;opentelemetry-instrument&lt;/code&gt;, our options are slightly different&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s see it in action shall we?&lt;/p&gt;

&lt;h4&gt;
  
  
  1- Follow Steps 1-3 from the “&lt;em&gt;Direct from Application&lt;/em&gt;” example
&lt;/h4&gt;

&lt;p&gt;Minor change: replace the libraries from Step 2 with these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# OTel-specific
pip install opentelemetry-launcher
pip install protobuf==3.20.1

# App-specific
pip install requests
pip install flask
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to force a specific version of &lt;code&gt;protobuf&lt;/code&gt; because of Launcher compatibility issues with newer versions. This was &lt;a href="https://github.com/open-telemetry/opentelemetry-python/pull/2720"&gt;already fixed&lt;/a&gt; in &lt;a href="https://github.com/open-telemetry/opentelemetry-python"&gt;opentelemetry-python&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When we install the &lt;code&gt;opentelemetry-launcher&lt;/code&gt; package, it also does double-duty and doesn’t require that we run &lt;code&gt;opentelemetry-bootstrap -a install&lt;/code&gt;. &lt;/p&gt;

&lt;h4&gt;
  
  
  2- Run the app
&lt;/h4&gt;

&lt;p&gt;Be sure to replace &lt;code&gt;&amp;lt;LS_ACCESS_TOKEN&amp;gt;&lt;/code&gt; with your own &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens"&gt;Lightstep Access Token&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;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;LS_ACCESS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;LS_ACCESS_TOKEN&amp;gt;"&lt;/span&gt;

opentelemetry-instrument &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--service_name&lt;/span&gt; test-py-auto-launcher-server &lt;span class="se"&gt;\&lt;/span&gt;
    python server.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks like we have fewer options, don’t we? Let's dig in a bit to some noteworthy items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We don’t need to specify an &lt;code&gt;--exporter_otlp_traces_endpoint&lt;/code&gt;, because that’s already implicitly done for us, and as set to &lt;code&gt;ingest.lightstep.com:443&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;Instead of setting a messy-looking environment var for our &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens"&gt;Lightstep Access Token&lt;/a&gt; (&lt;code&gt;export OTEL_EXPORTER_OTLP_TRACES_HEADERS="lightstep-access-token=&amp;lt;LS_ACCESS_TOKEN&amp;gt;"&lt;/code&gt;), we just have to do this: &lt;code&gt;export LS_ACCESS_TOKEN="&amp;lt;LS_ACCESS_TOKEN&amp;gt;"&lt;/code&gt;, which looks way cleaner.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you wish to send your OTel data via a Collector instance first, rather than direct from your application, you would do this instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opentelemetry-instrument &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--service_name&lt;/span&gt; test-py-auto-launcher-server &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--exporter_otlp_traces_endpoint&lt;/span&gt; &lt;span class="s2"&gt;"0.0.0.0:4317"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--exporter_otlp_traces_insecure&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    python server.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Noteworthy items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do not set &lt;code&gt;LS_ACCESS_TOKEN&lt;/code&gt;, since that's already configured in the Collector's &lt;a href="https://github.com/lightstep/opentelemetry-examples/blob/main/collector/vanilla/collector.yml"&gt;config.yml&lt;/a&gt; file.&lt;/li&gt;
&lt;li&gt;If you attempt to override &lt;code&gt;exporter_otlp_endpoint&lt;/code&gt; to send traces to a Collector, the traces will be sent directly to &lt;code&gt;ingest.lightstep.com:443&lt;/code&gt; instead of via the Collector. Instead, you need to override &lt;code&gt;exporter_otlp_traces_endpoint&lt;/code&gt;, &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;exporter_otlp_traces_endpoint&lt;/code&gt; sends traces to a Collector running on &lt;code&gt;0.0.0.0:4317&lt;/code&gt; (gRPC). If you wish to use a different Collector address, simply include &lt;code&gt;exporter_otlp_traces_endpoint&lt;/code&gt;, using your own Collector's endpoint.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;exporter_otlp_traces_insecure&lt;/code&gt; is set to &lt;code&gt;true&lt;/code&gt;. This is required if you are using a Collector &lt;em&gt;and&lt;/em&gt; if a certificate isn't configured in the Collector.&lt;/li&gt;
&lt;li&gt;There is currently no HTTP support for Python Launchers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sample output:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/33H6uIrUld7fJmIw2mc7f2/df3e74ca607b215c9ba9a7565c9bf147/python-server-output-nogrpc-debug.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/33H6uIrUld7fJmIw2mc7f2/df3e74ca607b215c9ba9a7565c9bf147/python-server-output-nogrpc-debug.png" alt="Screen captuere of Python server app sample output" title="Screen captuere of Python server app sample output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  3- Call the /rolldice service
&lt;/h4&gt;

&lt;p&gt;Open up a new terminal window, and run 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;curl http://localhost:8082/rolldice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample output:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/4hMKXhMO5AFySvWw4UyCXo/86f5128f0e4c17c30ff0efb2e484cfda/python-server-launcher-output.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/4hMKXhMO5AFySvWw4UyCXo/86f5128f0e4c17c30ff0efb2e484cfda/python-server-launcher-output.png" alt="Screen captuere of Python server app sample output for Launcher" title="Screen captuere of Python server app sample output for Launcher"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that since our &lt;code&gt;opentelemetry-instrument&lt;/code&gt; call didn't specify a &lt;code&gt;--traces_exporter&lt;/code&gt;, it's the equivalent of saying &lt;code&gt;--traces_exporter otlp_proto_grpc&lt;/code&gt;. I also means that there's no trace output to the console (stdout).&lt;/p&gt;

&lt;h4&gt;
  
  
  4- See it in Lightstep
&lt;/h4&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/5CC4dgB5r5VXGPURY9KDXz/8476025486f151f0eee8faa9f0779ef1/otel-to-ls-via-launcher.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/5CC4dgB5r5VXGPURY9KDXz/8476025486f151f0eee8faa9f0779ef1/otel-to-ls-via-launcher.png" alt="Screen capture of trace in Lightstep - Launcher" title="Screen capture of trace in Lightstep - Launcher"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Should I always use the auto-instrumentation binary?
&lt;/h2&gt;

&lt;p&gt;Is &lt;code&gt;opentelemetry-instrument&lt;/code&gt; still helpful even if you’re not using a Python library that’s not auto-instrumented? Personally, I think so! Consider this file, &lt;code&gt;client.py&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="n"&gt;sys&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;opentelemetry.propagate&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;inject&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="nf"&gt;get_tracer_provider&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&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="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&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="nf"&gt;start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;client&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&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="nf"&gt;start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;client-server&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
       &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
       &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="n"&gt;requested&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
           &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8082/rolldice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;param&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;argv&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;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;)&lt;/span&gt;

       &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;requested&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s run the above program with the auto-instrumentation binary. Be sure to replace &lt;code&gt;&amp;lt;LS_ACCESS_TOKEN&amp;gt;&lt;/code&gt; with your own &lt;a href="https://docs.lightstep.com/docs/create-and-manage-access-tokens"&gt;Lightstep Access Token&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;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_EXPORTER_OTLP_TRACES_HEADERS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"lightstep-access-token=&amp;lt;LS_ACCESS_TOKEN&amp;gt;"&lt;/span&gt;

opentelemetry-instrument &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--traces_exporter&lt;/span&gt; console,otlp &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--service_name&lt;/span&gt; test-py-auto-client &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--exporter_otlp_endpoint&lt;/span&gt; &lt;span class="s2"&gt;"ingest.lightstep.com:443"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    python client.py &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that aside from creating spans in &lt;code&gt;client.py&lt;/code&gt;, there’s no OTel configuration in there. You don’t configure the service name, the exporter, or the endpoint. That’s all taken care of when you run &lt;code&gt;opentelemetry-instrument&lt;/code&gt;. Plus, if your code happens to use a library that is auto-instrumented, you don’t have to do anything else.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;/strong&gt; If you’re wondering why we’re executing the command &lt;code&gt;python client.py test&lt;/code&gt;, it’s because &lt;code&gt;client.py&lt;/code&gt; takes a single parameter, which in this case is called &lt;code&gt;test&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  gRPC Debugging
&lt;/h2&gt;

&lt;p&gt;Do you ever wonder if your gRPC calls are going into a black hole? I definitely do! When I was mucking around with gRPC for the Golang OTel libraries, I learned about some gRPC debug flags that would make my life easier for troubleshooting gRPC connectivity issues. Which of course got me wondering if there was a Python equivalent. Turns out there is. Set these environment variables before running your app, and you’re golden:&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;GRPC_VERBOSITY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;debug
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GRPC_TRACE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http,call_error,connectivity_state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means that when we start up our &lt;code&gt;server.py&lt;/code&gt;, we get something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/3rv0KroUvOnKBeIdYeA6Wq/e11cecbb82bf7974c1bcdf04dac6bad3/python-server-output.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/3rv0KroUvOnKBeIdYeA6Wq/e11cecbb82bf7974c1bcdf04dac6bad3/python-server-output.png" alt="Screen captuere of Python server app sample output with gRPC debug" title="Screen captuere of Python server app sample output with gRPC debug"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And then when we call our endpoint via &lt;code&gt;curl&lt;/code&gt;, we get this:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/6G0b5jOXy4Vx4NJXdGBz45/3a7c7e50334904f4ef96188257ef2739/grpc-debug-python.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/6G0b5jOXy4Vx4NJXdGBz45/3a7c7e50334904f4ef96188257ef2739/grpc-debug-python.png" alt="Screen capture of Python server app sample output with gRPC degug showing successful gRPC call" title="Screen capture of Python server app sample output with gRPC degug showing successful gRPC call"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The part highlighted above tells me that our gRPC call was successful!&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Auto-instrumentation in Python is pretty freaking awesome, and it really lowers the barrier to entry for OpenTelemetry. As we saw with the Direct from Application and Collector examples, the code stays pretty much the same. The only difference is that you need to change up some flags so that the auto-instrumentation binary knows where to send your traces to. Nice and easy!&lt;/p&gt;

&lt;p&gt;In case you’re wondering, there is a totally pure OTel Python manual instrumentation approach, which I will cover in a future blog post, so stay tuned! For now, bask in the fact that you learned something super cool today about OTel Python auto-instrumentation!&lt;/p&gt;

&lt;p&gt;And now, I will reward you with a picture of my rat Phoebe getting some cuddles.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/1JlxaPBnjJMw13EaPw0i5N/e0352eb83215221f5addb47b11800043/phoebe.JPG" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/1JlxaPBnjJMw13EaPw0i5N/e0352eb83215221f5addb47b11800043/phoebe.JPG" alt="Phoebe the rat gets cuddles" title="Phoebe the rat gets cuddles. Photo by Adriana Villela (https://adri-v.medium.com)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Peace, love, and code. 🦄 🌈 💫&lt;/p&gt;




&lt;p&gt;Got questions about OTel instrumentation with Python? Talk to me! Feel free to connect through &lt;a href="//mailto:devrel@lightstep.com"&gt;e-mail&lt;/a&gt;, or hit me up on &lt;a href="https://twitter.com/adrianamvillela"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/adrianavillela/"&gt;LinkedIn&lt;/a&gt;. Hope to hear from y’all!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>opensource</category>
      <category>opentelemetry</category>
      <category>observability</category>
    </item>
    <item>
      <title>Observability Mythbusters: Yes, Observability-Landscape-as-Code is a Thing</title>
      <dc:creator>Adriana Villela</dc:creator>
      <pubDate>Fri, 19 Aug 2022 15:51:54 +0000</pubDate>
      <link>https://forem.com/lightstep/observability-mythbusters-yes-observability-landscape-as-code-is-a-thing-2pip</link>
      <guid>https://forem.com/lightstep/observability-mythbusters-yes-observability-landscape-as-code-is-a-thing-2pip</guid>
      <description>&lt;p&gt;You might be thinking that this might be yet &lt;em&gt;another&lt;/em&gt; overloaded term to add to your Buzzword Bingo lineup. Maybe you’re rolling your eyes at me over Yet Another X-as-Code thing. Or maybe you want to hurl your shoe at your screen in frustration. But aren’t you the teeeeniest bit curious? I invite you to stick around to satisfy your curiosity before you pass judgment. &lt;/p&gt;

&lt;p&gt;Take my (virtual) hand, and let’s jump in!&lt;/p&gt;

&lt;h2&gt;
  
  
  Observability-Landscape-as-Code?? Wut?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#what-is-observability"&gt;Observability&lt;/a&gt; is about good practices. Good practices are useless unless you have a consistent landscape. In order to support these practices, there are a number of setup-y-type things that are required to enable teams to truly unlock Observability’s powers. &lt;/p&gt;

&lt;p&gt;With so many integrations and moving parts, it can be hard to keep track of all the things that you need in order to achieve Observability greatness. This is where Observability-Landscape-as-Code (OLaC) can help. OLaC means &lt;strong&gt;&lt;em&gt;supporting Observability by codifying your Observability landscape&lt;/em&gt;&lt;/strong&gt; to ensure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consistency ✅&lt;/li&gt;
&lt;li&gt;Maintainability ✅&lt;/li&gt;
&lt;li&gt;Reproducibility ✅&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our Observability Landscape is made up of the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application instrumentation&lt;/li&gt;
&lt;li&gt;Collecting and storing application telemetry&lt;/li&gt;
&lt;li&gt;An Observability back-end&lt;/li&gt;
&lt;li&gt;A set of meaningful SLOs&lt;/li&gt;
&lt;li&gt;An Incident Response system for alerting on-call Engineers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Observability-Landscape-as-Code makes this possible, through the following practices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Instrumenting your code with OpenTelemetry&lt;/li&gt;
&lt;li&gt;Codifying the deployment of the OTel Collector&lt;/li&gt;
&lt;li&gt;Using a &lt;a href="https://registry.terraform.io"&gt;Terraform Provider&lt;/a&gt; to configure your Observability back-end&lt;/li&gt;
&lt;li&gt;Codifying SLOs using the vendor-neutral &lt;a href="https://openslo.com/"&gt;OpenSLO specification&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Using APIs to configure your Incident Management systems&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s dig a little deeper.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/ne67e1btg495/32rmkuGgS139dW7Rl0GkAe/e54b4cb2b1b79efd35c3af7ff9a15680/o11y_landscape.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/ne67e1btg495/32rmkuGgS139dW7Rl0GkAe/e54b4cb2b1b79efd35c3af7ff9a15680/o11y_landscape.png" alt="Components of the Observability Landscape" title="Components of the Observability Landscape"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Instrument Your Code
&lt;/h2&gt;

&lt;p&gt;The most logical place to start when defining your Observability landscape is with instrumenting your application. After all, without instrumentation, you can’t send anything to your Observability back-end, which means that there is no way in Space that your system can be Observable.&lt;/p&gt;

&lt;p&gt;&lt;a href="//opentelemetry.io/docs"&gt;OpenTelemetry&lt;/a&gt; (OTel), the vendor-neutral open-source Observability framework, is the means by which applications are instrumented. OTel allows you to instrument code written in a number of &lt;a href="https://opentelemetry.io/docs/instrumentation/"&gt;major programming languages&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But…what exactly should you instrument? According to &lt;a href="https://twitter.com/tedsuo"&gt;Ted Young&lt;/a&gt;, you should “add tracing to your frameworks and libraries, and try to keep it out of your application code as much as possible.”&lt;/p&gt;

&lt;p&gt;But whyyyy??&lt;/p&gt;

&lt;p&gt;Let’s break it down. &lt;/p&gt;

&lt;p&gt;An application is typically made up of a combination of &lt;a href="https://www.baeldung.com/cs/framework-vs-library#how-does-a-framework-work"&gt;frameworks&lt;/a&gt; and &lt;a href="https://www.baeldung.com/cs/framework-vs-library#how-does-a-library-works"&gt;libraries&lt;/a&gt;. Think of your application code as the glue that holds these frameworks and libraries together. The frameworks and libraries being used in an application can be a combination of home-grown and third-party offerings. If you instrument your home-grown frameworks and libraries, then you have all the coverage you need, as far as tracing is concerned.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d3bkzhxwv8fv/2AaZrYOHKS3gjoDZTjRzNF/aa6b9c7e7d6e6582f70a1d694f9abd72/Framework-and-Library.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d3bkzhxwv8fv/2AaZrYOHKS3gjoDZTjRzNF/aa6b9c7e7d6e6582f70a1d694f9abd72/Framework-and-Library.png" alt="How frameworks and libraries fit into application code" title="How frameworks and libraries fit into application code. Image source: https://www.baeldung.com/cs/framework-vs-library#how-does-a-framework-work"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes, technically you &lt;em&gt;could&lt;/em&gt; add &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#spans"&gt;Spans&lt;/a&gt; to your application code, but chances are you’ll get caught up copying and pasting Spans all over the place. And next thing you know, you’ve polluted your code with unnecessary Spans. Plus, if you decide somewhere down the line that you want to change up how you’re creating your Spans, chances are you’ll end up having to do some search and replace to fix stuff up, which all know can be messyyyyy.&lt;/p&gt;

&lt;p&gt;That said, it is still useful to have your application code add &lt;a href="https://opentelemetry.io/docs/concepts/signals/traces/#span-events"&gt;Events&lt;/a&gt; and Span &lt;a href="https://opentelemetry.io/docs/concepts/signals/traces/#attributes"&gt;Attributes&lt;/a&gt; which are specific to that particular operation and would add value. This is how you model the actual business logic of your service! You just have to be selective.&lt;/p&gt;

&lt;p&gt;As Ted cautions, “Spans are a structural element and it's usually a smell if they end up mixed in with application code. In general, any instrumentation which gets repeated across many instances of application code, such as adding a **&lt;code&gt;project_id&lt;/code&gt; as an attribute, should get moved into a framework hook or a shared function of some kind.”&lt;/p&gt;

&lt;p&gt;Eeeee! That sounds a bit stressful, doesn’t it. And let’s face it…it’s a mighty tall order to tell developers to instrument all their framework and library (and &lt;em&gt;sometimes&lt;/em&gt; application) code, but it’s the only way that this Observability thing will really do right by you. But, DON’T PANIC. Here’s what you do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If you’re instrumenting previously-uninstrumented code, &lt;a href="https://lightstep.com/blog/deployment-strategies-for-opentelemetry/"&gt;focus on instrumenting your most important services first&lt;/a&gt;, so that it feels less overwhelming.&lt;/li&gt;
&lt;li&gt;When working with net-new code, follow &lt;a href="https://lightstep.com/blog/observability-mythbusters-not-only-for-sre#tdd-tbt-and-oodoh-my"&gt;Observability-Driven Development&lt;/a&gt; (ODD), whereby you instrument-as-you-code, instrumenting your code ends up being waaaay less overwhelming. Whew!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The idea is to add enough Traces and supporting data (including &lt;a href="https://opentelemetry.io/docs/concepts/signals/traces/#attributes"&gt;Attributes&lt;/a&gt;, &lt;a href="https://lightstep.com/blog/observability-mythbusters-logs-and-metrics-arent-enough#myth-1-logs-are-good-enough"&gt;Span Events&lt;/a&gt; and &lt;a href="https://lightstep.com/blog/observability-mythbusters-logs-and-metrics-arent-enough#myth-2-the-usefulness-of-metrics"&gt;Metrics&lt;/a&gt;) to your code, such that it can send enough data to your Observability back-end to allow developers and operators to troubleshoot issues quickly and effectively. Remember, we want to help them answer that nagging question of  “Why is this happening?”&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the OTel Collector &amp;amp; Codify its Deployment
&lt;/h2&gt;

&lt;p&gt;Congrats! You’ve instrumented your code! Your Observability Landscape is looking slick AF! Now, while &lt;a href="https://opentelemetry.io/docs/instrumentation/"&gt;OTel’s instrumentation libraries&lt;/a&gt; are used to instrument application code, we need another OTel goodie as part of our Landscape: the &lt;a href="https://storiesfromtheherd.com/unpacking-observability-the-observability-stack-93d4733e2a72"&gt;OTel Collector&lt;/a&gt;. The Collector is a vendor-neutral agent that ingests, transforms, and exports data to one or more Observability back-ends.&lt;/p&gt;

&lt;p&gt;And in case you’re wondering, yes, you &lt;em&gt;could&lt;/em&gt; technically send data directly from your instrumented code to your desired Observability back-end sans Collector; however, this isn’t the best way to go about it. The Collector serves as a data pipeline which allows you to do things like mask, filter, transform, and append data before sending it to your Observability back-end. It can also ingest Metrics data from your infrastructure, like your &lt;a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/prometheusreceiver"&gt;Prometheus-style metrics generated by your Hashi stack&lt;/a&gt; or &lt;a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/k8sclusterreceiver"&gt;Kubernetes cluster metrics&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d3bkzhxwv8fv/RsxVjm2Zz7M3bTNqgPgMb/6d806ab0967367d5d1eede3f8321391d/otel_collector_2.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d3bkzhxwv8fv/RsxVjm2Zz7M3bTNqgPgMb/6d806ab0967367d5d1eede3f8321391d/otel_collector_2.png" alt="The OpenTelemetry Collector" title="The OpenTelemetry Collector features receivers for ingesting data, processors for transforming data, and exporters for sending data to Observability back-ends. Image by Adriana Villela."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, if you’re adding an OTel Collector to the mix, then it means that it needs to run &lt;em&gt;somewhere&lt;/em&gt;. That somewhere can include &lt;a href="https://opentelemetry.io/docs/collector/getting-started/#kubernetes"&gt;Kubernetes&lt;/a&gt;, &lt;a href="https://opentelemetry.io/docs/collector/getting-started/#nomad"&gt;Nomad&lt;/a&gt;, or &lt;a href="https://opentelemetry.io/docs/collector/getting-started/#linux-packaging"&gt;Virtual Machines (VMs) as a binary&lt;/a&gt;. Regardless of where you deploy it, you will need some sort of Infrastructure-as-Code (IaC) thing to configure and deploy the Collector. Luckily, you can use IaC tools like trusty ‘ole &lt;a href="https://www.terraform.io"&gt;Terraform&lt;/a&gt;, superfly competitor &lt;a href="//pulumi.com"&gt;Pulumi&lt;/a&gt;, oft-forgotten-yet-still-cool-kid &lt;a href="//ansible.com"&gt;Ansible&lt;/a&gt;. Or heck, write your own custom scripts to do it. It’s up to you. Just. Codify. It.&lt;/p&gt;

&lt;h2&gt;
  
  
  Codify Observability Back-End Configuration
&lt;/h2&gt;

&lt;p&gt;Now we’ve covered application instrumentation and data transmission. Next we must tackle The Thing that Receives the Data. That is, your Observability back-end. Now, as with many-a-tool out there, Observability back-ends require some tweaking so that they work in a way that makes them useful to us. Such configurations can include creating &lt;a href="https://charity.wtf/2021/08/09/notes-on-the-perfidy-of-dashboards/"&gt;(non-sucky, meaningful) dashboards&lt;/a&gt;, Slack integrations, or &lt;a href="https://docs.lightstep.com/docs/monitor-a-service-level-indicator-with-streams"&gt;saved queries for your Traces&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And wouldn’t you know it…it turns out that most of the cool kids in the Observability back-end game have &lt;a href="https://registry.terraform.io/browse/providers"&gt;Terraform Providers&lt;/a&gt; that facilitate said configurations, including &lt;a href="https://registry.terraform.io/providers/lightstep/lightstep/latest/docs"&gt;Lightstep&lt;/a&gt;. If you don’t believe me, go to the &lt;a href="https://registry.terraform.io/browse/providers"&gt;Terraform Providers search bar&lt;/a&gt; and enter an Observability back-end name. I dare you. MUAHAHAHAHAHA…&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;/strong&gt; As far as I know, there are no equivalents from Pulumi and Ansible (wink wink, nudge nudge), but please correct me if I’m wrong!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Regardless of what Observability back-end you use, make sure that you use the heck out of your Observability back-end’s Terraform Provider to codify as much of that setup stuff as possible. That way, if you happen to bork your setup, you can always go back to your Terraform code. And remember: make sure that you resist the temptation to make tweaks to things on the UI that are supported by your Provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get your SLO game on, and Codify!
&lt;/h2&gt;

&lt;p&gt;If we take a step back and admire our Landscape, you’ll see that it now includes application instrumentation, sending data to our Observability back-end, and setting up our Observability back-end. And while we’re on our Observability back-end, let’s talk &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#reliability--metrics"&gt;Service-Level Objectives&lt;/a&gt; (SLOs).&lt;/p&gt;

&lt;p&gt;If you’re an on-call Engineer, chances are you don’t want to get woken up by just any alert. If you get woken up, it had better be for a damn good reason. I once got woken up at 2am over a development environment alert by the &lt;a href="https://en.wikipedia.org/wiki/Network_operations_center"&gt;Network Operations Center&lt;/a&gt; (NOC). Not fun. And not needed. Aaaand…because I was totally wired after being woken up, I had a really hard time getting back to sleep after that. Nobody wants to deal with unnecessary alerts.&lt;/p&gt;

&lt;p&gt;Alerts are usually triggered when a certain threshold is reached. For example: low disk space, high CPU, high RAM. There will always be a need for alerts, but if we have too many alerts, it becomes overwhelming, and it becomes hard to tell what’s important and what’s not.&lt;/p&gt;

&lt;p&gt;Good Observability practices dictate that we should only set up &lt;em&gt;meaningful&lt;/em&gt; alerts. Defining a small, yet meaningful number of alerts is a good thing, because it means that the alerts that you &lt;em&gt;do&lt;/em&gt; get are the really, &lt;em&gt;really&lt;/em&gt; important ones, and are less likely to get ignored as “white noise” or worse, cause wake you up when it wasn’t wake-up-worthy. &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/#reliability--metrics"&gt;Service-Level Objectives&lt;/a&gt; (SLOs) can help with this, by helping us identify what data ingested by your Observability back-end are important and alert-worthy. SLOs connect telemetry with specific customer experiences, so that the entire organization can understand the relationship between the software system and the business goals. &lt;/p&gt;

&lt;p&gt;If you really want to up your SLO game (and you definitely &lt;em&gt;should&lt;/em&gt;), you need to define your &lt;a href="https://www.businesswire.com/news/home/20220510005542/en/OpenSLO-Specification-Moves-to-Version-1.0"&gt;SLOs as code&lt;/a&gt;. This is where &lt;a href="https://github.com/OpenSLO/OpenSLO"&gt;OpenSLO&lt;/a&gt; comes to the rescue! Originally started up by &lt;a href="https://www.nobl9.com/"&gt;Nobl9&lt;/a&gt;, &lt;a href="https://github.com/OpenSLO/OpenSLO"&gt;OpenSLO&lt;/a&gt; is an open-source “specification for defining SLOs to enable a common, vendor–agnostic approach to tracking and interfacing with SLOs.” Super dope. Don’t you just love standardization? 💜&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;/strong&gt; Although it’s outside of the scope of this post to dig deep into this topic, in case you’re curious, you can check out what an OpenSLO YAML definition looks like &lt;a href="https://github.com/slok/sloth/blob/main/examples/openslo-getting-started.yml"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Use APIs for Incident Response Tool Configuration
&lt;/h2&gt;

&lt;p&gt;As we saw above, SLOs trigger alerts that are actioned by on-call Engineers. These on-call Engineers are made aware of said alerts, courtesy of an Incident Response (IR) tool. Like our Observability back-end, IR tools require a bunch of configuration in order for them to be useful to us and to our teams.&lt;/p&gt;

&lt;p&gt;Things you need to configure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On-call team and contact information&lt;/li&gt;
&lt;li&gt;On-call schedule&lt;/li&gt;
&lt;li&gt;Call tree&lt;/li&gt;
&lt;li&gt;Triggers (i.e. things that cause you go get paged)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Luckily, many IR tools have APIs that let you set up and configure them, in lieu of clickity-clicking through a UI. Choosing an incident management system with a robust API that lets you do all of the above through code ensures that there are no knowledge bottlenecks, as everyone in your team has access to config as code via a Git repo.&lt;/p&gt;

&lt;p&gt;To put things into perspective, I will tell you a little story. At one of my previous workplaces, we used an incident management system that was beyond annoying to configure and understand. I swear that it was a relic of the Web of the early 2000s. There was no API. There was one guy who knew how it all worked, and luckily I made friends with him, because there is no way that I would’ve known how to configure the on-call schedule for my team. Unfortunately, he was the only guy who knew how the system worked, so if he was on vacation, and you needed to onboard a new team or team members, you were hosed. &lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;Today we learned that OLaC isn’t just some bogus, buzzwordy, cringe-worthy term, but in fact refers to supporting Observability by codifying your Observability Landscape, thereby making it consistent, repeatable, and maintainable.&lt;/p&gt;

&lt;p&gt;Observability-Landscape-as-Code makes this possible, through the following practices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Instrumenting your code with OpenTelemetry&lt;/strong&gt;, following ODD to instrument your code as you go. ✅&lt;/li&gt;
&lt;li&gt;*&lt;em&gt;Codifying the deployment of the OTel Collector *&lt;/em&gt;(to Nomad, Kubernetes, or a VM) using tools such as &lt;a href="http://terraform.io"&gt;Terraform&lt;/a&gt;, &lt;a href="http://pulumi.com"&gt;Pulumi&lt;/a&gt;, or &lt;a href="http://ansible.com"&gt;Ansible&lt;/a&gt;. The Collector funnels your OTel data to your Observability back-end. ✅&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using a &lt;a href="https://registry.terraform.io"&gt;Terraform Provider&lt;/a&gt; to configure your Observability back-end.&lt;/strong&gt; Fortunately, most Observability back-ends, like &lt;a href="https://registry.terraform.io/providers/lightstep/lightstep/latest/docs"&gt;Lighstep&lt;/a&gt;, have their own Providers. ✅&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codifying SLOs using the vendor-neutral &lt;a href="https://openslo.com/"&gt;OpenSLO specification&lt;/a&gt;.&lt;/strong&gt; SLOs are important because they are the driving force behind alerts. SLO-based alerts ensure that on-call Engineers get meaningful, data-driven alerts. ✅&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using APIs to configure your Incident Management systems&lt;/strong&gt;, which are usually the conduit for paging on-call Engineers. Many incident management systems have APIs that can be used to configure the platform for your organization’s specific needs. This includes things like managing incidents and account settings. ✅&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That, my friends, is how you have a kick-ass Observability Landscape.&lt;/p&gt;

&lt;p&gt;Congrats! You made it! I will now reward you with a picture of my dearly departed rat, Susie.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d3bkzhxwv8fv/3OycJ0Vx65nXJzDiE2gDjS/43276111ef2455dc3963272fe810d003/susie_rat.jpg" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d3bkzhxwv8fv/3OycJ0Vx65nXJzDiE2gDjS/43276111ef2455dc3963272fe810d003/susie_rat.jpg" alt="Susie the rat" title="Susie the rat peers out of her cage. Photo by Adriana Villela"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Peace, love, and code. 🦄 🌈  💫 &lt;/p&gt;




&lt;p&gt;If you’d like to share stories of your Observability journey, or just ask Observability-related questions, hit me up on the &lt;a href="https://ltstp.run/discord"&gt;Lightstep Community Discord&lt;/a&gt;, or &lt;a href="//mailto:devrel@lightstep.com"&gt;get in touch by e-mail&lt;/a&gt;. Hope to hear from y’all!&lt;/p&gt;

</description>
      <category>observability</category>
      <category>opentelemetry</category>
      <category>opensource</category>
      <category>slo</category>
    </item>
    <item>
      <title>Leading SRE with Empathy</title>
      <dc:creator>Ana Margarita Medina 👩🏽‍💻</dc:creator>
      <pubDate>Mon, 15 Aug 2022 21:55:00 +0000</pubDate>
      <link>https://forem.com/lightstep/leading-sre-with-empathy-1g6b</link>
      <guid>https://forem.com/lightstep/leading-sre-with-empathy-1g6b</guid>
      <description>&lt;p&gt;Writing and operating software is hard. &lt;/p&gt;

&lt;p&gt;We've seen huge changes in how we build reliable software over the past two decades, driven by the DevOps movement and SRE practices the last few years. Some of the great things that came from the years of experience and great minds in the community are that operating software at scale is very hard, and it’s not just about code and processes. We have learned that people are also a big part of the puzzle. High-performing organizations and teams make sure to prioritize people. We need a lot more of that; it’s common for DevOps and SRE teams to burn out and suffer from pager fatigue or even on-call PTSD. &lt;/p&gt;

&lt;p&gt;Today, I’m only focusing on SREs. What if we train SREs to lead with empathy? It’s not just about keeping servers healthy and green and our users and CTOs happy. But what is empathy? While empathy may be defined as this definition, I think empathy is the ability to relate to other human beings by being curious, listening, offering help while building trust. &lt;/p&gt;

&lt;h2&gt;
  
  
  First, what is SRE?
&lt;/h2&gt;

&lt;p&gt;SRE stands for Site Reliability Engineering. This practice has been around for a while, but with different names like Operations Engineering, Systems Engineering, or Production Engineering through the years. SRE got more established when Google released the book on SRE, which I refer to as the SRE bible. You can read it for free online via this website.&lt;/p&gt;

&lt;p&gt;An SRE is an engineer that focuses on system reliability. Some folks like to think of SRE as engineers that spend 50% of their time writing code and the other 50% of their time operating and optimizing software for the purpose of reliability. When we talk about SRE, we often get asked how SRE and DevOps vary from one another. The best way I’ve seen it explained is covered on Google Cloud’s “&lt;a href="https://youtu.be/uTEL8Ff1Zvk"&gt;What's the Difference Between DevOps and SRE?&lt;/a&gt;” with Liz Fong-Jones and Seth Vargo. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;SRE is an implementation of DevOps. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s DevOps with the purpose of reliability. &lt;/p&gt;

&lt;h2&gt;
  
  
  What does an SRE do?
&lt;/h2&gt;

&lt;p&gt;SRE duties vary from company to company. Some of the duties one can find themselves doing while being an SRE include (and are not limited to):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing code&lt;/li&gt;
&lt;li&gt;Being on-call &lt;/li&gt;
&lt;li&gt;Setting up tooling (CI/CD, Observability, Incident Response tooling, Testing pipelines, etc.) &lt;/li&gt;
&lt;li&gt;Setting up alerting&lt;/li&gt;
&lt;li&gt;Setting up SLOs&lt;/li&gt;
&lt;li&gt;Capacity Planning&lt;/li&gt;
&lt;li&gt;Running Chaos Engineering Experiments &lt;/li&gt;
&lt;li&gt;Advocating for modern and best practices&lt;/li&gt;
&lt;li&gt;Running blameless postmortems &lt;/li&gt;
&lt;li&gt;Reviewing the company’s testing practices &lt;/li&gt;
&lt;li&gt;Auditing services &lt;/li&gt;
&lt;li&gt;Interviewing other teams&lt;/li&gt;
&lt;li&gt;Reading other company’s postmortems &lt;/li&gt;
&lt;li&gt;Improving processes&lt;/li&gt;
&lt;li&gt;Reviewing Architecture decisions &lt;/li&gt;
&lt;li&gt;Drafting new architecture plans or PRDs (Product Requirements Documents) on vendor/tooling changes &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When we talk to seasoned SREs, we learn that their empathy has allowed them to be more successful. This is because, as an SRE, you have empathy for the service teams one supports and understands that they are doing their work to the best of their ability. But it’s not just them; this also expands to the other SREs in one’s organization. As I said above, SRE varies from company to company, and every SRE comes from a different background. But this also extends to other duties like educating the rest of the organization on reliability best practices. &lt;/p&gt;

&lt;h2&gt;
  
  
  How can SREs embody empathy?
&lt;/h2&gt;

&lt;p&gt;Empathy goes a long way; being empathetic builds engineers' knowledge level and increases engineering morale and company culture. It begins with listening to the needs of the teams they support and the issues they are working on. Within their teams, this might mean being able to support one another when trading on-call shifts, triaging bugs, dealing with incidents, or leading incident review meetings. &lt;/p&gt;

&lt;p&gt;Building empathy takes practice, and every SRE might embody it differently. For some folks, it might look like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;curiosity&lt;/li&gt;
&lt;li&gt;patience&lt;/li&gt;
&lt;li&gt;open communication&lt;/li&gt;
&lt;li&gt;ability to admit being wrong&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SREs should stay curious and ask as many questions when reviewing documentation, incidents, or data. This allows one to try to get the most context into decisions and understand the “Why.” Curiosity lets an SRE embark on their continuous learning journey. &lt;/p&gt;

&lt;p&gt;We know that the work SREs do can be very time-demanding and stressful; the ability to stay calm under pressure is sometimes one of the best qualities of an SRE. As people are the ones that operate our systems, one needs to understand that the full context of a system might not be seen through a dashboard, and one must be patient to research what exactly happened and debug a bit further. We also see that when an SRE practice is still in its early days, you will need  a lot of patience to work with leadership and other teams for the organization to prioritize reliability action items every sprint. &lt;/p&gt;

&lt;p&gt;Additionally, like any work or personal relationship, the more one can communicate, the better; providing data with this is helpful, especially when dealing with incidents. Over-communication is appreciated and goes a long way. Let’s take, for example, when we are dealing with incidents. When Jewel sees that their system is starting to degrade its performance, they should say something. Jewel can say something and start researching on their own, but this gives the heads up to the rest of the team that something could be happening, and it also opens the door for another team to share any insights they might have. It’s common for two people to be working on the same system at the same time. In another scenario, let’s take an ongoing incident. There is a Slack channel set up and a Zoom call ongoing; engineers are researching, debugging, and trying to bring a system to a better state. It’s good practice to bring in as much relevant data from other sources that provides more context into why the system might have left its unhealthy state. With communication practices like this, one might be able to resolve an incident in less time or just be able to provide more context when a postmortem is written up. &lt;/p&gt;

&lt;p&gt;As we think about building distributed systems across distributed teams, we see that over-communication helps build empathy. This helps build trust and allows room for failure. After all, we are humans working on complex systems.  Additionally, the ability to be vulnerable, open, honest, and transparent with our peers is important. Our systems are changing rapidly, and our mental models will be outdated. Cultivating a culture where failure is embraced and a culture of experimentation is cultivated is important and essential. This will also be very helpful for an honest postmortem. One gets to show up for their team.&lt;/p&gt;

&lt;p&gt;As an SRE, leading with empathy means that you become an individual that has established a lot of trust in the organization. This has several benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Teams might ask you earlier in the development processes/issue process for feedback on the issues they are facing (ability to approach) &lt;/li&gt;
&lt;li&gt;Discourse at incident reviews is less about blaming others and more about learning and sharing and moving forward together (trust)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What world are we creating with more empathy?
&lt;/h2&gt;

&lt;p&gt;It is common to find empathy in SRE and DevOps communities, to the extent that there is a huge movement behind #HugOps. For all of our favorite companies, applications, and tools, there’s always a team of folks working hard to keep things running or firefighting when things go wrong. When those sites and applications go down, we acknowledge the outage and downtime sucks, but we send our condolences and support to those working to bring things back. We know the work is hard, and we stand together in support as a community. Hugs are sent just like packets across the internet to our friends.  &lt;/p&gt;

&lt;p&gt;_&lt;em&gt;We must continue cultivating this culture and ensuring that all engineers follow it.  _&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer: Emotional SRE Toil
&lt;/h2&gt;

&lt;p&gt;In SRE, we talk about toil. Toil is the manual work needed to keep systems running in production. Toil is defined as “the kind of work tied to running a production service that tends to be manual, repetitive, automatable, tactical, devoid of enduring value, and that scales linearly as a service grows.”  When we think about our SREs embodying more empathy, we need to look at the other side of the coin. We are asking our teammates to take on a larger work that can not be quantified: emotional work.&lt;/p&gt;

&lt;p&gt;Understanding other people and being in someone else’s shoes eventually catches up to you.&lt;br&gt;
We already know &lt;a href="https://www.apmdigest.com/should-sre-stand-for-site-reliability-engineer-or-stress-really-excessive"&gt;SREs sometimes struggle with mental health&lt;/a&gt;, which is worth mentioning in these discussions. How do we get ahead of the possible emotional toil that comes from being more empathetic? We first need to accept that this is already happening in our organizations, but we are not talking about it. &lt;/p&gt;

&lt;p&gt;Looking at the workload of SREs during the pandemic and # of incidents that they fire fought or the number of services they helped in, we can see that they’ve been a massive help to our organizations and keeping our users happy. How do we protect the humans that support our systems and keep our services up?&lt;/p&gt;

&lt;p&gt;This is a call to action for our leaders to check in with their teams more. This entails listening to quarterly retrospectives, attending post-mortem reviews, and revising the information we collect about our services, requests, and incidents. This also includes other manager-y duties like listening to ideas your team brings to the table, proactively tracking and ensuring your team takes time off (I’m looking at you managers at unlimited PTO companies) along with ensuring that on-call schedules are balanced across the organization, ensuring your SMEs (Subject Matter Experts) are not very close from burning out, and ensuring that other people are also getting trained to be on-call. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To make an impact, our SRE leaders need to lead with empathy and help the rest of the organization engineer with empathy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Stay tuned as we continue the conversation and talk about empathy with our users next. Do you have any thoughts? Feel free to reach out via Twitter at @&lt;a href="https://twitter.com/Ana_M_Medina"&gt;ana_m_medina&lt;/a&gt; or find me on &lt;a href="https://linkedin.com/in/anammedina"&gt;LinkedIn&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was originally posted on the &lt;a href="https://lightstep.com/blog/leading-sre-with-empathy"&gt;Lightstep Blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>sre</category>
      <category>devops</category>
      <category>cloud</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
