From 29194a9dd6f5d8a98096a4a01c33d9be89616ed7 Mon Sep 17 00:00:00 2001
From: Gary Moon <garymoon@users.noreply.github.com>
Date: Thu, 13 Apr 2023 09:14:06 -0400
Subject: [PATCH] Correct the access log format (#24085)

The default access log format has been unnecessarily escaped, leading to
spurious backslashes appearing in log lines.

Additionally, the `RemoteAddr` field includes the port, which breaks
most log parsers attempting to process it. I've added a call to
`net.SplitHostPort()` attempting to isolate the address alone, with a
fallback to the original address if it errs.

Signed-off-by: Gary Moon <gary@garymoon.net>
---
 custom/conf/app.example.ini                              | 2 +-
 .../doc/administration/config-cheat-sheet.en-us.md       | 2 +-
 .../doc/administration/config-cheat-sheet.zh-cn.md       | 2 +-
 .../doc/administration/logging-documentation.en-us.md    | 2 +-
 modules/context/access_log.go                            | 9 ++++++++-
 modules/setting/log.go                                   | 2 +-
 6 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index 88297fe0d9..f9f207522c 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -603,7 +603,7 @@ ROUTER = console
 ;ACCESS = file
 ;;
 ;; Sets the template used to create the access log.
-;ACCESS_LOG_TEMPLATE = {{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"
+;ACCESS_LOG_TEMPLATE = {{.Ctx.RemoteHost}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}" "{{.Ctx.Req.UserAgent}}"
 ;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;
diff --git a/docs/content/doc/administration/config-cheat-sheet.en-us.md b/docs/content/doc/administration/config-cheat-sheet.en-us.md
index 76952df40e..f26e7eaa08 100644
--- a/docs/content/doc/administration/config-cheat-sheet.en-us.md
+++ b/docs/content/doc/administration/config-cheat-sheet.en-us.md
@@ -878,7 +878,7 @@ Default templates for project boards:
 
 - `ENABLE_ACCESS_LOG`: **false**: Creates an access.log in NCSA common log format, or as per the following template
 - `ACCESS`: **file**: Logging mode for the access logger, use a comma to separate values. Configure each mode in per mode log subsections `\[log.modename.access\]`. By default the file mode will log to `$ROOT_PATH/access.log`. (If you set this to `,` it will log to the default Gitea logger.)
-- `ACCESS_LOG_TEMPLATE`: **`{{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"`**: Sets the template used to create the access log.
+- `ACCESS_LOG_TEMPLATE`: **`{{.Ctx.RemoteHost}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}" "{{.Ctx.Req.UserAgent}}"`**: Sets the template used to create the access log.
   - The following variables are available:
   - `Ctx`: the `context.Context` of the request.
   - `Identity`: the SignedUserName or `"-"` if not logged in.
diff --git a/docs/content/doc/administration/config-cheat-sheet.zh-cn.md b/docs/content/doc/administration/config-cheat-sheet.zh-cn.md
index 043cc42e53..83e212f32b 100644
--- a/docs/content/doc/administration/config-cheat-sheet.zh-cn.md
+++ b/docs/content/doc/administration/config-cheat-sheet.zh-cn.md
@@ -265,7 +265,7 @@ test01.xls: application/vnd.ms-excel; charset=binary
 - `LEVEL`: 日志级别,默认为 `Trace`。
 - `DISABLE_ROUTER_LOG`: 关闭日志中的路由日志。
 - `ENABLE_ACCESS_LOG`: 是否开启 Access Log, 默认为 false。
-- `ACCESS_LOG_TEMPLATE`: `access.log` 输出内容的模板,默认模板:**`{{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"`**
+- `ACCESS_LOG_TEMPLATE`: `access.log` 输出内容的模板,默认模板:**`{{.Ctx.RemoteHost}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}" "{{.Ctx.Req.UserAgent}}"`**
   模板支持以下参数:
   - `Ctx`: 请求上下文。
   - `Identity`: 登录用户名,默认: “`-`”。
diff --git a/docs/content/doc/administration/logging-documentation.en-us.md b/docs/content/doc/administration/logging-documentation.en-us.md
index 13de8ab882..029eb5de09 100644
--- a/docs/content/doc/administration/logging-documentation.en-us.md
+++ b/docs/content/doc/administration/logging-documentation.en-us.md
@@ -304,7 +304,7 @@ log using the value: `ACCESS = ,`
 
 This value represent a go template. It's default value is:
 
-`{{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"`
+`{{.Ctx.RemoteHost}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}" "{{.Ctx.Req.UserAgent}}"`
 
 The template is passed following options:
 
diff --git a/modules/context/access_log.go b/modules/context/access_log.go
index 515682b64b..64d204733b 100644
--- a/modules/context/access_log.go
+++ b/modules/context/access_log.go
@@ -7,6 +7,7 @@ import (
 	"bytes"
 	"context"
 	"fmt"
+	"net"
 	"net/http"
 	"strings"
 	"text/template"
@@ -67,17 +68,23 @@ func AccessLogger() func(http.Handler) http.Handler {
 				requestID = parseRequestIDFromRequestHeader(req)
 			}
 
+			reqHost, _, err := net.SplitHostPort(req.RemoteAddr)
+			if err != nil {
+				reqHost = req.RemoteAddr
+			}
+
 			next.ServeHTTP(w, r)
 			rw := w.(ResponseWriter)
 
 			buf := bytes.NewBuffer([]byte{})
-			err := logTemplate.Execute(buf, routerLoggerOptions{
+			err = logTemplate.Execute(buf, routerLoggerOptions{
 				req:            req,
 				Identity:       &identity,
 				Start:          &start,
 				ResponseWriter: rw,
 				Ctx: map[string]interface{}{
 					"RemoteAddr": req.RemoteAddr,
+					"RemoteHost": reqHost,
 					"Req":        req,
 				},
 				RequestID: &requestID,
diff --git a/modules/setting/log.go b/modules/setting/log.go
index dabdb543ab..1ff710073e 100644
--- a/modules/setting/log.go
+++ b/modules/setting/log.go
@@ -152,7 +152,7 @@ func loadLogFrom(rootCfg ConfigProvider) {
 	Log.EnableSSHLog = sec.Key("ENABLE_SSH_LOG").MustBool(false)
 	Log.EnableAccessLog = sec.Key("ENABLE_ACCESS_LOG").MustBool(false)
 	Log.AccessLogTemplate = sec.Key("ACCESS_LOG_TEMPLATE").MustString(
-		`{{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"`,
+		`{{.Ctx.RemoteHost}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}" "{{.Ctx.Req.UserAgent}}"`,
 	)
 	Log.RequestIDHeaders = sec.Key("REQUEST_ID_HEADERS").Strings(",")
 	// the `MustString` updates the default value, and `log.ACCESS` is used by `generateNamedLogger("access")` later