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  }