Skip to content

Commit 168df34

Browse files
committed
bugsnag: Initial commit of bugsnag module.
1 parent d53c509 commit 168df34

File tree

2 files changed

+152
-0
lines changed

2 files changed

+152
-0
lines changed

bugsnag/logger.go

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package bugsnag
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
7+
goerrors "github.com/go-errors/errors"
8+
9+
"github.com/octavore/nagax/logger"
10+
"github.com/octavore/nagax/util/errors"
11+
)
12+
13+
type bugsnagLogger struct {
14+
logger.Logger
15+
Notify func(error, ...interface{}) // Notify is the module.Notify which wraps bugsnag.Notify with added print
16+
}
17+
18+
func (b *bugsnagLogger) Error(args ...interface{}) {
19+
if len(args) == 1 {
20+
if originalErr, ok := args[0].(error); ok {
21+
// this handles case with only one error arugment
22+
var req *http.Request
23+
if re, ok := originalErr.(GetRequestable); ok {
24+
req = re.GetRequest()
25+
}
26+
if err, ok := originalErr.(*goerrors.Error); ok {
27+
if re, ok := err.Err.(GetRequestable); ok && re.GetRequest() != nil {
28+
req = re.GetRequest()
29+
}
30+
} else {
31+
originalErr = errors.WrapS(originalErr, 1)
32+
}
33+
if req != nil {
34+
b.Notify(originalErr, req)
35+
} else {
36+
b.Notify(originalErr)
37+
}
38+
return
39+
}
40+
}
41+
b.Notify(errors.New(fmt.Sprint(args...)))
42+
}
43+
44+
func (b *bugsnagLogger) Errorf(format string, args ...interface{}) {
45+
msg := fmt.Sprintf(format, args...)
46+
b.Error(msg)
47+
}

bugsnag/module.go

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
bugsnag module configures bugsnag and modifies the logger so logger.Error logs to bugsnag as well.
3+
*/
4+
package bugsnag
5+
6+
import (
7+
"fmt"
8+
"net/http"
9+
10+
bugsnagGo "github.com/bugsnag/bugsnag-go"
11+
goerrors "github.com/go-errors/errors"
12+
"github.com/octavore/naga/service"
13+
14+
"github.com/octavore/nagax/config"
15+
"github.com/octavore/nagax/logger"
16+
"github.com/octavore/nagax/util/errors"
17+
)
18+
19+
type Config struct {
20+
Bugsnag *struct {
21+
APIKey string `json:"api_key"`
22+
} `json:"bugsnag"`
23+
}
24+
25+
type Module struct {
26+
Logger *logger.Module
27+
Config *config.Module
28+
29+
config Config
30+
bugsnagEnabled bool
31+
originalErrorf func(format string, args ...interface{})
32+
}
33+
34+
var _ service.Module = &Module{}
35+
36+
// AppVersion is set via a build flag
37+
var AppVersion = ""
38+
39+
func (m *Module) Init(c *service.Config) {
40+
c.Setup = func() error {
41+
err := m.Config.ReadConfig(&m.config)
42+
if err != nil {
43+
return err
44+
}
45+
m.originalErrorf = m.Logger.Logger.Errorf
46+
if m.config.Bugsnag != nil {
47+
m.Logger.Info("bugsnag enabled: ", AppVersion)
48+
// note: this forces the app to restart
49+
bugsnagGo.Configure(bugsnagGo.Configuration{
50+
ReleaseStage: c.Env().String(),
51+
ProjectPackages: []string{
52+
"github.com/octavore/*",
53+
},
54+
APIKey: m.config.Bugsnag.APIKey,
55+
Logger: Printfer(m.Logger.Infof), // may be redundant with our own logging
56+
AppVersion: AppVersion,
57+
})
58+
m.bugsnagEnabled = true
59+
}
60+
61+
m.Logger.Logger = &bugsnagLogger{Logger: m.Logger.Logger, Notify: m.Notify}
62+
return nil
63+
}
64+
}
65+
66+
type Printfer func(fmt string, args ...interface{})
67+
68+
func (p Printfer) Printf(fmt string, args ...interface{}) {
69+
p(fmt, args...)
70+
}
71+
72+
type GetRequestable interface {
73+
GetRequest() *http.Request
74+
}
75+
76+
type ErrorStackable interface {
77+
ErrorStack() string
78+
}
79+
80+
// Notify bugsnag, note that m.Logger.Error calls Notify so Notify musn't call m.Logger.Error
81+
func (m *Module) Notify(err error, rawData ...interface{}) {
82+
rawData = append(rawData, bugsnagGo.SeverityError)
83+
if re, ok := err.(GetRequestable); ok && re.GetRequest() != nil {
84+
rawData = append(rawData, re.GetRequest())
85+
}
86+
errType := "error"
87+
if err2, ok := err.(*goerrors.Error); ok {
88+
errType = fmt.Sprintf("%T", err2.Err)
89+
rawData = append(rawData, bugsnagGo.ErrorClass{Name: errType})
90+
}
91+
92+
if !m.bugsnagEnabled {
93+
if re, ok := err.(ErrorStackable); ok {
94+
m.originalErrorf("bugsnag fake: %T %v %#v\n%s", errType, err, rawData, re.ErrorStack())
95+
} else {
96+
m.originalErrorf("bugsnag fake: %T %v %#v", errType, err, rawData)
97+
}
98+
return
99+
}
100+
e := bugsnagGo.Notify(err, rawData...)
101+
if e != nil {
102+
m.originalErrorf("%v", errors.WrapS(e, 2))
103+
}
104+
// note: bugsnag does its own logging
105+
}

0 commit comments

Comments
 (0)