From 1963e270704e8adb76521fe57dca1f8e1608cb26 Mon Sep 17 00:00:00 2001 From: Yusuke Komatsu Date: Fri, 10 Feb 2017 16:21:32 +0900 Subject: [PATCH 1/5] git add RequestID middleware --- glide.lock | 24 ++++++++-------- glide.yaml | 1 + middleware/request_id.go | 52 +++++++++++++++++++++++++++++++++++ middleware/request_id_test.go | 32 +++++++++++++++++++++ 4 files changed, 98 insertions(+), 11 deletions(-) create mode 100644 middleware/request_id.go create mode 100644 middleware/request_id_test.go diff --git a/glide.lock b/glide.lock index c51f7aac..3ad58b04 100644 --- a/glide.lock +++ b/glide.lock @@ -1,10 +1,10 @@ -hash: 3de2a96bbdc145cce325de2a482111b0524cc330f60a4fbc781a08ed3b879e58 -updated: 2017-01-28T10:22:00.230111692-08:00 +hash: ffd2479be96adb7a9a21f8672a826e1e5868ab022bc7da1aa71f7b5d005ce03e +updated: 2017-02-10T11:14:02.230514143+09:00 imports: - name: github.com/daaku/go.zipexe version: a5fe2436ffcb3236e175e5149162b41cd28bd27d - name: github.com/dgrijalva/jwt-go - version: a601269ab70c205d26370c16f7c81e9017c14e04 + version: 2268707a8f0843315e2004ee4f1d021dc08baedf - name: github.com/facebookgo/clock version: 600d898af40aa09a7a93ecb9265d87b0504b6f03 - name: github.com/facebookgo/grace @@ -27,7 +27,7 @@ imports: - name: github.com/gorilla/websocket version: c36f2fe5c330f0ac404b616b96c438b8616b1aaf - name: github.com/kardianos/osext - version: c2c54e542fb797ad986b31721e1baedf214ca413 + version: 9b883c5eb462dd5cb1b0a7a104fe86bc6b9bd391 - name: github.com/labstack/gommon version: f72d3c883f8ea180da8f085dd320804c41332ad1 subpackages: @@ -38,30 +38,32 @@ imports: - name: github.com/mattn/go-colorable version: d228849504861217f796da67fae4f6e347643f15 - name: github.com/mattn/go-isatty - version: 30a891c33c7cde7b02a981314b4228ec99380cca + version: 281032e84ae07510239465db46bf442aa44b953a +- name: github.com/satori/go.uuid + version: b061729afc07e77a8aa4fad0a2fd840958f1942a - name: github.com/tylerb/graceful - version: 0e9129e9c6d47da90dc0c188b26bd7bb1dab53cd + version: 4654dfbb6ad53cb5e27f37d99b02e16c1872fbbb - name: github.com/valyala/bytebufferpool version: e746df99fe4a3986f4d4f79e13c1e0117ce9c2f7 - name: github.com/valyala/fasttemplate version: d090d65668a286d9a180d43a19dfdc5dcad8fe88 - name: golang.org/x/crypto - version: 9477e0b78b9ac3d0b03822fd95422e2fe07627cd + version: 453249f01cfeb54c3d549ddb75ff152ca243f9d8 subpackages: - acme - acme/autocert - name: golang.org/x/net - version: f2499483f923065a842d38eb4c7f1927e6fc6e6d + version: a689eb3bc4b53af70390acc3cf68c9f549b6b8d6 subpackages: - context - context/ctxhttp - websocket - name: golang.org/x/sys - version: d75a52659825e75fff6158388dddc6a5b04f9ba5 + version: 7a6e5648d140666db5d920909e082ca00a87ba2c subpackages: - unix - name: google.golang.org/appengine - version: a2c54d2174c17540446e0ced57d9d459af61bc1c + version: 2e4a801b39fc199db615bfca7d0b9f8cd9580599 subpackages: - internal - internal/app_identity @@ -87,6 +89,6 @@ testImports: subpackages: - difflib - name: github.com/stretchr/testify - version: 2402e8e7a02fc811447d11f881aa9746cdc57983 + version: 4d4bfba8f1d1027c4fdbe371823030df51419987 subpackages: - assert diff --git a/glide.yaml b/glide.yaml index 9ac9d1c8..a14982d7 100644 --- a/glide.yaml +++ b/glide.yaml @@ -12,6 +12,7 @@ import: - color - log - random +- package: github.com/satori/go.uuid - package: github.com/tylerb/graceful - package: github.com/valyala/fasttemplate - package: golang.org/x/crypto diff --git a/middleware/request_id.go b/middleware/request_id.go new file mode 100644 index 00000000..2a6e3227 --- /dev/null +++ b/middleware/request_id.go @@ -0,0 +1,52 @@ +package middleware + +import ( + "github.com/labstack/echo" + "github.com/labstack/echo/middleware" + uuid "github.com/satori/go.uuid" +) + +type ( + // RequestIDConfig defines the config for RequestID middleware. + RequestIDConfig struct { + // Skipper defines a function to skip middleware. + Skipper middleware.Skipper + } +) + +var ( + // DefaultRequestIDConfig is the default RequestID middleware config. + DefaultRequestIDConfig = RequestIDConfig{ + Skipper: DefaultSkipper, + } +) + +// RequestID returns a X-Request-ID middleware. +func RequestID() echo.MiddlewareFunc { + return RequestIDWithConfig(DefaultRequestIDConfig) +} + +// RequestIDWithConfig returns a X-Request-ID middleware with config. +func RequestIDWithConfig(config RequestIDConfig) echo.MiddlewareFunc { + if config.Skipper == nil { + config.Skipper = DefaultRequestIDConfig.Skipper + } + + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + if config.Skipper(c) { + return next(c) + } + + req := c.Request() + res := c.Response() + rid := req.Header.Get("X-Request-ID") + if rid == "" { + rid = uuid.NewV4().String() + } + res.Header().Set("X-Request-ID", rid) + + return next(c) + } + } +} diff --git a/middleware/request_id_test.go b/middleware/request_id_test.go new file mode 100644 index 00000000..5895230b --- /dev/null +++ b/middleware/request_id_test.go @@ -0,0 +1,32 @@ +package middleware + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/davecgh/go-spew/spew" + "github.com/labstack/echo" + uuid "github.com/satori/go.uuid" + "github.com/stretchr/testify/assert" +) + +func TestRequestID(t *testing.T) { + e := echo.New() + req, _ := http.NewRequest(echo.GET, "/", nil) + rec := httptest.NewRecorder() + c := e.NewContext(req, rec) + rid := RequestIDWithConfig(RequestIDConfig{}) + h := rid(func(c echo.Context) error { + return c.String(http.StatusOK, "test") + }) + + h(c) + if assert.NotEmpty(t, rec.Header().Get("X-Request-ID")) { + u, err := uuid.FromString(rec.Header().Get("X-Request-ID")) + spew.Dump(u) + assert.NoError(t, err) + assert.Equal(t, uint(4), u.Version()) + assert.Equal(t, uint(uuid.VariantRFC4122), u.Variant()) + } +} From 4dc9cd0bb6b6cffdb877b2dbc3b4c9af6e21808d Mon Sep 17 00:00:00 2001 From: Yusuke Komatsu Date: Fri, 10 Feb 2017 16:38:45 +0900 Subject: [PATCH 2/5] remove debug print in request_id_test.go --- middleware/request_id_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/middleware/request_id_test.go b/middleware/request_id_test.go index 5895230b..766b6f3f 100644 --- a/middleware/request_id_test.go +++ b/middleware/request_id_test.go @@ -5,7 +5,6 @@ import ( "net/http/httptest" "testing" - "github.com/davecgh/go-spew/spew" "github.com/labstack/echo" uuid "github.com/satori/go.uuid" "github.com/stretchr/testify/assert" @@ -24,7 +23,6 @@ func TestRequestID(t *testing.T) { h(c) if assert.NotEmpty(t, rec.Header().Get("X-Request-ID")) { u, err := uuid.FromString(rec.Header().Get("X-Request-ID")) - spew.Dump(u) assert.NoError(t, err) assert.Equal(t, uint(4), u.Version()) assert.Equal(t, uint(uuid.VariantRFC4122), u.Variant()) From 2995614e54686976042ef1a25c7c83c80cec00d7 Mon Sep 17 00:00:00 2001 From: Yusuke Komatsu Date: Fri, 10 Feb 2017 16:40:54 +0900 Subject: [PATCH 3/5] add id (Request ID) in logger format. --- middleware/logger.go | 10 ++++++++-- middleware/logger_test.go | 32 +++++++++++++++++--------------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/middleware/logger.go b/middleware/logger.go index e26b5496..2c3cb452 100644 --- a/middleware/logger.go +++ b/middleware/logger.go @@ -26,7 +26,7 @@ type ( // - time_unix_nano // - time_rfc3339 // - time_rfc3339_nano - // - id (Request ID - Not implemented) + // - id (Request ID) // - remote_ip // - uri // - host @@ -62,7 +62,7 @@ var ( // DefaultLoggerConfig is the default Logger middleware config. DefaultLoggerConfig = LoggerConfig{ Skipper: DefaultSkipper, - Format: `{"time":"${time_rfc3339_nano}","remote_ip":"${remote_ip}","host":"${host}",` + + Format: `{"time":"${time_rfc3339_nano}","id":"${request_id}","remote_ip":"${remote_ip}","host":"${host}",` + `"method":"${method}","uri":"${uri}","status":${status}, "latency":${latency},` + `"latency_human":"${latency_human}","bytes_in":${bytes_in},` + `"bytes_out":${bytes_out}}` + "\n", @@ -169,6 +169,12 @@ func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc { return buf.WriteString(cl) case "bytes_out": return buf.WriteString(strconv.FormatInt(res.Size, 10)) + case "request_id": + ri := req.Header.Get("X-Request-ID") + if ri == "" { + ri = res.Header().Get("X-Request-ID") + } + return w.Write([]byte(ri)) default: switch { case strings.HasPrefix(tag, "header:"): diff --git a/middleware/logger_test.go b/middleware/logger_test.go index 5eacb51f..58e48d7b 100644 --- a/middleware/logger_test.go +++ b/middleware/logger_test.go @@ -86,7 +86,7 @@ func TestLoggerTemplate(t *testing.T) { e := echo.New() e.Use(LoggerWithConfig(LoggerConfig{ - Format: `{"time":"${time_rfc3339_nano}","remote_ip":"${remote_ip}","host":"${host}","user_agent":"${user_agent}",` + + Format: `{"time":"${time_rfc3339_nano}","id":"${request_id}","remote_ip":"${remote_ip}","host":"${host}","user_agent":"${user_agent}",` + `"method":"${method}","uri":"${uri}","status":${status}, "latency":${latency},` + `"latency_human":"${latency_human}","bytes_in":${bytes_in}, "path":"${path}", "referer":"${referer}",` + `"bytes_out":${bytes_out},"ch":"${header:X-Custom-Header}",` + @@ -104,6 +104,7 @@ func TestLoggerTemplate(t *testing.T) { req.Header.Add("Referer", "google.com") req.Header.Add("User-Agent", "echo-tests-agent") req.Header.Add("X-Custom-Header", "AAA-CUSTOM-VALUE") + req.Header.Add("X-Request-ID", "6ba7b810-9dad-11d1-80b4-00c04fd430c8") req.Form = url.Values{ "username": []string{"apagano-form"}, "password": []string{"secret-form"}, @@ -113,20 +114,21 @@ func TestLoggerTemplate(t *testing.T) { e.ServeHTTP(rec, req) cases := map[string]bool{ - "apagano-param": true, - "apagano-form": true, - "AAA-CUSTOM-VALUE": true, - "BBB-CUSTOM-VALUE": false, - "secret-form": false, - "hexvalue": false, - "GET": true, - "127.0.0.1": true, - "\"path\":\"/\"": true, - "\"uri\":\"/\"": true, - "\"status\":200": true, - "\"bytes_in\":0": true, - "google.com": true, - "echo-tests-agent": true, + "apagano-param": true, + "apagano-form": true, + "AAA-CUSTOM-VALUE": true, + "BBB-CUSTOM-VALUE": false, + "secret-form": false, + "hexvalue": false, + "GET": true, + "127.0.0.1": true, + "\"path\":\"/\"": true, + "\"uri\":\"/\"": true, + "\"status\":200": true, + "\"bytes_in\":0": true, + "google.com": true, + "echo-tests-agent": true, + "6ba7b810-9dad-11d1-80b4-00c04fd430c8": true, } for token, present := range cases { From 0c7c1b78e7c9e4ecef10ced430147ccef508a02b Mon Sep 17 00:00:00 2001 From: Yusuke Komatsu Date: Fri, 10 Feb 2017 17:09:11 +0900 Subject: [PATCH 4/5] fix import cycle middleware --- middleware/request_id.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/middleware/request_id.go b/middleware/request_id.go index 2a6e3227..54e61a12 100644 --- a/middleware/request_id.go +++ b/middleware/request_id.go @@ -2,7 +2,6 @@ package middleware import ( "github.com/labstack/echo" - "github.com/labstack/echo/middleware" uuid "github.com/satori/go.uuid" ) @@ -10,7 +9,7 @@ type ( // RequestIDConfig defines the config for RequestID middleware. RequestIDConfig struct { // Skipper defines a function to skip middleware. - Skipper middleware.Skipper + Skipper Skipper } ) From 81d86b90d07214ddbd6fd04e13a243afb2deb87e Mon Sep 17 00:00:00 2001 From: Vishal Rana Date: Mon, 6 Mar 2017 13:16:05 -0800 Subject: [PATCH 5/5] Using random.String() for generating id Signed-off-by: Vishal Rana --- echo.go | 1 + glide.lock | 24 +++++++++++------------- glide.yaml | 1 - middleware/logger.go | 14 +++++++------- middleware/logger_test.go | 2 +- middleware/request_id.go | 23 ++++++++++++++++++----- middleware/request_id_test.go | 9 +-------- website/content/middleware/logger.md | 2 +- 8 files changed, 40 insertions(+), 36 deletions(-) diff --git a/echo.go b/echo.go index 45667a9e..6ab20bf5 100644 --- a/echo.go +++ b/echo.go @@ -183,6 +183,7 @@ const ( HeaderXHTTPMethodOverride = "X-HTTP-Method-Override" HeaderXForwardedFor = "X-Forwarded-For" HeaderXRealIP = "X-Real-IP" + HeaderXRequestID = "X-Request-ID" HeaderServer = "Server" HeaderOrigin = "Origin" HeaderAccessControlRequestMethod = "Access-Control-Request-Method" diff --git a/glide.lock b/glide.lock index 3ad58b04..c51f7aac 100644 --- a/glide.lock +++ b/glide.lock @@ -1,10 +1,10 @@ -hash: ffd2479be96adb7a9a21f8672a826e1e5868ab022bc7da1aa71f7b5d005ce03e -updated: 2017-02-10T11:14:02.230514143+09:00 +hash: 3de2a96bbdc145cce325de2a482111b0524cc330f60a4fbc781a08ed3b879e58 +updated: 2017-01-28T10:22:00.230111692-08:00 imports: - name: github.com/daaku/go.zipexe version: a5fe2436ffcb3236e175e5149162b41cd28bd27d - name: github.com/dgrijalva/jwt-go - version: 2268707a8f0843315e2004ee4f1d021dc08baedf + version: a601269ab70c205d26370c16f7c81e9017c14e04 - name: github.com/facebookgo/clock version: 600d898af40aa09a7a93ecb9265d87b0504b6f03 - name: github.com/facebookgo/grace @@ -27,7 +27,7 @@ imports: - name: github.com/gorilla/websocket version: c36f2fe5c330f0ac404b616b96c438b8616b1aaf - name: github.com/kardianos/osext - version: 9b883c5eb462dd5cb1b0a7a104fe86bc6b9bd391 + version: c2c54e542fb797ad986b31721e1baedf214ca413 - name: github.com/labstack/gommon version: f72d3c883f8ea180da8f085dd320804c41332ad1 subpackages: @@ -38,32 +38,30 @@ imports: - name: github.com/mattn/go-colorable version: d228849504861217f796da67fae4f6e347643f15 - name: github.com/mattn/go-isatty - version: 281032e84ae07510239465db46bf442aa44b953a -- name: github.com/satori/go.uuid - version: b061729afc07e77a8aa4fad0a2fd840958f1942a + version: 30a891c33c7cde7b02a981314b4228ec99380cca - name: github.com/tylerb/graceful - version: 4654dfbb6ad53cb5e27f37d99b02e16c1872fbbb + version: 0e9129e9c6d47da90dc0c188b26bd7bb1dab53cd - name: github.com/valyala/bytebufferpool version: e746df99fe4a3986f4d4f79e13c1e0117ce9c2f7 - name: github.com/valyala/fasttemplate version: d090d65668a286d9a180d43a19dfdc5dcad8fe88 - name: golang.org/x/crypto - version: 453249f01cfeb54c3d549ddb75ff152ca243f9d8 + version: 9477e0b78b9ac3d0b03822fd95422e2fe07627cd subpackages: - acme - acme/autocert - name: golang.org/x/net - version: a689eb3bc4b53af70390acc3cf68c9f549b6b8d6 + version: f2499483f923065a842d38eb4c7f1927e6fc6e6d subpackages: - context - context/ctxhttp - websocket - name: golang.org/x/sys - version: 7a6e5648d140666db5d920909e082ca00a87ba2c + version: d75a52659825e75fff6158388dddc6a5b04f9ba5 subpackages: - unix - name: google.golang.org/appengine - version: 2e4a801b39fc199db615bfca7d0b9f8cd9580599 + version: a2c54d2174c17540446e0ced57d9d459af61bc1c subpackages: - internal - internal/app_identity @@ -89,6 +87,6 @@ testImports: subpackages: - difflib - name: github.com/stretchr/testify - version: 4d4bfba8f1d1027c4fdbe371823030df51419987 + version: 2402e8e7a02fc811447d11f881aa9746cdc57983 subpackages: - assert diff --git a/glide.yaml b/glide.yaml index a14982d7..9ac9d1c8 100644 --- a/glide.yaml +++ b/glide.yaml @@ -12,7 +12,6 @@ import: - color - log - random -- package: github.com/satori/go.uuid - package: github.com/tylerb/graceful - package: github.com/valyala/fasttemplate - package: golang.org/x/crypto diff --git a/middleware/logger.go b/middleware/logger.go index 2c3cb452..071d0f67 100644 --- a/middleware/logger.go +++ b/middleware/logger.go @@ -62,7 +62,7 @@ var ( // DefaultLoggerConfig is the default Logger middleware config. DefaultLoggerConfig = LoggerConfig{ Skipper: DefaultSkipper, - Format: `{"time":"${time_rfc3339_nano}","id":"${request_id}","remote_ip":"${remote_ip}","host":"${host}",` + + Format: `{"time":"${time_rfc3339_nano}","id":"${id}","remote_ip":"${remote_ip}","host":"${host}",` + `"method":"${method}","uri":"${uri}","status":${status}, "latency":${latency},` + `"latency_human":"${latency_human}","bytes_in":${bytes_in},` + `"bytes_out":${bytes_out}}` + "\n", @@ -126,6 +126,12 @@ func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc { return buf.WriteString(time.Now().Format(time.RFC3339)) case "time_rfc3339_nano": return buf.WriteString(time.Now().Format(time.RFC3339Nano)) + case "id": + id := req.Header.Get(echo.HeaderXRequestID) + if id == "" { + id = res.Header().Get(echo.HeaderXRequestID) + } + return buf.WriteString(id) case "remote_ip": return buf.WriteString(c.RealIP()) case "host": @@ -169,12 +175,6 @@ func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc { return buf.WriteString(cl) case "bytes_out": return buf.WriteString(strconv.FormatInt(res.Size, 10)) - case "request_id": - ri := req.Header.Get("X-Request-ID") - if ri == "" { - ri = res.Header().Get("X-Request-ID") - } - return w.Write([]byte(ri)) default: switch { case strings.HasPrefix(tag, "header:"): diff --git a/middleware/logger_test.go b/middleware/logger_test.go index 42c35206..f8b09cfd 100644 --- a/middleware/logger_test.go +++ b/middleware/logger_test.go @@ -86,7 +86,7 @@ func TestLoggerTemplate(t *testing.T) { e := echo.New() e.Use(LoggerWithConfig(LoggerConfig{ - Format: `{"time":"${time_rfc3339_nano}","id":"${request_id}","remote_ip":"${remote_ip}","host":"${host}","user_agent":"${user_agent}",` + + Format: `{"time":"${time_rfc3339_nano}","id":"${id}","remote_ip":"${remote_ip}","host":"${host}","user_agent":"${user_agent}",` + `"method":"${method}","uri":"${uri}","status":${status}, "latency":${latency},` + `"latency_human":"${latency_human}","bytes_in":${bytes_in}, "path":"${path}", "referer":"${referer}",` + `"bytes_out":${bytes_out},"ch":"${header:X-Custom-Header}",` + diff --git a/middleware/request_id.go b/middleware/request_id.go index 54e61a12..96261ae6 100644 --- a/middleware/request_id.go +++ b/middleware/request_id.go @@ -2,7 +2,7 @@ package middleware import ( "github.com/labstack/echo" - uuid "github.com/satori/go.uuid" + "github.com/labstack/gommon/random" ) type ( @@ -10,13 +10,18 @@ type ( RequestIDConfig struct { // Skipper defines a function to skip middleware. Skipper Skipper + + // Generator defines a function to generate an ID. + // Optional. Default value random.String(32). + Generator func() string } ) var ( // DefaultRequestIDConfig is the default RequestID middleware config. DefaultRequestIDConfig = RequestIDConfig{ - Skipper: DefaultSkipper, + Skipper: DefaultSkipper, + Generator: generator, } ) @@ -27,9 +32,13 @@ func RequestID() echo.MiddlewareFunc { // RequestIDWithConfig returns a X-Request-ID middleware with config. func RequestIDWithConfig(config RequestIDConfig) echo.MiddlewareFunc { + // Defaults if config.Skipper == nil { config.Skipper = DefaultRequestIDConfig.Skipper } + if config.Generator == nil { + config.Generator = generator + } return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { @@ -39,13 +48,17 @@ func RequestIDWithConfig(config RequestIDConfig) echo.MiddlewareFunc { req := c.Request() res := c.Response() - rid := req.Header.Get("X-Request-ID") + rid := req.Header.Get(echo.HeaderXRequestID) if rid == "" { - rid = uuid.NewV4().String() + rid = random.String(32) } - res.Header().Set("X-Request-ID", rid) + res.Header().Set(echo.HeaderXRequestID, rid) return next(c) } } } + +func generator() string { + return random.String(32) +} diff --git a/middleware/request_id_test.go b/middleware/request_id_test.go index 766b6f3f..f4b69149 100644 --- a/middleware/request_id_test.go +++ b/middleware/request_id_test.go @@ -6,7 +6,6 @@ import ( "testing" "github.com/labstack/echo" - uuid "github.com/satori/go.uuid" "github.com/stretchr/testify/assert" ) @@ -19,12 +18,6 @@ func TestRequestID(t *testing.T) { h := rid(func(c echo.Context) error { return c.String(http.StatusOK, "test") }) - h(c) - if assert.NotEmpty(t, rec.Header().Get("X-Request-ID")) { - u, err := uuid.FromString(rec.Header().Get("X-Request-ID")) - assert.NoError(t, err) - assert.Equal(t, uint(4), u.Version()) - assert.Equal(t, uint(uuid.VariantRFC4122), u.Variant()) - } + assert.Len(t, rec.Header().Get(echo.HeaderXRequestID), 32) } diff --git a/website/content/middleware/logger.md b/website/content/middleware/logger.md index 1674d845..82c1a168 100644 --- a/website/content/middleware/logger.md +++ b/website/content/middleware/logger.md @@ -51,7 +51,7 @@ LoggerConfig struct { // - time_unix_nano // - time_rfc3339 // - time_rfc3339_nano - // - id (Request ID - Not implemented) + // - id (Request ID) // - remote_ip // - uri // - host