feat: add Sentry setup
This commit is contained in:
+59
-10
@@ -9,12 +9,14 @@ import (
|
||||
"reflect"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql/handler"
|
||||
"github.com/99designs/gqlgen/graphql/playground"
|
||||
"github.com/alecthomas/kong"
|
||||
"github.com/apex/log"
|
||||
"github.com/apex/log/handlers/json"
|
||||
"github.com/getsentry/sentry-go"
|
||||
sentryhttp "github.com/getsentry/sentry-go/http"
|
||||
"github.com/rs/cors"
|
||||
"github.com/sparetimecoders/goamqp"
|
||||
@@ -30,21 +32,30 @@ import (
|
||||
"gitlab.com/unboundsoftware/schemas/store"
|
||||
)
|
||||
|
||||
var CLI struct {
|
||||
type CLI struct {
|
||||
AmqpURL string `name:"amqp-url" env:"AMQP_URL" help:"URL to use to connect to RabbitMQ" default:"amqp://user:password@localhost:5672/"`
|
||||
Port int `name:"port" env:"PORT" help:"Listen-port for GraphQL API" default:"8080"`
|
||||
APIKey string `name:"api-key" env:"API_KEY" help:"The API-key that is required"`
|
||||
LogLevel string `name:"log-level" env:"LOG_LEVEL" help:"The level of logging to use (debug, info, warn, error, fatal)" default:"info"`
|
||||
DatabaseURL string `name:"postgres-url" env:"POSTGRES_URL" help:"URL to use to connect to Postgres" default:"postgres://postgres:postgres@:5432/schemas?sslmode=disable"`
|
||||
DatabaseDriverName string `name:"db-driver" env:"DB_DRIVER" help:"Driver to use to connect to db" default:"postgres"`
|
||||
SentryConfig
|
||||
}
|
||||
|
||||
type SentryConfig struct {
|
||||
DSN string `name:"sentry-dsn" env:"SENTRY_DSN" help:"Sentry dsn" default:""`
|
||||
Environment string `name:"sentry-environment" env:"SENTRY_ENVIRONMENT" help:"Sentry environment" default:"development"`
|
||||
}
|
||||
|
||||
var buildVersion = "none"
|
||||
|
||||
const serviceName = "schemas"
|
||||
|
||||
func main() {
|
||||
_ = kong.Parse(&CLI)
|
||||
var cli CLI
|
||||
_ = kong.Parse(&cli)
|
||||
log.SetHandler(json.New(os.Stdout))
|
||||
log.SetLevelFromString(CLI.LogLevel)
|
||||
log.SetLevelFromString(cli.LogLevel)
|
||||
logger := log.WithField("service", serviceName)
|
||||
closeEvents := make(chan error)
|
||||
|
||||
@@ -52,16 +63,22 @@ func main() {
|
||||
closeEvents,
|
||||
logger,
|
||||
ConnectAMQP,
|
||||
cli,
|
||||
); err != nil {
|
||||
logger.WithError(err).Error("process error")
|
||||
}
|
||||
}
|
||||
|
||||
func start(closeEvents chan error, logger *log.Entry, connectToAmqpFunc func(url string) (Connection, error)) error {
|
||||
func start(closeEvents chan error, logger *log.Entry, connectToAmqpFunc func(url string) (Connection, error), cli CLI) error {
|
||||
if err := setupSentry(logger, cli.SentryConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
defer sentry.Flush(2 * time.Second)
|
||||
|
||||
rootCtx, rootCancel := context.WithCancel(context.Background())
|
||||
defer rootCancel()
|
||||
|
||||
db, err := store.SetupDB(CLI.DatabaseDriverName, CLI.DatabaseURL)
|
||||
db, err := store.SetupDB(cli.DatabaseDriverName, cli.DatabaseURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to setup DB: %v", err)
|
||||
}
|
||||
@@ -85,7 +102,7 @@ func start(closeEvents chan error, logger *log.Entry, connectToAmqpFunc func(url
|
||||
return fmt.Errorf("failed to create event publisher: %v", err)
|
||||
}
|
||||
amqp.New(eventPublisher)
|
||||
conn, err := connectToAmqpFunc(CLI.AmqpURL)
|
||||
conn, err := connectToAmqpFunc(cli.AmqpURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect to AMQP: %v", err)
|
||||
}
|
||||
@@ -121,7 +138,7 @@ func start(closeEvents chan error, logger *log.Entry, connectToAmqpFunc func(url
|
||||
logger.Info("Started")
|
||||
|
||||
mux := http.NewServeMux()
|
||||
httpSrv := &http.Server{Addr: fmt.Sprintf(":%d", CLI.Port), Handler: mux}
|
||||
httpSrv := &http.Server{Addr: fmt.Sprintf(":%d", cli.Port), Handler: mux}
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
|
||||
@@ -179,7 +196,7 @@ func start(closeEvents chan error, logger *log.Entry, connectToAmqpFunc func(url
|
||||
Resolvers: resolver,
|
||||
Complexity: generated.ComplexityRoot{},
|
||||
}
|
||||
apiKeyMiddleware := middleware.NewApiKey(CLI.APIKey, logger)
|
||||
apiKeyMiddleware := middleware.NewApiKey(cli.APIKey, logger)
|
||||
config.Directives.HasApiKey = apiKeyMiddleware.Directive
|
||||
srv := handler.NewDefaultServer(generated.NewExecutableSchema(
|
||||
config,
|
||||
@@ -187,10 +204,10 @@ func start(closeEvents chan error, logger *log.Entry, connectToAmqpFunc func(url
|
||||
|
||||
sentryHandler := sentryhttp.New(sentryhttp.Options{Repanic: true})
|
||||
mux.Handle("/", sentryHandler.HandleFunc(playground.Handler("GraphQL playground", "/query")))
|
||||
mux.Handle("/health", sentryHandler.HandleFunc(healthFunc))
|
||||
mux.Handle("/health", http.HandlerFunc(healthFunc))
|
||||
mux.Handle("/query", cors.AllowAll().Handler(sentryHandler.Handle(apiKeyMiddleware.Handler(srv))))
|
||||
|
||||
logger.Infof("connect to http://localhost:%d/ for GraphQL playground", CLI.Port)
|
||||
logger.Infof("connect to http://localhost:%d/ for GraphQL playground", cli.Port)
|
||||
|
||||
if err := httpSrv.ListenAndServe(); err != nil {
|
||||
logger.WithError(err).Error("listen http")
|
||||
@@ -206,6 +223,38 @@ func healthFunc(w http.ResponseWriter, _ *http.Request) {
|
||||
_, _ = w.Write([]byte("OK"))
|
||||
}
|
||||
|
||||
func setupSentry(logger log.Interface, args SentryConfig) error {
|
||||
if args.Environment == "" {
|
||||
return fmt.Errorf("no Sentry environment supplied, exiting")
|
||||
}
|
||||
cfg := sentry.ClientOptions{
|
||||
Dsn: args.DSN,
|
||||
Environment: args.Environment,
|
||||
Release: fmt.Sprintf("%s-%s", serviceName, buildVersion),
|
||||
}
|
||||
switch args.Environment {
|
||||
case "development":
|
||||
cfg.Debug = true
|
||||
cfg.EnableTracing = false
|
||||
cfg.TracesSampleRate = 0.0
|
||||
case "production":
|
||||
if args.DSN == "" {
|
||||
return fmt.Errorf("no DSN supplied for non-dev environment, exiting")
|
||||
}
|
||||
cfg.Debug = false
|
||||
cfg.EnableTracing = true
|
||||
cfg.TracesSampleRate = 1.0
|
||||
default:
|
||||
return fmt.Errorf("illegal environment %s", args.Environment)
|
||||
}
|
||||
|
||||
if err := sentry.Init(cfg); err != nil {
|
||||
return fmt.Errorf("sentry setup: %w", err)
|
||||
}
|
||||
logger.Infof("configured Sentry for env: %s", args.Environment)
|
||||
return nil
|
||||
}
|
||||
|
||||
func ConnectAMQP(url string) (Connection, error) {
|
||||
return goamqp.NewFromURL(serviceName, url)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ spec:
|
||||
data:
|
||||
POSTGRES_URL: "postgres://{{ .DB_USERNAME }}:{{ .DB_PASSWORD }}@{{ .DB_HOST }}:{{ .DB_PORT }}/schemas?sslmode=disable"
|
||||
API_KEY: "{{ .API_KEY }}"
|
||||
SENTRY_DSN: "{{ .SENTRY_DSN }}"
|
||||
SENTRY_ENVIRONMENT: "{{ .SENTRY_ENVIRONMENT }}"
|
||||
dataFrom:
|
||||
- extract:
|
||||
key: services/schemas
|
||||
|
||||
Reference in New Issue
Block a user