now does logging and auth middleware

This commit is contained in:
Jeffrey Paul 2020-09-30 00:48:56 -07:00
parent eaa2f2b929
commit 7bf1aee60f
7 changed files with 122 additions and 28 deletions

1
go.mod
View File

@ -4,6 +4,7 @@ go 1.15
require (
github.com/gorilla/mux v1.8.0
github.com/justinas/alice v1.2.0
github.com/rs/zerolog v1.20.0
github.com/spf13/viper v1.7.1
)

2
go.sum
View File

@ -96,6 +96,8 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo=
github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=

12
httpserver/handleadmin.go Normal file
View File

@ -0,0 +1,12 @@
package httpserver
import (
"fmt"
"net/http"
)
func (s *server) handleLogin() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello login")
}
}

View File

@ -6,8 +6,6 @@ import (
"fmt"
"net/http"
"time"
"github.com/rs/zerolog/log"
)
func (s *server) serveUntilShutdown() {
@ -24,9 +22,9 @@ func (s *server) serveUntilShutdown() {
// this does any necessary setup in each handler
s.routes()
log.Info().Str("listenaddr", listenAddr).Msg("http begin listen")
s.log.Info().Str("listenaddr", listenAddr).Msg("http begin listen")
if err := s.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Error().Msgf("listen:%+s\n", err)
s.log.Error().Msgf("listen:%+s\n", err)
if s.cancelFunc != nil {
s.cancelFunc()
}
@ -39,22 +37,21 @@ func (s *server) respondJSON(w http.ResponseWriter, r *http.Request, data interf
if data != nil {
err := json.NewEncoder(w).Encode(data)
if err != nil {
log.Error().Err(err).Msg("json encode error")
s.log.Error().Err(err).Msg("json encode error")
}
}
}
func (s *server) decodeJSON(w http.ResponseWriter, r *http.Request, v interface{}) error {
func (s *server) decodeJSON(w http.ResponseWriter, r *http.Request, v interface{}) error { // nolint
return json.NewDecoder(r.Body).Decode(v)
}
func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// FIXME logging or any other general purpose middleware here
s.router.ServeHTTP(w, r)
}
func (s *server) cleanupForExit() {
log.Info().Msg("cleaning up")
s.log.Info().Msg("cleaning up")
// FIXME unimplemented
// close database connections or whatever
}
@ -62,11 +59,14 @@ func (s *server) cleanupForExit() {
func (s *server) cleanShutdown() {
// initiate clean shutdown
s.exitCode = 0
ctxShutdown, _ := context.WithTimeout(context.Background(), 5*time.Second)
ctxShutdown, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second)
if err := s.httpServer.Shutdown(ctxShutdown); err != nil {
log.Error().
s.log.Error().
Err(err).
Msg("server clean shutdown failed")
}
if shutdownCancel != nil {
shutdownCancel()
}
s.cleanupForExit()
}

View File

@ -66,12 +66,12 @@ func (s *server) serve() int {
// signal watcher
go func() {
c := make(chan os.Signal)
c := make(chan os.Signal, 1)
signal.Ignore(syscall.SIGPIPE)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
// block and wait for signal
sig := <-c
log.Info().Msgf("signal received: %+v", sig)
s.log.Info().Msgf("signal received: %+v", sig)
if s.cancelFunc != nil {
// cancelling the main context will trigger a clean
// shutdown.
@ -81,15 +81,12 @@ func (s *server) serve() int {
go s.serveUntilShutdown()
for {
select {
case <-s.ctx.Done():
//aforementioned clean shutdown upon main context
//cancellation
s.cleanShutdown()
return s.exitCode
}
for range s.ctx.Done() {
//aforementioned clean shutdown upon main context
//cancellation
}
s.cleanShutdown()
return s.exitCode
}
@ -130,7 +127,6 @@ func (s *server) setupLogging() {
zerolog.TimestampFunc = func() time.Time {
return time.Now().UTC()
}
log.Logger = log.With().Caller().Logger()
zerolog.SetGlobalLevel(zerolog.InfoLevel)
tty := false
@ -138,11 +134,10 @@ func (s *server) setupLogging() {
tty = true
}
log.Info().Bool("tty", tty).Msg("tty detection")
var writers []io.Writer
if tty {
// this does cool colorization for console/dev
consoleWriter := zerolog.NewConsoleWriter(
func(w *zerolog.ConsoleWriter) {
// Customize time format
@ -152,10 +147,13 @@ func (s *server) setupLogging() {
writers = append(writers, consoleWriter)
} else {
// log json in prod for the machines
writers = append(writers, os.Stdout)
}
/*
// this is how you log to a file, if you do that
// sort of thing still
logfile := viper.GetString("Logfile")
if logfile != "" {
logfileDir := filepath.Dir(logfile)
@ -170,24 +168,24 @@ func (s *server) setupLogging() {
}
writers = append(writers, hp.logfh)
}
*/
multi := zerolog.MultiLevelWriter(writers...)
logger := zerolog.New(multi).With().Timestamp().Logger().With().Caller().Logger()
log.Logger = logger
s.log = &logger
//log.Logger = logger
if viper.GetBool("debug") {
zerolog.SetGlobalLevel(zerolog.DebugLevel)
log.Debug().Bool("debug", true).Send()
s.log.Debug().Bool("debug", true).Send()
}
s.identify()
}
func (s *server) identify() {
log.Info().
s.log.Info().
Str("appname", s.appname).
Str("version", s.version).
Str("buildarch", s.buildarch).

75
httpserver/middlewares.go Normal file
View File

@ -0,0 +1,75 @@
package httpserver
import (
"net"
"net/http"
"time"
)
// the following is from
// https://learning-cloud-native-go.github.io/docs/a6.adding_zerolog_logger/
func ipFromHostPort(hp string) string {
h, _, err := net.SplitHostPort(hp)
if err != nil {
return ""
}
if len(h) > 0 && h[0] == '[' {
return h[1 : len(h)-1]
}
return h
}
type loggingResponseWriter struct {
http.ResponseWriter
statusCode int
}
func NewLoggingResponseWriter(w http.ResponseWriter) *loggingResponseWriter {
return &loggingResponseWriter{w, http.StatusOK}
}
func (lrw *loggingResponseWriter) WriteHeader(code int) {
lrw.statusCode = code
lrw.ResponseWriter.WriteHeader(code)
}
// type Middleware func(http.Handler) http.Handler
// this returns a Middleware that is designed to do every request through the
// mux, note the signature:
func (s *server) LoggingMiddleware() func(http.Handler) http.Handler {
// FIXME this should use https://github.com/google/go-cloud/blob/master/server/requestlog/requestlog.go
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
lrw := NewLoggingResponseWriter(w)
defer func() {
latency := time.Since(start)
s.log.Info().
Time("request_start", start).
Str("method", r.Method).
Str("url", r.URL.String()).
Str("useragent", r.UserAgent()).
Str("referer", r.Referer()).
Str("proto", r.Proto).
Str("remoteIP", ipFromHostPort(r.RemoteAddr)).
Int("status", lrw.statusCode).
Dur("latency", latency).
Send()
}()
next.ServeHTTP(lrw, r)
})
}
}
// this is the HandleFunc wrapper type for a *specific route*, it doesn't go
// on the mux.
func (s *server) AuthMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
s.log.Info().Msg("AUTH: before request")
next(w, r)
}
}

View File

@ -1,9 +1,15 @@
package httpserver
import "github.com/gorilla/mux"
import (
"github.com/gorilla/mux"
)
func (s *server) routes() {
s.router = mux.NewRouter()
s.router.HandleFunc("/", s.handleIndex()).Methods("GET")
s.router.HandleFunc("/login", s.AuthMiddleware(s.handleLogin())).Methods("GET")
s.router.HandleFunc("/.well-known/healthcheck.json", s.handleHealthCheck()).Methods("GET")
s.router.Use(s.LoggingMiddleware())
}