Skip to content

Commit c1b508f

Browse files
authored
feat: Add sentry for serve.Serve function (#54)
* feat: Add sentry for serve.Serve function plugin developers will be able to direct their plugin errors to their own projects on sentry as we dont want to monitor any community errors anyway. Also each plugin should go into it's own project on sentry because we are using server side grouping which are mostly project based.
1 parent 34bef4b commit c1b508f

File tree

5 files changed

+60
-13
lines changed

5 files changed

+60
-13
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ require (
2727

2828
require (
2929
github.com/davecgh/go-spew v1.1.1 // indirect
30+
github.com/getsentry/sentry-go v0.13.0 // indirect
3031
github.com/gogo/protobuf v1.3.2 // indirect
3132
github.com/golang/protobuf v1.5.2 // indirect
3233
github.com/inconshreveable/mousetrap v1.0.0 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.
3030
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
3131
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
3232
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
33+
github.com/getsentry/sentry-go v0.13.0 h1:20dgTiUSfxRB/EhMPtxcL9ZEbM1ZdR+W/7f7NWD+xWo=
34+
github.com/getsentry/sentry-go v0.13.0/go.mod h1:EOsfu5ZdvKPfeHYV6pTVQnsjfp30+XA7//UooKNumH0=
3335
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
3436
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
3537
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=

plugins/source.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ func NewSourcePlugin(name string, version string, tables []*schema.Table, newExe
8181
if err := p.validate(); err != nil {
8282
panic(err)
8383
}
84+
// if ignore error is not set on a table level set it with the plugin
85+
for _, table := range p.tables {
86+
if table.IgnoreError == nil && p.ignoreError != nil {
87+
table.IgnoreError = p.ignoreError
88+
}
89+
}
8490
return &p
8591
}
8692

schema/table.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"time"
99

1010
"github.com/cloudquery/plugin-sdk/helpers"
11+
"github.com/getsentry/sentry-go"
1112
"github.com/iancoleman/strcase"
1213
"github.com/thoas/go-funk"
1314
)
@@ -178,10 +179,15 @@ func (t Table) Resolve(ctx context.Context, meta ClientMeta, syncTime time.Time,
178179
if err := t.Resolver(ctx, meta, parent, res); err != nil {
179180
if t.IgnoreError != nil {
180181
if ignore, errType := t.IgnoreError(err); ignore {
181-
meta.Logger().Debug().Str("table_name", t.Name).TimeDiff("duration", time.Now(), startTime).Str("error_type", errType).Msg("table resolver finished with error")
182+
meta.Logger().Debug().Stack().Str("table_name", t.Name).TimeDiff("duration", time.Now(), startTime).Str("error_type", errType).Err(err).Msg("table resolver finished with error")
182183
return
183184
}
184185
}
186+
sentry.WithScope(func(scope *sentry.Scope) {
187+
scope.SetTag("table", t.Name)
188+
scope.SetLevel(sentry.LevelError)
189+
sentry.CaptureMessage(err.Error())
190+
})
185191
meta.Logger().Error().Str("table_name", t.Name).TimeDiff("duration", time.Now(), startTime).Err(err).Msg("table resolver finished with error")
186192
return
187193
}
@@ -190,7 +196,7 @@ func (t Table) Resolve(ctx context.Context, meta ClientMeta, syncTime time.Time,
190196
totalResources := 0
191197
// we want to check for data integrity
192198
// in the future we can do that as an optinoal feature via a flag
193-
pks := map[string]bool{}
199+
// pks := map[string]bool{}
194200
// each result is an array of interface{}
195201
for elem := range res {
196202
objects := helpers.InterfaceSlice(elem)
@@ -211,16 +217,16 @@ func (t Table) Resolve(ctx context.Context, meta ClientMeta, syncTime time.Time,
211217
if t.PostResourceResolver != nil {
212218
meta.Logger().Trace().Str("table_name", t.Name).Msg("post resource resolver started")
213219
if err := t.PostResourceResolver(ctx, meta, resource); err != nil {
214-
meta.Logger().Error().Str("table_name", t.Name).Err(err).Msg("post resource resolver finished with error")
220+
meta.Logger().Error().Str("table_name", t.Name).Stack().Err(err).Msg("post resource resolver finished with error")
215221
} else {
216222
meta.Logger().Trace().Str("table_name", t.Name).Msg("post resource resolver finished successfully")
217223
}
218224
}
219-
if pks[resource.PrimaryKeyValue()] {
220-
meta.Logger().Error().Str("table_name", t.Name).Str("primary_key", resource.PrimaryKeyValue()).Msg("duplicate primary key found")
221-
} else {
222-
pks[resource.PrimaryKeyValue()] = true
223-
}
225+
// if pks[resource.PrimaryKeyValue()] {
226+
// meta.Logger().Error().Str("table_name", t.Name).Str("primary_key", resource.PrimaryKeyValue()).Msg("duplicate primary key found")
227+
// } else {
228+
// pks[resource.PrimaryKeyValue()] = true
229+
// }
224230
resolvedResources <- resource
225231
for _, rel := range t.Relations {
226232
totalResources += rel.Resolve(ctx, meta, syncTime, resource, resolvedResources)

serve/serve.go

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ import (
55
"net"
66
"os"
77
"strings"
8+
"time"
89

910
"github.com/cloudquery/plugin-sdk/internal/pb"
1011
"github.com/cloudquery/plugin-sdk/internal/servers"
1112
"github.com/cloudquery/plugin-sdk/plugins"
13+
"github.com/getsentry/sentry-go"
1214
grpczerolog "github.com/grpc-ecosystem/go-grpc-middleware/providers/zerolog/v2"
1315
middleware "github.com/grpc-ecosystem/go-grpc-middleware/v2"
1416
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
@@ -23,13 +25,14 @@ type Options struct {
2325
// Required: Source or destination plugin to serve.
2426
SourcePlugin *plugins.SourcePlugin
2527
DestinationPlugin plugins.DestinationPlugin
28+
SentryDsn string
2629
}
2730

28-
// bufSize used for unit testing grpc server and client
29-
const testBufSize = 1024 * 1024
30-
3131
const (
3232
serveShort = `Start plugin server`
33+
// bufSize used for unit testing grpc server and client
34+
testBufSize = 1024 * 1024
35+
flushTimeout = 5 * time.Second
3336
)
3437

3538
// lis used for unit testing grpc server and client
@@ -38,6 +41,7 @@ var testListener *bufconn.Listener
3841
func newCmdServe(opts Options) *cobra.Command {
3942
var address string
4043
var network string
44+
var noSentry bool
4145
logLevel := newEnum([]string{"trace", "debug", "info", "warn", "error"}, "info")
4246
logFormat := newEnum([]string{"text", "json"}, "text")
4347
cmd := &cobra.Command{
@@ -56,6 +60,7 @@ func newCmdServe(opts Options) *cobra.Command {
5660
} else {
5761
logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout}).Level(zerologLevel)
5862
}
63+
5964
// opts.Plugin.Logger = logger
6065
var listener net.Listener
6166
if network == "test" {
@@ -75,17 +80,39 @@ func newCmdServe(opts Options) *cobra.Command {
7580
middleware.WithStreamServerChain(
7681
logging.StreamServerInterceptor(grpczerolog.InterceptorLogger(logger)),
7782
),
78-
// grpc.ChainStreamInterceptor(grpc_zero),
79-
// grpc.ChainUnaryInterceptor(),
8083
)
8184

85+
version := "development"
8286
if opts.SourcePlugin != nil {
8387
opts.SourcePlugin.SetLogger(logger)
8488
pb.RegisterSourceServer(s, &servers.SourceServer{Plugin: opts.SourcePlugin})
89+
version = opts.SourcePlugin.Version()
8590
}
8691
if opts.DestinationPlugin != nil {
8792
// opts.DestinationPlugin.Logger = logger
8893
pb.RegisterDestinationServer(s, &servers.DestinationServer{Plugin: opts.DestinationPlugin})
94+
version = opts.DestinationPlugin.Version()
95+
}
96+
97+
if !noSentry && version != "development" {
98+
err = sentry.Init(sentry.ClientOptions{
99+
Dsn: opts.SentryDsn,
100+
Release: opts.SourcePlugin.Version(),
101+
// https://docs.sentry.io/platforms/go/configuration/options/#removing-default-integrations
102+
Integrations: func(integrations []sentry.Integration) []sentry.Integration {
103+
var filteredIntegrations []sentry.Integration
104+
for _, integration := range integrations {
105+
if integration.Name() == "Modules" {
106+
continue
107+
}
108+
filteredIntegrations = append(filteredIntegrations, integration)
109+
}
110+
return filteredIntegrations
111+
},
112+
})
113+
if err != nil {
114+
log.Error().Err(err).Msg("Error initializing sentry")
115+
}
89116
}
90117

91118
logger.Info().Str("address", listener.Addr().String()).Msg("server listening")
@@ -99,6 +126,8 @@ func newCmdServe(opts Options) *cobra.Command {
99126
cmd.Flags().StringVar(&network, "network", "tcp", `the network must be "tcp", "tcp4", "tcp6", "unix" or "unixpacket"`)
100127
cmd.Flags().Var(logLevel, "log-level", fmt.Sprintf("log level. one of: %s", strings.Join(logLevel.Allowed, ",")))
101128
cmd.Flags().Var(logFormat, "log-format", fmt.Sprintf("log format. one of: %s", strings.Join(logFormat.Allowed, ",")))
129+
cmd.Flags().BoolVar(&noSentry, "no-sentry", false, "disable sentry")
130+
102131
return cmd
103132
}
104133

@@ -113,7 +142,10 @@ func newCmdRoot(opts Options) *cobra.Command {
113142

114143
func Serve(opts Options) {
115144
if err := newCmdRoot(opts).Execute(); err != nil {
145+
sentry.CaptureMessage(err.Error())
146+
sentry.Flush(flushTimeout)
116147
fmt.Println(err)
117148
os.Exit(1)
118149
}
150+
sentry.Flush(flushTimeout)
119151
}

0 commit comments

Comments
 (0)