github.com/eden-framework/sqlx@v0.0.2/mysqlconnector/driver.go (about) 1 package mysqlconnector 2 3 import ( 4 "context" 5 "database/sql/driver" 6 "fmt" 7 "strings" 8 "time" 9 10 "github.com/go-sql-driver/mysql" 11 "github.com/sirupsen/logrus" 12 ) 13 14 func init() { 15 mysql.SetLogger(&logger{}) 16 } 17 18 type logger struct{} 19 20 func (l *logger) Print(args ...interface{}) { 21 } 22 23 var _ driver.Driver = (*MySqlLoggingDriver)(nil) 24 25 type MySqlLoggingDriver struct { 26 Logger *logrus.Logger 27 Driver *mysql.MySQLDriver 28 } 29 30 func (d *MySqlLoggingDriver) Open(dsn string) (driver.Conn, error) { 31 cfg, err := mysql.ParseDSN(dsn) 32 if err != nil { 33 panic(err) 34 } 35 cfg.Passwd = strings.Repeat("*", len(cfg.Passwd)) 36 conn, err := d.Driver.Open(dsn) 37 if err != nil { 38 d.Logger.Errorf("failed to open connection: %s %s", cfg.FormatDSN(), err) 39 return nil, err 40 } 41 42 d.Logger.Debugf("connected %s", cfg.FormatDSN()) 43 return &loggerConn{Conn: conn, cfg: cfg, logger: d.Logger.WithField("driver", "mysql")}, nil 44 } 45 46 var _ interface { 47 driver.ConnBeginTx 48 driver.ExecerContext 49 driver.QueryerContext 50 } = (*loggerConn)(nil) 51 52 type loggerConn struct { 53 logger *logrus.Entry 54 cfg *mysql.Config 55 driver.Conn 56 } 57 58 func (c *loggerConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { 59 logger := c.logger.WithContext(ctx) 60 61 logger.Debug("=========== Beginning Transaction ===========") 62 tx, err := c.Conn.(driver.ConnBeginTx).BeginTx(ctx, opts) 63 if err != nil { 64 logger.Errorf("failed to begin transaction: %s", err) 65 return nil, err 66 } 67 return &loggingTx{Tx: tx, logger: logger}, nil 68 } 69 70 func (c *loggerConn) Close() error { 71 if err := c.Conn.Close(); err != nil { 72 c.logger.Errorf("failed to close connection: %s", err) 73 return err 74 } 75 return nil 76 } 77 78 func (c *loggerConn) Prepare(query string) (driver.Stmt, error) { 79 panic(fmt.Errorf("don't use Prepare")) 80 } 81 82 func (c *loggerConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (rows driver.Rows, err error) { 83 cost := startTimer() 84 logger := c.logger.WithContext(ctx) 85 86 defer func() { 87 q := c.interpolateParams(query, args) 88 89 if err != nil { 90 if mysqlErr, ok := err.(*mysql.MySQLError); !ok { 91 logger.Errorf("failed query %s: %s", err, q) 92 } else { 93 logger.Warnf("failed query %s: %s", mysqlErr, q) 94 } 95 } else { 96 logger.WithField("cost", cost().String()).Debugf("%s", q) 97 } 98 }() 99 100 rows, err = c.Conn.(driver.QueryerContext).QueryContext(ctx, query, args) 101 return 102 } 103 104 func (c *loggerConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (result driver.Result, err error) { 105 cost := startTimer() 106 logger := c.logger.WithContext(ctx) 107 108 defer func() { 109 q := c.interpolateParams(query, args) 110 111 if err != nil { 112 if mysqlErr, ok := err.(*mysql.MySQLError); !ok { 113 logger.Errorf("failed exec %s: %s", err, q) 114 } else if mysqlErr.Number == DuplicateEntryErrNumber { 115 logger.Warnf("failed exec %s: %s", err, q) 116 } else { 117 logger.Errorf("failed exec %s: %s", mysqlErr, q) 118 } 119 return 120 } 121 122 logger.WithField("cost", cost().String()).Debugf("%s", q) 123 }() 124 125 result, err = c.Conn.(driver.ExecerContext).ExecContext(ctx, query, args) 126 return 127 } 128 129 func (c *loggerConn) interpolateParams(query string, args []driver.NamedValue) fmt.Stringer { 130 return &SqlPrinter{query, args, c.cfg} 131 } 132 133 type SqlPrinter struct { 134 query string 135 args []driver.NamedValue 136 cfg *mysql.Config 137 } 138 139 func (p *SqlPrinter) String() string { 140 if len(p.args) == 0 { 141 return p.query 142 } 143 argValues, err := namedValueToValue(p.args) 144 if err != nil { 145 return p.query 146 } 147 sqlForLog, err := interpolateParams(p.query, argValues, p.cfg.Loc, p.cfg.MaxAllowedPacket) 148 if err != nil { 149 return p.query 150 } 151 152 return sqlForLog 153 } 154 155 var DuplicateEntryErrNumber uint16 = 1062 156 157 func startTimer() func() time.Duration { 158 startTime := time.Now() 159 return func() time.Duration { 160 return time.Now().Sub(startTime) 161 } 162 } 163 164 type loggingTx struct { 165 logger *logrus.Entry 166 driver.Tx 167 } 168 169 func (tx *loggingTx) Commit() error { 170 if err := tx.Tx.Commit(); err != nil { 171 tx.logger.Debugf("failed to commit transaction: %s", err) 172 return err 173 } 174 tx.logger.Debug("=========== Committed Transaction ===========") 175 return nil 176 } 177 178 func (tx *loggingTx) Rollback() error { 179 if err := tx.Tx.Rollback(); err != nil { 180 tx.logger.Debugf("failed to rollback transaction: %s", err) 181 return err 182 } 183 tx.logger.Debug("=========== Rollback Transaction ===========") 184 return nil 185 }