<?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: Liz Fong-Jones</title>
    <description>The latest articles on Forem by Liz Fong-Jones (@lizthegrey).</description>
    <link>https://forem.com/lizthegrey</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F341241%2F2e279c9e-a7b7-4378-9e69-8a6c3f7feb63.jpeg</url>
      <title>Forem: Liz Fong-Jones</title>
      <link>https://forem.com/lizthegrey</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/lizthegrey"/>
    <language>en</language>
    <item>
      <title>Making Instrumentation Extensible</title>
      <dc:creator>Liz Fong-Jones</dc:creator>
      <pubDate>Wed, 09 Dec 2020 20:06:08 +0000</pubDate>
      <link>https://forem.com/honeycombio/making-instrumentation-extensible-4ejf</link>
      <guid>https://forem.com/honeycombio/making-instrumentation-extensible-4ejf</guid>
      <description>&lt;p&gt;&lt;span&gt;Observability-driven development requires both rich query capabilities and sufficient instrumentation in order to capture the nuances of developers' intention and useful dimensions of cardinality. When our systems are running in containers, we need an equivalent to our local debugging tools that is as easy to use as Printf and as powerful as gdb. We should empower developers to write instrumentation by ensuring that it's easy to add context to our data, and requires little maintenance work to add or replace telemetry providers after the fact. Instead of thinking about individual counters or log lines in isolation, we need to consider how the telemetry we might want to transmit fits into a wider whole.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bEglRhaj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2019/06/bee-extension-350x250.jpg" class="article-body-image-wrapper"&gt;&lt;img class="aligncenter wp-image-4809 size-medium" src="https://res.cloudinary.com/practicaldev/image/fetch/s--bEglRhaj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2019/06/bee-extension-350x250.jpg" alt="photo of a chain of bees connecting two parts of a hive" width="300" height="214"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Aggregated counters, gauges, and histograms can provide us with information broken down by host or endpoint, but not necessarily higher cardinality fields that we need for a rich understanding of our distributed systems. Automatic instrumentation of a language server framework, such as framework support for Node.js Express or Go http.Server in &lt;a href="https://www.honeycomb.io/auto-instrumentations/"&gt;Honeycomb's Beelines&lt;/a&gt;, can only provide a modest amount of context. It will capture request header fields such as URL and response durations/error codes, but not anything from the business logic or involving the logged-in user's metadata. Because observability requires the ability to understand the impact of user behavior upon our applications, we cannot just stop with collecting surface level data. Thus, we'll need to make changes to our code to instrument it.&lt;/span&gt;&lt;/p&gt;

&lt;h2&gt;Instrumentation should be reusable&lt;/h2&gt;

&lt;p&gt;&lt;span&gt;Typically, instrumenting code involves adding a vendor's library or a standard package like OpenCensus or slf4j to one's dependencies, then calling the library directly from instrumented code. If multiple providers and kinds of telemetry (e.g. logs, metrics, traces, events…) are in use, calls to each wind up sprinkled across the codebase. But should we have to re-instrument our entire codebase every time we gain access to new methods of data aggregation/visualization or change observability providers? Of course not. This gives rise to the need to separate observability plumbing from your business logic, or domain-specific code.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;To address this problem of abstracting instrumentation, Tyler Treat envisions a solution involving centralized inter-process collection in "&lt;/span&gt;&lt;a href="https://bravenewgeek.com/the-observability-pipeline/"&gt;&lt;span&gt;The Observability Pipeline&lt;/span&gt;&lt;/a&gt;&lt;span&gt;", and Pete Hodgson suggests abstracting collection in-process in "&lt;/span&gt;&lt;a href="https://martinfowler.com/articles/domain-oriented-observability.html"&gt;&lt;span&gt;Domain Oriented Observability&lt;/span&gt;&lt;/a&gt;&lt;span&gt;". Tyler's article explains creating structured events, and then streaming them to an out of process service that can aggregate them and send them onwards to a variety of instrumentation sinks. Pete's article suggests creating a separate class for handling the vendor-specific pieces of instrumentation, but still relies upon tight coupling between the instrumentation code and the domain-specific code--e.g. creating a method in the instrumentation code to handle each potential property we might want to record about an event (e.g. &lt;code&gt;discountCodeApplied()&lt;/code&gt;, &lt;code&gt;discountLookup{Failed,Succeeded}()&lt;/code&gt;).&lt;/span&gt;&lt;/p&gt;

