now supports remote error reporting to sentry, if desired
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
2020-09-30 21:59:20 -07:00
parent ae232ea6ab
commit 876f2d9a98
9 changed files with 251 additions and 55 deletions

16
httpserver/handlepanic.go Normal file
View File

@@ -0,0 +1,16 @@
package httpserver
import (
"fmt"
"net/http"
)
// CHANGEME you probably want to remove this,
// this is just a handler/route that throws a panic to test
// sentry events.
func (s *server) handlePanic() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
panic("y tho")
fmt.Fprintf(w, "hello world")
}
}

View File

@@ -1,7 +1,6 @@
package httpserver
import (
"context"
"encoding/json"
"fmt"
"net/http"
@@ -49,24 +48,3 @@ func (s *server) decodeJSON(w http.ResponseWriter, r *http.Request, v interface{
func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
s.router.ServeHTTP(w, r)
}
func (s *server) cleanupForExit() {
s.log.Info().Msg("cleaning up")
// FIXME unimplemented
// close database connections or whatever
}
func (s *server) cleanShutdown() {
// initiate clean shutdown
s.exitCode = 0
ctxShutdown, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second)
if err := s.httpServer.Shutdown(ctxShutdown); err != nil {
s.log.Error().
Err(err).
Msg("server clean shutdown failed")
}
if shutdownCancel != nil {
shutdownCancel()
}
s.cleanupForExit()
}

View File

@@ -14,21 +14,33 @@ import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spf13/viper"
"github.com/getsentry/sentry-go"
// spooky action at a distance!
// this populates the environment
// from a ./.env file automatically
// for development configuration.
// .env contents should be things like
// `DBURL=postgres://user:pass@.../`
// (without the backticks, of course)
_ "github.com/joho/godotenv/autoload"
)
type server struct {
appname string
version string
buildarch string
databaseURL string
startupTime time.Time
port int
exitCode int
log *zerolog.Logger
ctx context.Context
cancelFunc context.CancelFunc
httpServer *http.Server
router *mux.Router
appname string
version string
buildarch string
databaseURL string
startupTime time.Time
port int
exitCode int
sentryEnabled bool
log *zerolog.Logger
ctx context.Context
cancelFunc context.CancelFunc
httpServer *http.Server
router *mux.Router
}
func NewServer(options ...func(s *server)) *server {
@@ -52,6 +64,14 @@ func Run(appname, version, buildarch string) int {
i.buildarch = buildarch
})
// this does nothing if SENTRY_DSN is unset in env.
s.enableSentry()
// TODO remove:
if s.sentryEnabled {
sentry.CaptureMessage("It works!")
}
s.configure()
s.setupLogging()
@@ -61,6 +81,23 @@ func Run(appname, version, buildarch string) int {
return s.serve()
}
func (s *server) enableSentry() {
sentryDSN := os.Getenv("SENTRY_DSN")
if sentryDSN == "" {
s.sentryEnabled = false
return
}
err := sentry.Init(sentry.ClientOptions{
Dsn: sentryDSN,
Release: fmt.Sprintf("%s-%s", s.appname, s.version),
})
if err != nil {
log.Fatal().Err(err).Msg("sentry init failure")
}
s.sentryEnabled = true
}
func (s *server) serve() int {
s.ctx, s.cancelFunc = context.WithCancel(context.Background())
@@ -90,6 +127,32 @@ func (s *server) serve() int {
}
func (s *server) cleanupForExit() {
s.log.Info().Msg("cleaning up")
// FIXME unimplemented
// close database connections or whatever
}
func (s *server) cleanShutdown() {
// initiate clean shutdown
s.exitCode = 0
ctxShutdown, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second)
if err := s.httpServer.Shutdown(ctxShutdown); err != nil {
s.log.Error().
Err(err).
Msg("server clean shutdown failed")
}
if shutdownCancel != nil {
shutdownCancel()
}
s.cleanupForExit()
if s.sentryEnabled {
sentry.Flush(2 * time.Second)
}
}
func (s *server) uptime() time.Duration {
return time.Since(s.startupTime)
}
@@ -97,8 +160,10 @@ func (s *server) uptime() time.Duration {
func (s *server) configure() {
viper.SetConfigName(s.appname)
viper.SetConfigType("yaml")
viper.AddConfigPath(fmt.Sprintf("/etc/%s", s.appname)) // path to look for the config file in
viper.AddConfigPath(fmt.Sprintf("$HOME/.config/%s", s.appname)) // call multiple times to add many search paths
// path to look for the config file in:
viper.AddConfigPath(fmt.Sprintf("/etc/%s", s.appname))
// call multiple times to add many search paths:
viper.AddConfigPath(fmt.Sprintf("$HOME/.config/%s", s.appname))
//viper.SetEnvPrefix(strings.ToUpper(s.appname))
viper.AutomaticEnv()

View File

@@ -1,6 +1,7 @@
package httpserver
import (
sentryhttp "github.com/getsentry/sentry-go/http"
"github.com/gorilla/mux"
)
@@ -14,9 +15,22 @@ func (s *server) routes() {
// if you want to use a general purpose middleware (http.Handler
// wrapper) on a specific HandleFunc route, you need to take the
// .ServeHTTP of the http.Handler to get its HandleFunc, viz:
s.router.HandleFunc("/login", authMiddleware(s.handleLogin()).ServeHTTP).Methods("GET")
s.router.HandleFunc(
"/login",
authMiddleware(s.handleLogin()).ServeHTTP
).Methods("GET")
s.router.HandleFunc("/.well-known/healthcheck.json", s.handleHealthCheck()).Methods("GET")
s.router.HandleFunc(
"/.well-known/healthcheck.json",
s.handleHealthCheck(),
).Methods("GET")
// route that panics for testing
// CHANGEME remove this
s.router.HandleFunc(
"/panic",
s.handlePanic()
).Methods("GET")
// the Gorilla mux .Use() takes a http.Handler wrapper func, like
// most things that deal with "middlewares" like alice et c, and
@@ -24,4 +38,11 @@ func (s *server) routes() {
// (you can .Use() more than one) will be applied to every request
// into the service.
s.router.Use(s.LoggingMiddleware())
// this adds a sentry reporting middleware if and only if
// sentry is enabled via setting of SENTRY_DSN in env.
if s.sentryEnabled {
sentryHandler := sentryhttp.New(sentryhttp.Options{})
s.router.Use(sentryHandler.Handle)
}
}