Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deliver async reports from a goroutine pool #230

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions v2/bugsnag.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"sync"
"time"

"github.com/alitto/pond"
"github.com/bugsnag/bugsnag-go/v2/device"
"github.com/bugsnag/bugsnag-go/v2/errors"
"github.com/bugsnag/bugsnag-go/v2/sessions"
Expand Down Expand Up @@ -39,6 +40,8 @@ var DefaultSessionPublishInterval = 60 * time.Second
var defaultNotifier = Notifier{&Config, nil}
var sessionTracker sessions.SessionTracker

var asyncPool *pond.WorkerPool

// Configure Bugsnag. The only required setting is the APIKey, which can be
// obtained by clicking on "Settings" in your Bugsnag dashboard. This function
// is also responsible for installing the global panic handler, so it should be
Expand All @@ -51,6 +54,7 @@ func Configure(config Configuration) {
// Only do once in case the user overrides the default panichandler, and
// configures multiple times.
panicHandlerOnce.Do(Config.PanicHandler)
setupAsyncPool()
}

// StartSession creates new context from the context.Context instance with
Expand Down Expand Up @@ -253,6 +257,8 @@ func init() {
Logger: log.New(os.Stdout, log.Prefix(), log.Flags()),
PanicHandler: defaultPanicHandler,
Transport: http.DefaultTransport,
NumGoroutines: 10,
MaxPendingReports: 1000,

flushSessionsOnRepanic: true,
})
Expand Down Expand Up @@ -282,3 +288,7 @@ func updateSessionConfig() {
Logger: Config.Logger,
})
}

func setupAsyncPool() {
asyncPool = pond.New(Config.NumGoroutines, Config.MaxPendingReports)
}
23 changes: 23 additions & 0 deletions v2/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
)

Expand Down Expand Up @@ -98,9 +99,19 @@ type Configuration struct {
// can be configured if you are in an environment
// that has stringent conditions on making http requests.
Transport http.RoundTripper

// Whether bugsnag should notify synchronously. This defaults to false which
// causes bugsnag-go to spawn a new goroutine for each notification.
Synchronous bool

// Number of goroutines to use for sending notifications in asynchronous
// mode. This defaults to 10.
NumGoroutines int

// The maximum number of reports that can be pending in asynchronous mode.
// This defaults to 1000.
MaxPendingReports int

// Whether the notifier should send all sessions recorded so far to Bugsnag
// when repanicking to ensure that no session information is lost in a
// fatal crash.
Expand Down Expand Up @@ -307,6 +318,18 @@ func (config *Configuration) loadEnv() {
if synchronous := os.Getenv("BUGSNAG_SYNCHRONOUS"); synchronous != "" {
envConfig.Synchronous = synchronous == "1"
}
if numGoroutines := os.Getenv("BUGSNAG_NUM_GOROUTINES"); numGoroutines != "" {
num, err := strconv.Atoi(numGoroutines)
if err != nil {
envConfig.NumGoroutines = num
}
}
if maxPendingReports := os.Getenv("BUGSNAG_MAX_PENDING_REPORTS"); maxPendingReports != "" {
max, err := strconv.Atoi(maxPendingReports)
if err != nil {
envConfig.MaxPendingReports = max
}
}
if disablePanics := os.Getenv("BUGSNAG_DISABLE_PANIC_HANDLER"); disablePanics == "1" {
envConfig.PanicHandler = func() {}
}
Expand Down
1 change: 1 addition & 0 deletions v2/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/bugsnag/bugsnag-go/v2
go 1.15

require (
github.com/alitto/pond v1.8.3
github.com/bitly/go-simplejson v0.5.1
github.com/bugsnag/panicwrap v1.3.4
github.com/google/uuid v1.6.0
Expand Down
2 changes: 2 additions & 0 deletions v2/go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/alitto/pond v1.8.3 h1:ydIqygCLVPqIX/USe5EaV/aSRXTRXDEI9JwuDdu+/xs=
github.com/alitto/pond v1.8.3/go.mod h1:CmvIIGd5jKLasGI3D87qDkQxjzChdKMmnXMg3fG6M6Q=
github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow=
github.com/bitly/go-simplejson v0.5.1/go.mod h1:YOPVLzCfwK14b4Sff3oP1AmGhI9T9Vsg84etUnlyp+Q=
github.com/bugsnag/panicwrap v1.3.4 h1:A6sXFtDGsgU/4BLf5JT0o5uYg3EeKgGx3Sfs+/uk3pU=
Expand Down
6 changes: 4 additions & 2 deletions v2/report_publisher.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ func (*defaultReportPublisher) publishReport(p *payload) error {
return p.deliver()
}

go func(p *payload) {
if !asyncPool.TrySubmit(func() {
if err := p.deliver(); err != nil {
// Ensure that any errors are logged if they occur in a goroutine.
p.logf("bugsnag/defaultReportPublisher.publishReport: %v", err)
}
}(p)
}) {
return fmt.Errorf("failed to submit report to async pool")
}
return nil
}
Loading