&lt;h2&gt;Why not both?&lt;/h2&gt;

&lt;p&gt;&lt;span&gt;However, there's a simpler, within-process approach that is easier for developers to understand, test, configure, maintain, and operate. It's a fusion of that described by Pete in "Event-Based Observability" and "Collecting Instrumentation Context", and by Tyler's distributed event buffering solution. With the improved solution, we neither need to have an advanced understanding of mocking functions and classes, nor do we need to operate a Kafka pipeline from day 0. Instead, we just generate and consume &lt;a href="https://www.honeycomb.io/blog/how-are-structured-logs-different-from-events/"&gt;structured events&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0QahCs55--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2019/06/bee-chain-350x250.jpg" class="article-body-image-wrapper"&gt;&lt;img class="aligncenter wp-image-4811 size-medium" src="https://res.cloudinary.com/practicaldev/image/fetch/s--0QahCs55--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2019/06/bee-chain-350x250.jpg" alt="photo of a vertical chain of bees constructing a hive" width="300" height="214"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Within each span of work done in the domain-specific business logic, we populate a weakly-typed context dictionary with key/value pairs added from within instrumented code, as well as the default standard contextual fields (e.g. requestId, startTime, endTime, etc). Child units of work become separate contexts and spans, with appropriate fields (e.g. parentId, requestId) templated (or "partially applied" in Pete's words) from the parent context/span. Adding telemetry becomes as easy as Printf for developers -- it's just setting a &lt;code&gt;ctx[key] = val&lt;/code&gt; only for keys and values relevant to your code. We no longer need to create one function call to the instrumentation adapter for each telemetry action. Using Pete's example, we might set &lt;code&gt;discountCode =&amp;gt; FREESHIPPING&lt;/code&gt;, &lt;code&gt;responseCode =&amp;gt; 403&lt;/code&gt;, or &lt;code&gt;discountLookupSucceeded =&amp;gt; {true,false,nil}&lt;/code&gt; within &lt;/span&gt;&lt;i&gt;&lt;span&gt;one&lt;/span&gt;&lt;/i&gt;&lt;span&gt; event instead of making the multiple function calls above, or emitting multiple distinct "Announcement" objects for only one work unit. Writing tests to validate that the generated context map is correct becomes straightforward to do in table-based testsuites (e.g. &lt;code&gt;go functest&lt;/code&gt;), rather than requiring mocking functions and classes.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Once the work unit finishes, its context dictionary is sent in-process to the instrumentation adapter where any number of listeners can interpret it. Each listener sees the context maps for each received event, decides whether it's relevant to it, and if so, translates it according to its own rules into metrics, traces/events/structured logs, or human-readable logs. We no longer need to duplicate calls to the same instrumentation provider from each kind of telemetry function call, but can create single listeners for each common metric (e.g. response time metrics collection, response code) that act on a wider range of events. We can then measure the correctness of listeners, ensuring that each processor is only interested in the correct set of structured events, and dispatches them to the upstream structured event, log, metric, or trace provider(s)' APIs appropriately.&lt;/span&gt;&lt;/p&gt;

&lt;h2&gt;Correspondence is more useful when it's about the outcome&lt;/h2&gt;

&lt;p&gt;&lt;span&gt;Unlike Tyler's streaming design there need not be a 1:1 correspondence between listeners/routers and instrumentation sinks. Instead, the correspondence is between the action we'd like to coalesce or report on, and what related calls we make -- e.g. performing more than one metric counter increment, etc. to the same sink, or even scattering increments across many different sinks if we're transitioning between providers. This makes the code much more testable, as it's focused on the intent of "record these values from this specific kind of event, to whatever Sinks are relevant", rather than a catch-all of "duplicate everything in the kitchen we do in Sink A in Sink B instead". And the value of event stores such as Honeycomb quickly becomes clearer -- because you don't &lt;/span&gt;&lt;i&gt;&lt;span&gt;have&lt;/span&gt;&lt;/i&gt;&lt;span&gt; to do anything different to aggregate or process each such structured event, only pass it on to us directly. Let us worry about how to efficiently query the data when you ask a question, such as &lt;code&gt;P99(duration_ms)&lt;/code&gt; or &lt;code&gt;COUNT WHERE err exists GROUP BY customer_id ORDER BY COUNT desc&lt;/code&gt;.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Decoupling event creation from event consumption, even within the same process, is a great step between instrumentation spaghetti and needing a Kafka, Kinesis or PubSub queue. Never create a distributed system unless you need to, and run as few distributed systems as possible. Same-process structured event creation and structured event consumption is super easy to work with, test, and reason about, to boot! As you grow and your needs scale, you may wind up reaching for that Kafka queue. But you'll have an easier migration path, if so.&lt;/span&gt;&lt;/p&gt;

