Giter VIP home page Giter VIP logo

honeybadger-go's Introduction

Honeybadger for Go

Build Status

Go (golang) support for the ⚡ Honeybadger error notifier. Receive instant notification of panics and errors in your Go applications.

Getting Started

1. Install the library

To install, grab the package from GitHub:

go get github.com/honeybadger-io/honeybadger-go

Then add an import to your application code:

import "github.com/honeybadger-io/honeybadger-go"

2. Set your API key

Finally, configure your API key:

honeybadger.Configure(honeybadger.Configuration{APIKey: "{{PROJECT_API_KEY}}"})

You can also configure Honeybadger via environment variables. See Configuration for more information.

3. Enable automatic panic reporting

Panics during HTTP requests

To automatically report panics which happen during an HTTP request, wrap your http.Handler function with honeybadger.Handler:

log.Fatal(http.ListenAndServe(":8080", honeybadger.Handler(handler)))

Request data such as cookies and params will automatically be reported with errors which happen inside honeybadger.Handler. Make sure you recover from panics after honeybadger's Handler has been executed to ensure all panics are reported.

Unhandled Panics

To report all unhandled panics which happen in your application the following can be added to main():

func main() {
  defer honeybadger.Monitor()
  // application code...
}

Manually Reporting Errors

To report an error manually, use honeybadger.Notify:

if err != nil {
  honeybadger.Notify(err)
}

Sample Application

If you'd like to see the library in action before you integrate it with your apps, check out our sample application.

You can deploy the sample app to your Heroku account by clicking this button:

Deploy

Don't forget to destroy the Heroku app after you're done so that you aren't charged for usage.

The code for the sample app is available on Github, in case you'd like to read through it, or run it locally.

Configuration

To set configuration options, use the honeybadger.Configuration method, like so:

honeybadger.Configure(honeybadger.Configuration{
  APIKey: "{{PROJECT_API_KEY}}",
  Env: "staging"
})

The following options are available to you:

Name Type Default Example Environment variable
APIKey string "" "badger01" HONEYBADGER_API_KEY
Root string The current working directory "/path/to/project" HONEYBADGER_ROOT
Env string "" "production" HONEYBADGER_ENV
Hostname string The hostname of the current server. "badger01" HONEYBADGER_HOSTNAME
Endpoint string "https://api.honeybadger.io" "https://honeybadger.example.com/" HONEYBADGER_ENDPOINT
Sync bool false true HONEYBADGER_SYNC
Timeout time.Duration 3 seconds 10 * time.Second HONEYBADGER_TIMEOUT (nanoseconds)
Logger honeybadger.Logger Logs to stderr CustomLogger{} n/a
Backend honeybadger.Backend HTTP backend CustomBackend{} n/a

Public Interface

honeybadger.Notify(): Send an error to Honeybadger.

If you've handled a panic in your code, but would still like to report the error to Honeybadger, this is the method for you.

Examples:

if err != nil {
  honeybadger.Notify(err)
}

You can also add local context using an optional second argument when calling honeybadger.Notify:

honeybadger.Notify(err, honeybadger.Context{"user_id": 2})

Honeybadger uses the error's class name to group similar errors together. If your error classes are often generic (such as errors.errorString), you can improve grouping by overriding the default with something more unique:

honeybadger.Notify(err, honeybadger.ErrorClass{"CustomClassName"})

To override grouping entirely, you can send a custom fingerprint. All errors with the same fingerprint will be grouped together:

honeybadger.Notify(err, honeybadger.Fingerprint{"A unique string"})

To tag errors in Honeybadger:

honeybadger.Notify(err, honeybadger.Tags{"timeout", "http"})

honeybadger.SetContext(): Set metadata to be sent if an error occurs

This method lets you set context data that will be sent if an error should occur.

For example, it's often useful to record the current user's ID when an error occurs in a web app. To do that, just use SetContext to set the user id on each request. If an error occurs, the id will be reported with it.

Note: This method is currently shared across goroutines, and therefore may not be optimal for use in highly concurrent use cases, such as HTTP requests. See issue #35.

Examples:

honeybadger.SetContext(honeybadger.Context{
  "user_id": 1,
})

defer honeybadger.Monitor(): Automatically report panics from your functions

To automatically report panics in your functions or methods, add defer honeybadger.Monitor() to the beginning of the function or method you wish to monitor.

Examples:

func risky() {
  defer honeybadger.Monitor()
  // risky business logic...
}

Important: honeybadger.Monitor() will re-panic after it reports the error, so make sure that it is only called once before recovering from the panic (or allowing the process to crash).


honeybadger.BeforeNotify(): Add a callback to skip or modify error notification.

