From 4d3bbef32ac96a89e53859fe211363e6345481bf Mon Sep 17 00:00:00 2001
From: Gary Moon <gary@garymoon.net>
Date: Wed, 12 Apr 2023 16:50:47 +0000
Subject: [PATCH] Correct the access log format

Remove backslashes from template string
Add ctx.RemoteHost which contains a best-effort to strip the port from
req.RemoteAddr, falling back to untouch if an error is encountered
Default the first field of the access log to ctx.RemoteHost
Adjust templates and documentation accordingly

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 88297fe0d975e..f9f207522c7df 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 76952df40e751..f26e7eaa085ab 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 043cc42e53bc7..83e212f32b84a 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 13de8ab882bf0..029eb5de095f9 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 515682b64b0e6..64d204733b8a0 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 dabdb543abdf5..1ff710073e44f 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