1// Copyright (c) 2012-present The upper.io/db authors. All rights reserved. 2// 3// Permission is hereby granted, free of charge, to any person obtaining 4// a copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to 8// permit persons to whom the Software is furnished to do so, subject to 9// the following conditions: 10// 11// The above copyright notice and this permission notice shall be 12// included in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22package db 23 24import ( 25 "context" 26 "fmt" 27 "log" 28 "regexp" 29 "strings" 30 "time" 31) 32 33const ( 34 fmtLogSessID = `Session ID: %05d` 35 fmtLogTxID = `Transaction ID: %05d` 36 fmtLogQuery = `Query: %s` 37 fmtLogArgs = `Arguments: %#v` 38 fmtLogRowsAffected = `Rows affected: %d` 39 fmtLogLastInsertID = `Last insert ID: %d` 40 fmtLogError = `Error: %v` 41 fmtLogTimeTaken = `Time taken: %0.5fs` 42 fmtLogContext = `Context: %v` 43) 44 45var ( 46 reInvisibleChars = regexp.MustCompile(`[\s\r\n\t]+`) 47) 48 49// QueryStatus represents the status of a query after being executed. 50type QueryStatus struct { 51 SessID uint64 52 TxID uint64 53 54 RowsAffected *int64 55 LastInsertID *int64 56 57 Query string 58 Args []interface{} 59 60 Err error 61 62 Start time.Time 63 End time.Time 64 65 Context context.Context 66} 67 68// String returns a formatted log message. 69func (q *QueryStatus) String() string { 70 lines := make([]string, 0, 8) 71 72 if q.SessID > 0 { 73 lines = append(lines, fmt.Sprintf(fmtLogSessID, q.SessID)) 74 } 75 76 if q.TxID > 0 { 77 lines = append(lines, fmt.Sprintf(fmtLogTxID, q.TxID)) 78 } 79 80 if query := q.Query; query != "" { 81 query = reInvisibleChars.ReplaceAllString(query, ` `) 82 query = strings.TrimSpace(query) 83 lines = append(lines, fmt.Sprintf(fmtLogQuery, query)) 84 } 85 86 if len(q.Args) > 0 { 87 lines = append(lines, fmt.Sprintf(fmtLogArgs, q.Args)) 88 } 89 90 if q.RowsAffected != nil { 91 lines = append(lines, fmt.Sprintf(fmtLogRowsAffected, *q.RowsAffected)) 92 } 93 if q.LastInsertID != nil { 94 lines = append(lines, fmt.Sprintf(fmtLogLastInsertID, *q.LastInsertID)) 95 } 96 97 if q.Err != nil { 98 lines = append(lines, fmt.Sprintf(fmtLogError, q.Err)) 99 } 100 101 lines = append(lines, fmt.Sprintf(fmtLogTimeTaken, float64(q.End.UnixNano()-q.Start.UnixNano())/float64(1e9))) 102 103 if q.Context != nil { 104 lines = append(lines, fmt.Sprintf(fmtLogContext, q.Context)) 105 } 106 107 return strings.Join(lines, "\n") 108} 109 110// EnvEnableDebug can be used by adapters to determine if the user has enabled 111// debugging. 112// 113// If the user sets the `UPPERIO_DB_DEBUG` environment variable to a 114// non-empty value, all generated statements will be printed at runtime to 115// the standard logger. 116// 117// Example: 118// 119// UPPERIO_DB_DEBUG=1 go test 120// 121// UPPERIO_DB_DEBUG=1 ./go-program 122const ( 123 EnvEnableDebug = `UPPERIO_DB_DEBUG` 124) 125 126// Logger represents a logging collector. You can pass a logging collector to 127// db.DefaultSettings.SetLogger(myCollector) to make it collect db.QueryStatus messages 128// after executing a query. 129type Logger interface { 130 Log(*QueryStatus) 131} 132 133type defaultLogger struct { 134} 135 136func (lg *defaultLogger) Log(m *QueryStatus) { 137 log.Printf("\n\t%s\n\n", strings.Replace(m.String(), "\n", "\n\t", -1)) 138} 139 140var _ = Logger(&defaultLogger{}) 141 142func init() { 143 if envEnabled(EnvEnableDebug) { 144 DefaultSettings.SetLogging(true) 145 } 146} 147