diff --git a/Makefile b/Makefile index 68324c9..9f8be42 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ gen-swagger: install-swaggo gen-proto: install-proto protoc --go_out=. --go_opt=paths=source_relative \ --go-grpc_out=. --go-grpc_opt=paths=source_relative \ - proto/*.proto + proto/*.proto # Targets for development @@ -70,7 +70,7 @@ setup-dependencies: build get-front get-problem-packages ./bin/clean; \ else \ docker compose up -d postgres redis minio; \ - @echo "Wait 10 seconds for db setup"; \ + echo "Wait 10 seconds for db setup"; \ sleep 10s; \ fi ./bin/init; diff --git a/cmd/clean/main.go b/cmd/clean/main.go index 8300fb9..d07c5da 100644 --- a/cmd/clean/main.go +++ b/cmd/clean/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "log/slog" "github.com/minio/minio-go/v7" judge_model "github.com/oj-lab/platform/models/judge" @@ -11,14 +12,12 @@ import ( gorm_agent "github.com/oj-lab/platform/modules/agent/gorm" minio_agent "github.com/oj-lab/platform/modules/agent/minio" redis_agent "github.com/oj-lab/platform/modules/agent/redis" - - log_module "github.com/oj-lab/platform/modules/log" ) func clearCasbin() { enforcer := casbin_agent.GetDefaultCasbinEnforcer() enforcer.ClearPolicy() // no err return - log_module.AppLogger().Info("Clear Casbin success") + slog.Info("Clear Casbin success") } func removeMinioObjects() { @@ -30,7 +29,7 @@ func removeMinioObjects() { opts := minio.ListObjectsOptions{Recursive: true} for object := range minioClient.ListObjects(context.Background(), minio_agent.GetBucketName(), opts) { if object.Err != nil { - log_module.AppLogger().WithError(object.Err).Error("Get object error") + slog.With("err", object.Err).Error("Get object error") } objectsCh <- object } @@ -38,10 +37,10 @@ func removeMinioObjects() { errorCh := minioClient.RemoveObjects(context.Background(), minio_agent.GetBucketName(), objectsCh, minio.RemoveObjectsOptions{}) for e := range errorCh { - log_module.AppLogger().WithError(e.Err).Error("Failed to remove " + e.ObjectName) + slog.With("err", e.Err).Error("Failed to remove " + e.ObjectName) } - log_module.AppLogger().Info("Remove Minio Objects success") + slog.Info("Remove Minio Objects success") } func clearRedis() { @@ -49,10 +48,10 @@ func clearRedis() { redis_agent := redis_agent.GetDefaultRedisClient() err := redis_agent.FlushDB(ctx).Err() if err != nil { - log_module.AppLogger().WithError(err).Error("Failed to clear redis") + slog.With("err", err).Error("Failed to clear redis") } - log_module.AppLogger().Info("Clear Redis success") + slog.Info("Clear Redis success") } func clearDB() { db := gorm_agent.GetDefaultDB() @@ -73,7 +72,7 @@ func clearDB() { panic("failed to drop tables") } - log_module.AppLogger().Info("Clear DB success") + slog.Info("Clear DB success") } func main() { diff --git a/cmd/init/db.go b/cmd/init/db.go index 3812002..9721157 100644 --- a/cmd/init/db.go +++ b/cmd/init/db.go @@ -2,19 +2,19 @@ package main import ( "fmt" + "log/slog" judge_model "github.com/oj-lab/platform/models/judge" problem_model "github.com/oj-lab/platform/models/problem" user_model "github.com/oj-lab/platform/models/user" gorm_agent "github.com/oj-lab/platform/modules/agent/gorm" - config_module "github.com/oj-lab/platform/modules/config" - log_module "github.com/oj-lab/platform/modules/log" + core_module "github.com/oj-lab/platform/modules/core" ) -const rootPasswordProp = "auth.root_password" +const rootPasswordConfigKey = "auth.root_password" func initDB() { - rootPassword := config_module.AppConfig().GetString(rootPasswordProp) + rootPassword := core_module.Config.GetString(rootPasswordConfigKey) db := gorm_agent.GetDefaultDB() err := db.AutoMigrate( &user_model.User{}, @@ -47,5 +47,5 @@ func initDB() { panic(fmt.Sprintf("failed to create anonymous user: %v", err)) } - log_module.AppLogger().Info("migrate tables ans users success") + slog.Info("migrate tables ans users success") } diff --git a/cmd/init/problem_package.go b/cmd/init/problem_package.go index f0e2974..200fe79 100644 --- a/cmd/init/problem_package.go +++ b/cmd/init/problem_package.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io/fs" + "log/slog" "os" "path" "path/filepath" @@ -15,8 +16,7 @@ import ( problem_model "github.com/oj-lab/platform/models/problem" gorm_agent "github.com/oj-lab/platform/modules/agent/gorm" minio_agent "github.com/oj-lab/platform/modules/agent/minio" - config_module "github.com/oj-lab/platform/modules/config" - log_module "github.com/oj-lab/platform/modules/log" + core_module "github.com/oj-lab/platform/modules/core" "gopkg.in/yaml.v2" ) @@ -24,12 +24,12 @@ func loadProblemPackages(ctx context.Context) { db := gorm_agent.GetDefaultDB() minioClient := minio_agent.GetMinioClient() - packagePath := path.Join(config_module.ProjectRoot(), "problem-packages/icpc") + packagePath := path.Join(core_module.ProjectRoot(), "problem-packages/icpc") // Load Dirs under `packagePath` problemPackageDirs, err := os.ReadDir(packagePath) if err != nil { - log_module.AppLogger().WithError(err).Error("Read package path failed") + slog.With("err", err).Error("Read package path failed") panic(err) } for _, problemPackageDir := range problemPackageDirs { @@ -51,7 +51,7 @@ func loadProblemPackages(ctx context.Context) { problemPackagePath := path.Join(packagePath, problemPackageDir.Name()) err := filepath.Walk(problemPackagePath, func(path string, info fs.FileInfo, err error) error { if err != nil { - log_module.AppLogger().WithError(err).Error("Walk package path failed") + slog.With("err", err).Error("Walk package path failed") return err } if info == nil { @@ -61,33 +61,33 @@ func loadProblemPackages(ctx context.Context) { return nil } relativePath := strings.Replace(path, packagePath, "", 1) - log_module.AppLogger(). - WithField("relativePath", relativePath). - WithField("Ext", filepath.Ext(relativePath)). - WithField("Dir", filepath.Dir(relativePath)). + slog. + With("relativePath", relativePath). + With("Ext", filepath.Ext(relativePath)). + With("Dir", filepath.Dir(relativePath)). Debug("Read file from package") if filepath.Base(relativePath) == "problem.yaml" { resultMap := make(map[string]interface{}) yamlFile, err := os.ReadFile(path) if err != nil { - log_module.AppLogger().WithError(err).Error("Read problem.yaml failed") + slog.With("err", err).Error("Read problem.yaml failed") } err = yaml.Unmarshal(yamlFile, &resultMap) if err != nil { - log_module.AppLogger().WithError(err).Error("Unmarshal problem.yaml failed") + slog.With("err", err).Error("Unmarshal problem.yaml failed") } - log_module.AppLogger().WithField("resultMap", reflect.TypeOf(resultMap["limits"])).Debug("Read problem.yaml") + slog.With("resultMap", reflect.TypeOf(resultMap["limits"])).Debug("Read problem.yaml") if resultMap["name"] == nil { - log_module.AppLogger().Error("Problem name is nil") + slog.Error("Problem name is nil") return nil } title = resultMap["name"].(string) if title == "" { - log_module.AppLogger().Error("Problem title is empty") + slog.Error("Problem title is empty") } slug = strings.Split(relativePath, "/")[1] - log_module.AppLogger().WithField("title", title).WithField("slug", slug).Debug("Read problem.yaml") + slog.With("title", title).With("slug", slug).Debug("Read problem.yaml") if limits, ok := resultMap["limits"].(map[interface{}]interface{}); ok { if memoryLimit, ok := limits["memory"].(int); ok { limitDescription += fmt.Sprintf("
Memory Limit: %d MB
\n", memoryLimit) @@ -112,20 +112,20 @@ func loadProblemPackages(ctx context.Context) { if filepath.Base(relativePath) == "problem.md" { content, err := os.ReadFile(path) if err != nil { - log_module.AppLogger().WithError(err).Error("Read problem.md failed") + slog.With("err", err).Error("Read problem.md failed") } description = string(content) - log_module.AppLogger().WithField("description", description).Debug("Read problem.md") + slog.With("description", description).Debug("Read problem.md") } if filepath.Base(relativePath) == ".timelimit" { timeLimitStr, err := os.ReadFile(path) if err != nil { - log_module.AppLogger().WithError(err).Error("Read time limit file failed") + slog.With("err", err).Error("Read time limit file failed") return nil } timeLimit, err := strconv.Atoi(strings.Trim(string(timeLimitStr), "\n")) if err != nil { - log_module.AppLogger().WithError(err).Error("Parse time limit failed") + slog.With("err", err).Error("Parse time limit failed") return nil } limitDescription += fmt.Sprintf("
Time Limit: %d s
\n", timeLimit) @@ -133,18 +133,18 @@ func loadProblemPackages(ctx context.Context) { if filepath.Ext(relativePath) == ".in" && strings.HasSuffix(filepath.Dir(relativePath), "sample") { ansPath := strings.Replace(path, ".in", ".ans", 1) if _, err := os.Stat(ansPath); err != nil { - log_module.AppLogger().WithField("path", ansPath).Error("Answer file not found") + slog.With("path", ansPath).Error("Answer file not found") return nil } input, err := os.ReadFile(path) if err != nil { - log_module.AppLogger().WithError(err).Error("Read input file failed") + slog.With("err", err).Error("Read input file failed") return nil } inputStr := strings.Trim(string(input), "\n") output, err := os.ReadFile(ansPath) if err != nil { - log_module.AppLogger().WithError(err).Error("Read output file failed") + slog.With("err", err).Error("Read output file failed") return nil } outputStr := strings.Trim(string(output), "\n") @@ -159,7 +159,7 @@ func loadProblemPackages(ctx context.Context) { path, minio.PutObjectOptions{}) if err != nil { - log_module.AppLogger().WithError(err).Error("Put object to minio failed") + slog.With("err", err).Error("Put object to minio failed") } return err }) @@ -196,5 +196,5 @@ func loadProblemPackages(ctx context.Context) { } } - log_module.AppLogger().Info("Problem loaded") + slog.Info("Problem loaded") } diff --git a/cmd/rpc_server/main.go b/cmd/rpc_server/main.go index daf485d..3dae702 100644 --- a/cmd/rpc_server/main.go +++ b/cmd/rpc_server/main.go @@ -9,18 +9,18 @@ import ( "net" "github.com/oj-lab/platform/cmd/rpc_server/impls" - config_module "github.com/oj-lab/platform/modules/config" + core_module "github.com/oj-lab/platform/modules/core" "github.com/oj-lab/platform/proto" "google.golang.org/grpc" "google.golang.org/grpc/reflection" ) const ( - portProp = "rpc-server.port" + portConfigKey = "rpc-server.port" ) var ( - port = config_module.AppConfig().GetInt(portProp) + port = core_module.Config.GetInt(portConfigKey) ) func main() { diff --git a/cmd/web_server/handler/auth.go b/cmd/web_server/handler/auth.go index a7b8b2a..a82a670 100644 --- a/cmd/web_server/handler/auth.go +++ b/cmd/web_server/handler/auth.go @@ -2,6 +2,7 @@ package handler import ( "fmt" + "log/slog" "net/http" "time" @@ -11,21 +12,20 @@ import ( user_model "github.com/oj-lab/platform/models/user" gorm_agent "github.com/oj-lab/platform/modules/agent/gorm" auth_module "github.com/oj-lab/platform/modules/auth" - config_module "github.com/oj-lab/platform/modules/config" - log_module "github.com/oj-lab/platform/modules/log" + core_module "github.com/oj-lab/platform/modules/core" gin_utils "github.com/oj-lab/platform/modules/utils/gin" user_service "github.com/oj-lab/platform/services/user" ) const ( - emailBacklistProp = "auth.email_backlist" - callbackURL = "/auth/github/callback" + emailBacklistConfigKey = "auth.email_backlist" + callbackURL = "/auth/github/callback" ) var emailBacklist = map[string]bool{} func init() { - emailBacklistSlice := config_module.AppConfig().GetStringSlice(emailBacklistProp) + emailBacklistSlice := core_module.Config.GetStringSlice(emailBacklistConfigKey) for _, email := range emailBacklistSlice { emailBacklist[email] = true } @@ -52,7 +52,7 @@ func githubCallback(ginCtx *gin.Context) { return } - log_module.AppLogger().WithField("tokenResponse", tokenResponse).Info("github callback") + slog.With("tokenResponse", tokenResponse).Info("github callback") githubUser, err := auth_module.GetGithubUser(tokenResponse.AccessToken) if err != nil { gin_utils.NewInternalError(ginCtx, fmt.Sprintf("failed to get github user: %v", err)) diff --git a/cmd/web_server/handler/event.go b/cmd/web_server/handler/event.go index 68e01fb..95c1da1 100644 --- a/cmd/web_server/handler/event.go +++ b/cmd/web_server/handler/event.go @@ -3,10 +3,10 @@ package handler import ( "fmt" "io" + "log/slog" "time" "github.com/gin-gonic/gin" - log_module "github.com/oj-lab/platform/modules/log" ) func SetupEventRouter(baseRoute *gin.RouterGroup) { @@ -33,7 +33,7 @@ func Stream(ginCtx *gin.Context) { ginCtx.Stream(func(w io.Writer) bool { // With event type message := fmt.Sprintf("event: %s\ndata: %s\n\n", "eventType", time.Now().String()) - log_module.AppLogger().Infof("Send message:\n%s", message) + slog.Info(fmt.Sprintf("Send message:\n%s", message)) fmt.Fprint(w, message) time.Sleep(1 * time.Second) counter++ diff --git a/cmd/web_server/handler/problem.go b/cmd/web_server/handler/problem.go index 20e70e5..fd31a6d 100644 --- a/cmd/web_server/handler/problem.go +++ b/cmd/web_server/handler/problem.go @@ -232,8 +232,8 @@ func checkProblemSlug(ginCtx *gin.Context) { // PostJudgeBody // // @Description The body of a judge request, containing the code and the language used for the judge. -// @Property code (string) required "The source code of the judge" minlength(1) -// @Property language (ProgrammingLanguage) required "The programming language used for the judge" +// @ConfigKeyerty code (string) required "The source code of the judge" minlength(1) +// @ConfigKeyerty language (ProgrammingLanguage) required "The programming language used for the judge" type PostJudgeBody struct { Code string `json:"code" binding:"required"` Language judge_model.ProgrammingLanguage `json:"language" binding:"required"` diff --git a/cmd/web_server/handler/swaggo.go b/cmd/web_server/handler/swaggo.go index a987a44..21bf0d5 100644 --- a/cmd/web_server/handler/swaggo.go +++ b/cmd/web_server/handler/swaggo.go @@ -11,8 +11,8 @@ import ( ) const ( - servicePortProp = "service.port" - serviceHostProp = "service.host" + servicePortConfigKey = "service.port" + serviceHostConfigKey = "service.host" ) var ( @@ -24,8 +24,8 @@ func SetupSwaggoRouter(r *gin.RouterGroup) { } func init() { - sevicePort := viper.GetUint(servicePortProp) - seviceHost := viper.GetString(serviceHostProp) + sevicePort := viper.GetUint(servicePortConfigKey) + seviceHost := viper.GetString(serviceHostConfigKey) swaggerHost = fmt.Sprintf("%s:%d", seviceHost, sevicePort) println("Swagger host is set to: " + swaggerHost) // programmatically set swagger info diff --git a/cmd/web_server/main.go b/cmd/web_server/main.go index 06ba44c..0e329b3 100644 --- a/cmd/web_server/main.go +++ b/cmd/web_server/main.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "log/slog" "os" "path/filepath" "runtime" @@ -9,34 +10,27 @@ import ( "github.com/gin-gonic/gin" "github.com/oj-lab/platform/cmd/web_server/handler" "github.com/oj-lab/platform/cmd/web_server/middleware" + sloggin "github.com/samber/slog-gin" - config_module "github.com/oj-lab/platform/modules/config" - - log_module "github.com/oj-lab/platform/modules/log" + core_module "github.com/oj-lab/platform/modules/core" ) const ( - serviceForceConsoleColorProp = "service.force_console_color" - servicePortProp = "service.port" - serviceModeProp = "service.mode" - swaggerOnProp = "service.swagger_on" - frontendDistProp = "service.frontend_dist" + servicePortConfigKey = "service.port" + swaggerOnConfigKey = "service.swagger_on" + frontendDistConfigKey = "service.frontend_dist" ) var ( - serviceForceConsoleColor bool - servicePort uint - serviceMode string - swaggerOn bool - frontendDist string + servicePort uint + swaggerOn bool + frontendDist string ) func init() { - serviceForceConsoleColor = config_module.AppConfig().GetBool(serviceForceConsoleColorProp) - servicePort = config_module.AppConfig().GetUint(servicePortProp) - serviceMode = config_module.AppConfig().GetString(serviceModeProp) - swaggerOn = config_module.AppConfig().GetBool(swaggerOnProp) - frontendDist = config_module.AppConfig().GetString(frontendDistProp) + servicePort = core_module.Config.GetUint(servicePortConfigKey) + swaggerOn = core_module.Config.GetBool(swaggerOnConfigKey) + frontendDist = core_module.Config.GetString(frontendDistConfigKey) } func GetProjectDir() string { @@ -47,20 +41,19 @@ func GetProjectDir() string { } func main() { - if serviceForceConsoleColor { - gin.ForceConsoleColor() - } - r := gin.Default() + gin.SetMode(gin.ReleaseMode) + r := gin.New() + r.Use(sloggin.New(slog.Default().With("module", "gin"))) + r.Use(gin.Recovery()) r.Use(middleware.HandleError) - gin.SetMode(serviceMode) baseRouter := r.Group("/") if frontendDist != "" { // If dist folder is not empty, serve frontend if _, err := os.Stat(frontendDist); os.IsNotExist(err) { - log_module.AppLogger().Warn("Frontend dist is set but folder not found") + slog.Warn("Frontend dist is set but folder not found") } else { - log_module.AppLogger().Info("Serving frontend...") + slog.Info("Serving frontend...") r.LoadHTMLFiles(frontendDist + "/index.html") handler.SetupFrontendRoute(baseRouter, frontendDist) r.NoRoute(handler.RenderHTML) @@ -68,7 +61,7 @@ func main() { } if swaggerOn { - log_module.AppLogger().Info("Serving swagger Doc...") + slog.Info("Serving swagger Doc...") handler.SetupSwaggoRouter(baseRouter) } handler.SetupAuthRouter(baseRouter) diff --git a/cmd/web_server/middleware/casbin.go b/cmd/web_server/middleware/casbin.go index a2d72f8..608de97 100644 --- a/cmd/web_server/middleware/casbin.go +++ b/cmd/web_server/middleware/casbin.go @@ -1,9 +1,10 @@ package middleware import ( + "log/slog" + "github.com/gin-gonic/gin" casbin_agent "github.com/oj-lab/platform/modules/agent/casbin" - log_module "github.com/oj-lab/platform/modules/log" gin_utils "github.com/oj-lab/platform/modules/utils/gin" ) @@ -21,7 +22,7 @@ func BuildCasbinEnforceHandlerWithDomain(domain string) gin.HandlerFunc { allow, err := enforcer.Enforce(casbin_agent.UserSubjectPrefix+ls.Key.Account, "_", domain, path, method) if err != nil { - log_module.AppLogger().Errorf("Failed to enforce: %v", err) + slog.With("err", err).Error("Failed to enforce") gin_utils.NewInternalError(ginCtx, "Failed to enforce") ginCtx.Abort() return diff --git a/cmd/web_server/middleware/error.go b/cmd/web_server/middleware/error.go index bbee31c..77f0a44 100644 --- a/cmd/web_server/middleware/error.go +++ b/cmd/web_server/middleware/error.go @@ -2,10 +2,10 @@ package middleware import ( "fmt" + "log/slog" "net/http" "github.com/gin-gonic/gin" - log_module "github.com/oj-lab/platform/modules/log" gin_utils "github.com/oj-lab/platform/modules/utils/gin" ) @@ -27,7 +27,8 @@ func HandleError(ginCtx *gin.Context) { errCount := len(ginCtx.Errors) if errCount > 0 { - log_module.AppLogger().Errorf("Last error from GIN middleware: %+v", ginCtx.Errors[errCount-1].Err) + slog.With("err", ginCtx.Errors[errCount-1].Err). + Error("Last error from GIN middleware") err := GetServiceError(*ginCtx.Errors[errCount-1]) ginCtx.JSON(err.Code, gin.H{ "code": err.Code, diff --git a/cmd/web_server/middleware/internal.go b/cmd/web_server/middleware/internal.go index f6bbb35..57ea4b1 100644 --- a/cmd/web_server/middleware/internal.go +++ b/cmd/web_server/middleware/internal.go @@ -4,12 +4,12 @@ import ( "fmt" "github.com/gin-gonic/gin" - config_module "github.com/oj-lab/platform/modules/config" + core_module "github.com/oj-lab/platform/modules/core" gin_utils "github.com/oj-lab/platform/modules/utils/gin" ) const ( - internalTokenProp = "service.internal_token" + internalTokenConfigKey = "service.internal_token" ) var ( @@ -17,7 +17,7 @@ var ( ) func init() { - internalToken = config_module.AppConfig().GetString(internalTokenProp) + internalToken = core_module.Config.GetString(internalTokenConfigKey) } func HandleRequireInternalToken(ginCtx *gin.Context) { diff --git a/config.toml b/config.toml index 40da5d2..5d23bac 100644 --- a/config.toml +++ b/config.toml @@ -1,10 +1,8 @@ [log] level = "debug" -pretty_json = true -time_on = true -time_format = "2000-01-01 00:00:00" +format = "text" -[database] +[gorm] dsn = "user=postgres password=postgres host=localhost port=5432 dbname=oj_lab sslmode=disable TimeZone=Asia/Shanghai" [redis] diff --git a/go.mod b/go.mod index f5d6948..59598fc 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ toolchain go1.23.1 require ( github.com/google/uuid v1.6.0 github.com/redis/go-redis/v9 v9.6.1 - github.com/sirupsen/logrus v1.9.3 github.com/spf13/viper v1.19.0 gorm.io/driver/postgres v1.5.9 gorm.io/gorm v1.25.12 @@ -39,6 +38,7 @@ require ( github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/lib/pq v1.10.9 // indirect + github.com/lmittmann/tint v1.0.7 // indirect github.com/microsoft/go-mssqldb v1.7.2 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/paulmach/orb v0.11.1 // indirect @@ -48,6 +48,7 @@ require ( github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/sagikazarmark/locafero v0.6.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/samber/slog-gin v1.14.1 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect @@ -55,7 +56,7 @@ require ( go.opentelemetry.io/otel/trace v1.30.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect - golang.org/x/sync v0.8.0 // indirect + golang.org/x/sync v0.10.0 // indirect gorm.io/driver/mysql v1.5.7 // indirect gorm.io/driver/sqlserver v1.5.3 // indirect gorm.io/plugin/dbresolver v1.5.3 // indirect @@ -129,9 +130,9 @@ require ( github.com/swaggo/files v1.0.1 github.com/swaggo/gin-swagger v1.6.0 github.com/ugorji/go/codec v1.2.12 // indirect - golang.org/x/crypto v0.27.0 // indirect + golang.org/x/crypto v0.31.0 // indirect golang.org/x/net v0.29.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/text v0.18.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect google.golang.org/protobuf v1.34.2 ) diff --git a/go.sum b/go.sum index d100468..71a1dc2 100644 --- a/go.sum +++ b/go.sum @@ -191,6 +191,8 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lmittmann/tint v1.0.7 h1:D/0OqWZ0YOGZ6AyC+5Y2kD8PBEzBk6rFHVSfOqCkF9Y= +github.com/lmittmann/tint v1.0.7/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= @@ -243,12 +245,12 @@ github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3 github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/samber/slog-gin v1.14.1 h1:6DMAcy2gBFyyztrpYIvAcXZH1sA/j75iSSXuqhirLtg= +github.com/samber/slog-gin v1.14.1/go.mod h1:yS2C+cX5tRnPX0MqDby7a3tRFsJuMk7hNwAunyfDxQk= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= @@ -314,6 +316,8 @@ golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98y golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -347,6 +351,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -355,7 +361,6 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -364,6 +369,8 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -383,6 +390,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= diff --git a/modules/agent/casbin/enforcer.go b/modules/agent/casbin/enforcer.go index 827155e..f4f011b 100644 --- a/modules/agent/casbin/enforcer.go +++ b/modules/agent/casbin/enforcer.go @@ -1,6 +1,7 @@ package casbin_agent import ( + "log/slog" "strings" "github.com/casbin/casbin/persist" @@ -10,7 +11,6 @@ import ( rediswatcher "github.com/casbin/redis-watcher/v2" gorm_agent "github.com/oj-lab/platform/modules/agent/gorm" redis_agent "github.com/oj-lab/platform/modules/agent/redis" - log_module "github.com/oj-lab/platform/modules/log" ) var casbinEnforcer *casbin.SyncedCachedEnforcer @@ -53,10 +53,10 @@ func GetDefaultCasbinEnforcer() *casbin.SyncedCachedEnforcer { if err != nil { panic(err) } - log_module.AppLogger().Info("Casbin enforcer watcher initialized") + slog.Info("Casbin enforcer watcher initialized") } casbinEnforcer.AddFunction("keyMatchGin", keyMatchGinFunc) - log_module.AppLogger().Info("Casbin enforcer initialized") + slog.Info("Casbin enforcer initialized") } return casbinEnforcer diff --git a/modules/agent/gorm/database.go b/modules/agent/gorm/database.go index 8886589..3058abb 100644 --- a/modules/agent/gorm/database.go +++ b/modules/agent/gorm/database.go @@ -1,19 +1,24 @@ package gorm_agent import ( - config_module "github.com/oj-lab/platform/modules/config" + "log/slog" + + core_module "github.com/oj-lab/platform/modules/core" "gorm.io/driver/postgres" "gorm.io/gorm" ) -const dsnProp = "database.dsn" +const ( + dsnConfigKey = "gorm.dsn" + loggerIgnoreRunLogConfigKey = "gorm.logger.ignore_run_log" +) var db *gorm.DB var dsn string func init() { - dsn = config_module.AppConfig().GetString(dsnProp) + dsn = core_module.Config.GetString(dsnConfigKey) if dsn == "" { panic("database dsn is not set") } @@ -23,10 +28,12 @@ func GetDefaultDB() *gorm.DB { if db == nil { var err error db, err = gorm.Open(postgres.New(postgres.Config{ - DSN: dsn, - PreferSimpleProtocol: true, // disables implicit prepared statement usage + DSN: dsn, }), &gorm.Config{ - Logger: getLogger(), + Logger: NewSlogLogger(slog.Default().With("module", "gorm"), + slogLoggerConfig{ + IngoreRunLog: core_module.Config.GetBool(loggerIgnoreRunLogConfigKey), + }), }) if err != nil { panic("failed to connect database") diff --git a/modules/agent/gorm/logger.go b/modules/agent/gorm/logger.go index 4b9e420..fbb9766 100644 --- a/modules/agent/gorm/logger.go +++ b/modules/agent/gorm/logger.go @@ -1,24 +1,96 @@ package gorm_agent import ( - "log" - "os" + "context" + "errors" + "fmt" + "log/slog" "time" + "gorm.io/gorm" "gorm.io/gorm/logger" + "gorm.io/gorm/utils" ) -func getLogger() logger.Interface { - logger := logger.New( - log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer - logger.Config{ - SlowThreshold: time.Second, // Slow SQL threshold - LogLevel: logger.Silent, // Log level - IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger - ParameterizedQueries: true, // Don't include params in the SQL log - Colorful: false, // Disable color - }, - ) - - return logger +type slogLogger struct { + logger *slog.Logger + config slogLoggerConfig +} + +type slogLoggerConfig struct { + SlowThreshold time.Duration + IgnoreRecordNotFoundError bool + IngoreErrorLog bool + IgnoreSlowLog bool + IngoreRunLog bool +} + +func NewSlogLogger(logger *slog.Logger, config slogLoggerConfig) slogLogger { + if config.SlowThreshold == 0 { + config.SlowThreshold = 100 * time.Millisecond + } + return slogLogger{ + logger: logger, + config: config, + } +} + +func (l slogLogger) LogMode(level logger.LogLevel) logger.Interface { + return l +} + +func (l slogLogger) Info(ctx context.Context, msg string, data ...interface{}) { + l.logger.InfoContext(ctx, msg, data...) +} + +func (l slogLogger) Warn(ctx context.Context, msg string, data ...interface{}) { + l.logger.WarnContext(ctx, msg, data...) +} + +func (l slogLogger) Error(ctx context.Context, msg string, data ...interface{}) { + l.logger.ErrorContext(ctx, msg, data...) +} + +func (l slogLogger) Debug(ctx context.Context, msg string, data ...interface{}) { + l.logger.DebugContext(ctx, msg, data...) +} + +func (l slogLogger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) { + elapsed := time.Since(begin) + switch { + case !l.config.IngoreErrorLog && + err != nil && (!errors.Is(err, gorm.ErrRecordNotFound) || !l.config.IgnoreRecordNotFoundError): + sql, rows := fc() + errLog := "Error SQL" + if rows == -1 { + l.Error(ctx, errLog, "file", utils.FileWithLineNum(), + "elapsed", float64(elapsed.Nanoseconds())/1e6, "rows", "-", "sql", sql, + "err", err) + } else { + l.Error(ctx, errLog, "file", utils.FileWithLineNum(), + "elapsed", float64(elapsed.Nanoseconds())/1e6, "rows", rows, "sql", sql, + "err", err) + } + case !l.config.IgnoreSlowLog && + elapsed > l.config.SlowThreshold && l.config.SlowThreshold != 0: + sql, rows := fc() + slowLog := fmt.Sprintf("Slow SQL >= %v", l.config.SlowThreshold) + if rows == -1 { + l.Warn(ctx, slowLog, "file", utils.FileWithLineNum(), + "elapsed", float64(elapsed.Nanoseconds())/1e6, "rows", "-", "sql", sql) + } else { + l.Warn(ctx, slowLog, "file", utils.FileWithLineNum(), + "elapsed", float64(elapsed.Nanoseconds())/1e6, "rows", rows, "sql", sql) + } + case !l.config.IngoreRunLog: + sql, rows := fc() + runLog := "Run SQL" + if rows == -1 { + l.Debug(ctx, runLog, "file", utils.FileWithLineNum(), + "elapsed", float64(elapsed.Nanoseconds())/1e6, "rows", "-", "sql", sql) + } else { + l.Debug(ctx, runLog, "file", utils.FileWithLineNum(), + "elapsed", float64(elapsed.Nanoseconds())/1e6, "rows", rows, "sql", sql) + } + } } diff --git a/modules/agent/minio/client.go b/modules/agent/minio/client.go index 9627ad5..945fe48 100644 --- a/modules/agent/minio/client.go +++ b/modules/agent/minio/client.go @@ -2,20 +2,20 @@ package minio_agent import ( "context" + "log/slog" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" - config_module "github.com/oj-lab/platform/modules/config" - log_module "github.com/oj-lab/platform/modules/log" + core_module "github.com/oj-lab/platform/modules/core" ) const ( - minioEndpointProp = "minio.endpoint" - minioAccessKeyProp = "minio.access_key_id" - minioSecretAccessKeyProp = "minio.secret_access_key" - minioUseSSLProp = "minio.use_ssl" - minioRegionProp = "minio.region" - minioBucketNameProp = "minio.bucket_name" + minioEndpointConfigKey = "minio.endpoint" + minioAccessKeyConfigKey = "minio.access_key_id" + minioSecretAccessKeyConfigKey = "minio.secret_access_key" + minioUseSSLConfigKey = "minio.use_ssl" + minioRegionConfigKey = "minio.region" + minioBucketNameConfigKey = "minio.bucket_name" ) var ( @@ -29,12 +29,12 @@ var ( ) func init() { - endpoint = config_module.AppConfig().GetString(minioEndpointProp) - accessKeyID = config_module.AppConfig().GetString(minioAccessKeyProp) - secretAccessKey = config_module.AppConfig().GetString(minioSecretAccessKeyProp) - useSSL = config_module.AppConfig().GetBool(minioUseSSLProp) - region = config_module.AppConfig().GetString(minioRegionProp) - bucketName = config_module.AppConfig().GetString(minioBucketNameProp) + endpoint = core_module.Config.GetString(minioEndpointConfigKey) + accessKeyID = core_module.Config.GetString(minioAccessKeyConfigKey) + secretAccessKey = core_module.Config.GetString(minioSecretAccessKeyConfigKey) + useSSL = core_module.Config.GetBool(minioUseSSLConfigKey) + region = core_module.Config.GetString(minioRegionConfigKey) + bucketName = core_module.Config.GetString(minioBucketNameConfigKey) } func GetBucketName() string { @@ -56,16 +56,15 @@ func GetMinioClient() *minio.Client { exists, err := minioClient.BucketExists(ctx, bucketName) if err == nil && exists { - log_module.AppLogger().WithField("bucket", bucketName).Info("Bucket already exists") + slog.With("bucket", bucketName).Info("Bucket already exists") return minioClient } err = minioClient.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{}) if err != nil { - log_module.AppLogger().WithError(err). - WithField("bucket", bucketName).Error("Failed to create bucket") + slog.With("err", err, "bucket", bucketName).Error("Failed to create bucket") } else { - log_module.AppLogger().WithField("bucket", bucketName).Info("Successfully created bucket") + slog.With("bucket", bucketName).Info("Successfully created bucket") } } diff --git a/modules/agent/redis/client.go b/modules/agent/redis/client.go index 9cb3142..2099fb9 100644 --- a/modules/agent/redis/client.go +++ b/modules/agent/redis/client.go @@ -1,12 +1,12 @@ package redis_agent import ( - config_module "github.com/oj-lab/platform/modules/config" + core_module "github.com/oj-lab/platform/modules/core" "github.com/redis/go-redis/v9" ) const ( - redisHostsProp = "redis.hosts" + redisHostsConfigKey = "redis.hosts" ) var ( @@ -14,7 +14,7 @@ var ( ) func init() { - RedisHosts = config_module.AppConfig().GetStringSlice(redisHostsProp) + RedisHosts = core_module.Config.GetStringSlice(redisHostsConfigKey) } type RedisClientInterface interface { diff --git a/modules/auth/github.go b/modules/auth/github.go index fc1d0ad..d44f962 100644 --- a/modules/auth/github.go +++ b/modules/auth/github.go @@ -6,7 +6,7 @@ import ( "net/http" "net/url" - config_module "github.com/oj-lab/platform/modules/config" + core_module "github.com/oj-lab/platform/modules/core" ) const ( @@ -14,9 +14,9 @@ const ( githubAccessTokenURL = "https://github.com/login/oauth/access_token" githubApiUserURL = "https://api.github.com/user" - serviceBaseURLProp = "service.base_url" - githubClientIDProp = "auth.github_client_id" - githubClientSecretProp = "auth.github_client_secret" + serviceBaseURLConfigKey = "service.base_url" + githubClientIDConfigKey = "auth.github_client_id" + githubClientSecretConfigKey = "auth.github_client_secret" ) var ( @@ -27,9 +27,9 @@ var ( ) func init() { - githubClientID = config_module.AppConfig().GetString(githubClientIDProp) - githubClientSecret = config_module.AppConfig().GetString(githubClientSecretProp) - serviceBaseURLStr := config_module.AppConfig().GetString(serviceBaseURLProp) + githubClientID = core_module.Config.GetString(githubClientIDConfigKey) + githubClientSecret = core_module.Config.GetString(githubClientSecretConfigKey) + serviceBaseURLStr := core_module.Config.GetString(serviceBaseURLConfigKey) var err error serviceBaseURL, err = url.Parse(serviceBaseURLStr) if err != nil { diff --git a/modules/auth/jwt.go b/modules/auth/jwt.go index 19d70db..116219e 100644 --- a/modules/auth/jwt.go +++ b/modules/auth/jwt.go @@ -5,7 +5,7 @@ import ( "time" "github.com/golang-jwt/jwt/v4" - config_module "github.com/oj-lab/platform/modules/config" + core_module "github.com/oj-lab/platform/modules/core" ) var ( @@ -14,8 +14,8 @@ var ( ) func init() { - jwtSecret = config_module.AppConfig().GetString("jwt.secret") - jwtDuration = config_module.AppConfig().GetDuration("jwt.duration") + jwtSecret = core_module.Config.GetString("jwt.secret") + jwtDuration = core_module.Config.GetDuration("jwt.duration") } type AuthToken struct { diff --git a/modules/auth/login_session.go b/modules/auth/login_session.go index 6ff81de..1b79ab6 100644 --- a/modules/auth/login_session.go +++ b/modules/auth/login_session.go @@ -6,7 +6,7 @@ import ( "time" "github.com/google/uuid" - config_module "github.com/oj-lab/platform/modules/config" + core_module "github.com/oj-lab/platform/modules/core" ) const defaultLoginSessionDuration = time.Hour * 24 * 7 @@ -14,7 +14,7 @@ const defaultLoginSessionDuration = time.Hour * 24 * 7 var LoginSessionDuration time.Duration func init() { - LoginSessionDuration = config_module.AppConfig().GetDuration("service.login_session_duration") + LoginSessionDuration = core_module.Config.GetDuration("service.login_session_duration") if LoginSessionDuration == 0 { LoginSessionDuration = defaultLoginSessionDuration } diff --git a/modules/auth/redis.go b/modules/auth/redis.go index 1b8545e..f738961 100644 --- a/modules/auth/redis.go +++ b/modules/auth/redis.go @@ -3,9 +3,9 @@ package auth_module import ( "context" "fmt" + "log/slog" redis_agent "github.com/oj-lab/platform/modules/agent/redis" - log_module "github.com/oj-lab/platform/modules/log" "github.com/redis/go-redis/v9" ) @@ -76,7 +76,8 @@ func UpdateLoginSessionByAccount(ctx context.Context, account string, data Login // TODO: KeepTTL only works in redis v6+ err = redisClient.Set(ctx, redisKey, val, redis.KeepTTL).Err() if err != nil { - log_module.AppLogger().Errorf("failed to update login session: %v", err) + slog.With("err", err). + Error("failed to update login session") } } diff --git a/modules/config/config.go b/modules/core/config.go similarity index 66% rename from modules/config/config.go rename to modules/core/config.go index 98bf870..4444965 100644 --- a/modules/config/config.go +++ b/modules/core/config.go @@ -1,4 +1,4 @@ -package config_module +package core_module import ( "fmt" @@ -9,42 +9,13 @@ import ( "github.com/spf13/viper" ) -const serviceEnvEnvKey = "OJ_LAB_SERVICE_ENV" - const defaultConfigName = "config" const defaultOverrideConfigName = "override" const defaultProjectRootName = "platform" -type ServiceEnv string - -const ( - serviceEnvDev ServiceEnv = "development" - serviceEnvPrd ServiceEnv = "production" -) +var Config *viper.Viper -var serviceEnv ServiceEnv var projectRoot string -var appConfig *viper.Viper - -func (se ServiceEnv) isValid() bool { - if se == serviceEnvDev || se == serviceEnvPrd { - return true - } - return false -} - -func IsDevEnv() bool { - return serviceEnv == serviceEnvDev -} - -func loadServiceEnv() { - serviceEnv = serviceEnvDev - env := os.Getenv(serviceEnvEnvKey) - if ServiceEnv(env).isValid() { - serviceEnv = ServiceEnv(env) - } - println("Env:", serviceEnv) -} func loadConfig() error { viper.AddConfigPath(projectRoot) @@ -64,14 +35,10 @@ func loadConfig() error { viper.AutomaticEnv() viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) - appConfig = viper.GetViper() + Config = viper.GetViper() return nil } -func AppConfig() *viper.Viper { - return appConfig -} - func loadProjectRoot() { // Try to locate project root, then find the workdir wd, err := os.Getwd() @@ -95,7 +62,6 @@ func ProjectRoot() string { } func init() { - loadServiceEnv() loadProjectRoot() if _, err := os.Stat(projectRoot); err != nil { panic(fmt.Sprintf("Project root not found: %v", projectRoot)) @@ -104,4 +70,5 @@ func init() { if err := loadConfig(); err != nil { panic(fmt.Sprintf("Load config with error: %v", err)) } + setupLog() } diff --git a/modules/core/log.go b/modules/core/log.go new file mode 100644 index 0000000..3796443 --- /dev/null +++ b/modules/core/log.go @@ -0,0 +1,49 @@ +package core_module + +import ( + "log/slog" + "os" + + "github.com/lmittmann/tint" +) + +const ( + logLevelConfigKey = "log.level" + logFormatConfigKey = "log.format" +) + +var ( + LogLevel slog.Level + LogFormat string +) + +func StringToSlogLevel( + level string, +) slog.Level { + switch level { + case "debug": + return slog.LevelDebug + case "info": + return slog.LevelInfo + case "warn": + return slog.LevelWarn + case "error": + return slog.LevelError + default: + return slog.LevelInfo + } +} + +func setupLog() { + LogLevel = StringToSlogLevel(Config.GetString(logLevelConfigKey)) + LogFormat = Config.GetString(logFormatConfigKey) + + var handler slog.Handler + if LogFormat == "json" { + handler = slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: LogLevel}) + slog.SetDefault(slog.New(handler)) + } else { + handler = tint.NewHandler(os.Stdout, &tint.Options{Level: LogLevel}) + slog.SetDefault(slog.New(handler)) + } +} diff --git a/modules/log/init_test.go b/modules/log/init_test.go deleted file mode 100644 index 4f42017..0000000 --- a/modules/log/init_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package log_module - -import ( - "testing" - - "github.com/spf13/viper" -) - -func TestInit(T *testing.T) { - logLevel := viper.GetString(logLevelProp) - if logLevel != "debug" { - T.Errorf("log level is not debug but %s", logLevel) - } -} diff --git a/modules/log/log.go b/modules/log/log.go deleted file mode 100644 index 1878005..0000000 --- a/modules/log/log.go +++ /dev/null @@ -1,56 +0,0 @@ -package log_module - -import ( - "os" - "runtime" - "strconv" - - config_module "github.com/oj-lab/platform/modules/config" - "github.com/sirupsen/logrus" -) - -const logLevelProp = "log.level" -const logFormatProp = "log.format" -const logPrettyJson = "log.pretty_json" -const logTimeOn = "log.time_on" -const logTimeFormat = "log.time_format" - -func AppLogger() *logrus.Entry { - return logrus.WithFields(logrus.Fields{ - "caller": func() string { - pc := make([]uintptr, 1) - runtime.Callers(3, pc) - f := runtime.FuncForPC(pc[0]) - name, line := f.FileLine(pc[0]) - return name + ":" + strconv.Itoa(line) - }(), - }) -} - -func setupLog() { - logrus.SetOutput(os.Stdout) - - logrus.SetLevel(logrus.DebugLevel) - format := config_module.AppConfig().GetString(logFormatProp) - lvl := config_module.AppConfig().GetString(logLevelProp) - prettyJson := config_module.AppConfig().GetBool(logPrettyJson) - timeOn := config_module.AppConfig().GetBool(logTimeOn) - timestampFormat := config_module.AppConfig().GetString(logTimeFormat) - - logLevel, err := logrus.ParseLevel(lvl) - if err == nil { - println("log level:", lvl) - logrus.SetLevel(logLevel) - } - if format == "json" { - logrus.SetFormatter(&logrus.JSONFormatter{ - TimestampFormat: timestampFormat, - DisableTimestamp: !timeOn, - PrettyPrint: prettyJson, - }) - } -} - -func init() { - setupLog() -} diff --git a/override.example.toml b/override.example.toml index 447b0d4..c0fc922 100644 --- a/override.example.toml +++ b/override.example.toml @@ -12,4 +12,7 @@ level = "info" [service] # When you need to work with the lastest frontend code, # set the frontend_dist to the path of the frontend repository. -frontend_dist = "../frontend/dist" \ No newline at end of file +frontend_dist = "../frontend/dist" + +[gorm] +logger.ignore_run_log = true \ No newline at end of file diff --git a/services/judge/judge_cache.go b/services/judge/judge_cache.go index ad82196..7670f1a 100644 --- a/services/judge/judge_cache.go +++ b/services/judge/judge_cache.go @@ -19,7 +19,7 @@ func UpsertJudgeCache(ctx context.Context, uid uuid.UUID, verdict judge_model.Ju if err != nil { return err } - // log_module.AppLogger().WithField("judge", judge).Debug("getjudge") + // slog.With("judge", judge).Debug("getjudge") var problem *problem_model.Problem problem, err = problem_model.GetProblem(db, judge.ProblemSlug) if err != nil { @@ -53,7 +53,7 @@ func UpsertJudgeCache(ctx context.Context, uid uuid.UUID, verdict judge_model.Ju } } - // log_module.AppLogger().WithField("scoreCache", scoreCache).Debug("get scoreCache") + // slog.With("scoreCache", scoreCache).Debug("get scoreCache") // previous no ac || current more early // need to update @@ -77,7 +77,7 @@ func UpsertJudgeCache(ctx context.Context, uid uuid.UUID, verdict judge_model.Ju } rankCache.TotalSubmissions += scoreCache.SubmissionCount - preSubmissionCount problem.SubmitCount += scoreCache.SubmissionCount - preSubmissionCount - // log_module.AppLogger().WithField("scoreCache", scoreCache).Debug("update scoreCache") + // slog.With("scoreCache", scoreCache).Debug("update scoreCache") err = problem_model.UpdateProblem(db, *problem) if err != nil { diff --git a/services/user/user.go b/services/user/user.go index 88b0744..5947e68 100644 --- a/services/user/user.go +++ b/services/user/user.go @@ -3,13 +3,13 @@ package user_service import ( "context" "fmt" + "log/slog" judge_model "github.com/oj-lab/platform/models/judge" user_model "github.com/oj-lab/platform/models/user" casbin_agent "github.com/oj-lab/platform/modules/agent/casbin" gorm_agent "github.com/oj-lab/platform/modules/agent/gorm" auth_module "github.com/oj-lab/platform/modules/auth" - log_module "github.com/oj-lab/platform/modules/log" ) func CreateUser(ctx context.Context, request user_model.User) (*user_model.User, error) { @@ -53,20 +53,20 @@ func DeleteUser(ctx context.Context, account string) error { for _, judge := range judges { err = judge_model.DeleteJudgeResultByJudgeUID(db, judge.UID) if err != nil { - log_module.AppLogger().WithField("judge", judge).Errorf("delete judge result failed: %v", err) + slog.With("judge", judge, "error", err).Error("delete judge result failed") } } err = judge_model.DeleteJudgesByAccount(db, account) if err != nil { - log_module.AppLogger().WithField("account", account).Errorf("delete judges failed: %v", err) + slog.With("account", account, "error", err).Error("delete judges failed") } err = judge_model.DeleteJudgeRankCache(db, account) if err != nil { - log_module.AppLogger().WithField("account", account).Errorf("delete judge rank cache failed: %v", err) + slog.With("account", account, "error", err).Error("delete judge rank cache failed") } err = judge_model.DeleteJudgeScoreCacheByUserAccount(db, account) if err != nil { - log_module.AppLogger().WithField("account", account).Errorf("delete judge score cache failed: %v", err) + slog.With("account", account, "error", err).Error("delete judge score cache failed") } err = user_model.DeleteUser(db, account) @@ -180,10 +180,7 @@ func CheckUserExist(ctx context.Context, account string) (bool, error) { } if count > 1 { - log_module.AppLogger(). - WithField("account", account). - WithField("count", count). - Warn("user account is not unique") + slog.With("account", account, "count", count).Warn("user account is not unique") } return count > 0, nil