From e7a11164615188f9cbbf24bfeff96f26aed99beb Mon Sep 17 00:00:00 2001 From: RouxAntoine Date: Tue, 2 Mar 2021 00:53:22 +0100 Subject: [PATCH] Configure version and trace id --- cmd/weather/main.go | 3 +- go.mod | 2 ++ go.sum | 2 ++ internal/version/version.go | 31 +++++++++++++++++++++ internal/web/server.go | 18 +++++++++--- pkg/headers/header.go | 6 ++++ pkg/logger/logger.go | 55 +++++++++++++++++++------------------ 7 files changed, 85 insertions(+), 32 deletions(-) create mode 100644 internal/version/version.go create mode 100644 pkg/headers/header.go diff --git a/cmd/weather/main.go b/cmd/weather/main.go index dd032fd..2c82924 100644 --- a/cmd/weather/main.go +++ b/cmd/weather/main.go @@ -3,6 +3,7 @@ package main import ( "context" "flag" + "go/weather/internal/version" "go/weather/internal/web" "go/weather/pkg/logger" "log" @@ -32,7 +33,7 @@ func main() { addr := web.NewListenAddr("127.0.0.1", 8080) defaultLogger.Sugar().Infof("Weather server is listening on %s", addr) - server := web.New(defaultLogger, addr). + server := web.New(defaultLogger, addr, version.String()). WithTLSConfigure(). WithHandler(). WithHTTPLogging(). diff --git a/go.mod b/go.mod index 32906ed..9c7c22c 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,10 @@ require ( github.com/elastic/go-sysinfo v1.6.0 // indirect github.com/elastic/go-windows v1.0.1 // indirect github.com/gorilla/mux v1.8.0 + github.com/hashicorp/go-version v1.2.1 // indirect github.com/magefile/mage v1.11.0 // indirect github.com/prometheus/procfs v0.6.0 // indirect + go.elastic.co/apm v1.11.0 // indirect go.elastic.co/apm/module/apmzap v1.11.0 // indirect go.elastic.co/ecszap v1.0.0 go.uber.org/multierr v1.6.0 // indirect diff --git a/go.sum b/go.sum index eb9962b..a3868ed 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4= github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= diff --git a/internal/version/version.go b/internal/version/version.go new file mode 100644 index 0000000..20dce3e --- /dev/null +++ b/internal/version/version.go @@ -0,0 +1,31 @@ +package version + +import ( + "fmt" + + version "github.com/hashicorp/go-version" +) + +//Version ... +const Version = "1.0.0" + +//Prerelease such as "dev" (in development), "beta", "rc1", etc. +var Prerelease = "dev" + +//SemVer management +var SemVer *version.Version + +func init() { + SemVer = version.Must(version.NewVersion(Version)) +} + +// Header is the header name used to send the current in http requests. +const Header = "Weather-Version" + +// String returns the complete version string, including prerelease +func String() string { + if Prerelease != "" { + return fmt.Sprintf("%s-%s", Version, Prerelease) + } + return Version +} diff --git a/internal/web/server.go b/internal/web/server.go index 5d88ef1..d9df5f5 100644 --- a/internal/web/server.go +++ b/internal/web/server.go @@ -12,6 +12,7 @@ import ( "time" "github.com/gorilla/mux" + "go.elastic.co/apm" "go.uber.org/zap" ) @@ -26,17 +27,22 @@ type WeatherServer struct { router *mux.Router wlogger *logger.WeatherLogger subRouters map[string]*mux.Router + version string } //New construct new instance -func New(logger *logger.WeatherLogger, addr ListenAddr) *WeatherServer { +func New(logger *logger.WeatherLogger, addr ListenAddr, version string) *WeatherServer { return &WeatherServer{ Server: http.Server{ Addr: addr.String(), BaseContext: func(l net.Listener) context.Context { - customContext := context.Background() // here configure context properties - return customContext + dt := apm.DefaultTracer + tx := dt.StartTransactionOptions("http", "request", apm.TransactionOptions{ + Start: time.Now(), + }) + defer tx.End() + return apm.ContextWithTransaction(context.Background(), tx) }, WriteTimeout: 15 * time.Second, ReadTimeout: 15 * time.Second, @@ -44,6 +50,7 @@ func New(logger *logger.WeatherLogger, addr ListenAddr) *WeatherServer { router: mux.NewRouter(), wlogger: logger, subRouters: make(map[string]*mux.Router, 1), + version: version, } } @@ -56,7 +63,10 @@ func (ws *WeatherServer) WithHandler() *WeatherServer { api := ws.getOrCreateSubRouterByName(apiRouterName, apiPrefix) api.HandleFunc("/health", func(rw http.ResponseWriter, r *http.Request) { - json.NewEncoder(rw).Encode(map[string]bool{"ok": true}) + json.NewEncoder(rw).Encode(map[string]interface{}{ + "ok": true, + "version": ws.version, + }) }) return ws diff --git a/pkg/headers/header.go b/pkg/headers/header.go new file mode 100644 index 0000000..0e3245d --- /dev/null +++ b/pkg/headers/header.go @@ -0,0 +1,6 @@ +package headers + +const ( + //ContentType ... + ContentType = "Content-Type" +) diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index b4b4bb3..ef16812 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -2,6 +2,7 @@ package logger import ( "bytes" + "go/weather/pkg/headers" "io" "io/ioutil" "net/http" @@ -79,50 +80,50 @@ func NewLogger(loggerName, logPath string, level zap.AtomicLevel) *WeatherLogger //HTTPLogHandler http handler to log http request and http response func (wl *WeatherLogger) HTTPLogHandler(next http.Handler) http.Handler { + // dt := apm.DefaultTracer return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ww := ResponseWriter{ ResponseWriter: w, } + + // span, ctx := apm.StartSpan(r.Context(), "updateRequestCount", "custom") + // defer span.End() + wl.logHTTPRequest(r) next.ServeHTTP(&ww, r) wl.logHTTPResponse(ww) }) } -type readCloser struct { - io.Reader - io.Closer -} - -func useTeeReader(rc io.ReadCloser, w io.Writer) io.ReadCloser { - tee := io.TeeReader(rc, w) - teeCloser := readCloser{tee, rc} - return teeCloser -} - //logHTTPRequest print request information func (wl *WeatherLogger) logHTTPRequest(r *http.Request) { var buf bytes.Buffer b, err := ioutil.ReadAll(io.TeeReader(r.Body, &buf)) wl.handleError(err) - r.Body = readCloser{&buf, r.Body} - traceContextFields := apmzap.TraceContext(r.Context()) + r.Body = struct { + io.Reader + io.Closer + }{&buf, r.Body} - // var buf bytes.Buffer - // var buf2 bytes.Buffer - // b, err := io.Copy(io.MultiWriter(&buf, &buf2), r.Body) - // wl.handleError(err) - // r.Body = readCloser{&buf2, r.Body} + lwith := wl.Logger. + With(apmzap.TraceContext(r.Context())...). + With([]zap.Field{ + zap.String("http.request.method", r.Method), + // zap.Int64("http.request.bytes", r.Header ContentLength), // total body+header len + zap.String("http.request.mime_type", r.Header.Get(headers.ContentType)), + // zap.String(""), + // zap.String(""), + }...) - wl.Logger.With(traceContextFields...).Debug("Request", - zap.String("http.request.method", r.Method), - zap.ByteString("http.request.body.content", b), - zap.Int("http.request.body.bytes", len(b)), - // zap.Int64("http.request.bytes", r.Header ContentLength), // total body+header len - // zap.String(""), - // zap.String(""), - // zap.String(""), - ) + if len(b) > 0 { + lwith.With([]zap.Field{ + zap.ByteString("http.request.body.content", b), + zap.Int("http.request.body.bytes", len(b)), + }...) + } + + // realy log + lwith.Debug("Request") } //logHTTPResponse print response information