reliability: wrap all 27 bare goroutines with safe.Go/GoNamed panic recovery

Applied safe.GoNamed to:
- 9 exchange order_sync goroutines (OKX, Hyperliquid, Aster, Bybit, KuCoin, Gate, Bitget, Lighter, Binance×2)
- Drawdown monitor (auto_trader_risk.go)
- Brain news scanner + market briefs (agent/brain.go)
- Sentinel scanner (agent/sentinel.go)
- Agent scheduler (agent/scheduler.go)
- x402 idle watchdog (mcp/payment/x402.go)
- MCP stream idle watchdog (mcp/client.go)
- Rate limiter cleanup (api/rate_limiter.go)
- 3 telemetry fire-and-forget sends (telemetry/experience.go)
- CoinAnk WS handler (provider/coinank/coinank_api/kline_ws.go)
- API server goroutine (main.go)

Added manual defer/recover with error reporting to:
- Telegram AI agent handler (sends error msg to user on panic)
- Trader data fetch (returns error result on panic to prevent deadlock)

Before: a panic in ANY of these 27 goroutines would crash the entire
trading process with zero diagnostics. Now all panics are caught, logged
with stack traces, and the process continues running.
This commit is contained in:
shinchan-zhai
2026-03-23 10:30:13 +08:00
parent 07b4f30b85
commit 5e06037fa2
21 changed files with 84 additions and 48 deletions

View File

@@ -5,6 +5,7 @@ import (
"bytes"
"encoding/json"
"net/http"
"nofx/safe"
"sync"
"time"
)
@@ -108,9 +109,9 @@ func TrackTrade(event TradeEvent) {
}
// Send asynchronously to not block trading
go func() {
safe.Go(func() {
_ = sendTradeEvent(event)
}()
})
}
// sendTradeEvent sends the trade event to GA4
@@ -165,7 +166,7 @@ func TrackStartup(version string) {
return
}
go func() {
safe.Go(func() {
client.mu.RLock()
installationID := client.installationID
client.mu.RUnlock()
@@ -194,7 +195,7 @@ func TrackStartup(version string) {
resp.Body.Close()
}
}
}()
})
}
func TrackAIUsage(event AIUsageEvent) {
@@ -202,7 +203,7 @@ func TrackAIUsage(event AIUsageEvent) {
return
}
go func() {
safe.Go(func() {
client.mu.RLock()
installationID := client.installationID
client.mu.RUnlock()
@@ -238,5 +239,5 @@ func TrackAIUsage(event AIUsageEvent) {
resp.Body.Close()
}
}
}()
})
}