Sometimes you may want to modify the data sent to Honeybadger right before an error notification is sent, or skip the notification entirely. To do so, add a callback using honeybadger.BeforeNotify().

Examples:

honeybadger.BeforeNotify(
  func(notice *honeybadger.Notice) error {
    if notice.ErrorClass == "SkippedError" {
      return fmt.Errorf("Skipping this notification")
    }
    // Return nil to send notification for all other classes.
    return nil
  }
)

To modify information:

honeybadger.BeforeNotify(
  func(notice *honeybadger.Notice) error {
    // Errors in Honeybadger will always have the class name "GenericError".
    notice.ErrorClass = "GenericError"
    return nil
  }
)

honeybadger.NewNullBackend(): Disable data reporting.

NewNullBackend creates a backend which swallows all errors and does not send them to Honeybadger. This is useful for development and testing to disable sending unnecessary errors.

Examples:

honeybadger.Configure(honeybadger.Configuration{Backend: honeybadger.NewNullBackend()})

Creating a new client

In the same way that the log library provides a predefined "standard" logger, honeybadger defines a standard client which may be accessed directly via honeybadger. A new client may also be created by calling honeybadger.New:

hb := honeybadger.New(honeybadger.Configuration{APIKey: "some other api key"})
hb.Notify("This error was reported by an alternate client.")

Grouping

Honeybadger groups by the error class and the first line of the backtrace by default. In some cases it may be desirable to provide your own grouping algorithm. One use case for this is errors.errorString. Because that type is used for many different types of errors in Go, Honeybadger will appear to group unrelated errors together. Here's an example of providing a custom fingerprint which will group errors.errorString by message instead:

honeybadger.BeforeNotify(
  func(notice *honeybadger.Notice) error {
    if notice.ErrorClass == "errors.errorString" {
      notice.Fingerprint = notice.Message
    }
    return nil
  }
)

Note that in this example, the backtrace is ignored. If you want to group by message and backtrace, you could append data from notice.Backtrace to the fingerprint string.

An alternate approach would be to override notice.ErrorClass with a more specific class name that may be inferred from the message.

Sync Mode

By default, we send out all notices via a separate worker goroutine. This is awesome for long running applications as it keeps Honeybadger from blocking during execution. However, this can be a problem for short running applications (lambdas, for example) as the program might terminate before all messages are processed. To combat this, you can configure Honeybadger to work in "Sync" mode which blocks until notices are sent when honeybadger.Notify is executed. You can enable sync mode by setting the HONEYBADGER_SYNC environment variable or updating the config:

honeybadger.Configure(honeybadger.Configuration{Sync: true})

"Sync" mode is most useful for situations when you are not sending the notice directly. If you are sending them directly and you want the same functionality, you can call honeybadger.Flush after sending the Notice to block until the worker has completed processing.

honeybadger.Notify("I errored.")
honeybadger.Flush()

Versioning

We use Semantic Versioning to version releases of honeybadger-go. Because there is no official method to specify version dependencies in Go, we will do our best never to introduce a breaking change on the master branch of this repo after reaching version 1. Until we reach version 1 there is a small chance that we may introduce a breaking change (changing the signature of a function or method, for example), but we'll always tag a new minor release and broadcast that we made the change.

If you're concerned about versioning, there are two options:

Vendor your dependencies

If you're really concerned about changes to this library, then copy it into your source control management system so that you can perform upgrades on your own time.

Use gopkg.in

Rather than importing directly from GitHub, gopkg.in allows you to use their special URL format to transparently import a branch or tag from GitHub. Because we tag each release, using gopkg.in can enable you to depend explicitly on a certain version of this library. Importing from gopkg.in instead of directly from GitHub is as easy as:

import "gopkg.in/honeybadger-io/honeybadger-go.v0"

Check out the gopkg.in homepage for more information on how to request versions.

Changelog

See https://github.com/honeybadger-io/honeybadger-go/blob/master/CHANGELOG.md

Contributing

If you're adding a new feature, please submit an issue as a preliminary step; that way you can be (moderately) sure that your pull request will be accepted.

To contribute your code:

  1. Fork it.
  2. Create a topic branch git checkout -b my_branch
  3. Commit your changes git commit -am "Boom"
  4. Push to your branch git push origin my_branch
  5. Send a pull request

Releasing

  1. Update VERSION in honeybadger.go
  2. Include version in CHANGELOG.md
  3. Commit release and push tag with release version

License

This library is MIT licensed. See the LICENSE file in this repository for details.

honeybadger-go's People

Contributors

calebhearth avatar dependabot[bot] avatar gaffneyc avatar gogolok avatar honeybadger-robot avatar izumin5210 avatar james-lawrence avatar joshuap avatar kostyantyn avatar miry avatar mperham avatar rabidpraxis avatar starrhorne avatar stympy avatar subzero10 avatar sypher7 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

honeybadger-go's Issues

Context is shared across goroutines

As described in #33, our context implementation in honeybadger-go is a bit naive; because it's global, it is prone to collisions when multiple goroutines update it (which is especially common in server implementations). Consider this scenario from @odarriba:

  1. request A starts (one goroutine) -> set context A
  2. request B starts (another goroutine) -> set context B
  3. request A fails -> sends to HB with context B

The way we solve this issue in Ruby is using a thread local variable which solves the concurrent access problem, and then we reset the context after each request which solves the local request context issue. We do something similar in Elixir--the context is stored in the process dictionary.

I've been reading up on how Go handles passing request context across goroutines, specifically:

https://blog.golang.org/context
http://www.gorillatoolkit.org/pkg/context

From what understand, Go does not provide anything similar to thread local variables or a process dictionary, preferring to pass a context value as described in the blog post.

There may be a way we can integrate with this, but I'm still wrapping my head around it, so I'm not sure what that might be yet. I'm open to suggestions!

In the meantime, we have to live with the caveat that honeybadger.SetContext will not work reliably across goroutines.

Front logo Front conversations

Consider some mechanism for handling worker errors in the default client?

Hello!

First of all, thanks for the great service and the great Go client.

