Libhoney for Go is Honeycomb’s structured logging library for Go applications. It is a low-level library that helps you send structured events to Honeycomb’s Events API.
go get -v github.com/honeycombio/libhoney-go
Initialize the library by passing in your Team API key and the default dataset name to which it should send events.
When you call the library’s initialization routine, it spins up background threads to handle sending all the events.
You must shut down these background threads on shutdown by calling libhoney.Close()
.
libhoney.Init(libhoney.Config{
WriteKey: "YOUR_API_KEY",
Dataset: "honeycomb-golang-example",
})
defer libhoney.Close() // Flush any pending calls to Honeycomb
Further configuration options can be found in the API reference.
libhoney.Config
also contains an APIHost
key, which defaults to Honeycomb’s API server.
Overriding this with an empty string is a good way to drop events in a test environment.Once initialized, libhoney
is ready to send events.
Events go through three phases:
event := builder.NewEvent()
event.AddField("key", "val")
, event.Add(dataMap)
event.Send()
Upon calling .Send()
, the event is dispatched to be sent to Honeycomb.
All libraries set defaults that will allow your application to function as smoothly as possible during error conditions.
When creating events faster than they can be sent, overflowed events will be dropped instead of backing up and slowing down your application.
In its simplest form, you can add a single attribute to an event with the .AddField(k, v)
method.
If you add the same key multiple times, only the last value added will be kept.
More complex structures (maps and structs—things that can be serialized into a JSON object) can be added to an event with the .Add(data)
method.
Events can have metadata associated with them that is not sent to Honeycomb. This metadata is used to identify the event when processing the response. More detail about metadata is below in the Response section.
Sending an event is an asynchronous action and will avoid blocking by default.
.Send()
will enqueue the event to be sent as soon as possible (thus, the return value does not indicate that the event was successfully sent).
Use the chan
returned by .Responses()
to check whether events were successfully received by Honeycomb’s servers.
Before sending an event, you have the option to attach metadata to that event.
This metadata is not sent to Honeycomb; instead, it is used to help you match up individual responses with sent events.
When sending an event, libhoney
will take the metadata from the event and attach it to the response object for you to consume.
Add metadata by populating the .Metadata
attribute directly on an event.
Responses have a number of fields describing the result of an attempted event send:
2xx
indicates success.time.Duration
it took to send the event.You do not have to process responses if you are not interested in them—simply ignoring them is perfectly safe. Unread responses will be dropped.
Honeycomb can calculate all sorts of statistics, so send the data you care about and let us crunch the averages, percentiles, lower/upper bounds, cardinality—whatever you want—for you.
import "github.com/honeycombio/libhoney-go"
// Call Init to configure libhoney
libhoney.Init(libhoney.Config{
WriteKey: "YOUR_API_KEY",
Dataset: "honeycomb-golang-example",
})
defer libhoney.Close() // Flush any pending calls to Honeycomb
ev := libhoney.NewEvent()
ev.Add(map[string]interface{}{
"duration_ms": 153.12,
"method": "get",
"hostname": "appserver15",
"payload_length": 27,
})
ev.Send()
You do not need to know all of the fields that should be added to an event up front. In fact, it is very common to add fields to an event as circumstances change or as new information comes in. For example, you might want to indicate success or failure, and if a failure occurred, you will want to submit the associated error message.
import "github.com/honeycombio/libhoney-go"
func main() {
// Call Init to configure libhoney
libhoney.Init(libhoney.Config{
WriteKey: "YOUR_WRITE_KEY",
Dataset: "honeycomb-golang-example",
})
// Flush any pending calls to Honeycomb before exiting
defer libhoney.Close()
// Create an event, add some data
ev := libhoney.NewEvent()
ev.Add(map[string]interface{}{
"method": "get",
"hostname": "appserver15",
"payload_length": 27,
}))
// This event will be sent regardless of how we exit
defer ev.Send()
if err := myOtherFunc(); err != nil {
ev.AddField("error", err.Error())
ev.AddField("success", false)
return
}
// do some work, maybe measure some things
ev.AddField("duration_ms", 153.12)
ev.AddField("success", true)
}
// ... Initialization code ...
params := map[string]interface{}{
"hostname": "foo.local",
"built": false,
"user_id": -1,
}
libhoney.Add(params)
builder := libhoney.NewBuilder()
builder.AddField("built", true)
// Spawn a new event and override the timestamp
event := builder.NewEvent()
event.AddField("user_id", 15)
event.AddField("latency_ms", time.Since(start).Milliseconds())
event.Timestamp = time.Date(2016, time.February, 29, 1, 1, 1, 0, time.UTC)
event.Send()
Further examples can be found on GitHub.
Builders are, at their simplest, a convenient way to avoid repeating common attributes that may not apply globally. Creating a builder for a given component allows a variety of different events to be spawned and sent within the component, without having to repeat the component name as an attribute for each.
You can clone builders—the cloned builder will have a copy of all the fields and dynamic fields in the original. As your application forks down into more and more specific functionality, you can create more detailed builders. The final event creation in the leaves of your application’s tree will have all the data you have added along the way in addition to the specifics of this event.
The global scope is essentially a specialized builder, for capturing attributes that are likely useful to all events (for example, hostname or environment). Adding this kind of peripheral and normally unavailable information to every event gives you enormous power to identify patterns that would otherwise be invisible in the context of a single request.
The top-level libhoney
and Builders support .AddDynamicField(func)
.
Adding a dynamic field to a Builder or top-level libhoney
ensures that each time an event is created, the provided function is executed and the returned key/value pair is added to the event.
This may be useful for including dynamic process information such as memory used, number of threads, concurrent requests, and so on to each event.
Adding this kind of dynamic data to an event makes it easy to understand the application’s context when looking at an individual event or error condition.
Refer to Common Issues with Sending Data in Honeycomb.
Features, bug fixes and other changes to libhoney
are gladly accepted.
Please open issues or a pull request with your change.
Remember to add your name to the CONTRIBUTORS file!
All contributions will be released under the Apache License 2.0.