diff --git a/docs/content/observability/access-logs.md b/docs/content/observability/access-logs.md index 234204ed3..bbcbd62b1 100644 --- a/docs/content/observability/access-logs.md +++ b/docs/content/observability/access-logs.md @@ -195,6 +195,7 @@ accessLog: | `RequestMethod` | The HTTP method. | | `RequestPath` | The HTTP request URI, not including the scheme, host or port. | | `RequestProtocol` | The version of HTTP requested. | + | `RequestScheme` | The HTTP scheme requested `http` or `https`. | | `RequestLine` | `RequestMethod` + `RequestPath` + `RequestProtocol` | | `RequestContentSize` | The number of bytes in the request entity (a.k.a. body) sent by the client. | | `OriginDuration` | The time taken by the origin server ('upstream') to return its response. | diff --git a/integration/simple_test.go b/integration/simple_test.go index 5144f7136..72dfe4143 100644 --- a/integration/simple_test.go +++ b/integration/simple_test.go @@ -616,7 +616,7 @@ func (s *SimpleSuite) TestUDPServiceConfigErrors(c *check.C) { c.Assert(err, checker.IsNil) defer cmd.Process.Kill() - err = try.GetRequest("http://127.0.0.1:8080/api/udp/services", 1000*time.Millisecond, try.BodyContains(`["the udp service \"service1@file\" does not have any type defined"]`)) + err = try.GetRequest("http://127.0.0.1:8080/api/udp/services", 3000*time.Millisecond, try.BodyContains(`["the udp service \"service1@file\" does not have any type defined"]`)) c.Assert(err, checker.IsNil) err = try.GetRequest("http://127.0.0.1:8080/api/udp/services/service1@file", 1000*time.Millisecond, try.BodyContains(`"status":"disabled"`)) diff --git a/pkg/middlewares/accesslog/logdata.go b/pkg/middlewares/accesslog/logdata.go index a9e3f2906..5347d1b8e 100644 --- a/pkg/middlewares/accesslog/logdata.go +++ b/pkg/middlewares/accesslog/logdata.go @@ -42,6 +42,8 @@ const ( RequestPath = "RequestPath" // RequestProtocol is the map key used for the version of HTTP requested. RequestProtocol = "RequestProtocol" + // RequestScheme is the map key used for the HTTP request scheme. + RequestScheme = "RequestScheme" // RequestContentSize is the map key used for the number of bytes in the request entity (a.k.a. body) sent by the client. RequestContentSize = "RequestContentSize" // RequestRefererHeader is the Referer header in the request @@ -85,6 +87,7 @@ var defaultCoreKeys = [...]string{ RequestMethod, RequestPath, RequestProtocol, + RequestScheme, RequestContentSize, OriginDuration, OriginContentSize, diff --git a/pkg/middlewares/accesslog/logger.go b/pkg/middlewares/accesslog/logger.go index f6e0e4920..60901effe 100644 --- a/pkg/middlewares/accesslog/logger.go +++ b/pkg/middlewares/accesslog/logger.go @@ -193,6 +193,11 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http core[RequestPath] = urlCopyString core[RequestProtocol] = req.Proto + core[RequestScheme] = "http" + if req.TLS != nil { + core[RequestScheme] = "https" + } + core[ClientAddr] = req.RemoteAddr core[ClientHost], core[ClientPort] = silentSplitHostPort(req.RemoteAddr) diff --git a/pkg/middlewares/accesslog/logger_test.go b/pkg/middlewares/accesslog/logger_test.go index ad81c1c0b..53f082f1f 100644 --- a/pkg/middlewares/accesslog/logger_test.go +++ b/pkg/middlewares/accesslog/logger_test.go @@ -1,6 +1,7 @@ package accesslog import ( + "crypto/tls" "encoding/json" "fmt" "io/ioutil" @@ -31,6 +32,7 @@ var ( testPath = "testpath" testPort = 8181 testProto = "HTTP/0.0" + testScheme = "http" testMethod = http.MethodPost testReferer = "testReferer" testUserAgent = "testUserAgent" @@ -183,6 +185,7 @@ func TestLoggerJSON(t *testing.T) { testCases := []struct { desc string config *types.AccessLog + tls bool expected map[string]func(t *testing.T, value interface{}) }{ { @@ -198,6 +201,47 @@ func TestLoggerJSON(t *testing.T) { RequestMethod: assertString(testMethod), RequestPath: assertString(testPath), RequestProtocol: assertString(testProto), + RequestScheme: assertString(testScheme), + RequestPort: assertString("-"), + DownstreamStatus: assertFloat64(float64(testStatus)), + DownstreamContentSize: assertFloat64(float64(len(testContent))), + OriginContentSize: assertFloat64(float64(len(testContent))), + OriginStatus: assertFloat64(float64(testStatus)), + RequestRefererHeader: assertString(testReferer), + RequestUserAgentHeader: assertString(testUserAgent), + RouterName: assertString(testRouterName), + ServiceURL: assertString(testServiceName), + ClientUsername: assertString(testUsername), + ClientHost: assertString(testHostname), + ClientPort: assertString(fmt.Sprintf("%d", testPort)), + ClientAddr: assertString(fmt.Sprintf("%s:%d", testHostname, testPort)), + "level": assertString("info"), + "msg": assertString(""), + "downstream_Content-Type": assertString("text/plain; charset=utf-8"), + RequestCount: assertFloat64NotZero(), + Duration: assertFloat64NotZero(), + Overhead: assertFloat64NotZero(), + RetryAttempts: assertFloat64(float64(testRetryAttempts)), + "time": assertNotEmpty(), + "StartLocal": assertNotEmpty(), + "StartUTC": assertNotEmpty(), + }, + }, + { + desc: "default config, with TLS request", + config: &types.AccessLog{ + FilePath: "", + Format: JSONFormat, + }, + tls: true, + expected: map[string]func(t *testing.T, value interface{}){ + RequestContentSize: assertFloat64(0), + RequestHost: assertString(testHostname), + RequestAddr: assertString(testHostname), + RequestMethod: assertString(testMethod), + RequestPath: assertString(testPath), + RequestProtocol: assertString(testProto), + RequestScheme: assertString("https"), RequestPort: assertString("-"), DownstreamStatus: assertFloat64(float64(testStatus)), DownstreamContentSize: assertFloat64(float64(len(testContent))), @@ -319,7 +363,11 @@ func TestLoggerJSON(t *testing.T) { logFilePath := filepath.Join(tmpDir, logFileNameSuffix) test.config.FilePath = logFilePath - doLogging(t, test.config) + if test.tls { + doLoggingTLS(t, test.config) + } else { + doLogging(t, test.config) + } logData, err := ioutil.ReadFile(logFilePath) require.NoError(t, err) @@ -597,7 +645,7 @@ func createTempDir(t *testing.T, prefix string) string { return tmpDir } -func doLogging(t *testing.T, config *types.AccessLog) { +func doLoggingTLSOpt(t *testing.T, config *types.AccessLog, enableTLS bool) { logger, err := NewHandler(config) require.NoError(t, err) defer logger.Close() @@ -621,10 +669,21 @@ func doLogging(t *testing.T, config *types.AccessLog) { Path: testPath, }, } + if enableTLS { + req.TLS = &tls.ConnectionState{} + } logger.ServeHTTP(httptest.NewRecorder(), req, http.HandlerFunc(logWriterTestHandlerFunc)) } +func doLoggingTLS(t *testing.T, config *types.AccessLog) { + doLoggingTLSOpt(t, config, true) +} + +func doLogging(t *testing.T, config *types.AccessLog) { + doLoggingTLSOpt(t, config, false) +} + func logWriterTestHandlerFunc(rw http.ResponseWriter, r *http.Request) { if _, err := rw.Write([]byte(testContent)); err != nil { http.Error(rw, err.Error(), http.StatusInternalServerError)