now supports remote error reporting to sentry, if desired
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
16
httpserver/handlepanic.go
Normal file
16
httpserver/handlepanic.go
Normal 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")
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user