From b66030eff89f06d7db43becab989327567f377ca Mon Sep 17 00:00:00 2001 From: lysu Date: Fri, 25 Oct 2019 14:32:29 +0800 Subject: [PATCH] server: log last executed prepared stmt when panic (#12905) --- server/conn.go | 42 +++++++++++++++++++++++++++++++++++++----- server/conn_stmt.go | 3 +++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/server/conn.go b/server/conn.go index 4ae0b0a145c2f..3f570c6d9a6a6 100644 --- a/server/conn.go +++ b/server/conn.go @@ -144,7 +144,7 @@ type clientConn struct { dbname string // default database name. salt []byte // random bytes used for authentication. alloc arena.Allocator // an memory allocator for reducing memory allocation. - lastCmd string // latest sql query string, currently used for logging error. + lastPacket []byte // latest sql query string, currently used for logging error. ctx QueryCtx // an interface to execute sql statements. attrs map[string]string // attributes parsed from client handshake response, not used for now. status int32 // dispatching/reading/shutdown/waitshutdown @@ -612,7 +612,7 @@ func (cc *clientConn) Run(ctx context.Context) { stackSize := runtime.Stack(buf, false) buf = buf[:stackSize] logutil.Logger(ctx).Error("connection running loop panic", - zap.String("lastCmd", cc.lastCmd), + zap.Stringer("lastSQL", getLastStmtInConn{cc}), zap.Reflect("err", r), zap.String("stack", string(buf)), ) @@ -683,7 +683,7 @@ func (cc *clientConn) Run(ctx context.Context) { zap.String("connInfo", cc.String()), zap.String("command", mysql.Command2Str[data[0]]), zap.String("status", cc.SessionStatusToString()), - zap.String("sql", queryStrForLog(string(data[1:]))), + zap.Stringer("sql", getLastStmtInConn{cc}), zap.String("err", errStrForLog(err)), ) err1 := cc.writeError(err) @@ -864,9 +864,9 @@ func (cc *clientConn) dispatch(ctx context.Context, data []byte) error { span := opentracing.StartSpan("server.dispatch") t := time.Now() + cc.lastPacket = data cmd := data[0] data = data[1:] - cc.lastCmd = string(hack.String(data)) token := cc.server.getToken() defer func() { // if handleChangeUser failed, cc.ctx may be nil @@ -1285,7 +1285,7 @@ func (cc *clientConn) writeResultset(ctx context.Context, rs ResultSet, binary b buf := make([]byte, 4096) stackSize := runtime.Stack(buf, false) buf = buf[:stackSize] - logutil.Logger(ctx).Error("write query result panic", zap.String("lastCmd", cc.lastCmd), zap.String("stack", string(buf))) + logutil.Logger(ctx).Error("write query result panic", zap.Stringer("lastSQL", getLastStmtInConn{cc}), zap.String("stack", string(buf))) }() var err error if mysql.HasCursorExistsFlag(serverStatus) { @@ -1515,3 +1515,35 @@ func (cc *clientConn) handleChangeUser(ctx context.Context, data []byte) error { return cc.writeOK() } + +var _ fmt.Stringer = getLastStmtInConn{} + +type getLastStmtInConn struct { + *clientConn +} + +func (cc getLastStmtInConn) String() string { + if len(cc.lastPacket) == 0 { + return "" + } + cmd, data := cc.lastPacket[0], cc.lastPacket[1:] + switch cmd { + case mysql.ComInitDB: + return "Use " + string(data) + case mysql.ComFieldList: + return "ListFields " + string(data) + case mysql.ComQuery, mysql.ComStmtPrepare: + return queryStrForLog(string(hack.String(data))) + case mysql.ComStmtExecute, mysql.ComStmtFetch: + stmtID := binary.LittleEndian.Uint32(data[0:4]) + return queryStrForLog(cc.preparedStmt2String(stmtID)) + case mysql.ComStmtClose, mysql.ComStmtReset: + stmtID := binary.LittleEndian.Uint32(data[0:4]) + return mysql.Command2Str[cmd] + " " + strconv.Itoa(int(stmtID)) + default: + if cmdStr, ok := mysql.Command2Str[cmd]; ok { + return cmdStr + } + return string(hack.String(data)) + } +} diff --git a/server/conn_stmt.go b/server/conn_stmt.go index b4b1ebcfd0ef6..5669074a732a6 100644 --- a/server/conn_stmt.go +++ b/server/conn_stmt.go @@ -581,6 +581,9 @@ func (cc *clientConn) handleSetOption(data []byte) (err error) { func (cc *clientConn) preparedStmt2String(stmtID uint32) string { sv := cc.ctx.GetSessionVars() + if sv == nil { + return "" + } if prepared, ok := sv.PreparedStmts[stmtID]; ok { return prepared.Stmt.Text() + sv.PreparedParams.String() }