diff --git a/config/config.go b/config/config.go index 9b7461ab..e74734de 100644 --- a/config/config.go +++ b/config/config.go @@ -28,6 +28,7 @@ var ( PGDSN string PhoneNumberReport phonenumbers.PhoneNumber PhoneNumberReportStr string + SentryDSN string TwilioAuthToken string TwilioAccountSID string TwilioMessagingServiceSID string @@ -139,6 +140,10 @@ func Parse() (err error) { } PhoneNumberReport = *p + SentryDSN = os.Getenv("SENTRY_DSN") + if SentryDSN == "" { + return fmt.Errorf("You must specify a non-empty SENTRY_DSN") + } TwilioAccountSID = os.Getenv("TWILIO_ACCOUNT_SID") if TwilioAccountSID == "" { return fmt.Errorf("You must specify a non-empty TWILIO_ACCOUNT_SID") diff --git a/go.mod b/go.mod index 2f610adf..5b735d71 100644 --- a/go.mod +++ b/go.mod @@ -40,6 +40,8 @@ require ( github.com/beevik/etree v1.1.0 // indirect github.com/buger/jsonparser v1.1.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect + github.com/getsentry/sentry-go v0.42.0 // indirect + github.com/getsentry/sentry-go/zerolog v0.42.0 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/golang-jwt/jwt/v5 v5.2.2 // indirect github.com/golang/mock v1.6.0 // indirect diff --git a/go.sum b/go.sum index 2a0b0ed7..09a450ae 100644 --- a/go.sum +++ b/go.sum @@ -59,6 +59,10 @@ github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0o github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/getsentry/sentry-go v0.42.0 h1:eeFMACuZTbUQf90RE8dE4tXeSe4CZyfvR1MBL7RLEt8= +github.com/getsentry/sentry-go v0.42.0/go.mod h1:eRXCoh3uvmjQLY6qu63BjUZnaBu5L5WhMV1RwYO8W5s= +github.com/getsentry/sentry-go/zerolog v0.42.0 h1:FDvGyxz7IGW8bzomAJuEFiEToq89PrOLsxQ28XYLsDc= +github.com/getsentry/sentry-go/zerolog v0.42.0/go.mod h1:cY2lSFOu3cJXhvJuUfcBeyq19QRPYt6OIAvpdwhKkfE= github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE= github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= diff --git a/main.go b/main.go index 3c695f90..5a6fe7d0 100644 --- a/main.go +++ b/main.go @@ -19,6 +19,9 @@ import ( "github.com/Gleipnir-Technology/nidus-sync/platform/text" "github.com/Gleipnir-Technology/nidus-sync/public-report" nidussync "github.com/Gleipnir-Technology/nidus-sync/sync" + "github.com/getsentry/sentry-go" + sentryhttp "github.com/getsentry/sentry-go/http" + "github.com/getsentry/sentry-go/zerolog" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/hostrouter" @@ -27,38 +30,75 @@ import ( ) func main() { - zerolog.TimeFieldFormat = zerolog.TimeFormatUnix - log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) - err := config.Parse() if err != nil { log.Error().Err(err).Msg("Failed to parse config") os.Exit(1) } log.Info().Msg("Starting...") + + err = sentry.Init(sentry.ClientOptions{ + Debug: !config.IsProductionEnvironment(), + Dsn: config.SentryDSN, + EnableTracing: true, + SendDefaultPII: true, + TracesSampleRate: 1.0, + }) + if err != nil { + log.Error().Err(err).Msg("Failed to start sentry connection") + os.Exit(2) + } + defer sentry.Flush(2 * time.Second) + + sentryWriter, err := sentryzerolog.New(sentryzerolog.Config{ + ClientOptions: sentry.ClientOptions{ + Dsn: config.SentryDSN, + }, + Options: sentryzerolog.Options{ + Levels: []zerolog.Level{zerolog.ErrorLevel, zerolog.FatalLevel, zerolog.PanicLevel}, + WithBreadcrumbs: true, + FlushTimeout: 3 * time.Second, + }, + }) + if err != nil { + log.Fatal().Err(err).Msg("Failed to create sentry writer") + os.Exit(2) + } + defer sentryWriter.Close() + + zerolog.TimeFieldFormat = zerolog.TimeFormatUnix + log.Logger = log.Output(zerolog.MultiLevelWriter(zerolog.ConsoleWriter{Out: os.Stderr}, sentryWriter)) + err = db.InitializeDatabase(context.TODO(), config.PGDSN) if err != nil { log.Error().Err(err).Msg("Failed to connect to database") - os.Exit(2) + os.Exit(3) } err = email.LoadTemplates() if err != nil { log.Error().Err(err).Msg("Failed to load email templates") - os.Exit(3) + os.Exit(4) } err = text.StoreSources() if err != nil { log.Error().Err(err).Msg("Failed to store text source phone numbers") - os.Exit(4) + os.Exit(5) } router_logger := log.With().Logger() + sentryMiddleware := sentryhttp.New(sentryhttp.Options{ + Repanic: true, + }) r := chi.NewRouter() r.Use(LoggerMiddleware(&router_logger)) + r.Use(middleware.RequestID) r.Use(middleware.RealIP) + r.Use(middleware.Logger) + r.Use(middleware.Recoverer) + r.Use(sentryMiddleware.Handle) r.Use(auth.NewSessionManager().LoadAndSave) hr := hostrouter.New() @@ -81,7 +121,7 @@ func main() { err = llm.CreateOpenAIClient(ctx, &openai_logger) if err != nil { log.Error().Err(err).Msg("Failed to start openAI client") - os.Exit(5) + os.Exit(6) } background.Start(ctx) server := &http.Server{