github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/ginx/hlog/sqlstore.go (about) 1 package hlog 2 3 import ( 4 "strings" 5 6 "github.com/bingoohuang/gg/pkg/sqx" 7 "github.com/gin-gonic/gin" 8 "github.com/sirupsen/logrus" 9 ) 10 11 // SQLStore stores the log into database. 12 type SQLStore struct { 13 DB sqx.SqxDB 14 LogTables []string 15 16 TableCols map[string]*tableSchema 17 } 18 19 // NewSQLStore creates a new SQLStore. 20 func NewSQLStore(db sqx.SqxDB, defaultLogTables ...string) *SQLStore { 21 s := &SQLStore{DB: db} 22 s.LogTables = defaultLogTables 23 s.TableCols = make(map[string]*tableSchema) 24 25 return s 26 } 27 28 func (s *SQLStore) loadTableSchema(tableName string) (*tableSchema, error) { 29 if v, ok := s.TableCols[tableName]; ok { 30 return v, nil 31 } 32 33 sqy := sqx.NewSQL(` 34 select column_name, column_comment, data_type, extra, is_nullable nullable, 35 character_maximum_length max_length 36 from information_schema.columns 37 where table_schema = database() 38 and table_name = ?`, tableName) 39 var tableCols []TableCol 40 if err := sqy.Query(s.DB, &tableCols); err != nil { 41 return nil, err 42 } 43 44 v := &tableSchema{ 45 Name: tableName, 46 Cols: tableCols, 47 } 48 v.createInsertSQL() 49 s.TableCols[tableName] = v 50 return v, nil 51 } 52 53 // TableCol defines the schema of a table. 54 type TableCol struct { 55 Name string `name:"column_name"` 56 Comment string `name:"column_comment"` 57 DataType string `name:"data_type"` 58 Extra string `name:"extra"` 59 Nullable string `name:"nullable"` 60 MaxLength int `name:"max_length"` 61 62 ValueGetter col `name:"-"` 63 } 64 65 func (s *TableCol) IsNullable() bool { 66 return strings.HasPrefix(strings.ToLower(s.Nullable), "y") 67 } 68 69 // Store stores the log in database like MySQL, InfluxDB, and etc. 70 func (s *SQLStore) Store(c *gin.Context, l *Log) { 71 tables := l.Option.Tables 72 if len(tables) == 0 { 73 tables = s.LogTables 74 } 75 76 for _, t := range tables { 77 schema, err := s.loadTableSchema(t) 78 if err != nil { 79 logrus.Errorf("failed to loadTableSchema for table %s, error: %v", t, err) 80 continue 81 } 82 83 schema.log(s.DB, l) 84 } 85 } 86 87 type tableSchema struct { 88 Name string 89 Cols []TableCol 90 InsertSQL string 91 ValueGetters []col 92 } 93 94 func (t tableSchema) log(db sqx.SqxDB, l *Log) { 95 if len(t.ValueGetters) == 0 { 96 return 97 } 98 99 params := make([]interface{}, len(t.ValueGetters)) 100 for i, vg := range t.ValueGetters { 101 params[i] = vg.get(l) 102 } 103 104 if result, err := sqx.NewSQL(t.InsertSQL, params...).Update(db); err != nil { 105 logrus.Warnf("do insert error: %v", err) 106 } else { 107 logrus.Debugf("log result %+v", result) 108 } 109 } 110 111 func (t *tableSchema) createInsertSQL() { 112 colsNum := len(t.Cols) 113 if colsNum == 0 { 114 logrus.Warnf("table %s not found", t.Name) 115 116 return 117 } 118 119 getters := make([]col, 0, colsNum) 120 columns := make([]string, 0, colsNum) 121 marks := make([]string, 0, colsNum) 122 123 for _, c := range t.Cols { 124 c.parseComment() 125 126 if c.ValueGetter == nil { 127 continue 128 } 129 130 columns = append(columns, c.Name) 131 marks = append(marks, "?") 132 getters = append(getters, c.ValueGetter) 133 } 134 135 t.InsertSQL = "insert into " + t.Name + "(" + 136 strings.Join(columns, ",") + 137 ") values(" + 138 strings.Join(marks, ",") + ")" 139 t.ValueGetters = getters 140 }