diff --git a/atsf_server/common/init.go b/atsf_server/common/init.go index 922a34ae..8e437110 100644 --- a/atsf_server/common/init.go +++ b/atsf_server/common/init.go @@ -3,6 +3,7 @@ package common import ( "flag" "fmt" + "log/slog" "os" "path/filepath" "strings" @@ -58,12 +59,14 @@ func init() { var err error *LogDir, err = filepath.Abs(*LogDir) if err != nil { - FatalLog(err) + slog.Error("resolve log directory failed", "error", err) + os.Exit(1) } if _, err := os.Stat(*LogDir); os.IsNotExist(err) { err = os.Mkdir(*LogDir, 0777) if err != nil { - FatalLog(err) + slog.Error("create log directory failed", "error", err) + os.Exit(1) } } } diff --git a/atsf_server/common/logger.go b/atsf_server/common/logger.go index 3eba74fa..e2a6ff0a 100644 --- a/atsf_server/common/logger.go +++ b/atsf_server/common/logger.go @@ -192,25 +192,6 @@ func SetupGinLog() { 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 { switch { case level <= slog.LevelDebug: diff --git a/atsf_server/common/redis.go b/atsf_server/common/redis.go index 56db2b40..fa74ee19 100644 --- a/atsf_server/common/redis.go +++ b/atsf_server/common/redis.go @@ -3,6 +3,7 @@ package common import ( "context" "github.com/go-redis/redis/v8" + "log/slog" "os" "time" ) @@ -14,7 +15,7 @@ var RedisEnabled = true func InitRedisClient() (err error) { if os.Getenv("REDIS_CONN_STRING") == "" { 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 } opt, err := redis.ParseURL(os.Getenv("REDIS_CONN_STRING")) diff --git a/atsf_server/common/utils.go b/atsf_server/common/utils.go index 8134304b..9f8a8478 100644 --- a/atsf_server/common/utils.go +++ b/atsf_server/common/utils.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/google/uuid" "html/template" + "log/slog" "net" "os/exec" "runtime" @@ -23,14 +24,14 @@ func OpenBrowser(url string) { err = exec.Command("open", url).Start() } if err != nil { - SysError(err.Error()) + slog.Error("open browser failed", "error", err) } } func GetIp() (ip string) { ips, err := net.InterfaceAddrs() if err != nil { - SysError(err.Error()) + slog.Error("get interface addresses failed", "error", err) return ip } diff --git a/atsf_server/controller/github.go b/atsf_server/controller/github.go index d2930c47..55a9b119 100644 --- a/atsf_server/controller/github.go +++ b/atsf_server/controller/github.go @@ -9,6 +9,7 @@ import ( "fmt" "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" + "log/slog" "net/http" "strconv" "time" @@ -46,7 +47,7 @@ func getGitHubUserInfoByCode(code string) (*GitHubUser, error) { } res, err := client.Do(req) if err != nil { - common.SysLog(err.Error()) + slog.Error("github oauth access token request failed", "error", err) return nil, errors.New("无法连接至 GitHub 服务器,请稍后重试!") } defer res.Body.Close() @@ -62,7 +63,7 @@ func getGitHubUserInfoByCode(code string) (*GitHubUser, error) { req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", oAuthResponse.AccessToken)) res2, err := client.Do(req) if err != nil { - common.SysLog(err.Error()) + slog.Error("github user info request failed", "error", err) return nil, errors.New("无法连接至 GitHub 服务器,请稍后重试!") } defer res2.Body.Close() diff --git a/atsf_server/main.go b/atsf_server/main.go index 3771d1e5..6a865447 100644 --- a/atsf_server/main.go +++ b/atsf_server/main.go @@ -12,6 +12,7 @@ import ( "github.com/gin-contrib/sessions/cookie" "github.com/gin-contrib/sessions/redis" "github.com/gin-gonic/gin" + "log/slog" "os" "strconv" ) @@ -37,26 +38,29 @@ var indexPage []byte // @description Agent API 使用节点专属 Agent Token 或全局 Discovery Token func main() { common.SetupGinLog() - common.SysLog("ATSFlare " + common.Version + " started") + slog.Info("ATSFlare started", "version", common.Version) if os.Getenv("GIN_MODE") != "debug" { gin.SetMode(gin.ReleaseMode) } // Initialize SQL Database err := model.InitDB() if err != nil { - common.FatalLog(err) + slog.Error("initialize database failed", "error", err) + os.Exit(1) } defer func() { err := model.CloseDB() if err != nil { - common.FatalLog(err) + slog.Error("close database failed", "error", err) + os.Exit(1) } }() // Initialize Redis err = common.InitRedisClient() if err != nil { - common.FatalLog(err) + slog.Error("initialize redis failed", "error", err) + os.Exit(1) } // Initialize options @@ -82,11 +86,11 @@ func main() { if 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)) - common.SysLog(fmt.Sprintf("server listening on :%s", port)) + 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) + slog.Info("server listening", "address", fmt.Sprintf(":%s", port)) err = server.Run(":" + port) if err != nil { - common.SysError(err.Error()) + slog.Error("server run failed", "error", err) } } diff --git a/atsf_server/middleware/rate-limit.go b/atsf_server/middleware/rate-limit.go index d7c69131..538ee945 100644 --- a/atsf_server/middleware/rate-limit.go +++ b/atsf_server/middleware/rate-limit.go @@ -4,6 +4,7 @@ import ( "atsflare/common" "context" "github.com/gin-gonic/gin" + "log/slog" "net/http" "time" ) @@ -18,7 +19,7 @@ func redisRateLimiter(c *gin.Context, maxRequestNum int, duration int64, mark st key := "rateLimit:" + mark + c.ClientIP() listLength, err := rdb.LLen(ctx, key).Result() if err != nil { - common.SysError(err.Error()) + slog.Error("redis rate limiter llen failed", "error", err) c.Status(http.StatusInternalServerError) c.Abort() return @@ -30,7 +31,7 @@ func redisRateLimiter(c *gin.Context, maxRequestNum int, duration int64, mark st oldTimeStr, _ := rdb.LIndex(ctx, key, -1).Result() oldTime, err := time.Parse(timeFormat, oldTimeStr) if err != nil { - common.SysError(err.Error()) + slog.Error("parse redis rate limiter old timestamp failed", "error", err) c.Status(http.StatusInternalServerError) c.Abort() return @@ -38,7 +39,7 @@ func redisRateLimiter(c *gin.Context, maxRequestNum int, duration int64, mark st nowTimeStr := time.Now().Format(timeFormat) nowTime, err := time.Parse(timeFormat, nowTimeStr) if err != nil { - common.SysError(err.Error()) + slog.Error("parse redis rate limiter current timestamp failed", "error", err) c.Status(http.StatusInternalServerError) c.Abort() return diff --git a/atsf_server/middleware/turnstile-check.go b/atsf_server/middleware/turnstile-check.go index 92f570d4..26cc40d2 100644 --- a/atsf_server/middleware/turnstile-check.go +++ b/atsf_server/middleware/turnstile-check.go @@ -5,6 +5,7 @@ import ( "encoding/json" "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" + "log/slog" "net/http" "net/url" ) @@ -37,7 +38,7 @@ func TurnstileCheck() gin.HandlerFunc { "remoteip": {c.ClientIP()}, }) if err != nil { - common.SysError(err.Error()) + slog.Error("turnstile verification request failed", "error", err) c.JSON(http.StatusOK, gin.H{ "success": false, "message": err.Error(), @@ -49,7 +50,7 @@ func TurnstileCheck() gin.HandlerFunc { var res turnstileCheckResponse err = json.NewDecoder(rawRes.Body).Decode(&res) if err != nil { - common.SysError(err.Error()) + slog.Error("decode turnstile verification response failed", "error", err) c.JSON(http.StatusOK, gin.H{ "success": false, "message": err.Error(), diff --git a/atsf_server/model/main.go b/atsf_server/model/main.go index c9d65fe0..35610830 100644 --- a/atsf_server/model/main.go +++ b/atsf_server/model/main.go @@ -3,6 +3,7 @@ package model import ( "atsflare/common" "github.com/glebarez/sqlite" + "log/slog" "gorm.io/driver/mysql" "gorm.io/gorm" "os" @@ -24,7 +25,7 @@ func createRootAccountIfNeed() error { var user User //if user.Status != common.UserStatusEnabled { 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") if err != nil { return err @@ -58,7 +59,7 @@ func InitDB() (err error) { db, err = gorm.Open(sqlite.Open(common.SQLitePath), &gorm.Config{ 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 { DB = db @@ -104,7 +105,8 @@ func InitDB() (err error) { err = createRootAccountIfNeed() return err } else { - common.FatalLog(err) + slog.Error("open database failed", "error", err) + os.Exit(1) } return err } diff --git a/atsf_server/service/agent.go b/atsf_server/service/agent.go index 773682bf..e39b30a2 100644 --- a/atsf_server/service/agent.go +++ b/atsf_server/service/agent.go @@ -5,6 +5,7 @@ import ( "atsflare/model" "encoding/json" "errors" + "log/slog" "strings" "time" @@ -111,7 +112,7 @@ func RegisterNode(node *model.Node, payload AgentNodePayload) (*AgentRegistratio } 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 = normalizeAgentNodePayload(payload) if err := validateAgentNodePayload(payload); err != nil { @@ -165,7 +166,7 @@ func GetActiveConfigMetaForAgent() (*ActiveConfigMeta, error) { func GetActiveConfigForAgent() (*AgentConfigResponse, error) { version, err := model.GetActiveConfigVersion() 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 } var supportFiles []SupportFile @@ -174,7 +175,7 @@ func GetActiveConfigForAgent() (*AgentConfigResponse, error) { 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{ Version: version.Version, Checksum: version.Checksum, @@ -204,7 +205,7 @@ func ReportApplyLog(payload ApplyLogPayload) (*model.ApplyLog, error) { if payload.Result != ApplyResultOK && payload.Result != ApplyResultFailed { 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{ NodeID: payload.NodeID, @@ -239,9 +240,9 @@ func ReportApplyLog(payload ApplyLogPayload) (*model.ApplyLog, error) { return nil, err } 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 { - 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 } @@ -256,9 +257,9 @@ func ListNodeViews() ([]*NodeView, error) { computedStatus := computeNodeStatus(node) if node.Status != computedStatus { 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 { - 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 node.Status = computedStatus diff --git a/atsf_server/service/node.go b/atsf_server/service/node.go index 3d3a9b4d..3a5830c9 100644 --- a/atsf_server/service/node.go +++ b/atsf_server/service/node.go @@ -7,6 +7,7 @@ import ( "crypto/rand" "encoding/hex" "errors" + "log/slog" "strings" "time" ) @@ -73,7 +74,7 @@ func CreateNode(input NodeInput) (*NodeView, error) { } 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 } @@ -91,7 +92,7 @@ func UpdateNode(id uint, input NodeInput) (*NodeView, error) { if err = node.Update(); err != nil { 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 } @@ -100,7 +101,7 @@ func DeleteNode(id uint) error { if err != nil { 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() } @@ -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 { 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 } @@ -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 { 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 } @@ -286,7 +287,7 @@ func RegisterNodeWithAgentToken(node *model.Node, payload AgentNodePayload) (*Ag if err := node.Update(); err != nil { 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{ NodeID: node.NodeID, AgentToken: node.AgentToken, @@ -323,7 +324,7 @@ func RegisterNodeWithDiscovery(payload AgentNodePayload) (*AgentRegistrationResp } 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{ NodeID: node.NodeID, AgentToken: node.AgentToken, diff --git a/atsf_server/service/update.go b/atsf_server/service/update.go index 26127122..805dd442 100644 --- a/atsf_server/service/update.go +++ b/atsf_server/service/update.go @@ -8,6 +8,7 @@ import ( "encoding/json" "fmt" "io" + "log/slog" "net/http" "os" "os/exec" @@ -136,7 +137,7 @@ func ScheduleServerUpgrade(channel string) (*LatestServerRelease, error) { go func(task *preparedServerUpgrade) { time.Sleep(serverUpgradeDispatchDelay) 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.inProgress = false serverUpgradeState.Unlock() @@ -250,7 +251,7 @@ func ConfirmManualServerUpgrade(uploadToken string) (*UploadedServerBinary, erro go func(task *manualServerBinaryCandidate) { time.Sleep(serverUpgradeDispatchDelay) 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.inProgress = false serverUpgradeState.Unlock() @@ -501,12 +502,12 @@ func executeServerUpgrade(task *preparedServerUpgrade) error { 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) } 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) }