diff --git a/server/dao/dao.go b/server/dao/dao.go index e916766..e53acd1 100644 --- a/server/dao/dao.go +++ b/server/dao/dao.go @@ -1,6 +1,8 @@ package dao import ( + "io" + "log" "time" "github.com/spf13/viper" @@ -12,10 +14,17 @@ import ( var db *gorm.DB -func InitDb() { +func InitDb(logWriter io.Writer) { var err error db, err = gorm.Open(mysql.Open(viper.GetString("dsn")), &gorm.Config{ - Logger: logger.Default.LogMode(logger.Info), // show basically all sql gorm generated for DEBUG + Logger: logger.New( + log.New(logWriter, "[GORM] ", log.LstdFlags|log.Llongfile|log.Lmicroseconds), + logger.Config{ + SlowThreshold: 100 * time.Millisecond, // Slow SQL threshold + Colorful: viper.GetBool("enable_console_color"), + IgnoreRecordNotFoundError: false, // Ignore ErrRecordNotFound error for logger + LogLevel: logger.Info, // show basically all sql gorm generated for DEBUG + }), }) if err != nil { panic(errors.Because(errors.New("initialization failed"), err, "")) diff --git a/server/dao/exam_session.go b/server/dao/exam_session.go index d400f27..1683a4e 100644 --- a/server/dao/exam_session.go +++ b/server/dao/exam_session.go @@ -459,7 +459,7 @@ func CalculateScores(examId int) { if utils.IsAnagram(e.StudentAnswer, e.RightAnswer) { // 全选对得满分 sessionId2ScoreMap[e.ExamSessionID] += int(exam.MaqScore) * 10 statistics.overallCorrectScore += int(exam.MaqScore) * 10 - } else if utils.Contains(e.RightAnswer, e.StudentAnswer) { // 漏选得半分 + } else if e.StudentAnswer != "" && utils.Contains(e.RightAnswer, e.StudentAnswer) { // 漏选得半分(留空不算漏选) sessionId2ScoreMap[e.ExamSessionID] += int(exam.MaqScore) * 10 / 2 statistics.overallCorrectScore += int(exam.MaqScore) * 10 / 2 } diff --git a/server/go.mod b/server/go.mod index 31a3d5f..51d17ee 100644 --- a/server/go.mod +++ b/server/go.mod @@ -6,8 +6,8 @@ require ( github.com/appleboy/gin-jwt/v2 v2.7.0 github.com/gin-contrib/cors v1.3.1 github.com/gin-gonic/gin v1.7.4 - github.com/google/logger v1.1.1 github.com/patrickmn/go-cache v2.1.0+incompatible + github.com/rs/zerolog v1.27.0 github.com/spf13/viper v1.9.0 github.com/subchen/go-trylock/v2 v2.0.0 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 @@ -31,7 +31,8 @@ require ( github.com/json-iterator/go v1.1.11 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/magiconair/properties v1.8.5 // indirect - github.com/mattn/go-isatty v0.0.12 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect github.com/mitchellh/mapstructure v1.4.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -42,7 +43,7 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.2.0 // indirect github.com/ugorji/go/codec v1.1.7 // indirect - golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect + golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect golang.org/x/text v0.3.6 // indirect google.golang.org/protobuf v1.27.1 // indirect gopkg.in/ini.v1 v1.63.2 // indirect diff --git a/server/go.sum b/server/go.sum index 18b4fb2..423f847 100644 --- a/server/go.sum +++ b/server/go.sum @@ -67,6 +67,7 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnht github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -159,8 +160,6 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/logger v1.1.1 h1:+6Z2geNxc9G+4D4oDO9njjjn2d0wN5d7uOo0vOIW1NQ= -github.com/google/logger v1.1.1/go.mod h1:BkeJZ+1FhQ+/d087r4dzojEg1u2ZX+ZqG1jTUrLM+zQ= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -238,13 +237,16 @@ github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPK github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= @@ -267,6 +269,7 @@ github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTK github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -275,6 +278,9 @@ github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSg github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.27.0 h1:1T7qCieN22GVc8S4Q2yuexzBb1EqjbgjSH9RohbMjKs= +github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= @@ -487,7 +493,6 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -495,8 +500,9 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/server/handlers/cache.go b/server/handlers/cache.go index 8116c9c..409b849 100644 --- a/server/handlers/cache.go +++ b/server/handlers/cache.go @@ -6,8 +6,8 @@ import ( jwt "github.com/appleboy/gin-jwt/v2" "github.com/gin-gonic/gin" - "github.com/google/logger" "github.com/patrickmn/go-cache" + "github.com/rs/zerolog/log" "github.com/gonearewe/EasyTesting/utils" ) @@ -17,7 +17,7 @@ func GetCacheHandler(c *gin.Context) { if ret, found := utils.MemoryStore.Get(key); found { c.String(200, ret.(string)) } else { - logger.Info("no cache found for exam_session_id: " + key) + log.Info().Str("exam_session_id", key).Msg("no cache found") } } diff --git a/server/handlers/exam_session_task.go b/server/handlers/exam_session_task.go index 53db80a..3c39530 100644 --- a/server/handlers/exam_session_task.go +++ b/server/handlers/exam_session_task.go @@ -1,20 +1,23 @@ package handlers import ( - "github.com/google/logger" - "github.com/gonearewe/EasyTesting/dao" "github.com/gonearewe/EasyTesting/models" + "github.com/rs/zerolog/log" ) func InitTaskConsumers() { const consumerNum = 8 for i := 0; i < consumerNum; i++ { go func(id int) { - logger.Infof("task consumer %d started\n", id) + log.Info().Msgf("task consumer %d started", id) defer func() { if e := recover(); e != nil { - logger.Fatalf("task consumer %d panicked: %v", id, e) + if err, ok := e.(error); ok { + log.Fatal().Stack().Err(err).Msgf("task consumer %d panicked", id) + } else { + log.Fatal().Stack().Interface("panic value", e).Msgf("task consumer %d panicked", id) + } } }() diff --git a/server/init.go b/server/init.go index 34c1ee9..1b58fa4 100644 --- a/server/init.go +++ b/server/init.go @@ -1,6 +1,13 @@ package main import ( + "io" + "os" + "path/filepath" + + "github.com/gin-gonic/gin" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" "github.com/spf13/viper" ) @@ -15,3 +22,34 @@ func initViper() { // Config file found and successfully parsed } + +func initLogWriter() io.Writer { + var file io.Writer + ex, err := os.Executable() + if err != nil { + panic(err) + } + exPath := filepath.Dir(ex) + logPath := filepath.Join(exPath, viper.GetString("log_file_name")) + file, err = os.Create(logPath) + if err != nil { + panic(err) + } + writer := io.MultiWriter(file, os.Stdout) // write log to both stdout and file + return writer +} + +func initZeroLog(logWriter io.Writer) { + zerolog.SetGlobalLevel(zerolog.InfoLevel) + log.Logger = zerolog.New(logWriter).With().Timestamp().Caller().Logger() + log.Info().Msg("ZeroLog initialized") +} + +func initGin(logWriter io.Writer) { + if !viper.GetBool("enable_console_color") { + gin.DisableConsoleColor() + } + gin.DefaultWriter = logWriter + gin.SetMode(gin.ReleaseMode) + log.Info().Msg("Gin initialized") +} diff --git a/server/main.go b/server/main.go index 011a74f..cc6a16a 100644 --- a/server/main.go +++ b/server/main.go @@ -4,10 +4,8 @@ import ( "fmt" "log" "net/http" - "os" "github.com/gin-gonic/gin" - "github.com/google/logger" "github.com/spf13/viper" "github.com/gonearewe/EasyTesting/dao" @@ -17,16 +15,14 @@ import ( func init() { initViper() - logger.Init("EasyTesting", true, false, os.Stdout) + writer := initLogWriter() + initZeroLog(writer) handlers.InitTaskConsumers() - dao.InitDb() + dao.InitDb(writer) + initGin(writer) } func main() { - if viper.GetBool("disable_console_color") { - gin.DisableConsoleColor() - } - gin.SetMode(gin.ReleaseMode) r := gin.New() teacherAuthRouter, adminAuthRouter, studentAuthRouter := middlewares.SetupMiddleWares(r) SetupRoute(r, teacherAuthRouter, adminAuthRouter, studentAuthRouter) diff --git a/server/middlewares/recovery.go b/server/middlewares/recovery.go index 993d756..3c59538 100644 --- a/server/middlewares/recovery.go +++ b/server/middlewares/recovery.go @@ -1,21 +1,18 @@ package middlewares import ( - "runtime" - "github.com/gin-gonic/gin" - "github.com/google/logger" + "github.com/rs/zerolog/log" ) func recovery(c *gin.Context) { defer func() { - if err := recover(); err != nil { - trace := make([]byte, 1<<16) - // get error stack info - n := runtime.Stack(trace, false) - logger.Errorf("%v\n%s", err, string(trace[:n])) - // logger.Error(err) - c.AbortWithStatus(400) + if e := recover(); e != nil { + if err, ok := e.(error); ok { + log.Error().Stack().Err(err).Msg("Gin recover") + } else { + log.Error().Stack().Interface("panic value", e).Msg("Gin recover") + } } }() c.Next() diff --git a/server/server-config.yaml b/server/server-config.yaml index f56a548..aa43175 100644 --- a/server/server-config.yaml +++ b/server/server-config.yaml @@ -1,5 +1,6 @@ # 服务端软件的配置文件,应当与程序放在同一级目录下,不支持热重载,修改后需要重启程序才能生效 -disable_console_color: true # console 输出是否禁用颜色,如果日志输出到文件,控制颜色的字符会变成乱码 +enable_console_color: false # console 输出是否使用颜色,如果日志输出到文件,控制颜色的字符会变成乱码 dsn: "root:12345@tcp(127.0.0.1:3306)/easy_testing?charset=utf8mb4&parseTime=True&loc=Local" # 数据库 DSN port: 9345 # 服务器监听的端口 -jwt_secret_key: "4xg8jlAh8s00KR1Fdc6mY" # JWT 使用的密钥,建议正式部署时修改它 \ No newline at end of file +jwt_secret_key: "4xg8jlAh8s00KR1Fdc6mY" # JWT 使用的密钥,建议正式部署时修改它 +log_file_name: "debug.log" # 日志文件的名字,文件将位于执行程序目录(或其快捷方式目录)下;日志会在 stdout 和文件中同步出现 \ No newline at end of file diff --git a/test/student.py b/test/student.py index e57c5f0..b4f2ca2 100644 --- a/test/student.py +++ b/test/student.py @@ -1,4 +1,4 @@ -# Run this with `pipenv run locust -f .\student.py -H http://localhost:9000`, +# Run this with `pipenv run locust -f .\student.py -H http://localhost:9345`, # and use Web UI at `http://localhost:8089/` import random