fix(gateway): log startup errors before exit (#2414)
* fix(gateway): log startup errors before exit * preserve deferred startup failure logging
This commit is contained in:
parent
282ebcd956
commit
491418775b
2 changed files with 122 additions and 3 deletions
|
|
@ -111,7 +111,7 @@ func (p *startupBlockedProvider) GetDefaultModel() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run starts the gateway runtime using the configuration loaded from configPath.
|
// Run starts the gateway runtime using the configuration loaded from configPath.
|
||||||
func Run(debug bool, homePath, configPath string, allowEmptyStartup bool) error {
|
func Run(debug bool, homePath, configPath string, allowEmptyStartup bool) (runErr error) {
|
||||||
panicPath := filepath.Join(homePath, logPath, panicFile)
|
panicPath := filepath.Join(homePath, logPath, panicFile)
|
||||||
panicFunc, err := logger.InitPanic(panicPath)
|
panicFunc, err := logger.InitPanic(panicPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -129,14 +129,25 @@ func Run(debug bool, homePath, configPath string, allowEmptyStartup bool) error
|
||||||
} else {
|
} else {
|
||||||
logger.SetLevelFromString(config.ResolveGatewayLogLevel(configPath))
|
logger.SetLevelFromString(config.ResolveGatewayLogLevel(configPath))
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
if runErr != nil {
|
||||||
|
logger.ErrorCF("gateway", "Gateway startup failed", map[string]any{
|
||||||
|
"config_path": configPath,
|
||||||
|
"error": runErr.Error(),
|
||||||
|
"home_path": homePath,
|
||||||
|
"allow_empty": allowEmptyStartup,
|
||||||
|
"debug": debug,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
cfg, err := config.LoadConfig(configPath)
|
cfg, err := config.LoadConfig(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatalf("error loading config: %v", err)
|
return fmt.Errorf("error loading config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = preCheckConfig(cfg); err != nil {
|
if err = preCheckConfig(cfg); err != nil {
|
||||||
logger.Fatalf("config pre-check failed: %v", err)
|
return fmt.Errorf("config pre-check failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug mode permanently overrides the config log level to DEBUG.
|
// Debug mode permanently overrides the config log level to DEBUG.
|
||||||
|
|
|
||||||
108
pkg/gateway/gateway_test.go
Normal file
108
pkg/gateway/gateway_test.go
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/sipeed/picoclaw/pkg/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRun_StartupFailuresReturnErrorAndEmitStructuredLog(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
prepare func(t *testing.T, dir string) string
|
||||||
|
wantErr string
|
||||||
|
wantLogSub string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "invalid config returns load error",
|
||||||
|
prepare: func(t *testing.T, dir string) string {
|
||||||
|
t.Helper()
|
||||||
|
cfgPath := filepath.Join(dir, "invalid-config.json")
|
||||||
|
if err := os.WriteFile(cfgPath, []byte("{invalid-json"), 0o644); err != nil {
|
||||||
|
t.Fatalf("WriteFile(invalid config) error = %v", err)
|
||||||
|
}
|
||||||
|
return cfgPath
|
||||||
|
},
|
||||||
|
wantErr: "error loading config:",
|
||||||
|
wantLogSub: "error loading config:",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid config returns pre-check error",
|
||||||
|
prepare: func(t *testing.T, dir string) string {
|
||||||
|
t.Helper()
|
||||||
|
cfg := config.DefaultConfig()
|
||||||
|
cfg.Gateway.Port = 0
|
||||||
|
cfgPath := filepath.Join(dir, "config.json")
|
||||||
|
if err := config.SaveConfig(cfgPath, cfg); err != nil {
|
||||||
|
t.Fatalf("SaveConfig() error = %v", err)
|
||||||
|
}
|
||||||
|
return cfgPath
|
||||||
|
},
|
||||||
|
wantErr: "config pre-check failed: invalid gateway port: 0",
|
||||||
|
wantLogSub: "config pre-check failed: invalid gateway port: 0",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
homeDir := t.TempDir()
|
||||||
|
configPath := tt.prepare(t, homeDir)
|
||||||
|
|
||||||
|
cmd := exec.Command(os.Args[0], "-test.run=TestGatewayRunStartupFailureHelper")
|
||||||
|
cmd.Env = append(os.Environ(),
|
||||||
|
"GO_WANT_GATEWAY_RUN_HELPER=1",
|
||||||
|
"PICO_TEST_HOME="+homeDir,
|
||||||
|
"PICO_TEST_CONFIG="+configPath,
|
||||||
|
)
|
||||||
|
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("helper exited unexpectedly: %v\noutput:\n%s", err, string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
out := string(output)
|
||||||
|
if !strings.Contains(out, tt.wantErr) {
|
||||||
|
t.Fatalf("helper output missing expected error substring %q:\n%s", tt.wantErr, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
logData, readErr := os.ReadFile(filepath.Join(homeDir, logPath, logFile))
|
||||||
|
if readErr != nil {
|
||||||
|
t.Fatalf("ReadFile(gateway.log) error = %v", readErr)
|
||||||
|
}
|
||||||
|
logText := string(logData)
|
||||||
|
if !strings.Contains(logText, "Gateway startup failed") {
|
||||||
|
t.Fatalf("gateway.log missing structured startup failure log:\n%s", logText)
|
||||||
|
}
|
||||||
|
if !strings.Contains(logText, tt.wantLogSub) {
|
||||||
|
t.Fatalf("gateway.log missing expected failure detail %q:\n%s", tt.wantLogSub, logText)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGatewayRunStartupFailureHelper(t *testing.T) {
|
||||||
|
if os.Getenv("GO_WANT_GATEWAY_RUN_HELPER") != "1" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
homeDir := os.Getenv("PICO_TEST_HOME")
|
||||||
|
configPath := os.Getenv("PICO_TEST_CONFIG")
|
||||||
|
|
||||||
|
err := Run(false, homeDir, configPath, false)
|
||||||
|
if err == nil {
|
||||||
|
fmt.Fprintln(os.Stdout, "expected startup error, got nil")
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(os.Stdout, err.Error())
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue