From 9d0a2a94f7be83efbf5a72051b3f77f1698b7cec Mon Sep 17 00:00:00 2001 From: Toby Chui Date: Thu, 23 Oct 2025 22:41:09 +0800 Subject: [PATCH] Add option to disable request logging per endpoint --- src/mod/dynamicproxy/Server.go | 24 ++++++++++------- src/mod/dynamicproxy/access.go | 4 +-- src/mod/dynamicproxy/dynamicproxy.go | 2 +- src/mod/dynamicproxy/proxyRequestHandler.go | 30 ++++++++++++--------- src/mod/dynamicproxy/ratelimit.go | 2 +- src/mod/dynamicproxy/typedef.go | 1 + src/reverseproxy.go | 8 ++++++ src/web/components/httprp.html | 14 ++++++++++ src/web/components/rules.html | 13 +++++++-- 9 files changed, 70 insertions(+), 28 deletions(-) diff --git a/src/mod/dynamicproxy/Server.go b/src/mod/dynamicproxy/Server.go index a9b1aa7..17ec73b 100644 --- a/src/mod/dynamicproxy/Server.go +++ b/src/mod/dynamicproxy/Server.go @@ -48,7 +48,7 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { //Check if this is a redirection url if h.Parent.Option.RedirectRuleTable.IsRedirectable(r) { statusCode := h.Parent.Option.RedirectRuleTable.HandleRedirect(w, r) - h.Parent.logRequest(r, statusCode != 500, statusCode, "redirect", r.Host, "") + h.Parent.logRequest(r, statusCode != 500, statusCode, "redirect", r.Host, "", nil) return } @@ -70,7 +70,7 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { //Use default rule ruleID = "default" } - if h.handleAccessRouting(ruleID, w, r) { + if h.handleAccessRouting(ruleID, w, r, sep) { //Request handled by subroute return } @@ -79,7 +79,9 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if sep.RequireRateLimit { err := h.handleRateLimitRouting(w, r, sep) if err != nil { - h.Parent.Option.Logger.LogHTTPRequest(r, "host", 307, r.Host, "") + if !sep.DisableLogging { + h.Parent.Option.Logger.LogHTTPRequest(r, "host", 307, r.Host, "") + } return } } @@ -109,7 +111,9 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if potentialProxtEndpoint != nil && !potentialProxtEndpoint.Disabled { //Missing tailing slash. Redirect to target proxy endpoint http.Redirect(w, r, r.RequestURI+"/", http.StatusTemporaryRedirect) - h.Parent.Option.Logger.LogHTTPRequest(r, "redirect", 307, r.Host, "") + if !sep.DisableLogging { + h.Parent.Option.Logger.LogHTTPRequest(r, "redirect", 307, r.Host, "") + } return } } @@ -124,7 +128,7 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { */ //Root access control based on default rule - blocked := h.handleAccessRouting("default", w, r) + blocked := h.handleAccessRouting("default", w, r, h.Parent.Root) if blocked { return } @@ -210,19 +214,19 @@ func (h *ProxyHandler) handleRootRouting(w http.ResponseWriter, r *http.Request) } hostname := parsedURL.Hostname() if hostname == domainOnly { - h.Parent.logRequest(r, false, 500, "root-redirect", domainOnly, "") + h.Parent.logRequest(r, false, 500, "root-redirect", domainOnly, "", h.Parent.Root) http.Error(w, "Loopback redirects due to invalid settings", 500) return } - h.Parent.logRequest(r, false, 307, "root-redirect", domainOnly, "") + h.Parent.logRequest(r, false, 307, "root-redirect", domainOnly, "", h.Parent.Root) http.Redirect(w, r, redirectTarget, http.StatusTemporaryRedirect) case DefaultSite_NotFoundPage: //Serve the not found page, use template if exists h.serve404PageWithTemplate(w, r) case DefaultSite_NoResponse: //No response. Just close the connection - h.Parent.logRequest(r, false, 444, "root-no_resp", domainOnly, "") + h.Parent.logRequest(r, false, 444, "root-no_resp", domainOnly, "", h.Parent.Root) hijacker, ok := w.(http.Hijacker) if !ok { w.WriteHeader(http.StatusNoContent) @@ -236,11 +240,11 @@ func (h *ProxyHandler) handleRootRouting(w http.ResponseWriter, r *http.Request) conn.Close() case DefaultSite_TeaPot: //I'm a teapot - h.Parent.logRequest(r, false, 418, "root-teapot", domainOnly, "") + h.Parent.logRequest(r, false, 418, "root-teapot", domainOnly, "", h.Parent.Root) http.Error(w, "I'm a teapot", http.StatusTeapot) default: //Unknown routing option. Send empty response - h.Parent.logRequest(r, false, 544, "root-unknown", domainOnly, "") + h.Parent.logRequest(r, false, 544, "root-unknown", domainOnly, "", h.Parent.Root) http.Error(w, "544 - No Route Defined", 544) } } diff --git a/src/mod/dynamicproxy/access.go b/src/mod/dynamicproxy/access.go index dca919c..1044fc3 100644 --- a/src/mod/dynamicproxy/access.go +++ b/src/mod/dynamicproxy/access.go @@ -13,7 +13,7 @@ import ( // Handle access check (blacklist / whitelist), return true if request is handled (aka blocked) // if the return value is false, you can continue process the response writer -func (h *ProxyHandler) handleAccessRouting(ruleID string, w http.ResponseWriter, r *http.Request) bool { +func (h *ProxyHandler) handleAccessRouting(ruleID string, w http.ResponseWriter, r *http.Request, sep *ProxyEndpoint) bool { accessRule, err := h.Parent.Option.AccessController.GetAccessRuleByID(ruleID) if err != nil { //Unable to load access rule. Target rule not found? @@ -25,7 +25,7 @@ func (h *ProxyHandler) handleAccessRouting(ruleID string, w http.ResponseWriter, isBlocked, blockedReason := accessRequestBlocked(accessRule, h.Parent.Option.WebDirectory, w, r) if isBlocked { - h.Parent.logRequest(r, false, 403, blockedReason, r.Host, "") + h.Parent.logRequest(r, false, 403, blockedReason, r.Host, "", sep) } return isBlocked } diff --git a/src/mod/dynamicproxy/dynamicproxy.go b/src/mod/dynamicproxy/dynamicproxy.go index c4421c1..f90f1d2 100644 --- a/src/mod/dynamicproxy/dynamicproxy.go +++ b/src/mod/dynamicproxy/dynamicproxy.go @@ -156,7 +156,7 @@ func (router *Router) StartProxyService() error { if err != nil { http.ServeFile(w, r, "./web/hosterror.html") router.Option.Logger.PrintAndLog("dprouter", "failed to get upstream for hostname", err) - router.logRequest(r, false, 404, "vdir-http", r.Host, "") + router.logRequest(r, false, 404, "vdir-http", r.Host, "", sep) } endpointProxyRewriteRules := GetDefaultHeaderRewriteRules() diff --git a/src/mod/dynamicproxy/proxyRequestHandler.go b/src/mod/dynamicproxy/proxyRequestHandler.go index 366dcc4..dba00bd 100644 --- a/src/mod/dynamicproxy/proxyRequestHandler.go +++ b/src/mod/dynamicproxy/proxyRequestHandler.go @@ -141,7 +141,7 @@ func (h *ProxyHandler) upstreamHostSwap(w http.ResponseWriter, r *http.Request, } else { //Endpoint disabled, return 503 http.ServeFile(w, r, "./web/rperror.html") - h.Parent.logRequest(r, false, 521, "host-http", r.Host, upstreamHostname) + h.Parent.logRequest(r, false, 521, "host-http", r.Host, upstreamHostname, currentTarget) } return true } @@ -159,7 +159,7 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe if err != nil { http.ServeFile(w, r, "./web/rperror.html") h.Parent.Option.Logger.PrintAndLog("proxy", "Failed to assign an upstream for this request", err) - h.Parent.logRequest(r, false, 521, "subdomain-http", r.URL.Hostname(), r.Host) + h.Parent.logRequest(r, false, 521, "subdomain-http", r.URL.Hostname(), r.Host, target) return } @@ -187,7 +187,7 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe if selectedUpstream.RequireTLS { u, _ = url.Parse("wss://" + wsRedirectionEndpoint + requestURL) } - h.Parent.logRequest(r, true, 101, "host-websocket", reqHostname, selectedUpstream.OriginIpOrDomain) + h.Parent.logRequest(r, true, 101, "host-websocket", reqHostname, selectedUpstream.OriginIpOrDomain, target) if target.HeaderRewriteRules == nil { target.HeaderRewriteRules = GetDefaultHeaderRewriteRules() @@ -249,18 +249,18 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe if err != nil { if errors.As(err, &dnsError) { http.ServeFile(w, r, "./web/hosterror.html") - h.Parent.logRequest(r, false, 404, "host-http", reqHostname, upstreamHostname) + h.Parent.logRequest(r, false, 404, "host-http", reqHostname, upstreamHostname, target) } else if errors.Is(err, context.Canceled) { //Request canceled by client, usually due to manual refresh before page load http.Error(w, "Request canceled", http.StatusRequestTimeout) - h.Parent.logRequest(r, false, http.StatusRequestTimeout, "host-http", reqHostname, upstreamHostname) + h.Parent.logRequest(r, false, http.StatusRequestTimeout, "host-http", reqHostname, upstreamHostname, target) } else { http.ServeFile(w, r, "./web/rperror.html") - h.Parent.logRequest(r, false, 521, "host-http", reqHostname, upstreamHostname) + h.Parent.logRequest(r, false, 521, "host-http", reqHostname, upstreamHostname, target) } } - h.Parent.logRequest(r, true, statusCode, "host-http", reqHostname, upstreamHostname) + h.Parent.logRequest(r, true, statusCode, "host-http", reqHostname, upstreamHostname, target) } // Handle vdir type request @@ -286,7 +286,7 @@ func (h *ProxyHandler) vdirRequest(w http.ResponseWriter, r *http.Request, targe target.parent.HeaderRewriteRules = GetDefaultHeaderRewriteRules() } - h.Parent.logRequest(r, true, 101, "vdir-websocket", r.Host, target.Domain) + h.Parent.logRequest(r, true, 101, "vdir-websocket", r.Host, target.Domain, target.parent) wspHandler := websocketproxy.NewProxy(u, websocketproxy.Options{ SkipTLSValidation: target.SkipCertValidations, SkipOriginCheck: true, //You should not use websocket via virtual directory. But keep this to true for compatibility @@ -342,19 +342,25 @@ func (h *ProxyHandler) vdirRequest(w http.ResponseWriter, r *http.Request, targe if errors.As(err, &dnsError) { http.ServeFile(w, r, "./web/hosterror.html") log.Println(err.Error()) - h.Parent.logRequest(r, false, 404, "vdir-http", reqHostname, target.Domain) + h.Parent.logRequest(r, false, 404, "vdir-http", reqHostname, target.Domain, target.parent) } else { http.ServeFile(w, r, "./web/rperror.html") log.Println(err.Error()) - h.Parent.logRequest(r, false, 521, "vdir-http", reqHostname, target.Domain) + h.Parent.logRequest(r, false, 521, "vdir-http", reqHostname, target.Domain, target.parent) } } - h.Parent.logRequest(r, true, statusCode, "vdir-http", reqHostname, target.Domain) + h.Parent.logRequest(r, true, statusCode, "vdir-http", reqHostname, target.Domain, target.parent) } // This logger collect data for the statistical analysis. For log to file logger, check the Logger and LogHTTPRequest handler -func (router *Router) logRequest(r *http.Request, succ bool, statusCode int, forwardType string, originalHostname string, upstreamHostname string) { +func (router *Router) logRequest(r *http.Request, succ bool, statusCode int, forwardType string, originalHostname string, upstreamHostname string, endpoint *ProxyEndpoint) { + if endpoint != nil && endpoint.DisableLogging { + // Notes: endpoint can be nil if the request has been handled before a host name can be resolved + // e.g. Redirection matching rule + // in that case we will log it by default and will not enter this routine + return + } if router.Option.StatisticCollector != nil { go func() { requestInfo := statistic.RequestInfo{ diff --git a/src/mod/dynamicproxy/ratelimit.go b/src/mod/dynamicproxy/ratelimit.go index a809cc9..04fa1e7 100644 --- a/src/mod/dynamicproxy/ratelimit.go +++ b/src/mod/dynamicproxy/ratelimit.go @@ -51,7 +51,7 @@ func (t *RequestCountPerIpTable) Clear() { func (h *ProxyHandler) handleRateLimitRouting(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint) error { err := h.Parent.handleRateLimit(w, r, pe) if err != nil { - h.Parent.logRequest(r, false, 429, "ratelimit", r.URL.Hostname(), "") + h.Parent.logRequest(r, false, 429, "ratelimit", r.URL.Hostname(), "", pe) } return err } diff --git a/src/mod/dynamicproxy/typedef.go b/src/mod/dynamicproxy/typedef.go index 3caf5b3..414b6d7 100644 --- a/src/mod/dynamicproxy/typedef.go +++ b/src/mod/dynamicproxy/typedef.go @@ -208,6 +208,7 @@ type ProxyEndpoint struct { //Uptime Monitor DisableUptimeMonitor bool //Disable uptime monitor for this endpoint + DisableLogging bool //Disable logging of reverse proxy requests // Chunked Transfer Encoding DisableChunkedTransferEncoding bool //Disable chunked transfer encoding for this endpoint diff --git a/src/reverseproxy.go b/src/reverseproxy.go index 9eea778..652ae66 100644 --- a/src/reverseproxy.go +++ b/src/reverseproxy.go @@ -244,6 +244,9 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) { enableUtm = true } + // Disable logging? + disableLog, _ := utils.PostBool(r, "disableLog") + useBypassGlobalTLS := bypassGlobalTLS == "true" //Enable TLS validation? @@ -416,6 +419,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) { Tags: tags, DisableUptimeMonitor: !enableUtm, + DisableLogging: disableLog, } preparedEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(&thisProxyEndpoint) @@ -570,6 +574,9 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { // Disable chunked Encoding disableChunkedEncoding, _ := utils.PostBool(r, "dChunkedEnc") + // Disable logging + disableLogging, _ := utils.PostBool(r, "dLogging") + //Load the previous basic auth credentials from current proxy rules targetProxyEntry, err := dynamicProxyRouter.LoadProxy(rootNameOrMatchingDomain) if err != nil { @@ -611,6 +618,7 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { newProxyEndpoint.UseStickySession = useStickySession newProxyEndpoint.DisableUptimeMonitor = disbleUtm newProxyEndpoint.DisableChunkedTransferEncoding = disableChunkedEncoding + newProxyEndpoint.DisableLogging = disableLogging newProxyEndpoint.Tags = tags //Prepare to replace the current routing rule diff --git a/src/web/components/httprp.html b/src/web/components/httprp.html index 6e63f80..15de241 100644 --- a/src/web/components/httprp.html +++ b/src/web/components/httprp.html @@ -284,6 +284,12 @@ +
+
+ + +
@@ -885,6 +891,7 @@ let rateLimit = $(editor).find(".RateLimit").val(); let bypassGlobalTLS = $(editor).find(".BypassGlobalTLS")[0].checked; let disableChunkedTransferEncoding = $(editor).find(".DisableChunkedTransferEncoding")[0].checked; + let disableLogging = $(editor).find(".DisableLogging")[0].checked; let tags = getTagsArrayFromEndpoint(uuid); if (tags.length > 0){ tags = tags.join(","); @@ -901,6 +908,7 @@ "authprovider" :authProviderType, "rate" :requireRateLimit, "dChunkedEnc": disableChunkedTransferEncoding, + "dLogging": disableLogging, "ratenum" :rateLimit, "tags": tags, }; @@ -1238,6 +1246,12 @@ editor.find(".BypassGlobalTLS").on("change", function() { saveProxyInlineEdit(uuid); }); + + editor.find(".DisableLogging").off('change'); + editor.find(".DisableLogging").prop("checked", subd.DisableLogging); + editor.find(".DisableLogging").on("change", function() { + saveProxyInlineEdit(uuid); + }); //Bind the edit button editor.find(".downstream_primary_hostname_edit_btn").off("click").on("click", function(){ diff --git a/src/web/components/rules.html b/src/web/components/rules.html index 5a60d14..c017f21 100644 --- a/src/web/components/rules.html +++ b/src/web/components/rules.html @@ -63,13 +63,20 @@ -
+
+
+
+ + +
+
@@ -268,6 +275,7 @@ let useStickySessionLB = $("#useStickySessionLB")[0].checked; let tags = $("#proxyTags").val().trim(); let enableUtm = $("#enableUtm")[0].checked; + let disableLog = $("#disableLog")[0].checked; if (rootname.trim() == ""){ $("#rootname").parent().addClass("error"); @@ -302,7 +310,8 @@ access: accessRuleToUse, stickysess: useStickySessionLB, tags: tags, - enableUtm: enableUtm, + enableUtm: enableUtm, + disableLog: disableLog, }, success: function(data){ if (data.error != undefined){