feat: 重构日志处理,使用slog包替换原有日志函数,增强日志记录能力

This commit is contained in:
ryan
2026-03-13 15:25:33 +08:00
parent 79a7e024d3
commit 6d58d00c9d
12 changed files with 58 additions and 60 deletions
+5 -2
View File
@@ -3,6 +3,7 @@ package common
import ( import (
"flag" "flag"
"fmt" "fmt"
"log/slog"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@@ -58,12 +59,14 @@ func init() {
var err error var err error
*LogDir, err = filepath.Abs(*LogDir) *LogDir, err = filepath.Abs(*LogDir)
if err != nil { if err != nil {
FatalLog(err) slog.Error("resolve log directory failed", "error", err)
os.Exit(1)
} }
if _, err := os.Stat(*LogDir); os.IsNotExist(err) { if _, err := os.Stat(*LogDir); os.IsNotExist(err) {
err = os.Mkdir(*LogDir, 0777) err = os.Mkdir(*LogDir, 0777)
if err != nil { if err != nil {
FatalLog(err) slog.Error("create log directory failed", "error", err)
os.Exit(1)
} }
} }
} }
-19
View File
@@ -192,25 +192,6 @@ func SetupGinLog() {
ensureLogger() ensureLogger()
} }
func SysLog(s string) {
if !shouldLog(logLevelInfo) {
return
}
ensureLogger().Info(s)
}
func SysError(s string) {
if !shouldLog(logLevelError) {
return
}
ensureLogger().Error(s)
}
func FatalLog(v ...any) {
ensureLogger().Error("fatal error", "details", v)
os.Exit(1)
}
func levelLabel(level slog.Level) string { func levelLabel(level slog.Level) string {
switch { switch {
case level <= slog.LevelDebug: case level <= slog.LevelDebug:
+2 -1
View File
@@ -3,6 +3,7 @@ package common
import ( import (
"context" "context"
"github.com/go-redis/redis/v8" "github.com/go-redis/redis/v8"
"log/slog"
"os" "os"
"time" "time"
) )
@@ -14,7 +15,7 @@ var RedisEnabled = true
func InitRedisClient() (err error) { func InitRedisClient() (err error) {
if os.Getenv("REDIS_CONN_STRING") == "" { if os.Getenv("REDIS_CONN_STRING") == "" {
RedisEnabled = false RedisEnabled = false
SysLog("REDIS_CONN_STRING not set, Redis is not enabled") slog.Info("redis disabled because REDIS_CONN_STRING is not set")
return nil return nil
} }
opt, err := redis.ParseURL(os.Getenv("REDIS_CONN_STRING")) opt, err := redis.ParseURL(os.Getenv("REDIS_CONN_STRING"))
+3 -2
View File
@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"github.com/google/uuid" "github.com/google/uuid"
"html/template" "html/template"
"log/slog"
"net" "net"
"os/exec" "os/exec"
"runtime" "runtime"
@@ -23,14 +24,14 @@ func OpenBrowser(url string) {
err = exec.Command("open", url).Start() err = exec.Command("open", url).Start()
} }
if err != nil { if err != nil {
SysError(err.Error()) slog.Error("open browser failed", "error", err)
} }
} }
func GetIp() (ip string) { func GetIp() (ip string) {
ips, err := net.InterfaceAddrs() ips, err := net.InterfaceAddrs()
if err != nil { if err != nil {
SysError(err.Error()) slog.Error("get interface addresses failed", "error", err)
return ip return ip
} }
+3 -2
View File
@@ -9,6 +9,7 @@ import (
"fmt" "fmt"
"github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"log/slog"
"net/http" "net/http"
"strconv" "strconv"
"time" "time"
@@ -46,7 +47,7 @@ func getGitHubUserInfoByCode(code string) (*GitHubUser, error) {
} }
res, err := client.Do(req) res, err := client.Do(req)
if err != nil { if err != nil {
common.SysLog(err.Error()) slog.Error("github oauth access token request failed", "error", err)
return nil, errors.New("无法连接至 GitHub 服务器,请稍后重试!") return nil, errors.New("无法连接至 GitHub 服务器,请稍后重试!")
} }
defer res.Body.Close() defer res.Body.Close()
@@ -62,7 +63,7 @@ func getGitHubUserInfoByCode(code string) (*GitHubUser, error) {
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", oAuthResponse.AccessToken)) req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", oAuthResponse.AccessToken))
res2, err := client.Do(req) res2, err := client.Do(req)
if err != nil { if err != nil {
common.SysLog(err.Error()) slog.Error("github user info request failed", "error", err)
return nil, errors.New("无法连接至 GitHub 服务器,请稍后重试!") return nil, errors.New("无法连接至 GitHub 服务器,请稍后重试!")
} }
defer res2.Body.Close() defer res2.Body.Close()
+11 -7
View File
@@ -12,6 +12,7 @@ import (
"github.com/gin-contrib/sessions/cookie" "github.com/gin-contrib/sessions/cookie"
"github.com/gin-contrib/sessions/redis" "github.com/gin-contrib/sessions/redis"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"log/slog"
"os" "os"
"strconv" "strconv"
) )
@@ -37,26 +38,29 @@ var indexPage []byte
// @description Agent API 使用节点专属 Agent Token 或全局 Discovery Token // @description Agent API 使用节点专属 Agent Token 或全局 Discovery Token
func main() { func main() {
common.SetupGinLog() common.SetupGinLog()
common.SysLog("ATSFlare " + common.Version + " started") slog.Info("ATSFlare started", "version", common.Version)
if os.Getenv("GIN_MODE") != "debug" { if os.Getenv("GIN_MODE") != "debug" {
gin.SetMode(gin.ReleaseMode) gin.SetMode(gin.ReleaseMode)
} }
// Initialize SQL Database // Initialize SQL Database
err := model.InitDB() err := model.InitDB()
if err != nil { if err != nil {
common.FatalLog(err) slog.Error("initialize database failed", "error", err)
os.Exit(1)
} }
defer func() { defer func() {
err := model.CloseDB() err := model.CloseDB()
if err != nil { if err != nil {
common.FatalLog(err) slog.Error("close database failed", "error", err)
os.Exit(1)
} }
}() }()
// Initialize Redis // Initialize Redis
err = common.InitRedisClient() err = common.InitRedisClient()
if err != nil { if err != nil {
common.FatalLog(err) slog.Error("initialize redis failed", "error", err)
os.Exit(1)
} }
// Initialize options // Initialize options
@@ -82,11 +86,11 @@ func main() {
if port == "" { if port == "" {
port = strconv.Itoa(*common.Port) port = strconv.Itoa(*common.Port)
} }
common.SysLog(fmt.Sprintf("server config: port=%s gin_mode=%s log_level=%s sqlite_path=%s redis_enabled=%t upload_path=%s log_dir=%s agent_token_configured=%t node_offline_threshold=%s", port, gin.Mode(), common.GetLogLevel(), common.SQLitePath, common.RedisEnabled, common.UploadPath, valueOrDefault(*common.LogDir, "stdout"), common.AgentToken != "", common.NodeOfflineThreshold)) slog.Info("server config", "port", port, "gin_mode", gin.Mode(), "log_level", common.GetLogLevel(), "sqlite_path", common.SQLitePath, "redis_enabled", common.RedisEnabled, "upload_path", common.UploadPath, "log_dir", valueOrDefault(*common.LogDir, "stdout"), "agent_token_configured", common.AgentToken != "", "node_offline_threshold", common.NodeOfflineThreshold)
common.SysLog(fmt.Sprintf("server listening on :%s", port)) slog.Info("server listening", "address", fmt.Sprintf(":%s", port))
err = server.Run(":" + port) err = server.Run(":" + port)
if err != nil { if err != nil {
common.SysError(err.Error()) slog.Error("server run failed", "error", err)
} }
} }
+4 -3
View File
@@ -4,6 +4,7 @@ import (
"atsflare/common" "atsflare/common"
"context" "context"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"log/slog"
"net/http" "net/http"
"time" "time"
) )
@@ -18,7 +19,7 @@ func redisRateLimiter(c *gin.Context, maxRequestNum int, duration int64, mark st
key := "rateLimit:" + mark + c.ClientIP() key := "rateLimit:" + mark + c.ClientIP()
listLength, err := rdb.LLen(ctx, key).Result() listLength, err := rdb.LLen(ctx, key).Result()
if err != nil { if err != nil {
common.SysError(err.Error()) slog.Error("redis rate limiter llen failed", "error", err)
c.Status(http.StatusInternalServerError) c.Status(http.StatusInternalServerError)
c.Abort() c.Abort()
return return
@@ -30,7 +31,7 @@ func redisRateLimiter(c *gin.Context, maxRequestNum int, duration int64, mark st
oldTimeStr, _ := rdb.LIndex(ctx, key, -1).Result() oldTimeStr, _ := rdb.LIndex(ctx, key, -1).Result()
oldTime, err := time.Parse(timeFormat, oldTimeStr) oldTime, err := time.Parse(timeFormat, oldTimeStr)
if err != nil { if err != nil {
common.SysError(err.Error()) slog.Error("parse redis rate limiter old timestamp failed", "error", err)
c.Status(http.StatusInternalServerError) c.Status(http.StatusInternalServerError)
c.Abort() c.Abort()
return return
@@ -38,7 +39,7 @@ func redisRateLimiter(c *gin.Context, maxRequestNum int, duration int64, mark st
nowTimeStr := time.Now().Format(timeFormat) nowTimeStr := time.Now().Format(timeFormat)
nowTime, err := time.Parse(timeFormat, nowTimeStr) nowTime, err := time.Parse(timeFormat, nowTimeStr)
if err != nil { if err != nil {
common.SysError(err.Error()) slog.Error("parse redis rate limiter current timestamp failed", "error", err)
c.Status(http.StatusInternalServerError) c.Status(http.StatusInternalServerError)
c.Abort() c.Abort()
return return
+3 -2
View File
@@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"log/slog"
"net/http" "net/http"
"net/url" "net/url"
) )
@@ -37,7 +38,7 @@ func TurnstileCheck() gin.HandlerFunc {
"remoteip": {c.ClientIP()}, "remoteip": {c.ClientIP()},
}) })
if err != nil { if err != nil {
common.SysError(err.Error()) slog.Error("turnstile verification request failed", "error", err)
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"success": false, "success": false,
"message": err.Error(), "message": err.Error(),
@@ -49,7 +50,7 @@ func TurnstileCheck() gin.HandlerFunc {
var res turnstileCheckResponse var res turnstileCheckResponse
err = json.NewDecoder(rawRes.Body).Decode(&res) err = json.NewDecoder(rawRes.Body).Decode(&res)
if err != nil { if err != nil {
common.SysError(err.Error()) slog.Error("decode turnstile verification response failed", "error", err)
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"success": false, "success": false,
"message": err.Error(), "message": err.Error(),
+5 -3
View File
@@ -3,6 +3,7 @@ package model
import ( import (
"atsflare/common" "atsflare/common"
"github.com/glebarez/sqlite" "github.com/glebarez/sqlite"
"log/slog"
"gorm.io/driver/mysql" "gorm.io/driver/mysql"
"gorm.io/gorm" "gorm.io/gorm"
"os" "os"
@@ -24,7 +25,7 @@ func createRootAccountIfNeed() error {
var user User var user User
//if user.Status != common.UserStatusEnabled { //if user.Status != common.UserStatusEnabled {
if err := DB.First(&user).Error; err != nil { if err := DB.First(&user).Error; err != nil {
common.SysLog("no user exists, create a root user for you: username is root, password is 123456") slog.Info("no user exists, create a root user", "username", "root")
hashedPassword, err := common.Password2Hash("123456") hashedPassword, err := common.Password2Hash("123456")
if err != nil { if err != nil {
return err return err
@@ -58,7 +59,7 @@ func InitDB() (err error) {
db, err = gorm.Open(sqlite.Open(common.SQLitePath), &gorm.Config{ db, err = gorm.Open(sqlite.Open(common.SQLitePath), &gorm.Config{
PrepareStmt: true, // precompile SQL PrepareStmt: true, // precompile SQL
}) })
common.SysLog("SQL_DSN not set, using SQLite as database") slog.Info("SQL_DSN not set, using SQLite as database")
} }
if err == nil { if err == nil {
DB = db DB = db
@@ -104,7 +105,8 @@ func InitDB() (err error) {
err = createRootAccountIfNeed() err = createRootAccountIfNeed()
return err return err
} else { } else {
common.FatalLog(err) slog.Error("open database failed", "error", err)
os.Exit(1)
} }
return err return err
} }
+9 -8
View File
@@ -5,6 +5,7 @@ import (
"atsflare/model" "atsflare/model"
"encoding/json" "encoding/json"
"errors" "errors"
"log/slog"
"strings" "strings"
"time" "time"
@@ -111,7 +112,7 @@ func RegisterNode(node *model.Node, payload AgentNodePayload) (*AgentRegistratio
} }
func HeartbeatNode(node *model.Node, payload AgentNodePayload) (*HeartbeatResponse, error) { func HeartbeatNode(node *model.Node, payload AgentNodePayload) (*HeartbeatResponse, error) {
common.SysLog("agent heartbeat received: node_id=" + node.NodeID + " current_version=" + strings.TrimSpace(payload.CurrentVersion)) slog.Info("agent heartbeat received", "node_id", node.NodeID, "current_version", strings.TrimSpace(payload.CurrentVersion))
payload.NodeID = node.NodeID payload.NodeID = node.NodeID
payload = normalizeAgentNodePayload(payload) payload = normalizeAgentNodePayload(payload)
if err := validateAgentNodePayload(payload); err != nil { if err := validateAgentNodePayload(payload); err != nil {
@@ -165,7 +166,7 @@ func GetActiveConfigMetaForAgent() (*ActiveConfigMeta, error) {
func GetActiveConfigForAgent() (*AgentConfigResponse, error) { func GetActiveConfigForAgent() (*AgentConfigResponse, error) {
version, err := model.GetActiveConfigVersion() version, err := model.GetActiveConfigVersion()
if err != nil { if err != nil {
common.SysError("agent requested active config but no active version is available") slog.Error("agent requested active config but no active version is available")
return nil, err return nil, err
} }
var supportFiles []SupportFile var supportFiles []SupportFile
@@ -174,7 +175,7 @@ func GetActiveConfigForAgent() (*AgentConfigResponse, error) {
return nil, err return nil, err
} }
} }
common.SysLog("agent fetched active config: version=" + version.Version + " checksum=" + version.Checksum) slog.Info("agent fetched active config", "version", version.Version, "checksum", version.Checksum)
return &AgentConfigResponse{ return &AgentConfigResponse{
Version: version.Version, Version: version.Version,
Checksum: version.Checksum, Checksum: version.Checksum,
@@ -204,7 +205,7 @@ func ReportApplyLog(payload ApplyLogPayload) (*model.ApplyLog, error) {
if payload.Result != ApplyResultOK && payload.Result != ApplyResultFailed { if payload.Result != ApplyResultOK && payload.Result != ApplyResultFailed {
return nil, errors.New("result 仅支持 success 或 failed") return nil, errors.New("result 仅支持 success 或 failed")
} }
common.SysLog("agent apply log received: node_id=" + payload.NodeID + " version=" + payload.Version + " result=" + payload.Result) slog.Info("agent apply log received", "node_id", payload.NodeID, "version", payload.Version, "result", payload.Result)
log := &model.ApplyLog{ log := &model.ApplyLog{
NodeID: payload.NodeID, NodeID: payload.NodeID,
@@ -239,9 +240,9 @@ func ReportApplyLog(payload ApplyLogPayload) (*model.ApplyLog, error) {
return nil, err return nil, err
} }
if payload.Result == ApplyResultOK { if payload.Result == ApplyResultOK {
common.SysLog("agent apply reported success: node_id=" + payload.NodeID + " version=" + payload.Version) slog.Info("agent apply reported success", "node_id", payload.NodeID, "version", payload.Version)
} else { } else {
common.SysError("agent apply reported failure: node_id=" + payload.NodeID + " version=" + payload.Version + " message=" + payload.Message) slog.Error("agent apply reported failure", "node_id", payload.NodeID, "version", payload.Version, "message", payload.Message)
} }
return log, nil return log, nil
} }
@@ -256,9 +257,9 @@ func ListNodeViews() ([]*NodeView, error) {
computedStatus := computeNodeStatus(node) computedStatus := computeNodeStatus(node)
if node.Status != computedStatus { if node.Status != computedStatus {
if computedStatus == NodeStatusOffline { if computedStatus == NodeStatusOffline {
common.SysError("node offline: node_id=" + node.NodeID + " name=" + node.Name + " ip=" + node.IP + " last_seen_at=" + node.LastSeenAt.Format(time.RFC3339)) slog.Error("node offline", "node_id", node.NodeID, "name", node.Name, "ip", node.IP, "last_seen_at", node.LastSeenAt.Format(time.RFC3339))
} else if computedStatus == NodeStatusOnline { } else if computedStatus == NodeStatusOnline {
common.SysLog("node online: node_id=" + node.NodeID + " name=" + node.Name + " ip=" + node.IP) slog.Info("node online", "node_id", node.NodeID, "name", node.Name, "ip", node.IP)
} }
_ = model.DB.Model(node).Update("status", computedStatus).Error _ = model.DB.Model(node).Update("status", computedStatus).Error
node.Status = computedStatus node.Status = computedStatus
+8 -7
View File
@@ -7,6 +7,7 @@ import (
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/hex"
"errors" "errors"
"log/slog"
"strings" "strings"
"time" "time"
) )
@@ -73,7 +74,7 @@ func CreateNode(input NodeInput) (*NodeView, error) {
} }
return nil, err return nil, err
} }
common.SysLog("node created: name=" + node.Name + " node_id=" + node.NodeID) slog.Info("node created", "name", node.Name, "node_id", node.NodeID)
return buildNodeView(node), nil return buildNodeView(node), nil
} }
@@ -91,7 +92,7 @@ func UpdateNode(id uint, input NodeInput) (*NodeView, error) {
if err = node.Update(); err != nil { if err = node.Update(); err != nil {
return nil, err return nil, err
} }
common.SysLog("node updated: name=" + node.Name + " node_id=" + node.NodeID) slog.Info("node updated", "name", node.Name, "node_id", node.NodeID)
return buildNodeView(node), nil return buildNodeView(node), nil
} }
@@ -100,7 +101,7 @@ func DeleteNode(id uint) error {
if err != nil { if err != nil {
return err return err
} }
common.SysLog("node deleted: name=" + node.Name + " node_id=" + node.NodeID) slog.Info("node deleted", "name", node.Name, "node_id", node.NodeID)
return node.Delete() return node.Delete()
} }
@@ -141,7 +142,7 @@ func RequestNodeAgentUpdate(id uint, input NodeAgentUpdateInput) (*NodeView, err
if err = model.DB.Model(node).Select("update_requested", "update_channel", "update_tag").Updates(node).Error; err != nil { if err = model.DB.Model(node).Select("update_requested", "update_channel", "update_tag").Updates(node).Error; err != nil {
return nil, err return nil, err
} }
common.SysLog("agent manual update requested: node_id=" + node.NodeID + " name=" + node.Name + " channel=" + channel.String() + " tag=" + tagName) slog.Info("agent manual update requested", "node_id", node.NodeID, "name", node.Name, "channel", channel.String(), "tag", tagName)
return buildNodeView(node), nil return buildNodeView(node), nil
} }
@@ -154,7 +155,7 @@ func RequestNodeOpenrestyRestart(id uint) (*NodeView, error) {
if err = model.DB.Model(node).Select("restart_openresty_requested").Updates(node).Error; err != nil { if err = model.DB.Model(node).Select("restart_openresty_requested").Updates(node).Error; err != nil {
return nil, err return nil, err
} }
common.SysLog("openresty restart requested: node_id=" + node.NodeID + " name=" + node.Name) slog.Info("openresty restart requested", "node_id", node.NodeID, "name", node.Name)
return buildNodeView(node), nil return buildNodeView(node), nil
} }
@@ -286,7 +287,7 @@ func RegisterNodeWithAgentToken(node *model.Node, payload AgentNodePayload) (*Ag
if err := node.Update(); err != nil { if err := node.Update(); err != nil {
return nil, err return nil, err
} }
common.SysLog("agent register succeeded on reserved node: node_id=" + node.NodeID + " name=" + node.Name) slog.Info("agent register succeeded on reserved node", "node_id", node.NodeID, "name", node.Name)
return &AgentRegistrationResponse{ return &AgentRegistrationResponse{
NodeID: node.NodeID, NodeID: node.NodeID,
AgentToken: node.AgentToken, AgentToken: node.AgentToken,
@@ -323,7 +324,7 @@ func RegisterNodeWithDiscovery(payload AgentNodePayload) (*AgentRegistrationResp
} }
return nil, err return nil, err
} }
common.SysLog("agent discovery register succeeded: node_id=" + node.NodeID + " name=" + node.Name) slog.Info("agent discovery register succeeded", "node_id", node.NodeID, "name", node.Name)
return &AgentRegistrationResponse{ return &AgentRegistrationResponse{
NodeID: node.NodeID, NodeID: node.NodeID,
AgentToken: node.AgentToken, AgentToken: node.AgentToken,
+5 -4
View File
@@ -8,6 +8,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"log/slog"
"net/http" "net/http"
"os" "os"
"os/exec" "os/exec"
@@ -136,7 +137,7 @@ func ScheduleServerUpgrade(channel string) (*LatestServerRelease, error) {
go func(task *preparedServerUpgrade) { go func(task *preparedServerUpgrade) {
time.Sleep(serverUpgradeDispatchDelay) time.Sleep(serverUpgradeDispatchDelay)
if err := executeServerUpgrade(task); err != nil { if err := executeServerUpgrade(task); err != nil {
common.SysError(fmt.Sprintf("server self-update failed: %v", err)) slog.Error("server self-update failed", "error", err)
serverUpgradeState.Lock() serverUpgradeState.Lock()
serverUpgradeState.inProgress = false serverUpgradeState.inProgress = false
serverUpgradeState.Unlock() serverUpgradeState.Unlock()
@@ -250,7 +251,7 @@ func ConfirmManualServerUpgrade(uploadToken string) (*UploadedServerBinary, erro
go func(task *manualServerBinaryCandidate) { go func(task *manualServerBinaryCandidate) {
time.Sleep(serverUpgradeDispatchDelay) time.Sleep(serverUpgradeDispatchDelay)
if err := executeManualServerUpgrade(task); err != nil { if err := executeManualServerUpgrade(task); err != nil {
common.SysError(fmt.Sprintf("server manual upgrade failed: %v", err)) slog.Error("server manual upgrade failed", "error", err)
serverUpgradeState.Lock() serverUpgradeState.Lock()
serverUpgradeState.inProgress = false serverUpgradeState.inProgress = false
serverUpgradeState.Unlock() serverUpgradeState.Unlock()
@@ -501,12 +502,12 @@ func executeServerUpgrade(task *preparedServerUpgrade) error {
return err return err
} }
common.SysLog("server self-update starting: from=" + strings.TrimSpace(common.Version) + " to=" + task.release.TagName) slog.Info("server self-update starting", "from", strings.TrimSpace(common.Version), "to", task.release.TagName)
return serverBinaryUpgradeExecutor(task.execPath, tmpPath) return serverBinaryUpgradeExecutor(task.execPath, tmpPath)
} }
func executeManualServerUpgrade(task *manualServerBinaryCandidate) error { func executeManualServerUpgrade(task *manualServerBinaryCandidate) error {
common.SysLog("server manual self-update starting: from=" + strings.TrimSpace(task.CurrentVersion) + " to=" + strings.TrimSpace(task.DetectedVersion)) slog.Info("server manual self-update starting", "from", strings.TrimSpace(task.CurrentVersion), "to", strings.TrimSpace(task.DetectedVersion))
return serverBinaryUpgradeExecutor(task.ExecPath, task.TempPath) return serverBinaryUpgradeExecutor(task.ExecPath, task.TempPath)
} }