We use cookies or similar technologies to personalize your online experience and tailor marketing to you.
Many of our product features require cookies to function properly. Your use of this site and online
product constitutes your consent to these personalization technologies. Read
our Privacy Policy to find out more.

Beeline for Go

The Go Beeline for Honeycomb is quick and easy way to instrument your Go application.
It includes several optional wrappers that automatically instrument HTTP requests and
database queries. It also supports tracing out of the box,
linking database queries to the HTTP request from which they originated.
While this is a great way to get general insights about your app as quickly
as possible, as you forge ahead on your observability journey you may find
you’d like to add new events or traces to add more details specific to your app.
The Go Beeline provides simple interfaces for adding both.

Augment the data with interesting information from your app and extra
context such as errors so that you can see rich information about your app
in Honeycomb. The context.Context (containing the automatically constructed
HTTP event) to use should be present on the request if the Beeline HTTP wrapper
was used.

funchandleEndpoint(whttp.ResponseWriter,r*http.Request){ctx:=r.Context()iferr:=beeline.AddField(ctx,"user_id",userIDFromSession(r));err!=nil{beeline.AddField(ctx,"get_session_error",err)}// ... do the rest of work for the request ...
}

Add additional spans and turn a series of events into a trace:

funcslowOp(ctxcontext.Context){ctx,span:=beeline.StartSpan(ctx,"slowOp")deferspan.Send()// ... go on and do the slow opp, add more data along the way
beeline.AddField(ctx,"interesting_thing","banana")}

Make sure to call beeline.Close() to send any pending events to Honeycomb
before your program exits.

Adding context to events

The middleware wrapper creates a Honeycomb event in the request context as a span in the overall trace. This span exists throughout the request’s lifecycle, allowing you to add as many additional
custom fields as you like.

Additional fields are added under the app. namespace. For example, the
field above would appear in your event as app.big_num.
The namespace groups your fields together to make them easy to find and examine.

These additional fields are your opportunity to add important and detailed
context to your instrumentation. Put a timer around a section of code, add per-
user information, include details about what it took to craft a response, and so
on. It is expected that some fields will only be present on some requests. Error
handlers are a great example of this; they will obviously only exist when an
error has occurred.

It is common practice to add in these fields along the way as they are processed
in different levels of middleware. For example, if you have an authentication
middleware, it would add a field with the authenticated user’s ID and name as
soon as it resolves them. Later on in the call stack, you might add additional
fields describing what the user is trying to achieve with this specific HTTP
request.

Adding spans to a trace

We encourage people to think about instrumentation in terms of “units of work”.
As your program grows, what constitutes a unit of work is likely to be portions
of your overall service rather than an entire run. Spans are a way of breaking
up a single external action (say, an HTTP request) into several smaller units in
order to gain more insight into your service. Together, many spans make a trace,
which you can visualize traces within the Honeycomb query builder.

Adding spans with the Go Beeline is easy! Here is an example, where
calls to slowOp get their own span within the trace:

funcslowOp(ctxcontext.Context){ctx,span:=beeline.StartSpan(ctx,"slowOp")deferspan.Send()// ... go on and do the slow opp, add more data along the way
beeline.AddField(ctx,"interesting_thing","banana")}

Spans always get a few fields:

a name - in this case slowOp

a duration - how much time elapsed between when the span was started and sent

a service_name - generally configured during Beeline initialization

several IDs - trace, span, and parent identifiers (UUIDs)

You are always welcome (and encouraged!) to add additional fields to spans using
the beeline.AddField function.

Wrappers and other middleware

After the router has parsed the request, more fields specific to that router are
available, such as the specific handler matched or any request parameters that
might be attached to the URL.

You can use a wrapper to capture additional fields specific to a particular
type of request. The available wrappers are listed below. You can find
additional information and detailed instructions in the GoDoc links for
each subpackage.

HTTP wrappers:

Database wrappers:

Optional configuration