I have not been able to find an existing mechanism in the library to be able to take action in the case of a worker failure in the default client, for example due to network issues (say, my server is unable to connect to Honeybadger's API).

I am able to work around it with your current implementation, but my solution feels a bit hackish. I am wrapping the Notify method of the default server backend so that I can do something when it fails (since errors that get returned to workers just get logged).

So, I have two possible proposals:

  • Boolean "Synchronous" field in the Configuration struct
    • In the default Client's Notify method, check client.Config.Synchronous
      • If false: use the same behavior as now (which prevents me from using my own buffering and dealing with network failures, timeouts etc.)
      • If true: return the value of calling the backend's notify directly
    • Pros:
      • Backward compatible
      • Lets users implement their own queuing, circuit breakers, additional logic on failure & success
    • Cons:
      • User has to implement their own queuing/workers if they use it
      • This doesn't take the metric collector's use of asynchronous workers into consideration-- since they don't use the Client, and since their use of Notify is invisible to end-users, it might be confusing to have the Synchronous value impact error reporting in the metrics collector. I think this is a solvable problem, but I have not solved it (I am not currently using the metrics collector).
  • Optional "WorkerErrors" channel on the client, which, if it is set, the worker writes to when it runs into issues with its work
    • Pros:
      • Backwards compatible
      • User doesn't have to implement their own workers
      • The metrics collector gets it for free
    • Cons:
      • User has less control over how the library talks to the API

I don't have a strong opinion—I didn't want to just write a feature request without trying to think it through. Either of these would solve the issue I'm having, and I think that it would be nice if the library provided access to worker errors in some fashion.

Thanks!

Add config option to report errors synchronously

This feature will add a boolean Synchronous config option. When true, calls to Notify() will skip the worker and call the backend directly. The return value should still be a string UUID. When false (default), the behavior will be what it is now -- the notice is passed to the worker and the UUID is returned immediately.

See #22 for context around this feature request. PRs for this are welcome!

Move several types to pointers

There's a couple of spots where you are mixing pointers and value types for core types like Configuration and Context. For instance, your Configuration type is a pointer here but a value here, forcing you to awkwardly deference it. Unless you are explicitly trying for a functional-ish API (i.e. immutable), I'd suggest moving to use pointers exclusively for composite types; it ensures you aren't spending extra cycles copying data needlessly and can mutate method parameters.

errcheck lint warnings

This issue isn't necessarily actionable; it's just to provide some further documentation of lint warnings. Today I fixed all warnings but the following:

client.go:64:3:warning: error return value not checked (client.Notify(newError(err, 2))) (errcheck)
client.go:75:5:warning: error return value not checked (client.Notify(newError(err, 2), Params(r.Form), getCGIData(r), *r.URL)) (errcheck)
honeybadger.go:69:3:warning: error return value not checked (client.Notify(newError(err, 2))) (errcheck)
server.go:61:8:warning: error return value not checked (defer resp.Body.Close()) (errcheck)

In the first 3 I really don't want to do anything with an error if there is one (I'm already logging it to the logger, and in a recovered state I don't want to do extra error handling related to error notification). The last warning is because I am deferring resp.Body.Close(), which I took directly from the documentation for the http package; doesn't seem like it's a big deal.

Concurrent map writes

Greetings,

Looks like there is an issue with type hash map[string]interface{} used in

type Context hash

// Update applies the values in other Context to context.
func (context Context) Update(other Context) {
	for k, v := range other {
		context[k] = v
	}
}

Maps are not safe for concurrent use as per https://golang.org/doc/faq#atomic_maps and so we often face the following error:

fatal error: concurrent map writes

goroutine 1387 [running]:
runtime.throw(0x9de572, 0x15)
	/usr/local/go/src/runtime/panic.go:605 +0x95 fp=0xc420049c58 sp=0xc420049c38 pc=0x42c115
runtime.mapassign_faststr(0x942180, 0xc42006db60, 0xc421a877c0, 0xd, 0x900480)
	/usr/local/go/src/runtime/hashmap_fast.go:861 +0x4da fp=0xc420049cd8 sp=0xc420049c58 pc=0x40e99a
github.com/honeybadger-io/honeybadger-go.Context.Update(0xc42006db60, 0xc4223a43f0)
	/go/src/github.com/honeybadger-io/honeybadger-go/context.go:9 +0xd7 fp=0xc420049d80 sp=0xc420049cd8 pc=0x7842b7
github.com/honeybadger-io/honeybadger-go.(*Client).SetContext(0xc4200894c0, 0xc4223a43f0)
	/go/src/github.com/honeybadger-io/honeybadger-go/client.go:39 +0x3c fp=0xc420049da0 sp=0xc420049d80 pc=0x7826dc
github.com/honeybadger-io/honeybadger-go.SetContext(0xc4223a43f0)
	/go/src/github.com/honeybadger-io/honeybadger-go/honeybadger.go:60 +0x37 fp=0xc420049dc0 sp=0xc420049da0 pc=0x784c37

Probably better replace with sync.Map or something like that.

Can't `go get` honeybadger-go

When I try to run go get honeybadger-io/honeybadger-go, I get

github.com/honeybadger-io/honeybadger-go/notice.go:101: undefined: load.LoadAvg

load is from shirou/gopsutil. I think the problem is that gopsutil recently updated to v2 and when doing so changed load.LoadAvg to just load.Avg, as seen in this file. It appears that honeybadger-go isn't using Godeps to vendor dependencies, so I think it was automatically trying to get the latest version of shirou/gopsutil, which now defines a new api. At least I think this could be what's going on, but I'm not positive.

Looking forward to using honeybadger on my go project!

Can't access the original error in before notify.

What are the steps to reproduce this issue?

honeybadger.Notice(MyErrorf("example"))

honeybadger.BeforeNotify(func(notice *honeybadger.Notice) error {
   log.Printf("%T\n", notice.Error) // is a honeybadger.Error which doesn't expose the underlying error.
   return nil
})

What happens?

before notify handlers receive a honeybadger.Notice, but the error being wrapped isn't accessible.

What were you expecting to happen?

I'd expect to be able to access the underlying cause using stdlib functions like unwrap, is, as. I'd also expect to be able to control the stack trace from error provided to the notice function.

What versions are you using?

Operating System: linux
Package Version: v0.6.0
Go Version: 1.21

Publicizing stack frame generation

Hi! We currently use Logrus + Hooks to dispatch errors to Honeybadger using the package https://github.com/bobbytables/logrus-honeybadger

We've noticed that the Class when using Go typically defaults to *errors.errorString, usually because of this line: https://github.com/honeybadger-io/honeybadger-go/blob/master/error.go#L50

This can be solved by calling Notify on the client with the honeybadger.Error type where you can set the Class struct property manually. However, getting the frames for the current stacktrace is an unexported function in this package.

So what I'm proposing is to export the function defined here https://github.com/honeybadger-io/honeybadger-go/blob/master/error.go#L55

This would allow generating the Error type very easy to pass into the Notify function.

Filtering sensitive data

We need a way to filter sensitive data, like we do for Ruby and Elixir:

https://docs.honeybadger.io/lib/ruby/getting-started/filtering-sensitive-data.html#disable-data-completely
https://docs.honeybadger.io/lib/elixir.html#configuration

Three potential features related to this:

  • List of keys to filter (see filter_keys in Elixir)
  • Programatically filter data (see filter and before_notify in Elixir and Ruby respectively)
  • Disable certain request data entirely (see disable_params, disable_session, etc. in Ruby)

Error Class

Many of the errors in our applications are errors.errorString struct. However, when the errors are sent to Honeybadger, they are all treated as the same error because they are of the same class.

Can Error.Class be updated to be the message if the type is errorString struct? It would make find errors easier, and not force us to create new error types.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.