From be8e6ab08bbcc39990fd6f99299c05d523a77b2f Mon Sep 17 00:00:00 2001 From: xvlet Date: Wed, 29 Jul 2026 12:27:09 +0900 Subject: [PATCH] feat(echosvr): enhance startup logging and streamline build process - Build: Updated Makefile to simplify local builds using a temporary staging directory and streamlined the `all` and `clean` targets. - Config: Simplified config.yml mock route examples into a unified `/api/mock` endpoint demonstrating dynamic routing, delayed responses, and custom headers. - Core: Generated a unique UUID (startupReqID) for startup logs to ensure consistent log formatting without empty spaces. - Core: Formatted the HTTP and WebSocket server startup logs with cyan arrow headers for better terminal visibility. - Core: Highlighted the wildcard (Catch-all) route log with bright yellow text for quick identification. - Core: Refactored WebSocket route registration logging to match the standardized HTTP route output format. --- .gitignore | 2 ++ Makefile | 21 ++++++++++++++++++--- config.yml | 36 +++++------------------------------- main.go | 50 +++++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 66 insertions(+), 43 deletions(-) diff --git a/.gitignore b/.gitignore index 769307a..d464a56 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ build/ *.log + +logs/ diff --git a/Makefile b/Makefile index 6b8894a..9b370d0 100644 --- a/Makefile +++ b/Makefile @@ -11,9 +11,24 @@ export GOTOOLCHAIN=auto MKDOCDIR = mkdir -p SBOM_DIR = build -.PHONY: all clean check docs linux_amd64 linux_arm64 darwin_amd64 darwin_arm64 windows_amd64 windows_arm64 aix_ppc64 +STAGING_DIR = build_staging -all: clean linux_amd64 linux_arm64 darwin_amd64 darwin_arm64 windows_amd64 windows_arm64 aix_ppc64 +.PHONY: all build clean check docs release linux_amd64 linux_arm64 darwin_amd64 darwin_arm64 windows_amd64 windows_arm64 aix_ppc64 + +all: build + +build: clean + @echo "Starting build process in staging directory: $(STAGING_DIR)..." + @rm -rf $(STAGING_DIR) + @mkdir -p $(STAGING_DIR) + @echo "Performing Go build (CGO_ENABLED=0)..." + CGO_ENABLED=0 go build -ldflags="$(LDFLAGS)" -o $(STAGING_DIR)/$(APP_NAME) $(CMD_PATH) + @if [ -f config.yml ]; then cp config.yml $(STAGING_DIR)/; fi + @echo "Build successful. Finalizing build directory..."; \ + rm -rf $(BASE_DIR); \ + mv $(STAGING_DIR) $(BASE_DIR) + +release: clean linux_amd64 linux_arm64 darwin_amd64 darwin_arm64 windows_amd64 windows_arm64 aix_ppc64 linux_amd64: @echo "Building for linux/amd64..." @@ -79,7 +94,7 @@ aix_ppc64: @rm -rf build_staging_$@ clean: - rm -rf $(BASE_DIR) build_staging_* + rm -rf $(BASE_DIR) $(STAGING_DIR) build_staging_* check: golangci-lint run diff --git a/config.yml b/config.yml index fff03c2..eec4fe7 100644 --- a/config.yml +++ b/config.yml @@ -51,41 +51,15 @@ server: routes: # ----------------------------------------------------- - # Example 1: Standard Echo Endpoint (Explicitly defined) + # Example: Dynamic Route with Multiple Methods and Custom Response # ----------------------------------------------------- - - path: "/api/echo" - method: "POST,PUT,PATCH" - # No custom body/status defined, so it will echo the request payload back - - # ----------------------------------------------------- - # Example 2: Forced Error Response - # ----------------------------------------------------- - - path: "/api/error" - method: "GET,POST" - status_code: 500 - response_headers: - "X-Error-Code": "ERR-999" - response_body: '{"error": "Internal Server Error Simulation"}' - - # ----------------------------------------------------- - # Example 3: Simulated Latency (e.g., for Timeout Testing) - # ----------------------------------------------------- - - path: "/api/slow" - method: "GET" + - path: "/api/mock" + method: "GET,POST,PUT" status_code: 200 - response_body: "This response was delayed by 500ms" delay_ms: 500 - - # ----------------------------------------------------- - # Example 4: Custom Headers and JSON Response - # ----------------------------------------------------- - - path: "/api/auth/login" - method: "POST" - status_code: 200 response_headers: - "Content-Type": "application/json" - "Authorization": "Bearer mock-token-12345" - response_body: '{"status": "success", "userId": "mock_user"}' + "X-Mock-Status": "Active" + response_body: '{"message": "This is a dynamic mock response delayed by 500ms"}' # ----------------------------------------------------- # VJM Internal Test Routes (Legacy/Compatibility) diff --git a/main.go b/main.go index e0d96f5..8a81cf5 100644 --- a/main.go +++ b/main.go @@ -66,8 +66,21 @@ func main() { } defer logger.Sync() + var startupReqID string + b := make([]byte, 16) + if _, err := rand.Read(b); err == nil { + b[6] = (b[6] & 0x0f) | 0x40 + b[8] = (b[8] & 0x3f) | 0x80 + startupReqID = fmt.Sprintf("%08x-%04x-%04x-%04x-%012x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) + } else { + startupReqID = "startup" + } + if logger.S != nil { - logger.S.Infof("Loaded configuration: port=%d, routes=%d", config.Server.Port, len(config.Server.Routes)) + logger.WithReqID(startupReqID).Infof("\x1b[38;5;45m▶ HTTP Server\x1b[0m") + logger.WithReqID(startupReqID).Infof("Loaded configuration: port=%d, routes=%d", config.Server.Port, len(config.Server.Routes)) + } else { + fmt.Println("\x1b[38;5;45m▶ HTTP Server\x1b[0m") } // Create Echo instance @@ -104,9 +117,9 @@ func main() { for _, r := range config.Server.Routes { route := r // localized copy for closure if logger.S != nil { - logger.S.Infof("Registering route: [%s] %s (ResponseHeaders: %v)", route.Method, route.Path, route.ResponseHeaders) + logger.WithReqID(startupReqID).Infof("Registering route: [%s] %s", route.Method, route.Path) } else { - fmt.Printf("Registering route: [%s] %s (ResponseHeaders: %v)\n", route.Method, route.Path, route.ResponseHeaders) + fmt.Printf("Registering route: [%s] %s\n", route.Method, route.Path) } methods := strings.Split(route.Method, ",") @@ -138,11 +151,23 @@ func main() { } // Always provide a catch-all fallback for undefined routes + if logger.S != nil { + logger.WithReqID(startupReqID).Infof("Registering wildcard route: \x1b[93m[ANY] /* (Catch-all)\x1b[0m") + } else { + fmt.Println("Registering wildcard route: \x1b[93m[ANY] /* (Catch-all)\x1b[0m") + } e.Any("/*", func(c echo.Context) error { return handleAny(c, config.Server.TransactionIDHeader, RouteConfig{}) }) // WebSocket Echo Route + if logger.S != nil { + logger.WithReqID(startupReqID).Infof("") + logger.WithReqID(startupReqID).Infof("\x1b[38;5;45m▶ WebSocket Server\x1b[0m") + } else { + fmt.Println() + fmt.Println("\x1b[38;5;45m▶ WebSocket Server\x1b[0m") + } wsPaths := config.Server.Websocket.Paths wsHandler := echo.WrapHandler(websocket.Handler(func(ws *websocket.Conn) { _, _ = io.Copy(ws, ws) @@ -157,14 +182,16 @@ func main() { wsEcho := echo.New() wsEcho.HideBanner = true wsEcho.HidePort = true + if logger.S != nil { + logger.WithReqID(startupReqID).Infof("Loaded configuration: port=%d, routes=%d", wsPort, len(wsPaths)) + } for _, wsPath := range wsPaths { + if logger.S != nil { + logger.WithReqID(startupReqID).Infof("Registering route: [%s]", wsPath) + } wsEcho.GET(wsPath, wsHandler) } go func() { - if logger.S != nil { - logger.S.Infof("Starting WebSocket server on port: %d, paths: %v", wsPort, wsPaths) - } - // Output directly with the exact same format and color (ANSI Green) as the Echo framework fmt.Printf("⇨ websocket server started on \x1b[32m[::]:%d\x1b[0m\n", wsPort) @@ -173,11 +200,16 @@ func main() { } else { // Run WebSocket on the same HTTP port if logger.S != nil { - logger.S.Infof("Starting WebSocket server on HTTP port: %d, paths: %v", config.Server.Port, wsPaths) + logger.WithReqID(startupReqID).Infof("Loaded configuration: port=%d, routes=%d", config.Server.Port, len(wsPaths)) } else { - fmt.Printf("Starting WebSocket server on HTTP port: %d, paths: %v\n", config.Server.Port, wsPaths) + fmt.Printf("Loaded configuration: port=%d, routes=%d\n", config.Server.Port, len(wsPaths)) } for _, wsPath := range wsPaths { + if logger.S != nil { + logger.WithReqID(startupReqID).Infof("Registering route: [%s]", wsPath) + } else { + fmt.Printf("Registering route: [%s]\n", wsPath) + } e.GET(wsPath, wsHandler) } }