From 9bb5c54e7c96b7af99a28f5a6d3d8ed1efbbbc2e Mon Sep 17 00:00:00 2001 From: yusing Date: Mon, 23 Feb 2026 23:09:24 +0800 Subject: [PATCH] refactor(rules): defer error logging until after FlushRelease Split error handling into isUnexpectedError predicate and logFlushError function. Use rm.AppendError() to collect unexpected errors during rule execution, then log after FlushRelease completes rather than immediately. Also updates goutils dependency for AppendError method availability. --- goutils | 2 +- internal/route/rules/rules.go | 34 +++++++++++++++++++++++++--------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/goutils b/goutils index 482b5bca..3be815cb 160000 --- a/goutils +++ b/goutils @@ -1 +1 @@ -Subproject commit 482b5bca9f2eae9d293c1cc46674635a201af231 +Subproject commit 3be815cb6e3b7b872d71dd041427ee6674683bda diff --git a/internal/route/rules/rules.go b/internal/route/rules/rules.go index 04cf8280..f69f47bd 100644 --- a/internal/route/rules/rules.go +++ b/internal/route/rules/rules.go @@ -288,7 +288,7 @@ func (rules Rules) BuildHandler(up http.HandlerFunc) http.HandlerFunc { rm := httputils.NewResponseModifier(w) defer func() { if _, err := rm.FlushRelease(); err != nil { - logError(err, r) + logFlushError(err, r) } }() @@ -323,7 +323,10 @@ func (rules Rules) BuildHandler(up http.HandlerFunc) http.HandlerFunc { preTerminated = true continue } - logError(err, r) + if isUnexpectedError(err) { + // will logged by logFlushError after FlushRelease + rm.AppendError("executing pre rule (%s): %w", rule.Do.raw, err) + } hasError = true } } @@ -346,7 +349,10 @@ func (rules Rules) BuildHandler(up http.HandlerFunc) http.HandlerFunc { if errors.Is(err, errTerminateRule) { continue } - logError(err, r) + if isUnexpectedError(err) { + // will logged by logFlushError after FlushRelease + rm.AppendError("executing post rule (%s): %w", rule.Do.raw, err) + } } } @@ -361,13 +367,19 @@ func (rules Rules) BuildHandler(up http.HandlerFunc) http.HandlerFunc { if errors.Is(err, errTerminateRule) { continue } - logError(err, r) + if isUnexpectedError(err) { + // will logged by logFlushError after FlushRelease + rm.AppendError("executing pre rule (%s): %w", rule.Do.raw, err) + } } if err := execPostCommand(rule.Do, rm, r); err != nil { if errors.Is(err, errTerminateRule) { continue } - logError(err, r) + if isUnexpectedError(err) { + // will logged by logFlushError after FlushRelease + rm.AppendError("executing post rule (%s): %w", rule.Do.raw, err) + } } } } @@ -390,14 +402,14 @@ var errStreamClosed error //go:linkname errClientDisconnected golang.org/x/net/http2.errClientDisconnected var errClientDisconnected error -func logError(err error, r *http.Request) { +func isUnexpectedError(err error) bool { if errors.Is(err, errStreamClosed) || errors.Is(err, errClientDisconnected) { - return + return false } if h2Err, ok := errors.AsType[http2.StreamError](err); ok { // ignore these errors if h2Err.Code == http2.ErrCodeStreamClosed { - return + return false } } if h3Err, ok := errors.AsType[*http3.Error](err); ok { @@ -406,8 +418,12 @@ func logError(err error, r *http.Request) { case http3.ErrCodeNoError, http3.ErrCodeRequestCanceled: - return + return false } } + return true +} + +func logFlushError(err error, r *http.Request) { log.Err(err).Str("method", r.Method).Str("url", r.Host+r.URL.Path).Msg("error executing rules") }