Browse Source

Merge pull request #283 from SliverHorn/sliverhorn_dev

gorm日志收集进log功能
奇淼(piexlmax 4 years ago
parent
commit
52f5e831cc

+ 2 - 1
server/config.yaml

@@ -56,10 +56,11 @@ mysql:
   config: 'charset=utf8mb4&parseTime=True&loc=Local'
   db-name: 'qmPlus'
   username: 'root'
-  password: 'root123'
+  password: 'Aa@6447985'
   max-idle-conns: 10
   max-open-conns: 100
   log-mode: false
+  log-zap: false
 
 # local configuration
 local:

+ 1 - 0
server/config/gorm.go

@@ -9,4 +9,5 @@ type Mysql struct {
 	MaxIdleConns int    `mapstructure:"max-idle-conns" json:"maxIdleConns" yaml:"max-idle-conns"`
 	MaxOpenConns int    `mapstructure:"max-open-conns" json:"maxOpenConns" yaml:"max-open-conns"`
 	LogMode      bool   `mapstructure:"log-mode" json:"logMode" yaml:"log-mode"`
+	LogZap       bool   `mapstructure:"log-zap" json:"logZap" yaml:"log-zap"`
 }

+ 1 - 1
server/core/server.go

@@ -29,7 +29,7 @@ func RunWindowsServer() {
 
 	fmt.Printf(`
 	欢迎使用 Gin-Vue-Admin
-	当前版本:V2.3.6
+	当前版本:V2.3.7
 	默认自动化文档地址:http://127.0.0.1%s/swagger/index.html
 	默认前端文件运行地址:http://127.0.0.1:8080
 	如果项目让您获得了收益,希望您能请团队喝杯可乐:https://www.gin-vue-admin.com/docs/coffee

+ 6 - 0
server/initialize/gorm.go

@@ -73,6 +73,12 @@ func GormMysql() *gorm.DB {
 
 // gormConfig 根据配置决定是否开启日志
 func gormConfig(mod bool) *gorm.Config {
+	if global.GVA_CONFIG.Mysql.LogZap {
+		return &gorm.Config{
+			Logger:                                   Default.LogMode(logger.Info),
+			DisableForeignKeyConstraintWhenMigrating: true,
+		}
+	}
 	if mod {
 		return &gorm.Config{
 			Logger:                                   logger.Default.LogMode(logger.Info),

+ 150 - 0
server/initialize/logger.go

@@ -0,0 +1,150 @@
+package initialize
+
+import (
+	"context"
+	"fmt"
+	"gin-vue-admin/global"
+	"go.uber.org/zap"
+	"gorm.io/gorm/logger"
+	"gorm.io/gorm/utils"
+	"io/ioutil"
+	"log"
+	"os"
+	"time"
+)
+
+var (
+	Discard = New(log.New(ioutil.Discard, "", log.LstdFlags), GormConfig{})
+	Default = New(log.New(os.Stdout, "\r\n", log.LstdFlags), GormConfig{
+		SlowThreshold: 200 * time.Millisecond,
+		LogLevel:      logger.Warn,
+		Colorful:      true,
+	})
+	Recorder = traceRecorder{Interface: Default, BeginAt: time.Now()}
+)
+
+type traceRecorder struct {
+	logger.Interface
+	BeginAt      time.Time
+	SQL          string
+	RowsAffected int64
+	Err          error
+}
+
+func New(writer Writer, config GormConfig) logger.Interface {
+	var (
+		infoStr      = "%s\n[info] "
+		warnStr      = "%s\n[warn] "
+		errStr       = "%s\n[error] "
+		traceStr     = "%s\n[%.3fms] [rows:%v] %s"
+		traceWarnStr = "%s %s\n[%.3fms] [rows:%v] %s"
+		traceErrStr  = "%s %s\n[%.3fms] [rows:%v] %s"
+	)
+
+	if config.Colorful {
+		infoStr = logger.Green + "%s\n" + logger.Reset + logger.Green + "[info] " + logger.Reset
+		warnStr = logger.BlueBold + "%s\n" + logger.Reset + logger.Magenta + "[warn] " + logger.Reset
+		errStr = logger.Magenta + "%s\n" + logger.Reset + logger.Red + "[error] " + logger.Reset
+		traceStr = logger.Green + "%s\n" + logger.Reset + logger.Yellow + "[%.3fms] " + logger.BlueBold + "[rows:%v]" + logger.Reset + " %s"
+		traceWarnStr = logger.Green + "%s " + logger.Yellow + "%s\n" + logger.Reset + logger.RedBold + "[%.3fms] " + logger.Yellow + "[rows:%v]" + logger.Magenta + " %s" + logger.Reset
+		traceErrStr = logger.RedBold + "%s " + logger.MagentaBold + "%s\n" + logger.Reset + logger.Yellow + "[%.3fms] " + logger.BlueBold + "[rows:%v]" + logger.Reset + " %s"
+	}
+
+	return &GormLogger{
+		Writer:       writer,
+		GormConfig:   config,
+		infoStr:      infoStr,
+		warnStr:      warnStr,
+		errStr:       errStr,
+		traceStr:     traceStr,
+		traceWarnStr: traceWarnStr,
+		traceErrStr:  traceErrStr,
+	}
+}
+
+// Writer log writer interface
+type Writer interface {
+	Printf(string, ...interface{})
+}
+
+type GormConfig struct {
+	SlowThreshold time.Duration
+	Colorful      bool
+	LogLevel      logger.LogLevel
+}
+
+type GormLogger struct {
+	Writer
+	GormConfig
+	infoStr, warnStr, errStr            string
+	traceStr, traceErrStr, traceWarnStr string
+}
+
+func (g *GormLogger) LogMode(level logger.LogLevel) logger.Interface {
+	newLogger := *g
+	newLogger.LogLevel = level
+	return &newLogger
+}
+
+func (g *GormLogger) Info(ctx context.Context, message string, data ...interface{}) {
+	if g.LogLevel >= logger.Info {
+		g.Printf(g.infoStr+message, append([]interface{}{utils.FileWithLineNum()}, data...)...)
+	}
+}
+
+func (g *GormLogger) Warn(ctx context.Context, message string, data ...interface{}) {
+	if g.LogLevel >= logger.Warn {
+		g.Printf(g.warnStr+message, append([]interface{}{utils.FileWithLineNum()}, data...)...)
+	}
+}
+
+func (g *GormLogger) Error(ctx context.Context, message string, data ...interface{}) {
+	if g.LogLevel >= logger.Error {
+		g.Printf(g.errStr+message, append([]interface{}{utils.FileWithLineNum()}, data...)...)
+	}
+}
+
+func (g *GormLogger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
+	if g.LogLevel > 0 {
+		elapsed := time.Since(begin)
+		switch {
+		case err != nil && g.LogLevel >= logger.Error:
+			sql, rows := fc()
+			if rows == -1 {
+				g.Printf(g.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, "-", sql)
+			} else {
+				g.Printf(g.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, rows, sql)
+			}
+		case elapsed > g.SlowThreshold && g.SlowThreshold != 0 && g.LogLevel >= logger.Warn:
+			sql, rows := fc()
+			slowLog := fmt.Sprintf("SLOW SQL >= %v", g.SlowThreshold)
+			if rows == -1 {
+				g.Printf(g.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, "-", sql)
+			} else {
+				g.Printf(g.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, rows, sql)
+			}
+		case g.LogLevel >= logger.Info:
+			sql, rows := fc()
+			if rows == -1 {
+				g.Printf(g.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, "-", sql)
+			} else {
+				g.Printf(g.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, rows, sql)
+			}
+		}
+	}
+}
+
+func (g *GormLogger) Printf(message string, data ...interface{}) {
+	switch len(data) {
+	case 0:
+		global.GVA_LOG.Info(message)
+	case 1:
+		global.GVA_LOG.Info("gorm", zap.Any("src", data[0]))
+	case 2:
+		global.GVA_LOG.Info("gorm", zap.Any("src", data[0]), zap.Any("duration", data[1]))
+	case 3:
+		global.GVA_LOG.Info("gorm", zap.Any("src", data[0]), zap.Any("duration", data[1]), zap.Any("rows", data[2]))
+	case 4:
+		global.GVA_LOG.Info("gorm", zap.Any("src", data[0]), zap.Any("duration", data[1]), zap.Any("rows", data[2]), zap.Any("sql", data[3]))
+	}
+}

+ 1 - 1
server/initialize/router.go

@@ -29,7 +29,7 @@ func Routers() *gin.Engine {
 		router.InitBaseRouter(PublicGroup) // 注册基础功能路由 不做鉴权
 	}
 	PrivateGroup := Router.Group("")
-	PrivateGroup.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()).Use(middleware.OperationRecord())
+	PrivateGroup.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler())
 	{
 		router.InitApiRouter(PrivateGroup)                   // 注册功能api路由
 		router.InitJwtRouter(PrivateGroup)                   // jwt相关路由

+ 6 - 6
server/middleware/operation.go

@@ -11,7 +11,6 @@ import (
 	"io/ioutil"
 	"net/http"
 	"strconv"
-	"strings"
 	"time"
 )
 
@@ -31,7 +30,7 @@ func OperationRecord() gin.HandlerFunc {
 		if claims, ok := c.Get("claims"); ok {
 			waitUse := claims.(*request.CustomClaims)
 			userId = int(waitUse.ID)
-		}else {
+		} else {
 			id, err := strconv.Atoi(c.Request.Header.Get("x-user-id"))
 			if err != nil {
 				userId = 0
@@ -46,10 +45,11 @@ func OperationRecord() gin.HandlerFunc {
 			Body:   string(body),
 			UserID: userId,
 		}
-		values := c.Request.Header.Values("content-type")
-		if len(values) >0 && strings.Contains(values[0], "boundary") {
-			record.Body = "file"
-		}
+		// 存在某些未知错误 TODO
+		//values := c.Request.Header.Values("content-type")
+		//if len(values) >0 && strings.Contains(values[0], "boundary") {
+		//	record.Body = "file"
+		//}
 		writer := responseBodyWriter{
 			ResponseWriter: c.Writer,
 			body:           &bytes.Buffer{},

+ 2 - 1
server/router/exp_customer.go

@@ -2,11 +2,12 @@ package router
 
 import (
 	"gin-vue-admin/api/v1"
+	"gin-vue-admin/middleware"
 	"github.com/gin-gonic/gin"
 )
 
 func InitCustomerRouter(Router *gin.RouterGroup) {
-	ApiRouter := Router.Group("customer")
+	ApiRouter := Router.Group("customer").Use(middleware.OperationRecord())
 	{
 		ApiRouter.POST("customer", v1.CreateExaCustomer)     // 创建客户
 		ApiRouter.PUT("customer", v1.UpdateExaCustomer)      // 更新客户

+ 2 - 1
server/router/sys_api.go

@@ -2,11 +2,12 @@ package router
 
 import (
 	"gin-vue-admin/api/v1"
+	"gin-vue-admin/middleware"
 	"github.com/gin-gonic/gin"
 )
 
 func InitApiRouter(Router *gin.RouterGroup) {
-	ApiRouter := Router.Group("api")
+	ApiRouter := Router.Group("api").Use(middleware.OperationRecord())
 	{
 		ApiRouter.POST("createApi", v1.CreateApi)   // 创建Api
 		ApiRouter.POST("deleteApi", v1.DeleteApi)   // 删除Api

+ 2 - 1
server/router/sys_authority.go

@@ -2,11 +2,12 @@ package router
 
 import (
 	"gin-vue-admin/api/v1"
+	"gin-vue-admin/middleware"
 	"github.com/gin-gonic/gin"
 )
 
 func InitAuthorityRouter(Router *gin.RouterGroup) {
-	AuthorityRouter := Router.Group("authority")
+	AuthorityRouter := Router.Group("authority").Use(middleware.OperationRecord())
 	{
 		AuthorityRouter.POST("createAuthority", v1.CreateAuthority)   // 创建角色
 		AuthorityRouter.POST("deleteAuthority", v1.DeleteAuthority)   // 删除角色

+ 2 - 1
server/router/sys_casbin.go

@@ -2,11 +2,12 @@ package router
 
 import (
 	"gin-vue-admin/api/v1"
+	"gin-vue-admin/middleware"
 	"github.com/gin-gonic/gin"
 )
 
 func InitCasbinRouter(Router *gin.RouterGroup) {
-	CasbinRouter := Router.Group("casbin")
+	CasbinRouter := Router.Group("casbin").Use(middleware.OperationRecord())
 	{
 		CasbinRouter.POST("updateCasbin", v1.UpdateCasbin)
 		CasbinRouter.POST("getPolicyPathByAuthorityId", v1.GetPolicyPathByAuthorityId)

+ 2 - 1
server/router/sys_dictionary.go

@@ -2,11 +2,12 @@ package router
 
 import (
 	"gin-vue-admin/api/v1"
+	"gin-vue-admin/middleware"
 	"github.com/gin-gonic/gin"
 )
 
 func InitSysDictionaryRouter(Router *gin.RouterGroup) {
-	SysDictionaryRouter := Router.Group("sysDictionary")
+	SysDictionaryRouter := Router.Group("sysDictionary").Use(middleware.OperationRecord())
 	{
 		SysDictionaryRouter.POST("createSysDictionary", v1.CreateSysDictionary)   // 新建SysDictionary
 		SysDictionaryRouter.DELETE("deleteSysDictionary", v1.DeleteSysDictionary) // 删除SysDictionary

+ 2 - 1
server/router/sys_dictionary_detail.go

@@ -2,11 +2,12 @@ package router
 
 import (
 	"gin-vue-admin/api/v1"
+	"gin-vue-admin/middleware"
 	"github.com/gin-gonic/gin"
 )
 
 func InitSysDictionaryDetailRouter(Router *gin.RouterGroup) {
-	SysDictionaryDetailRouter := Router.Group("sysDictionaryDetail")
+	SysDictionaryDetailRouter := Router.Group("sysDictionaryDetail").Use(middleware.OperationRecord())
 	{
 		SysDictionaryDetailRouter.POST("createSysDictionaryDetail", v1.CreateSysDictionaryDetail)   // 新建SysDictionaryDetail
 		SysDictionaryDetailRouter.DELETE("deleteSysDictionaryDetail", v1.DeleteSysDictionaryDetail) // 删除SysDictionaryDetail

+ 2 - 1
server/router/sys_email.go

@@ -2,11 +2,12 @@ package router
 
 import (
 	"gin-vue-admin/api/v1"
+	"gin-vue-admin/middleware"
 	"github.com/gin-gonic/gin"
 )
 
 func InitEmailRouter(Router *gin.RouterGroup) {
-	UserRouter := Router.Group("email")
+	UserRouter := Router.Group("email").Use(middleware.OperationRecord())
 	{
 		UserRouter.POST("emailTest", v1.EmailTest) // 发送测试邮件
 	}

+ 2 - 1
server/router/sys_jwt.go

@@ -2,11 +2,12 @@ package router
 
 import (
 	"gin-vue-admin/api/v1"
+	"gin-vue-admin/middleware"
 	"github.com/gin-gonic/gin"
 )
 
 func InitJwtRouter(Router *gin.RouterGroup) {
-	ApiRouter := Router.Group("jwt")
+	ApiRouter := Router.Group("jwt").Use(middleware.OperationRecord())
 	{
 		ApiRouter.POST("jsonInBlacklist", v1.JsonInBlacklist) // jwt加入黑名单
 	}

+ 2 - 1
server/router/sys_menu.go

@@ -2,11 +2,12 @@ package router
 
 import (
 	"gin-vue-admin/api/v1"
+	"gin-vue-admin/middleware"
 	"github.com/gin-gonic/gin"
 )
 
 func InitMenuRouter(Router *gin.RouterGroup) (R gin.IRoutes) {
-	MenuRouter := Router.Group("menu")
+	MenuRouter := Router.Group("menu").Use(middleware.OperationRecord())
 	{
 		MenuRouter.POST("getMenu", v1.GetMenu)                   // 获取菜单树
 		MenuRouter.POST("getMenuList", v1.GetMenuList)           // 分页获取基础menu列表

+ 6 - 5
server/router/sys_operation_record.go

@@ -2,17 +2,18 @@ package router
 
 import (
 	"gin-vue-admin/api/v1"
+	"gin-vue-admin/middleware"
 	"github.com/gin-gonic/gin"
 )
 
 func InitSysOperationRecordRouter(Router *gin.RouterGroup) {
-	SysOperationRecordRouter := Router.Group("sysOperationRecord")
+	SysOperationRecordRouter := Router.Group("sysOperationRecord").Use(middleware.OperationRecord())
 	{
-		SysOperationRecordRouter.POST("createSysOperationRecord", v1.CreateSysOperationRecord)   // 新建SysOperationRecord
-		SysOperationRecordRouter.DELETE("deleteSysOperationRecord", v1.DeleteSysOperationRecord) // 删除SysOperationRecord
+		SysOperationRecordRouter.POST("createSysOperationRecord", v1.CreateSysOperationRecord)             // 新建SysOperationRecord
+		SysOperationRecordRouter.DELETE("deleteSysOperationRecord", v1.DeleteSysOperationRecord)           // 删除SysOperationRecord
 		SysOperationRecordRouter.DELETE("deleteSysOperationRecordByIds", v1.DeleteSysOperationRecordByIds) // 批量删除SysOperationRecord
-		SysOperationRecordRouter.GET("findSysOperationRecord", v1.FindSysOperationRecord)        // 根据ID获取SysOperationRecord
-		SysOperationRecordRouter.GET("getSysOperationRecordList", v1.GetSysOperationRecordList)  // 获取SysOperationRecord列表
+		SysOperationRecordRouter.GET("findSysOperationRecord", v1.FindSysOperationRecord)                  // 根据ID获取SysOperationRecord
+		SysOperationRecordRouter.GET("getSysOperationRecordList", v1.GetSysOperationRecordList)            // 获取SysOperationRecord列表
 
 	}
 }

+ 2 - 1
server/router/sys_system.go

@@ -2,11 +2,12 @@ package router
 
 import (
 	"gin-vue-admin/api/v1"
+	"gin-vue-admin/middleware"
 	"github.com/gin-gonic/gin"
 )
 
 func InitSystemRouter(Router *gin.RouterGroup) {
-	SystemRouter := Router.Group("system")
+	SystemRouter := Router.Group("system").Use(middleware.OperationRecord())
 	{
 		SystemRouter.POST("getSystemConfig", v1.GetSystemConfig) // 获取配置文件内容
 		SystemRouter.POST("setSystemConfig", v1.SetSystemConfig) // 设置配置文件内容

+ 2 - 1
server/router/sys_user.go

@@ -2,11 +2,12 @@ package router
 
 import (
 	"gin-vue-admin/api/v1"
+	"gin-vue-admin/middleware"
 	"github.com/gin-gonic/gin"
 )
 
 func InitUserRouter(Router *gin.RouterGroup) {
-	UserRouter := Router.Group("user")
+	UserRouter := Router.Group("user").Use(middleware.OperationRecord())
 	{
 		UserRouter.POST("register", v1.Register)
 		UserRouter.POST("changePassword", v1.ChangePassword)     // 修改密码

+ 2 - 1
server/router/sys_workflow.go

@@ -2,11 +2,12 @@ package router
 
 import (
 	"gin-vue-admin/api/v1"
+	"gin-vue-admin/middleware"
 	"github.com/gin-gonic/gin"
 )
 
 func InitWorkflowRouter(Router *gin.RouterGroup) {
-	WorkflowRouter := Router.Group("workflow")
+	WorkflowRouter := Router.Group("workflow").Use(middleware.OperationRecord())
 	{
 		WorkflowRouter.POST("createWorkFlow", v1.CreateWorkFlow) // 创建工作流
 	}

+ 5 - 7
server/service/sys_auto_code.go

@@ -209,7 +209,6 @@ func addAutoMoveFile(data *tplData) {
 	}
 }
 
-
 //@author: [piexlmax](https://github.com/piexlmax)
 //@author: [SliverHorn](https://github.com/SliverHorn)
 //@function: CreateApi
@@ -259,14 +258,13 @@ func AutoCreateApi(a *model.AutoCodeStruct) (err error) {
 	err = global.GVA_DB.Transaction(func(tx *gorm.DB) error {
 		for _, v := range apiList {
 			var api model.SysApi
-			if err := tx.Where("path = ? AND method = ?", v.Path, v.Method).First(&api).Error; err != nil {
-				return err
-			}
-			if err := tx.Create(&v).Error; err != nil { // 遇到错误时回滚事务
-				return err
+			if errors.Is(tx.Where("path = ? AND method = ?", v.Path, v.Method).First(&api).Error, gorm.ErrRecordNotFound) {
+				if err := tx.Create(&v).Error; err != nil { // 遇到错误时回滚事务
+					return err
+				}
 			}
 		}
 		return nil
 	})
 	return err
-}
+}

+ 1 - 1
web/src/main.js

@@ -66,7 +66,7 @@ Vue.prototype.$echarts = echarts;
 
 console.log(`
        欢迎使用 Gin-Vue-Admin
-       当前版本:V2.3.6
+       当前版本:V2.3.7
        默认自动化文档地址:http://127.0.0.1%s/swagger/index.html
        默认前端文件运行地址:http://127.0.0.1:8080
        如果项目让您获得了收益,希望您能请团队喝杯可乐:https://www.gin-vue-admin.com/docs/coffee