From 16b4ca0c26d05a9ad9ce7869bbaa7edb2d8acab1 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Wed, 27 May 2026 20:43:05 +0200 Subject: [PATCH] chore(logging): include request + caller context in jsonMsgObj warnings The generic "X-UI: Something went wrong. Error: record not found" log gave no clue about which endpoint, client, or controller line emitted it. Prepend a context block: [POST /panel/api/clients/del/ADMIN ip=109.124.234.127 handler=controller.(*ClientController).delete client.go:146] Handler frame is located by scanning the stack for the first caller outside util.go, so it points at the right controller method whether the path went through jsonMsg, jsonObj, or jsonMsgObj directly. --- web/controller/util.go | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/web/controller/util.go b/web/controller/util.go index 7d77f580..d9358828 100644 --- a/web/controller/util.go +++ b/web/controller/util.go @@ -5,6 +5,8 @@ import ( "net" "net/http" "net/netip" + "path/filepath" + "runtime" "strings" "github.com/mhsanaei/3x-ui/v3/logger" @@ -125,6 +127,32 @@ func jsonObj(c *gin.Context, obj any, err error) { jsonMsgObj(c, "", obj, err) } +func requestErrorContext(c *gin.Context) string { + handler, loc := callerOutsideUtil() + return fmt.Sprintf("[%s %s handler=%s %s]", c.Request.Method, c.Request.URL.Path, handler, loc) +} + +func callerOutsideUtil() (string, string) { + var pcs [12]uintptr + n := runtime.Callers(2, pcs[:]) + frames := runtime.CallersFrames(pcs[:n]) + for { + frame, more := frames.Next() + base := filepath.Base(frame.File) + if base != "util.go" { + name := frame.Function + if idx := strings.LastIndex(name, "/"); idx >= 0 { + name = name[idx+1:] + } + return name, fmt.Sprintf("%s:%d", base, frame.Line) + } + if !more { + break + } + } + return "unknown", "unknown" +} + // jsonMsgObj sends a JSON response with a message, object, and error status. func jsonMsgObj(c *gin.Context, msg string, obj any, err error) { m := entity.Msg{ @@ -137,16 +165,18 @@ func jsonMsgObj(c *gin.Context, msg string, obj any, err error) { } } else { m.Success = false + ctx := requestErrorContext(c) + fail := I18nWeb(c, "fail") errStr := err.Error() if errStr != "" { m.Msg = msg + " (" + errStr + ")" - logger.Warning(msg+" "+I18nWeb(c, "fail")+": ", err) + logger.Warningf("%s %s %s: %v", ctx, msg, fail, err) } else if msg != "" { m.Msg = msg - logger.Warning(msg + " " + I18nWeb(c, "fail")) + logger.Warningf("%s %s %s", ctx, msg, fail) } else { m.Msg = I18nWeb(c, "somethingWentWrong") - logger.Warning(I18nWeb(c, "somethingWentWrong") + " " + I18nWeb(c, "fail")) + logger.Warningf("%s %s %s", ctx, m.Msg, fail) } } c.JSON(http.StatusOK, m)