If you would like to use another framework that supports middleware, you may
be able to adapt one of the standard wrappers. We recommend starting with
the hnynethttp
wrapper, as it expects a function that takes a http.Handler
and returns a http.Handler.

In order to have traces connect HTTP wrapped packages all the way down to the
database (using the sql or sqlx wrappers), you must pass the context from the
*http.Request through to the SQL package using the appropriate Context-enabled
function calls. This context ties the SQL calls back to specific HTTP requests,
so you can include additional details such as how much time was spent in the DB.
It also connects the request IDs from separate events so you can see exactly
which DB calls were triggered by a given event.

For very high throughput services, you can send only a portion of the events
flowing through your service by setting the SampleRate during initialization.
This sample rate will send 1/n events, so a sample rate of 5 would send 20% of
all events. For high throughput services, a sample rate of 100 is a good start.

Sampling events

To sample a portion of events for very
high throughput services, include an integer SampleRate in the initialization of the Go Beeline. This sends 1/n of all events, so a sample rate of 5 would send 20% of your events. Try starting with
a value of 10:

Sampling is performed by default on a per-trace level in the Go Beeline, so adding sampling won’t break your traces. Either all spans in a trace will be sent, or no spans in the trace will be sent.

Customizing sampling logic

Our Beeline lets you define a SamplerHook in order to customize the logic used for deterministic per-trace sampling.

For example, assume you have instrumented an HTTP server.
You’d like to keep all errored requests and heavily sample healthy traffic
(200 response codes).
Also, you don’t really care about 302 redirects in your app, so want to drop those.
You could define a sampler function like so:

import("crypto/sha1""math")// Deterministic shouldSample taken from https://github.com/honeycombio/beeline-go/blob/7df4c61d91994bd39cc4c458c2e4cc3c0be007e7/sample/deterministic_sampler.go#L55-L57
funcshouldSample(traceIdstring,sampleRateint)bool{upperBound:=math.MaxUint32/uint32(sampleRate)sum:=sha1.Sum([]byte(traceId))// convert last 4 digits to uint32
b:=sum[:4]v:=uint32(b[3])|(uint32(b[2])<<8)|(uint32(b[1])<<16)|(uint32(b[0])<<24)returnv<upperBound}funcsampler(fieldsmap[string]interface{})(bool,int){sampleRate:=1switchfields["response_code"]{case302:returnfalse,0case200:// Only keep 1 out of every 100 traces for successful requests
sampleRate=100default:// Capture everything else
ifshouldSample(fields["trace.trace_id"].(string),sampleRate){returntrue,sampleRate}}returnfalse,0}funcmain(){beeline.Init(beeline.Config{WriteKey:"YOUR_API_KEY",Dataset:"MyGoApp",Debug:true,SamplerHook:sampler,})}

Note: Defining a sampling hook overrides the deterministic sampling
behavior for trace IDs. Unless you take trace.trace_id into account (as we did
above by taking the sha1.Sum of the trace ID), you will get incomplete traces.

Troubleshooting the Beeline

There are two general approaches to finding out what’s wrong when the Go Beeline isn’t doing what you expect.

“The events I’m generating don’t contain the content I expect”

Set STDOUT to true in the initialization of the Go Beeline. This will print the JSON representation
of events to the terminal instead of sending them to Honeycomb. This lets you quickly see what’s getting sent
and allows you to modify your code accordingly.

Note: When STDOUT is set to true, events will only be sent to the
terminal, not to Honeycomb itself. Remove it when you are finished inspecting
the events to start sending to the Honeycomb API again.

“The events I’m sending aren’t being accepted by Honeycomb”

Set Debug to true in the initialization of the Go Beeline. This will print the responses that come back from
Honeycomb to the terminal when sending events. These responses will have extra detail explaining why events are being
rejected (or that they are being accepted) by Honeycomb.

Example event

Here is a sample event created by the Go Beeline. All events
contain Timestamp, duration_ms, and meta.type fields, but each wrapper
adds a different set of additional fields appropriate for its type of request.