Add support for error (panic) reporting through Sentry
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/internal/auth"
|
||||
@@ -28,6 +29,29 @@ func InitGeneralServices(logOpt *options.LogOpt, smtpOpt *options.SMTPOpt, jwtOp
|
||||
)
|
||||
}
|
||||
|
||||
func InitSentry(sentryOpt *options.SentryOpt) error {
|
||||
if sentryOpt.DSN == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return sentry.Init(sentry.ClientOptions{
|
||||
Dsn: sentryOpt.DSN,
|
||||
Debug: sentryOpt.Debug,
|
||||
AttachStacktrace: sentryOpt.AttachStacktrace,
|
||||
SampleRate: sentryOpt.SampleRate,
|
||||
MaxBreadcrumbs: sentryOpt.MaxBreadcrumbs,
|
||||
IgnoreErrors: nil,
|
||||
BeforeSend: nil,
|
||||
BeforeBreadcrumb: nil,
|
||||
Integrations: nil,
|
||||
Transport: nil,
|
||||
ServerName: sentryOpt.ServerName,
|
||||
Release: sentryOpt.Release,
|
||||
Dist: sentryOpt.Dist,
|
||||
Environment: sentryOpt.Environment,
|
||||
})
|
||||
}
|
||||
|
||||
func HandleError(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
|
||||
@@ -50,6 +50,11 @@ func fill(opt interface{}, pfix string) {
|
||||
continue
|
||||
}
|
||||
|
||||
if f.Kind() == reflect.Float32 {
|
||||
v.FieldByName(t.Name).SetFloat(float64(EnvFloat32(pfix, tag, float32(f.Float()))))
|
||||
continue
|
||||
}
|
||||
|
||||
panic("unsupported type/kind for field " + t.Name)
|
||||
|
||||
}
|
||||
@@ -94,6 +99,17 @@ func EnvInt(pfix, key string, def int) int {
|
||||
return def
|
||||
}
|
||||
|
||||
func EnvFloat32(pfix, key string, def float32) float32 {
|
||||
for _, key = range makeEnvKeys(pfix, key) {
|
||||
if val, has := os.LookupEnv(key); has {
|
||||
if i, err := cast.ToFloat32E(val); err == nil {
|
||||
return i
|
||||
}
|
||||
}
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
func EnvDuration(pfix, key string, def time.Duration) time.Duration {
|
||||
for _, key = range makeEnvKeys(pfix, key) {
|
||||
if val, has := os.LookupEnv(key); has {
|
||||
|
||||
+11
-4
@@ -6,9 +6,10 @@ import (
|
||||
|
||||
type (
|
||||
HTTPOpt struct {
|
||||
Addr string `env:"HTTP_ADDR"`
|
||||
Logging bool `env:"HTTP_LOG_REQUESTS"`
|
||||
Tracing bool `env:"HTTP_ERROR_TRACING"`
|
||||
Addr string `env:"HTTP_ADDR"`
|
||||
LogRequest bool `env:"HTTP_LOG_REQUEST"`
|
||||
LogResponse bool `env:"HTTP_LOG_RESPONSE"`
|
||||
Tracing bool `env:"HTTP_ERROR_TRACING"`
|
||||
|
||||
EnableVersionRoute bool `env:"HTTP_ENABLE_VERSION_ROUTE"`
|
||||
EnableDebugRoute bool `env:"HTTP_ENABLE_DEBUG_ROUTE"`
|
||||
@@ -17,13 +18,16 @@ type (
|
||||
MetricsServiceLabel string `env:"HTTP_METRICS_NAME"`
|
||||
MetricsUsername string `env:"HTTP_METRICS_USERNAME"`
|
||||
MetricsPassword string `env:"HTTP_METRICS_PASSWORD"`
|
||||
|
||||
EnablePanicReporting bool `env:"HTTP_REPORT_PANIC"`
|
||||
}
|
||||
)
|
||||
|
||||
func HTTP(pfix string) (o *HTTPOpt) {
|
||||
o = &HTTPOpt{
|
||||
Addr: ":80",
|
||||
Logging: true,
|
||||
LogRequest: false,
|
||||
LogResponse: false,
|
||||
Tracing: false,
|
||||
EnableVersionRoute: true,
|
||||
EnableDebugRoute: false,
|
||||
@@ -31,6 +35,9 @@ func HTTP(pfix string) (o *HTTPOpt) {
|
||||
MetricsServiceLabel: "corteza",
|
||||
MetricsUsername: "metrics",
|
||||
|
||||
// Reports panics to Sentry throught HTTP middleware
|
||||
EnablePanicReporting: true,
|
||||
|
||||
// Setting metrics password to random string to prevent security accidents...
|
||||
MetricsPassword: string(rand.Bytes(5)),
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza-server/internal/version"
|
||||
)
|
||||
|
||||
type (
|
||||
SentryOpt struct {
|
||||
DSN string `env:"SENTRY_DSN"`
|
||||
|
||||
Debug bool `env:"SENTRY_DEBUG"`
|
||||
AttachStacktrace bool `env:"SENTRY_ATTACH_STACKTRACE"`
|
||||
SampleRate float32 `env:"SENTRY_SAMPLE_RATE"`
|
||||
MaxBreadcrumbs int `env:"SENTRY_MAX_BREADCRUMBS"`
|
||||
|
||||
ServerName string `env:"SENTRY_SERVERNAME"`
|
||||
Release string `env:"SENTRY_RELEASE"`
|
||||
Dist string `env:"SENTRY_DIST"`
|
||||
Environment string `env:"SENTRY_ENVIRONMENT"`
|
||||
}
|
||||
)
|
||||
|
||||
func Sentry(pfix string) (o *SentryOpt) {
|
||||
o = &SentryOpt{
|
||||
AttachStacktrace: true,
|
||||
MaxBreadcrumbs: 0,
|
||||
|
||||
Release: version.Version,
|
||||
}
|
||||
|
||||
fill(o, pfix)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package cli
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -48,6 +49,7 @@ type (
|
||||
HttpClientOpt *options.HttpClientOpt
|
||||
DbOpt *options.DBOpt
|
||||
ProvisionOpt *options.ProvisionOpt
|
||||
SentryOpt *options.SentryOpt
|
||||
|
||||
// DB Connection name, defaults to ServiceName
|
||||
DatabaseName string
|
||||
@@ -179,6 +181,7 @@ func (c *Config) Init() {
|
||||
c.HttpClientOpt = options.HttpClient(c.EnvPrefix)
|
||||
c.DbOpt = options.DB(c.ServiceName)
|
||||
c.ProvisionOpt = options.Provision(c.ServiceName)
|
||||
c.SentryOpt = options.Sentry(c.EnvPrefix)
|
||||
|
||||
if c.RootCommandDBSetup == nil {
|
||||
c.RootCommandDBSetup = Runners{func(ctx context.Context, cmd *cobra.Command, c *Config) (err error) {
|
||||
@@ -213,6 +216,11 @@ func (c *Config) MakeCLI(ctx context.Context) (cmd *cobra.Command) {
|
||||
Use: c.RootCommandName,
|
||||
TraverseChildren: true,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
if err = InitSentry(c.SentryOpt); err != nil {
|
||||
c.Log.Error("could not initialize Sentry", zap.Error(err))
|
||||
}
|
||||
|
||||
defer sentry.Recover()
|
||||
InitGeneralServices(c.LogOpt, c.SmtpOpt, c.JwtOpt, c.HttpClientOpt)
|
||||
|
||||
err = c.RootCommandDBSetup.Run(ctx, cmd, c)
|
||||
@@ -232,6 +240,7 @@ func (c *Config) MakeCLI(ctx context.Context) (cmd *cobra.Command) {
|
||||
}
|
||||
|
||||
serveApiCmd := c.ApiServer.Command(ctx, c.ApiServerCommandName, c.EnvPrefix, func(ctx context.Context) (err error) {
|
||||
defer sentry.Recover()
|
||||
return c.ApiServerPreRun.Run(ctx, cmd, c)
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user