From 2e04012ae105541fec183590c7b124f90502c321 Mon Sep 17 00:00:00 2001 From: Tetsuro Aoki Date: Fri, 7 Apr 2023 23:51:08 +0900 Subject: [PATCH] Make logger configurable per connection --- AUTHORS | 1 + README.md | 10 +++++ auth.go | 2 +- connection.go | 23 ++++++---- connection_test.go | 1 + connector.go | 9 +++- driver_test.go | 2 +- dsn.go | 37 +++++++++++++++ dsn_test.go | 110 ++++++++++++++++++++++++++++++++++++++++++--- errors.go | 53 ++++++++++++++++++++-- errors_test.go | 6 +-- packets.go | 26 +++++------ packets_test.go | 1 + statement.go | 4 +- 14 files changed, 247 insertions(+), 38 deletions(-) diff --git a/AUTHORS b/AUTHORS index fb1478c3b..769db0f47 100644 --- a/AUTHORS +++ b/AUTHORS @@ -95,6 +95,7 @@ Stan Putrya Stanley Gunawan Steven Hartland Tan Jinhua <312841925 at qq.com> +Tetsuro Aoki Thomas Wodarek Tim Ruffles Tom Jenkinson diff --git a/README.md b/README.md index 25de2e5aa..da0f557d2 100644 --- a/README.md +++ b/README.md @@ -279,6 +279,16 @@ Note that this sets the location for time.Time values but does not change MySQL' Please keep in mind, that param values must be [url.QueryEscape](https://golang.org/pkg/net/url/#QueryEscape)'ed. Alternatively you can manually replace the `/` with `%2F`. For example `US/Pacific` would be `loc=US%2FPacific`. +##### `logging` + +``` +Type: bool / string +Valid Values: true, false, +Default: true +``` + +`logging=false` disables logging. You can use a custom logger after registering it with [`mysql.RegisterLogger`](https://godoc.org/github.com/go-sql-driver/mysql#RegisterLogger). + ##### `maxAllowedPacket` ``` Type: decimal number diff --git a/auth.go b/auth.go index 1ff203e57..f2e35afaf 100644 --- a/auth.go +++ b/auth.go @@ -291,7 +291,7 @@ func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, error) { return enc, err default: - errLog.Print("unknown auth plugin:", plugin) + mc.errLog().Print("unknown auth plugin:", plugin) return nil, ErrUnknownPlugin } } diff --git a/connection.go b/connection.go index 947a883e3..933e97457 100644 --- a/connection.go +++ b/connection.go @@ -105,7 +105,7 @@ func (mc *mysqlConn) Begin() (driver.Tx, error) { func (mc *mysqlConn) begin(readOnly bool) (driver.Tx, error) { if mc.closed.Load() { - errLog.Print(ErrInvalidConn) + mc.errLog().Print(ErrInvalidConn) return nil, driver.ErrBadConn } var q string @@ -147,7 +147,7 @@ func (mc *mysqlConn) cleanup() { return } if err := mc.netConn.Close(); err != nil { - errLog.Print(err) + mc.errLog().Print(err) } } @@ -163,14 +163,14 @@ func (mc *mysqlConn) error() error { func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) { if mc.closed.Load() { - errLog.Print(ErrInvalidConn) + mc.errLog().Print(ErrInvalidConn) return nil, driver.ErrBadConn } // Send command err := mc.writeCommandPacketStr(comStmtPrepare, query) if err != nil { // STMT_PREPARE is safe to retry. So we can return ErrBadConn here. - errLog.Print(err) + mc.errLog().Print(err) return nil, driver.ErrBadConn } @@ -204,7 +204,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin buf, err := mc.buf.takeCompleteBuffer() if err != nil { // can not take the buffer. Something must be wrong with the connection - errLog.Print(err) + mc.errLog().Print(err) return "", ErrInvalidConn } buf = buf[:0] @@ -296,7 +296,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) { if mc.closed.Load() { - errLog.Print(ErrInvalidConn) + mc.errLog().Print(ErrInvalidConn) return nil, driver.ErrBadConn } if len(args) != 0 { @@ -357,7 +357,7 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro func (mc *mysqlConn) query(query string, args []driver.Value) (*textRows, error) { if mc.closed.Load() { - errLog.Print(ErrInvalidConn) + mc.errLog().Print(ErrInvalidConn) return nil, driver.ErrBadConn } if len(args) != 0 { @@ -451,7 +451,7 @@ func (mc *mysqlConn) finish() { // Ping implements driver.Pinger interface func (mc *mysqlConn) Ping(ctx context.Context) (err error) { if mc.closed.Load() { - errLog.Print(ErrInvalidConn) + mc.errLog().Print(ErrInvalidConn) return driver.ErrBadConn } @@ -648,3 +648,10 @@ func (mc *mysqlConn) ResetSession(ctx context.Context) error { func (mc *mysqlConn) IsValid() bool { return !mc.closed.Load() } + +func (mc *mysqlConn) errLog() Logger { + if mc.cfg.Logger != nil { + return mc.cfg.Logger + } + return defaultLogger +} diff --git a/connection_test.go b/connection_test.go index b6764a2f6..98c985ae1 100644 --- a/connection_test.go +++ b/connection_test.go @@ -179,6 +179,7 @@ func TestPingErrInvalidConn(t *testing.T) { buf: newBuffer(nc), maxAllowedPacket: defaultMaxAllowedPacket, closech: make(chan struct{}), + cfg: NewConfig(), } err := ms.Ping(context.Background()) diff --git a/connector.go b/connector.go index d567b4e4f..570d2bb84 100644 --- a/connector.go +++ b/connector.go @@ -92,7 +92,7 @@ func (c *connector) Connect(ctx context.Context) (driver.Conn, error) { authResp, err := mc.auth(authData, plugin) if err != nil { // try the default auth plugin, if using the requested plugin failed - errLog.Print("could not use requested auth plugin '"+plugin+"': ", err.Error()) + c.errLog().Print("could not use requested auth plugin '"+plugin+"': ", err.Error()) plugin = defaultAuthPlugin authResp, err = mc.auth(authData, plugin) if err != nil { @@ -144,3 +144,10 @@ func (c *connector) Connect(ctx context.Context) (driver.Conn, error) { func (c *connector) Driver() driver.Driver { return &MySQLDriver{} } + +func (c *connector) errLog() Logger { + if c.cfg.Logger != nil { + return c.cfg.Logger + } + return defaultLogger +} diff --git a/driver_test.go b/driver_test.go index 4850498d0..44ac97754 100644 --- a/driver_test.go +++ b/driver_test.go @@ -1995,7 +1995,7 @@ func TestInsertRetrieveEscapedData(t *testing.T) { func TestUnixSocketAuthFail(t *testing.T) { runTests(t, dsn, func(dbt *DBTest) { // Save the current logger so we can restore it. - oldLogger := errLog + oldLogger := defaultLogger // Set a new logger so we can capture its output. buffer := bytes.NewBuffer(make([]byte, 0, 64)) diff --git a/dsn.go b/dsn.go index 4b71aaab0..741e35c2f 100644 --- a/dsn.go +++ b/dsn.go @@ -50,6 +50,8 @@ type Config struct { Timeout time.Duration // Dial timeout ReadTimeout time.Duration // I/O read timeout WriteTimeout time.Duration // I/O write timeout + LoggingConfig string // Logging configuration + Logger Logger // Logger AllowAllFiles bool // Allow all files to be used with LOAD DATA LOCAL INFILE AllowCleartextPasswords bool // Allows the cleartext client side plugin @@ -153,6 +155,20 @@ func (cfg *Config) normalize() error { } } + if cfg.Logger == nil { + switch cfg.LoggingConfig { + case "true", "": + // use default logger + case "false": + cfg.Logger = defaultNopLogger + default: + cfg.Logger = getLogger(cfg.LoggingConfig) + if cfg.Logger == nil { + return errors.New("invalid value / unknown logger name: " + cfg.LoggingConfig) + } + } + } + return nil } @@ -282,6 +298,10 @@ func (cfg *Config) FormatDSN() string { writeDSNParam(&buf, &hasParam, "maxAllowedPacket", strconv.Itoa(cfg.MaxAllowedPacket)) } + if len(cfg.LoggingConfig) > 0 { + writeDSNParam(&buf, &hasParam, "logging", url.QueryEscape(cfg.LoggingConfig)) + } + // other params if cfg.Params != nil { var params []string @@ -554,6 +574,23 @@ func parseDSNParams(cfg *Config, params string) (err error) { if err != nil { return } + + case "logging": + boolValue, isBool := readBool(value) + if isBool { + if boolValue { + cfg.LoggingConfig = "true" + } else { + cfg.LoggingConfig = "false" + } + } else { + name, err := url.QueryUnescape(value) + if err != nil { + return fmt.Errorf("invalid value for logger name: %v", err) + } + cfg.LoggingConfig = name + } + default: // lazy init if cfg.Params == nil { diff --git a/dsn_test.go b/dsn_test.go index 41a6a29fa..73c58ca23 100644 --- a/dsn_test.go +++ b/dsn_test.go @@ -11,7 +11,9 @@ package mysql import ( "crypto/tls" "fmt" + "log" "net/url" + "os" "reflect" "testing" "time" @@ -33,14 +35,14 @@ var testDSNs = []struct { "user@unix(/path/to/socket)/dbname?charset=utf8", &Config{User: "user", Net: "unix", Addr: "/path/to/socket", DBName: "dbname", Params: map[string]string{"charset": "utf8"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, CheckConnLiveness: true}, }, { - "user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true", - &Config{User: "user", Passwd: "password", Net: "tcp", Addr: "localhost:5555", DBName: "dbname", Params: map[string]string{"charset": "utf8"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, CheckConnLiveness: true, TLSConfig: "true"}, + "user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true&logging=false", + &Config{User: "user", Passwd: "password", Net: "tcp", Addr: "localhost:5555", DBName: "dbname", Params: map[string]string{"charset": "utf8"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, CheckConnLiveness: true, TLSConfig: "true", LoggingConfig: "false"}, }, { - "user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify", - &Config{User: "user", Passwd: "password", Net: "tcp", Addr: "localhost:5555", DBName: "dbname", Params: map[string]string{"charset": "utf8mb4,utf8"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, CheckConnLiveness: true, TLSConfig: "skip-verify"}, + "user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify&logging=testLogger", + &Config{User: "user", Passwd: "password", Net: "tcp", Addr: "localhost:5555", DBName: "dbname", Params: map[string]string{"charset": "utf8mb4,utf8"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, CheckConnLiveness: true, TLSConfig: "skip-verify", LoggingConfig: "testLogger"}, }, { - "user:password@/dbname?loc=UTC&timeout=30s&readTimeout=1s&writeTimeout=1s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci&maxAllowedPacket=16777216&tls=false&allowCleartextPasswords=true&parseTime=true&rejectReadOnly=true", - &Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8mb4_unicode_ci", Loc: time.UTC, TLSConfig: "false", AllowCleartextPasswords: true, AllowNativePasswords: true, Timeout: 30 * time.Second, ReadTimeout: time.Second, WriteTimeout: time.Second, AllowAllFiles: true, AllowOldPasswords: true, CheckConnLiveness: true, ClientFoundRows: true, MaxAllowedPacket: 16777216, ParseTime: true, RejectReadOnly: true}, + "user:password@/dbname?loc=UTC&timeout=30s&readTimeout=1s&writeTimeout=1s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci&maxAllowedPacket=16777216&tls=false&allowCleartextPasswords=true&parseTime=true&rejectReadOnly=true&logging=true", + &Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8mb4_unicode_ci", Loc: time.UTC, TLSConfig: "false", AllowCleartextPasswords: true, AllowNativePasswords: true, Timeout: 30 * time.Second, ReadTimeout: time.Second, WriteTimeout: time.Second, LoggingConfig: "true", AllowAllFiles: true, AllowOldPasswords: true, CheckConnLiveness: true, ClientFoundRows: true, MaxAllowedPacket: 16777216, ParseTime: true, RejectReadOnly: true}, }, { "user:password@/dbname?allowNativePasswords=false&checkConnLiveness=false&maxAllowedPacket=0&allowFallbackToPlaintext=true", &Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: 0, AllowFallbackToPlaintext: true, AllowNativePasswords: false, CheckConnLiveness: false}, @@ -75,6 +77,10 @@ var testDSNs = []struct { } func TestDSNParser(t *testing.T) { + logger := log.New(os.Stderr, "", log.LstdFlags) + RegisterLogger("testLogger", logger) + defer DeregisterLogger("testLogger") + for i, tst := range testDSNs { cfg, err := ParseDSN(tst.in) if err != nil { @@ -83,6 +89,7 @@ func TestDSNParser(t *testing.T) { // pointer not static cfg.TLS = nil + cfg.Logger = nil if !reflect.DeepEqual(cfg, tst.out) { t.Errorf("%d. ParseDSN(%q) mismatch:\ngot %+v\nwant %+v", i, tst.in, cfg, tst.out) @@ -112,6 +119,10 @@ func TestDSNParserInvalid(t *testing.T) { } func TestDSNReformat(t *testing.T) { + logger := log.New(os.Stderr, "", log.LstdFlags) + RegisterLogger("testLogger", logger) + defer DeregisterLogger("testLogger") + for i, tst := range testDSNs { dsn1 := tst.in cfg1, err := ParseDSN(dsn1) @@ -268,6 +279,93 @@ func TestDSNWithCustomTLSQueryEscape(t *testing.T) { } } +func TestDSNWithCustomLogger(t *testing.T) { + baseDSN := "User:password@tcp(localhost:5555)/dbname?logging=" + + t.Run("custom logger is registered", func(tt *testing.T) { + logger := log.New(os.Stderr, "", log.LstdFlags) + + RegisterLogger("testKey", logger) + defer DeregisterLogger("testKey") + + tst := baseDSN + "testKey" + + cfg, err := ParseDSN(tst) + if err != nil { + tt.Fatal(err.Error()) + } + + if cfg.LoggingConfig != "testKey" { + tt.Errorf("unexpected cfg.LoggingConfig value: %q", cfg.LoggingConfig) + } + if cfg.Logger != logger { + tt.Error("logger pointer doesn't match") + } + }) + + t.Run("custom logger is missing", func(tt *testing.T) { + tst := baseDSN + "invalid_name" + + cfg, err := ParseDSN(tst) + if err == nil { + tt.Errorf("invalid name in DSN (%s) but did not error. Got config: %#v", tst, cfg) + } + }) +} + +func TestDSNLoggingConfig(t *testing.T) { + t.Run("logging=true", func(tt *testing.T) { + dsn := "User:password@tcp(localhost:5555)/dbname?logging=true" + + cfg, err := ParseDSN(dsn) + if err != nil { + tt.Fatal(err.Error()) + } + + if cfg.LoggingConfig != "true" { + tt.Errorf("unexpected cfg.LoggingConfig value: %q", cfg.LoggingConfig) + } + if cfg.Logger != nil { + tt.Error("cfg.Logger should be nil") + } + }) + + t.Run("logging=false", func(tt *testing.T) { + dsn := "User:password@tcp(localhost:5555)/dbname?logging=false" + + cfg, err := ParseDSN(dsn) + if err != nil { + tt.Fatal(err.Error()) + } + + if cfg.LoggingConfig != "false" { + tt.Errorf("unexpected cfg.LoggingConfig value: %q", cfg.LoggingConfig) + } + if cfg.Logger != defaultNopLogger { + tt.Error("logger pointer doesn't match") + } + }) +} + +func TestDSNWithCustomLoggerQueryEscape(t *testing.T) { + const name = "&%!:" + dsn := "User:password@tcp(localhost:5555)/dbname?logging=" + url.QueryEscape(name) + + logger := log.New(os.Stderr, "", log.LstdFlags) + + RegisterLogger(name, logger) + defer DeregisterTLSConfig(name) + + cfg, err := ParseDSN(dsn) + if err != nil { + t.Fatal(err.Error()) + } + + if cfg.Logger != logger { + t.Error("logger pointer doesn't match") + } +} + func TestDSNUnsafeCollation(t *testing.T) { _, err := ParseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=true") if err != errInvalidDSNUnsafeCollation { diff --git a/errors.go b/errors.go index 7c037e7d6..0da6f16e8 100644 --- a/errors.go +++ b/errors.go @@ -13,6 +13,7 @@ import ( "fmt" "log" "os" + "sync" ) // Various errors the driver might return. Can change between driver versions. @@ -35,25 +36,71 @@ var ( // to trigger a resend. // See https://github.com/go-sql-driver/mysql/pull/302 errBadConnNoWrite = errors.New("bad connection") + + loggerLock sync.RWMutex + loggerRegistry map[string]Logger ) -var errLog = Logger(log.New(os.Stderr, "[mysql] ", log.Ldate|log.Ltime|log.Lshortfile)) +var ( + defaultLogger = Logger(log.New(os.Stderr, "[mysql] ", log.Ldate|log.Ltime|log.Lshortfile)) + defaultNopLogger = &nopLogger{} +) // Logger is used to log critical error messages. type Logger interface { Print(v ...interface{}) } -// SetLogger is used to set the logger for critical errors. +type nopLogger struct{} + +func (nl *nopLogger) Print(_ ...interface{}) {} + +// SetLogger is used to set the default logger for critical errors. // The initial logger is os.Stderr. func SetLogger(logger Logger) error { if logger == nil { return errors.New("logger is nil") } - errLog = logger + defaultLogger = logger return nil } +// RegisterLogger registers a custom logger. +// Registered keys can afterwards be used adding logging= to the DSN. +// +// Note: The provided logger is exclusively owned by the driver after +// registering it. +// +// logger := log.New(os.Stdout, "[custom] ", log.LstdFlags) +// mysql.RegisterLogger("custom", logger) +// db, err := sql.Open("mysql", "user@tcp(localhost:3306)/test?logging=custom") +func RegisterLogger(name string, logger Logger) { + loggerLock.Lock() + defer loggerLock.Unlock() + + if loggerRegistry == nil { + loggerRegistry = make(map[string]Logger, 1) + } + + loggerRegistry[name] = logger +} + +// DeregisterLogger removes the logger with the given name. +func DeregisterLogger(name string) { + loggerLock.Lock() + defer loggerLock.Unlock() + + if loggerRegistry != nil { + delete(loggerRegistry, name) + } +} + +func getLogger(name string) Logger { + loggerLock.RLock() + defer loggerLock.RUnlock() + return loggerRegistry[name] +} + // MySQLError is an error type which represents a single MySQL error type MySQLError struct { Number uint16 diff --git a/errors_test.go b/errors_test.go index 43213f98e..53d634454 100644 --- a/errors_test.go +++ b/errors_test.go @@ -16,9 +16,9 @@ import ( ) func TestErrorsSetLogger(t *testing.T) { - previous := errLog + previous := defaultLogger defer func() { - errLog = previous + defaultLogger = previous }() // set up logger @@ -28,7 +28,7 @@ func TestErrorsSetLogger(t *testing.T) { // print SetLogger(logger) - errLog.Print("test") + defaultLogger.Print("test") // check result if actual := buffer.String(); actual != expected { diff --git a/packets.go b/packets.go index ee05c95a8..73254e309 100644 --- a/packets.go +++ b/packets.go @@ -34,7 +34,7 @@ func (mc *mysqlConn) readPacket() ([]byte, error) { if cerr := mc.canceled.Value(); cerr != nil { return nil, cerr } - errLog.Print(err) + mc.errLog().Print(err) mc.Close() return nil, ErrInvalidConn } @@ -56,7 +56,7 @@ func (mc *mysqlConn) readPacket() ([]byte, error) { if pktLen == 0 { // there was no previous packet if prevData == nil { - errLog.Print(ErrMalformPkt) + mc.errLog().Print(ErrMalformPkt) mc.Close() return nil, ErrInvalidConn } @@ -70,7 +70,7 @@ func (mc *mysqlConn) readPacket() ([]byte, error) { if cerr := mc.canceled.Value(); cerr != nil { return nil, cerr } - errLog.Print(err) + mc.errLog().Print(err) mc.Close() return nil, ErrInvalidConn } @@ -119,7 +119,7 @@ func (mc *mysqlConn) writePacket(data []byte) error { } } if err != nil { - errLog.Print("closing bad idle connection: ", err) + mc.errLog().Print("closing bad idle connection: ", err) mc.Close() return driver.ErrBadConn } @@ -161,7 +161,7 @@ func (mc *mysqlConn) writePacket(data []byte) error { // Handle error if err == nil { // n != len(data) mc.cleanup() - errLog.Print(ErrMalformPkt) + mc.errLog().Print(ErrMalformPkt) } else { if cerr := mc.canceled.Value(); cerr != nil { return cerr @@ -171,7 +171,7 @@ func (mc *mysqlConn) writePacket(data []byte) error { return errBadConnNoWrite } mc.cleanup() - errLog.Print(err) + mc.errLog().Print(err) } return ErrInvalidConn } @@ -322,7 +322,7 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string data, err := mc.buf.takeSmallBuffer(pktLen + 4) if err != nil { // cannot take the buffer. Something must be wrong with the connection - errLog.Print(err) + mc.errLog().Print(err) return errBadConnNoWrite } @@ -404,7 +404,7 @@ func (mc *mysqlConn) writeAuthSwitchPacket(authData []byte) error { data, err := mc.buf.takeSmallBuffer(pktLen) if err != nil { // cannot take the buffer. Something must be wrong with the connection - errLog.Print(err) + mc.errLog().Print(err) return errBadConnNoWrite } @@ -424,7 +424,7 @@ func (mc *mysqlConn) writeCommandPacket(command byte) error { data, err := mc.buf.takeSmallBuffer(4 + 1) if err != nil { // cannot take the buffer. Something must be wrong with the connection - errLog.Print(err) + mc.errLog().Print(err) return errBadConnNoWrite } @@ -443,7 +443,7 @@ func (mc *mysqlConn) writeCommandPacketStr(command byte, arg string) error { data, err := mc.buf.takeBuffer(pktLen + 4) if err != nil { // cannot take the buffer. Something must be wrong with the connection - errLog.Print(err) + mc.errLog().Print(err) return errBadConnNoWrite } @@ -464,7 +464,7 @@ func (mc *mysqlConn) writeCommandPacketUint32(command byte, arg uint32) error { data, err := mc.buf.takeSmallBuffer(4 + 1 + 4) if err != nil { // cannot take the buffer. Something must be wrong with the connection - errLog.Print(err) + mc.errLog().Print(err) return errBadConnNoWrite } @@ -938,7 +938,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error { } if err != nil { // cannot take the buffer. Something must be wrong with the connection - errLog.Print(err) + mc.errLog().Print(err) return errBadConnNoWrite } @@ -1137,7 +1137,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error { if valuesCap != cap(paramValues) { data = append(data[:pos], paramValues...) if err = mc.buf.store(data); err != nil { - errLog.Print(err) + mc.errLog().Print(err) return errBadConnNoWrite } } diff --git a/packets_test.go b/packets_test.go index b61e4dbf7..cacec1c68 100644 --- a/packets_test.go +++ b/packets_test.go @@ -265,6 +265,7 @@ func TestReadPacketFail(t *testing.T) { mc := &mysqlConn{ buf: newBuffer(conn), closech: make(chan struct{}), + cfg: NewConfig(), } // illegal empty (stand-alone) packet diff --git a/statement.go b/statement.go index 10ece8bd6..8ef08cff5 100644 --- a/statement.go +++ b/statement.go @@ -51,7 +51,7 @@ func (stmt *mysqlStmt) CheckNamedValue(nv *driver.NamedValue) (err error) { func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) { if stmt.mc.closed.Load() { - errLog.Print(ErrInvalidConn) + stmt.mc.errLog().Print(ErrInvalidConn) return nil, driver.ErrBadConn } // Send command @@ -99,7 +99,7 @@ func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) { func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) { if stmt.mc.closed.Load() { - errLog.Print(ErrInvalidConn) + stmt.mc.errLog().Print(ErrInvalidConn) return nil, driver.ErrBadConn } // Send command