&lt;h2&gt;Ideas for future-proofing&lt;/h2&gt;

&lt;p&gt;&lt;span&gt;How does this relate to OpenTelemetry née Open{Census,Tracing}? Despite the creation of the new consensus standard, the ongoing transition to OpenTelemetry is proof indeed that we ought to future-proof our work by ensuring we can switch to and from instrumentation providers, including those that do not support the current newest standard, without further breaking domain code. Instead of using the OpenTelemetry API directly within your domain-specific code, it still may be wise to use one context/span propagation library of your choice (which could still be OTel's), and write an InstrumentationAdapter that passes data it receives through to OpenTelemetry's metrics &amp;amp; trace consumers, as well as to legacy and future instrumentation providers.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;I hope that this article was helpful! If you're looking for more detailed examples of how Honeycomb Beelines work, check out our &lt;a href="https://github.com/honeycombio/examples"&gt;Examples repo in Github &lt;/a&gt;, such as &lt;a href="https://github.com/honeycombio/examples"&gt;this&lt;/a&gt; &lt;a href="https://github.com/honeycombio/examples/blob/master/golang-gatekeeper"&gt;example of using our Beeline for Go alongside custom instrumentation&lt;/a&gt;. &lt;/span&gt;&lt;/p&gt;




&lt;p&gt;Looking to find out more? Get started with &lt;a href="https://ui.honeycomb.io/signup?&amp;amp;utm_source=Devto&amp;amp;utm_Devto=blog&amp;amp;utm_campaign=referral&amp;amp;utm_keyword=%7Bkeyword%7D&amp;amp;utm_content=making-instrumentation-extensible"&gt;Honeycomb for free&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>instrumentation</category>
      <category>observability</category>
      <category>sre</category>
      <category>devops</category>
    </item>
    <item>
      <title>From 0 to Insight with OpenTelemetry in Go</title>
      <dc:creator>Liz Fong-Jones</dc:creator>
      <pubDate>Mon, 09 Nov 2020 19:26:56 +0000</pubDate>
      <link>https://forem.com/honeycombio/from-0-to-insight-with-opentelemetry-in-go-27hc</link>
      <guid>https://forem.com/honeycombio/from-0-to-insight-with-opentelemetry-in-go-27hc</guid>
      <description>&lt;p&gt;Honeycomb allows you to send in data from a variety of sources. The steps to get there are a choose-your-own-adventure path and the choices to make may not always be clear to folks new to Honeycomb. This guide is aimed at developers that own Go applications in production and would like to send instrumentation data to Honeycomb using &lt;a href="https://opentelemetry.io" rel="noopener noreferrer"&gt;OpenTelemetry&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This step-by-step guide will help you add OpenTelemetry to your web service, leverage automatic instrumentation, show you how to add additional custom context to that instrumentation, and ensure that instrumentation data is being sent to Honeycomb. This is a zero-to-insight follow along guide to getting started quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a free Honeycomb account
&lt;/h2&gt;

&lt;p&gt;There are several ways to get started with Honeycomb. For simplicity, we’ll start by signing up for an account in Honeycomb’s free tier. Go to &lt;a href="https://ui.honeycomb.io/signup?&amp;amp;utm_source=Devto&amp;amp;utm_Devto=blog&amp;amp;utm_campaign=referral&amp;amp;utm_keyword=%7Bkeyword%7D&amp;amp;utm_content=from-0-to-insight-with-opentelemetry-in-go" rel="noopener noreferrer"&gt;ui.honeycomb.io/signup&lt;/a&gt; to create a free honeycomb account. We support SSO with Google and if you choose that, there are no new passwords to create or remember!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.honeycomb.io/wp-content/uploads/2020/08/signup.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.honeycomb.io%2Fwp-content%2Fuploads%2F2020%2F08%2Fsignup.png" alt="Honeycomb signup flow. text: Join the swarm. Sign up for a free trial. No credit card required. Button: sign up with Google."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Provide your organization’s name when prompted. This is a unique ID, so you can either use your company's name, or if it's a large company you might go with &lt;code&gt;company-team&lt;/code&gt;, e.g. &lt;code&gt;huuli-research&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you're prompted to name your first dataset, give it the same name as your app. (If you change your mind about the name later, you can just create a new dataset.) Select the Go integration and click &lt;b&gt;Create&lt;/b&gt;.&lt;/p&gt;

&lt;p&gt;Once you’ve created your team, you’ll automatically receive an email inviting you to the Honeycomb Pollinators Slack community. &lt;a href="https://www.honeycomb.io/blog/spread-the-love-appreciating-our-pollinators-community/" rel="noopener noreferrer"&gt;This blog post&lt;/a&gt; from Customer Advocate Jenni Boyer details why you should join, definitely check it out when you get the chance!&lt;/p&gt;

&lt;p&gt;At this point you may be prompted to choose how you want to send data to Honeycomb. Click “Show All Integrations”, then “OpenTelemetry”. Now you’ll see a prompt with your API key and some sample code. But instead of using the sample code shown to you in the start up instructions, follow along here with the rest of this post, as these instructions here are more comprehensive and detailed.&lt;/p&gt;

&lt;p&gt;Grab your API key and let's continue on to set up the configuration in your Go app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automatically create traces/spans on HTTP requests
&lt;/h2&gt;

&lt;p&gt;OpenTelemetry can help you jumpstart your way to observability by providing automatic instrumentation for HTTP requests. You have your choice of request routers in OpenTelemetry or you can use the standard HTTP handler. You should pick the mux that’s right for your framework.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automatic instrumentation with request routers
&lt;/h3&gt;

&lt;p&gt;Add one line to your &lt;code&gt;import()&lt;/code&gt; stanza depending upon your request router:&lt;/p&gt;

&lt;h4&gt;
  
  
  If you are using gin/gonic:
&lt;/h4&gt;



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

&lt;/div&gt;



&lt;h4&gt;
  
  
  If you are using gorillamux:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;middleware&lt;/span&gt; &lt;span class="s"&gt;"go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  If you are using echo:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;middleware&lt;/span&gt; &lt;span class="s"&gt;"go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Inject OpenTelemetry middleware
&lt;/h3&gt;

&lt;p&gt;Then, in your &lt;code&gt;main()&lt;/code&gt; function, right after you create your primary router, inject the OpenTelemetry middleware:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Yes, it was that simple with request routers. Two lines of code!&lt;/p&gt;

&lt;h3&gt;
  
  
  If you don’t use a request router
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;otelhttp&lt;/span&gt; &lt;span class="s"&gt;"go.opentelemetry.io/contrib/instrumentation/net/http"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In each place where you pass an http.Handler to a ServeMux, you’ll wrap the handler function. For instance, you'll make the following replacements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- mux.Handle("/path", h)
&lt;/span&gt;&lt;span class="gi"&gt;+ mux.Handle("/path", otelhttp.NewHandler(h, "description of path"))
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- mux.Handle("/path", http.HandlerFunc(f))
&lt;/span&gt;&lt;span class="gi"&gt;+ mux.Handle("/path", otelhttp.NewHandler(http.HandlerFunc(f), "description of path"))
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this fashion, you can ensure that every function you wrap with &lt;code&gt;othttp&lt;/code&gt; will automatically have its metadata collected and a corresponding trace started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automatically create traces/spans on gRPC server requests
&lt;/h2&gt;

&lt;p&gt;Similarly, OpenTelemetry can also help you automatically instrument gRPC requests. To instrument any gRPC servers you have, add an&lt;code&gt;Interceptor&lt;/code&gt; to the instantiation of the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;grpcotel&lt;/span&gt; &lt;span class="s"&gt;"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UnaryInterceptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grpcotel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UnaryServerInterceptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;global&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tracer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt;
    &lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StreamInterceptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grpcotel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StreamServerInterceptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;global&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tracer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configure export to Honeycomb
&lt;/h2&gt;

&lt;p&gt;Now that your code is set up to emit instrumentation, you’ll need to send it to Honeycomb in order for it to be useful. We’ve written a Honeycomb exporter for OpenTelemetry to help make that easier. You’ll need to import the Honeycomb exporter in your main module, and place the initialization code first in your main() function. Use the API key you found in the getting started screen from the Honeycomb website...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"github.com/honeycombio/opentelemetry-exporter-go/honeycomb"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"go.opentelemetry.io/otel/api/global"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;initTracer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;apikey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LookupEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"HNY_KEY"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LookupEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"HNY_DATASET"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;hny&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;honeycomb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewExporter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;honeycomb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;APIKey&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"*0123456789abcdef0123456789abcdef*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;honeycomb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TargetingDataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;honeycomb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithServiceName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serviceName&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sdktrace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;sdktrace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sdktrace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DefaultSampler&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sdktrace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AlwaysSample&lt;/span&gt;&lt;span class="p"&gt;()}),&lt;/span&gt;
    &lt;span class="n"&gt;sdktrace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithSyncer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hny&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;global&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetTraceProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hny&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;cleanup&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;initTracer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;cleanup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="c"&gt;// rest of initialization, including creating HTTP and gRPC servers/handlers...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add custom instrumentation
&lt;/h2&gt;

&lt;p&gt;OpenTelemetry includes a fair amount of automatic instrumentation that can help you get started quickly. However, you’ll likely soon discover that it would be helpful to understand additional bits of context for each event that is now emitting automatic instrumentation data. This is where the power of observability particularly shines. Honeycomb encourages you to send in as much context as you need for these events. You can make your events incredibly wide with no penalties for doing so. I know we’re using the Free tier for this tutorial, but even in that tier the only limitation is 20M events per month. Each of those events can include as much additional context as you want to add in: it still only counts as one event to Honeycomb.&lt;/p&gt;

&lt;p&gt;(Yes, there’s actually a cap on max event size, but for most folks this is essentially unlimited).&lt;/p&gt;

&lt;h3&gt;
  
  
  Attributes
&lt;/h3&gt;

&lt;p&gt;Attributes are any arbitrary key value pairs you can add to any event. Anything at all! If there’s a property you want to see that isn’t captured by the HTTP instrumentation, that’s not a problem! Inside of any HTTP handler, you can access the current span from the request’s &lt;code&gt;Context&lt;/code&gt;() and add any number of key-value pairs using &lt;code&gt;SetAttributes()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"go.opentelemetry.io/otel/api/kv"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"go.opentelemetry.io/otel/api/trace"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&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="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;span&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SpanFromContext&lt;/span&gt;&lt;span class="p"&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;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="c"&gt;// [...]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Child spans
&lt;/h3&gt;

&lt;p&gt;Sometimes you want to track units of work performed within a single HTTP request. For instance, suppose you needed to look up 50 different items inside a function or goroutine before returning the results to the user. OpenTelemetry allows you to easily create a new span with &lt;code&gt;tracer.Start()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For instance, in your &lt;code&gt;cart/item_lookup.go&lt;/code&gt; file for a hypothetical shopping service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"go.opentelemetry.io/otel/api/global"&lt;/span&gt;
&lt;span class="k"&gt;var&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;global&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tracer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cart"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&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;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;tracer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="c"&gt;// [...]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a child span for each item that is retrieved, allowing you to understand whether the items are being fetched in serial or parallel, and how long each item is taking to retrieve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context propagation
&lt;/h2&gt;

&lt;p&gt;Part of why tracing is so useful is because it can be distributed across multiple services. In order to get the most benefit out of distributed tracing, you’ll need to make sure that any downstream services that are issued HTTP client calls, or gRPC client calls, are aware of your traces so that you can build upon them. You’ll need to add OpenTelemetry to those services to see their spans once you’ve added propagation between services.&lt;/p&gt;

&lt;p&gt;For HTTP calls, you’ll need to specify a custom &lt;code&gt;http.Client&lt;/code&gt; instrumented with OpenTelemetry hooks. Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;remoteCall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;otelhttp&lt;/span&gt; &lt;span class="s"&gt;"go.opentelemetry.io/contrib/instrumentation/net/http"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;httpClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Transport&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;otelhttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTransport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultTransport&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;remoteCall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRequestWithContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&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="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;httpClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&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;For gRPC, you’ll just add an &lt;code&gt;Interceptor&lt;/code&gt; to your outgoing connections, without needing to modify your gRPC calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;grpcotel&lt;/span&gt; &lt;span class="s"&gt;"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientConn&lt;/span&gt;
  &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"remotehost:1234"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithUnaryInterceptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grpcotel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UnaryClientInterceptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;global&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tracer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt;
    &lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithStreamInterceptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grpcotel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StreamClientInterceptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;global&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tracer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Start observing your application!
&lt;/h2&gt;

&lt;p&gt;Now that you’ve added OpenTelemetry and custom instrumentation to your code, you’ll need to deploy and run it in order for any data emitted to the Honeycomb exporter to send it to Honeycomb. Once you deploy your binary (or multiple binaries) with the changes you just made above, the “waiting for data...” screen in the Honeycomb UI will let you proceed into the analysis homepage, where you can see a &lt;a href="https://www.honeycomb.io/blog/welcome-to-home/" rel="noopener noreferrer"&gt;summary of your data&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can see the list of recent traces at the bottom of the home view, and can inspect a trace to verify it shows the spans and fields you instrumented. You can start digging around for more sophisticated insights using the ‘New Query’ button to create and issue custom queries against that data.&lt;/p&gt;

&lt;p&gt;Congratulations! You’ve just added your Go application to Honeycomb using OpenTelemetry. Have questions or comments? Go to your inbox and accept that invite to join the Honeycomb Pollinators Slack group. We look forward to seeing you there!&lt;/p&gt;




&lt;p&gt;Now that you've gotten data in, learn how to reach the next level of your observability practice: &lt;a href="https://www.honeycomb.io/guide-observability-for-developers/" rel="noopener noreferrer"&gt;download the Observability for Developers whitepaper&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>observability</category>
      <category>opentelemetry</category>
      <category>honeycomb</category>
      <category>go</category>
    </item>
    <item>
      <title>First experiences with Honeycomb LX2K</title>
      <dc:creator>Liz Fong-Jones</dc:creator>
      <pubDate>Wed, 13 May 2020 15:21:09 +0000</pubDate>
      <link>https://forem.com/lizthegrey/first-experiences-with-honeycomb-lx2k-26be</link>
      <guid>https://forem.com/lizthegrey/first-experiences-with-honeycomb-lx2k-26be</guid>
      <description>&lt;p&gt;I last wrote about arm64 &lt;a href="https://dev.to/lizthegrey/year-of-aarch64-arm64-on-the-desktop-5ddf"&gt;two months ago&lt;/a&gt; when I was excited for my Honeycomb LX2K board.&lt;/p&gt;

&lt;p&gt;Well, it's arrived! and it's glorious! Everything is just working, after some config tweaks and patching. I can stay connected with my team via Slack, write code, and deploy to Graviton2 in the cloud without touching an amd64 machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5UQ2ttLm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6dx6dtosp76dxqckjmqw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5UQ2ttLm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6dx6dtosp76dxqckjmqw.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://photos.app.goo.gl/NdfxWvuaFQE32EDBA"&gt;Here are the photos of the board and its installed setup inside the chassis&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Here's the missing manual, written on the very same Honeycomb LX2K arm64 board I did this setup on!&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Set up the hardware

&lt;ul&gt;
&lt;li&gt;Insert the NVMe boot drive&lt;/li&gt;
&lt;li&gt;Insert RAM sticks&lt;/li&gt;
&lt;li&gt;Plug in the PicoPSU&lt;/li&gt;
&lt;li&gt;Plug in the GPU&lt;/li&gt;
&lt;li&gt;Attach a low-noise fan adapter inline with the CPU fan to avoid the jet engine sounds (PWM isn't working yet).&lt;/li&gt;
&lt;li&gt;Attach the case button(s) to the power and reset jumpers; reset is more important than power.&lt;/li&gt;
&lt;li&gt;Do &lt;em&gt;not&lt;/em&gt; yet attach a monitor or mouse/keyboard.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Flash the &lt;a href="https://drive.google.com/file/d/1WS3ZuXHP_iNLt7Eaxu8PcO5FbffsdqEP/view"&gt;latest UEFI firmware&lt;/a&gt; to a micro SD card

&lt;ul&gt;
&lt;li&gt;Untar the image folder&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dd if=lx2160acex7_2200_700_2400_8_5_2.img of=/dev/sda&lt;/code&gt; where &lt;code&gt;sda&lt;/code&gt; is the SD card (make sure it's not mounted, and really is your SD card and not some other load-bearing device)&lt;/li&gt;
&lt;li&gt;Plug the SD card into the board. Make sure the firmware source jumpers are set &lt;code&gt;off-ON-ON-ON-ON-off&lt;/code&gt; (or otherwise as indicated by silkscreen on board).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Boot for the first time.

&lt;ul&gt;
&lt;li&gt;Plug an Ubuntu ARM64 20.04 USB stick into the board (created with Startup Disk Creator &amp;amp; the &lt;a href="http://cdimage.ubuntu.com/releases/20.04/release/ubuntu-20.04-live-server-arm64.iso"&gt;latest 20.04 ARM image&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Plug in an ethernet cable via USB adapter (the ports on the board won't work yet)&lt;/li&gt;
&lt;li&gt;Plug in the micro-USB serial console port to an existing machine, and run minicom configured to talk to /dev/ttyUSB0 with hardware/software buffering OFF (very important!)&lt;/li&gt;
&lt;li&gt;Power on the device by inserting the power cable or turning on the PSU&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Install Ubuntu 20.04

&lt;ul&gt;
&lt;li&gt;If Ubuntu installer GRUB menu doesn't show, hit &lt;code&gt;esc&lt;/code&gt; during boot and manually select the USB stick as the boot device.&lt;/li&gt;
&lt;li&gt;At GRUB menu, select &lt;code&gt;install&lt;/code&gt; rather than &lt;code&gt;try live&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;You'll need to update the installer snap when prompted, as the base ISO doesn't install on ARM64 properly&lt;/li&gt;
&lt;li&gt;Make sure you enable SSH, so you can do the next bit of work comfortably from a desktop environment&lt;/li&gt;
&lt;li&gt;Disk encryption just works.&lt;/li&gt;
&lt;li&gt;Reboot when prompted; if it doesn't cleanly shut down, kick the reset button after the prompt to remove the USB stick.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Boot the second time.

&lt;ul&gt;
&lt;li&gt;Enter the FDE password over serial console&lt;/li&gt;
&lt;li&gt;Once system is booted, ssh to it from your desktop to make sure everything works.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prepare to enable graphical mode
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Configure AMD PCIe settings: set &lt;code&gt;GRUB_CMDLINE_LINUX_DEFAULT="amdgpu.pcie_gen_cap=0x00040004"&lt;/code&gt; in &lt;code&gt;/etc/default/grub&lt;/code&gt; then run &lt;code&gt;sudo update-grub&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Install the PCIe bug patched kernel image PPA: &lt;a href="https://launchpad.net/%7Elizthegrey/+archive/ubuntu/kernel"&gt;https://launchpad.net/~lizthegrey/+archive/ubuntu/kernel&lt;/a&gt; and install the &lt;code&gt;linux-image-unsigned-5.4.0-42-generic&lt;/code&gt; package.&lt;/li&gt;
&lt;li&gt;Now, reboot, with display and mouse/keyboard attached. If it doesn't work, you can disconnect display and fall back to serial console to debug (and ask for help in SolidRun Discord)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Enabling graphical mode
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Log in after rebooting (verifying the new kernel is loaded)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sudo apt install ubuntu-desktop&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mkdir -p ~/.config/environment.d &amp;amp;&amp;amp; echo 'export MESA_EXTENSION_OVERRIDE=-GL_ARB_buffer_storage' &amp;gt; ~/.config/environment.d/00-mesa.conf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;If needed, run &lt;code&gt;sudo service gdm3 restart&lt;/code&gt; and/or &lt;code&gt;sudo systemctl isolate graphical.target&lt;/code&gt;. GDM should be showing. If not, reboot.&lt;/li&gt;
&lt;li&gt;You will want to log in &lt;em&gt;with wayland&lt;/em&gt;, not with the default Xorg; click the gear icon in the lower right hand corner to use &lt;code&gt;Ubuntu with Wayland&lt;/code&gt; after clicking your name, before entering your password.&lt;/li&gt;
&lt;li&gt;Congratulations, you have a working Gnome3 desktop.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Helpful things to install
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;First off, you probably want a web browser. &lt;code&gt;sudo apt install chromium-browser&lt;/code&gt; will give you a chromium updated with snap, supported by Ubuntu, for arm64, if you don't like Firefox.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sudo update-alternatives --install /usr/bin/x-www-browser x-www-browser /snap/bin/chromium 200&lt;/code&gt; will make it your default.&lt;/li&gt;
&lt;li&gt;Yes, security keys work out of the box!&lt;/li&gt;
&lt;li&gt;You can get graphics acceleration by running &lt;code&gt;snap refresh chromium --channel=candidate/vaapi&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Everything else should just work to taste when running &lt;code&gt;apt install&lt;/code&gt; -- e.g. golang-1.14, openjdk, etc.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>linux</category>
      <category>arm64</category>
      <category>porting</category>
    </item>
    <item>
      <title>Year of ARM64 on the desktop</title>
      <dc:creator>Liz Fong-Jones</dc:creator>
      <pubDate>Mon, 09 Mar 2020 17:14:04 +0000</pubDate>
      <link>https://forem.com/lizthegrey/year-of-aarch64-arm64-on-the-desktop-5ddf</link>
      <guid>https://forem.com/lizthegrey/year-of-aarch64-arm64-on-the-desktop-5ddf</guid>
      <description>&lt;p&gt;After getting &lt;a href="https://www.honeycomb.io/blog/observations-on-arm64-awss-amazon-ec2-m6g-instances/"&gt;my first exposure&lt;/a&gt; to aarch64/ARMv8/ARM64 at work the past few weeks, I'm curious to not just use it in the cloud, but also under my desk! We are cross-compiling from AMD64 to create ARM64 binaries for use in a subset of our canary environment, but it's started feeling weird to me that I cannot locally test my changes before they go out to production.&lt;/p&gt;

&lt;p&gt;So, yesterday I ordered a &lt;a href="https://www.solid-run.com/nxp-lx2160a-family/honeycomb-workstation/"&gt;Honeycomb LX2K Mini-ITX board&lt;/a&gt;, no relation to my employer &lt;a href="//honeycomb.io"&gt;honeycomb.io&lt;/a&gt;, and I'll have it up and running &lt;del&gt;sometime next week&lt;/del&gt;in May due to lack of stock! This post will be updated with my build and findings! and &lt;a href="https://dev.to/lizthegrey/first-experiences-with-honeycomb-lx2k-26be"&gt;I've written up the install instructions&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Part list (with the help of &lt;a href="https://www.reddit.com/r/sffpc/"&gt;/r/sffpc&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lone Industries L5 case&lt;/li&gt;
&lt;li&gt;Minibox 160 XT PSU&lt;/li&gt;
&lt;li&gt;Radeon Pro WX 3200 half-height GPU&lt;/li&gt;
&lt;li&gt;Honeycomb LX2K Mini-ITX board&lt;/li&gt;
&lt;li&gt;
&lt;del&gt;Any old DDR4 RAM&lt;/del&gt; G.SKILL Ripjaws Series 16GB (2 x 8GB) 260-Pin DDR4 SO-DIMM&lt;/li&gt;
&lt;li&gt;512GB M.2 SSD&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm excited to try:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Native builds of Debian/Ubuntu packages, with my own GPG keys available&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/AndreRH/hangover"&gt;Wine Hangover for x86_64 games&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Seeing if I can get Zoom, Chrome, etc. to work given they're built for ChromeOS aarch64 which is Linux under the hood&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>linux</category>
      <category>arm64</category>
      <category>porting</category>
      <category>go</category>
    </item>
  </channel>
</rss>
