Configure version and trace id

This commit is contained in:
RouxAntoine 2021-03-02 00:53:22 +01:00
parent 50d74f20f1
commit e7a1116461
Signed by: antoine
GPG Key ID: 098FB66FC0475E70
7 changed files with 85 additions and 32 deletions

View File

@ -3,6 +3,7 @@ package main
import ( import (
"context" "context"
"flag" "flag"
"go/weather/internal/version"
"go/weather/internal/web" "go/weather/internal/web"
"go/weather/pkg/logger" "go/weather/pkg/logger"
"log" "log"
@ -32,7 +33,7 @@ func main() {
addr := web.NewListenAddr("127.0.0.1", 8080) addr := web.NewListenAddr("127.0.0.1", 8080)
defaultLogger.Sugar().Infof("Weather server is listening on %s", addr) defaultLogger.Sugar().Infof("Weather server is listening on %s", addr)
server := web.New(defaultLogger, addr). server := web.New(defaultLogger, addr, version.String()).
WithTLSConfigure(). WithTLSConfigure().
WithHandler(). WithHandler().
WithHTTPLogging(). WithHTTPLogging().

2
go.mod
View File

@ -6,8 +6,10 @@ require (
github.com/elastic/go-sysinfo v1.6.0 // indirect github.com/elastic/go-sysinfo v1.6.0 // indirect
github.com/elastic/go-windows v1.0.1 // indirect github.com/elastic/go-windows v1.0.1 // indirect
github.com/gorilla/mux v1.8.0 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/magefile/mage v1.11.0 // indirect
github.com/prometheus/procfs v0.6.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/apm/module/apmzap v1.11.0 // indirect
go.elastic.co/ecszap v1.0.0 go.elastic.co/ecszap v1.0.0
go.uber.org/multierr v1.6.0 // indirect go.uber.org/multierr v1.6.0 // indirect

2
go.sum
View File

@ -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/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 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 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/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 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4=
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak=

View File

@ -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
}

View File

@ -12,6 +12,7 @@ import (
"time" "time"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"go.elastic.co/apm"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -26,17 +27,22 @@ type WeatherServer struct {
router *mux.Router router *mux.Router
wlogger *logger.WeatherLogger wlogger *logger.WeatherLogger
subRouters map[string]*mux.Router subRouters map[string]*mux.Router
version string
} }
//New construct new instance //New construct new instance
func New(logger *logger.WeatherLogger, addr ListenAddr) *WeatherServer { func New(logger *logger.WeatherLogger, addr ListenAddr, version string) *WeatherServer {
return &WeatherServer{ return &WeatherServer{
Server: http.Server{ Server: http.Server{
Addr: addr.String(), Addr: addr.String(),
BaseContext: func(l net.Listener) context.Context { BaseContext: func(l net.Listener) context.Context {
customContext := context.Background()
// here configure context properties // 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, WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second, ReadTimeout: 15 * time.Second,
@ -44,6 +50,7 @@ func New(logger *logger.WeatherLogger, addr ListenAddr) *WeatherServer {
router: mux.NewRouter(), router: mux.NewRouter(),
wlogger: logger, wlogger: logger,
subRouters: make(map[string]*mux.Router, 1), subRouters: make(map[string]*mux.Router, 1),
version: version,
} }
} }
@ -56,7 +63,10 @@ func (ws *WeatherServer) WithHandler() *WeatherServer {
api := ws.getOrCreateSubRouterByName(apiRouterName, apiPrefix) api := ws.getOrCreateSubRouterByName(apiRouterName, apiPrefix)
api.HandleFunc("/health", func(rw http.ResponseWriter, r *http.Request) { 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 return ws

6
pkg/headers/header.go Normal file
View File

@ -0,0 +1,6 @@
package headers
const (
//ContentType ...
ContentType = "Content-Type"
)

View File

@ -2,6 +2,7 @@ package logger
import ( import (
"bytes" "bytes"
"go/weather/pkg/headers"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "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 //HTTPLogHandler http handler to log http request and http response
func (wl *WeatherLogger) HTTPLogHandler(next http.Handler) http.Handler { func (wl *WeatherLogger) HTTPLogHandler(next http.Handler) http.Handler {
// dt := apm.DefaultTracer
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ww := ResponseWriter{ ww := ResponseWriter{
ResponseWriter: w, ResponseWriter: w,
} }
// span, ctx := apm.StartSpan(r.Context(), "updateRequestCount", "custom")
// defer span.End()
wl.logHTTPRequest(r) wl.logHTTPRequest(r)
next.ServeHTTP(&ww, r) next.ServeHTTP(&ww, r)
wl.logHTTPResponse(ww) 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 //logHTTPRequest print request information
func (wl *WeatherLogger) logHTTPRequest(r *http.Request) { func (wl *WeatherLogger) logHTTPRequest(r *http.Request) {
var buf bytes.Buffer var buf bytes.Buffer
b, err := ioutil.ReadAll(io.TeeReader(r.Body, &buf)) b, err := ioutil.ReadAll(io.TeeReader(r.Body, &buf))
wl.handleError(err) wl.handleError(err)
r.Body = readCloser{&buf, r.Body} r.Body = struct {
traceContextFields := apmzap.TraceContext(r.Context()) io.Reader
io.Closer
}{&buf, r.Body}
// var buf bytes.Buffer lwith := wl.Logger.
// var buf2 bytes.Buffer With(apmzap.TraceContext(r.Context())...).
// b, err := io.Copy(io.MultiWriter(&buf, &buf2), r.Body) With([]zap.Field{
// wl.handleError(err) zap.String("http.request.method", r.Method),
// r.Body = readCloser{&buf2, r.Body} // 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", if len(b) > 0 {
zap.String("http.request.method", r.Method), lwith.With([]zap.Field{
zap.ByteString("http.request.body.content", b), zap.ByteString("http.request.body.content", b),
zap.Int("http.request.body.bytes", len(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(""), // realy log
) lwith.Debug("Request")
} }
//logHTTPResponse print response information //logHTTPResponse print response information