How to: Logging Go Errors to Sentry
So you're picking up Go and wondering "where did all my exceptions go?" It takes a bit to wrap your head around using Go, especially if you're coming from an interpreted language. Go error tracking is a lot different from Python error tracking or Ruby error tracking.
Handling Go Errors
If you've sought guidance on managing bugs in Go, you've probably ended up with a lot of your code looking like this:
f, err := os.Open("filename.ext")
if err != nil {
log.Fatal(err)
}
If you're doing this, don't fret -- it's a straightforward approach to handling Go exceptions. The problem is that you're doing it via traditional logging. Even worse — this won't save you when your code hits a panic.
Addressing Panics in Go
One of the big adjustments coming from other languages to Go is that standard exceptions that can be caught the traditional way do not exist. While exceptions exist in the form of panics, they can only be recovered from within deferred functions:
func f() {
defer func() {
if r := recover(); r != nil {
log.Println("Recovered in f", r)
}
}()
doSomethingDangerous()
}
Reporting to Sentry
Now that you understand the difference between an error and a panic, let's talk about how we can feed those into Sentry.
We first need to initialize our Raven client with the DSN for our project:
import "github.com/getsentry/raven-go"
func init() {
raven.SetDSN("<your dsn>")
}
Now we can easily capture errors from anywhere in our application with raven.CaptureError
or raven.CaptureErrorAndWait
. The difference being, one is fire and forget, and the other blocks waiting for message to be sent.
fp, err := os.Open("filename.ext")
if err != nil {
// We explicitly want to wait here because we're exiting
// immediately after
raven.CaptureErrorAndWait(err, nil)
log.Fatal(err)
}
If you suspect that a function call may potentially panic, or you want to be overly cautious, you can wrap the function call in raven.CapturePanic
which will implement the standard defer
/recover
pattern and log the panic and continue on.
func doStuff() {
// Some stuff that we want to guard
}
// Make sure that the call to doStuff doesn't leak a panic
raven.CapturePanic(doStuff, nil)
Capturing Metadata
One of the most important aspects of an error monitoring tool like Sentry is its ability to collect additional metadata about errors. In Go this is no different, but the APIs are fairly different than what you might see with other integrations. Raven clients treat metadata as a context object requiring an explicit raven.Set{User,Http,Tags}Context()
and ultimately a raven.ClearContext()
to reset it back. This works great for http request middlewares, for example.
Users
Sentry is great for capturing user data to attach to the errors being logged. We can do this through raven.SetUserContext
and the User will get added to the context being sent along to all future errors.
raven.SetUserContext(&raven.User{ID: "123", Email: "matt@example.com"})
// Later on, call CaptureError and pass along the User
raven.CaptureError(err, nil)
// Now clear it for subsequent calls
raven.ClearContext()
Http Requests
When working with an http server, it’s extremely valuable to capture information about the request that triggered the error. To do that, Raven provides a convenient way to construct an *raven.Http
interface to be appended to the current context, raven.NewHttp
which takes a single argument, *http.Request
. This will provide information about URL, headers, Cookies, the request method, requests IP address, etc.
raven.SetHttpContext(raven.NewHttp(req))
We also provide a hook into the stdlib’s net/http
package that can wrap your handler:
func root(w http.ResponseWriter, r *http.Request) {
// ... do stuff
}
http.HandleFunc("/", raven.RecoveryHandler(root))
Tags
Tags can be used to filter for specific errors within Sentry, and they are easy to attach with Context as well with raven.SetTagsContext()
. Tags can also be set at the time of capturing the error, and they will get merged with the context, if there is any.
raven.SetTagsContext(map[string]string{"browser": "firefox"})
raven.CaptureError(err, map[string]string{"age": "20"})
In this example, you’d get tags that combined browser
and age
both in Sentry.
Learn More about Logging Go Errors
Take a look at the raven-go project on GitHub to learn more about how things are implemented, as well as additional details on using it with Sentry. To start logging Go errors to Sentry, sign up today.