Restructure project
We previously didn't really had any structure in our project apart from creating a new folder for each package in our project root. Now that we have accumulated some packages, we use the well-known Golang project layout in order to clearly communicate our intent with packages. See https://github.com/golang-standards/project-layout
This commit is contained in:
83
pkg/logging/logging.go
Normal file
83
pkg/logging/logging.go
Normal file
@ -0,0 +1,83 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
var log = &logrus.Logger{
|
||||
Out: os.Stderr,
|
||||
Formatter: &logrus.TextFormatter{
|
||||
DisableColors: true,
|
||||
FullTimestamp: true,
|
||||
},
|
||||
Hooks: make(logrus.LevelHooks),
|
||||
Level: logrus.InfoLevel,
|
||||
}
|
||||
|
||||
func InitializeLogging(loglevel string) {
|
||||
level, err := logrus.ParseLevel(loglevel)
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Error parsing loglevel")
|
||||
return
|
||||
}
|
||||
log.SetLevel(level)
|
||||
}
|
||||
|
||||
func GetLogger(pkg string) *logrus.Entry {
|
||||
return log.WithField("package", pkg)
|
||||
}
|
||||
|
||||
// loggingResponseWriter wraps the default http.ResponseWriter and catches the status code
|
||||
// that is written.
|
||||
type loggingResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
statusCode int
|
||||
}
|
||||
|
||||
func NewLoggingResponseWriter(w http.ResponseWriter) *loggingResponseWriter {
|
||||
return &loggingResponseWriter{w, http.StatusOK}
|
||||
}
|
||||
|
||||
func (writer *loggingResponseWriter) WriteHeader(code int) {
|
||||
writer.statusCode = code
|
||||
writer.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
func (writer *loggingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
conn, rw, err := writer.ResponseWriter.(http.Hijacker).Hijack()
|
||||
if err != nil {
|
||||
return conn, nil, fmt.Errorf("hijacking connection failed: %w", err)
|
||||
}
|
||||
return conn, rw, nil
|
||||
}
|
||||
|
||||
// HTTPLoggingMiddleware returns an http.Handler that logs different information about every request.
|
||||
func HTTPLoggingMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now().UTC()
|
||||
path := r.URL.Path
|
||||
|
||||
lrw := NewLoggingResponseWriter(w)
|
||||
next.ServeHTTP(lrw, r)
|
||||
|
||||
latency := time.Now().UTC().Sub(start)
|
||||
logEntry := log.WithFields(logrus.Fields{
|
||||
"code": lrw.statusCode,
|
||||
"method": r.Method,
|
||||
"path": path,
|
||||
"duration": latency,
|
||||
"user_agent": r.UserAgent(),
|
||||
})
|
||||
if lrw.statusCode >= http.StatusInternalServerError {
|
||||
logEntry.Warn()
|
||||
} else {
|
||||
logEntry.Debug()
|
||||
}
|
||||
})
|
||||
}
|
48
pkg/logging/logging_test.go
Normal file
48
pkg/logging/logging_test.go
Normal file
@ -0,0 +1,48 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func mockHTTPStatusHandler(status int) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(status)
|
||||
})
|
||||
}
|
||||
|
||||
func TestHTTPMiddlewareWarnsWhenInternalServerError(t *testing.T) {
|
||||
var hook *test.Hook
|
||||
log, hook = test.NewNullLogger()
|
||||
InitializeLogging(logrus.DebugLevel.String())
|
||||
|
||||
request, err := http.NewRequest(http.MethodGet, "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
recorder := httptest.NewRecorder()
|
||||
HTTPLoggingMiddleware(mockHTTPStatusHandler(500)).ServeHTTP(recorder, request)
|
||||
|
||||
assert.Equal(t, 1, len(hook.Entries))
|
||||
assert.Equal(t, logrus.WarnLevel, hook.LastEntry().Level)
|
||||
}
|
||||
|
||||
func TestHTTPMiddlewareDebugsWhenStatusOK(t *testing.T) {
|
||||
var hook *test.Hook
|
||||
log, hook = test.NewNullLogger()
|
||||
InitializeLogging(logrus.DebugLevel.String())
|
||||
|
||||
request, err := http.NewRequest(http.MethodGet, "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
recorder := httptest.NewRecorder()
|
||||
HTTPLoggingMiddleware(mockHTTPStatusHandler(200)).ServeHTTP(recorder, request)
|
||||
|
||||
assert.Equal(t, 1, len(hook.Entries))
|
||||
assert.Equal(t, logrus.DebugLevel, hook.LastEntry().Level)
|
||||
}
|
Reference in New Issue
Block a user