Skip to content

Commit eb84360

Browse files
Merge pull request #19 from kamil-holubicki/DISTMYSQL-157
DISTMYSQL-157: Improve error logging of MySQL errors
2 parents a091725 + b8b30e6 commit eb84360

File tree

14 files changed

+1270
-18
lines changed

14 files changed

+1270
-18
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,5 @@ replace (
4949
github.com/hashicorp/raft => github.com/openark/raft v0.0.0-20170918052300-fba9f909f7fe
5050
golang.org/x/text v0.3.0 => golang.org/x/text v0.3.8
5151
golang.org/x/text v0.3.7 => golang.org/x/text v0.3.8
52+
github.com/openark/golib => ./go/golib
5253
)

go/db/db.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"errors"
2222
"fmt"
2323
"regexp"
24+
"strconv"
2425
"strings"
2526
"sync"
2627
"time"
@@ -100,7 +101,8 @@ func openTopology(host string, port int, readTimeout int) (db *sql.DB, err error
100101
return nil, err
101102
}
102103
}
103-
if db, _, err = sqlutils.GetDB(mysql_uri); err != nil {
104+
sqlUtilsLogger := SqlUtilsLogger{client_context: host + ":" + strconv.Itoa(port)}
105+
if db, _, err = sqlutils.GetDB(mysql_uri, sqlUtilsLogger); err != nil {
104106
return nil, err
105107
}
106108
if config.Config.MySQLConnectionLifetimeSeconds > 0 {
@@ -123,7 +125,8 @@ func openOrchestratorMySQLGeneric() (db *sql.DB, fromCache bool, err error) {
123125
if config.Config.MySQLOrchestratorUseMutualTLS {
124126
uri, _ = SetupMySQLOrchestratorTLS(uri)
125127
}
126-
return sqlutils.GetDB(uri)
128+
sqlUtilsLogger := SqlUtilsLogger{client_context: config.Config.MySQLOrchestratorHost + ":" + config.Config.MySQLOrchestratorHost}
129+
return sqlutils.GetDB(uri, sqlUtilsLogger)
127130
}
128131

129132
func IsSQLite() bool {
@@ -174,7 +177,7 @@ func safeMySQLURI(dsn string) string {
174177
func OpenOrchestrator() (db *sql.DB, err error) {
175178
var fromCache bool
176179
if IsSQLite() {
177-
db, fromCache, err = sqlutils.GetSQLiteDB(config.Config.SQLite3DataFile)
180+
db, fromCache, err = sqlutils.GetSQLiteDB(config.Config.SQLite3DataFile, nil)
178181
if err == nil && !fromCache {
179182
log.Debugf("Connected to orchestrator backend: sqlite on %v", config.Config.SQLite3DataFile)
180183
}
@@ -191,7 +194,8 @@ func OpenOrchestrator() (db *sql.DB, err error) {
191194
}
192195
}
193196
dsn := getMySQLURI()
194-
db, fromCache, err = sqlutils.GetDB(dsn)
197+
sqlUtilsLogger := SqlUtilsLogger{client_context: dsn}
198+
db, fromCache, err = sqlutils.GetDB(dsn, sqlUtilsLogger)
195199
if err == nil && !fromCache {
196200
log.Debugf("Connected to orchestrator backend: %v", safeMySQLURI(dsn))
197201

go/db/tls.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package db
1919
import (
2020
"crypto/tls"
2121
"fmt"
22+
"strconv"
2223
"strings"
2324
"time"
2425

@@ -55,6 +56,23 @@ func init() {
5556
metrics.Register("instance_tls.write_cache", writeInstanceTLSCacheCounter)
5657
}
5758

59+
type SqlUtilsLogger struct {
60+
client_context string
61+
}
62+
63+
func (logger SqlUtilsLogger) OnError(caller_context string, query string, err error) error {
64+
query = strings.Join(strings.Fields(query), " ") // trim whitespaces
65+
query = strings.Replace(query, "%", "%%", -1) // escape %
66+
67+
msg := fmt.Sprintf("%+v(%+v) %+v: %+v",
68+
caller_context,
69+
logger.client_context,
70+
query,
71+
err)
72+
73+
return log.Errorf(msg)
74+
}
75+
5876
func requiresTLS(host string, port int, mysql_uri string) bool {
5977
cacheKey := fmt.Sprintf("%s:%d", host, port)
6078

@@ -64,7 +82,8 @@ func requiresTLS(host string, port int, mysql_uri string) bool {
6482
}
6583

6684
required := false
67-
db, _, _ := sqlutils.GetDB(mysql_uri)
85+
sqlUtilsLogger := SqlUtilsLogger{client_context: host + ":" + strconv.Itoa(port)}
86+
db, _, _ := sqlutils.GetDB(mysql_uri, sqlUtilsLogger)
6887
if err := db.Ping(); err != nil && (strings.Contains(err.Error(), Error3159) || strings.Contains(err.Error(), Error1045)) {
6988
required = true
7089
}

go/golib/go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module golib
2+
3+
go 1.18

go/golib/log/log.go

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
/*
2+
Copyright 2014 Outbrain Inc.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package log
18+
19+
import (
20+
"errors"
21+
"fmt"
22+
"log/syslog"
23+
"os"
24+
"runtime/debug"
25+
"time"
26+
)
27+
28+
// LogLevel indicates the severity of a log entry
29+
type LogLevel int
30+
31+
func (this LogLevel) String() string {
32+
switch this {
33+
case FATAL:
34+
return "FATAL"
35+
case CRITICAL:
36+
return "CRITICAL"
37+
case ERROR:
38+
return "ERROR"
39+
case WARNING:
40+
return "WARNING"
41+
case NOTICE:
42+
return "NOTICE"
43+
case INFO:
44+
return "INFO"
45+
case DEBUG:
46+
return "DEBUG"
47+
}
48+
return "unknown"
49+
}
50+
51+
func LogLevelFromString(logLevelName string) (LogLevel, error) {
52+
switch logLevelName {
53+
case "FATAL":
54+
return FATAL, nil
55+
case "CRITICAL":
56+
return CRITICAL, nil
57+
case "ERROR":
58+
return ERROR, nil
59+
case "WARNING":
60+
return WARNING, nil
61+
case "NOTICE":
62+
return NOTICE, nil
63+
case "INFO":
64+
return INFO, nil
65+
case "DEBUG":
66+
return DEBUG, nil
67+
}
68+
return 0, fmt.Errorf("Unknown LogLevel name: %+v", logLevelName)
69+
}
70+
71+
const (
72+
FATAL LogLevel = iota
73+
CRITICAL
74+
ERROR
75+
WARNING
76+
NOTICE
77+
INFO
78+
DEBUG
79+
)
80+
81+
const TimeFormat = "2006-01-02 15:04:05"
82+
83+
// globalLogLevel indicates the global level filter for all logs (only entries with level equals or higher
84+
// than this value will be logged)
85+
var globalLogLevel LogLevel = DEBUG
86+
var printStackTrace bool = false
87+
88+
// syslogWriter is optional, and defaults to nil (disabled)
89+
var syslogLevel LogLevel = ERROR
90+
var syslogWriter *syslog.Writer
91+
92+
// SetPrintStackTrace enables/disables dumping the stack upon error logging
93+
func SetPrintStackTrace(shouldPrintStackTrace bool) {
94+
printStackTrace = shouldPrintStackTrace
95+
}
96+
97+
// SetLevel sets the global log level. Only entries with level equals or higher than
98+
// this value will be logged
99+
func SetLevel(logLevel LogLevel) {
100+
globalLogLevel = logLevel
101+
}
102+
103+
// GetLevel returns current global log level
104+
func GetLevel() LogLevel {
105+
return globalLogLevel
106+
}
107+
108+
// EnableSyslogWriter enables, if possible, writes to syslog. These will execute _in addition_ to normal logging
109+
func EnableSyslogWriter(tag string) (err error) {
110+
syslogWriter, err = syslog.New(syslog.LOG_ERR, tag)
111+
if err != nil {
112+
syslogWriter = nil
113+
}
114+
return err
115+
}
116+
117+
// SetSyslogLevel sets the minimal syslog level. Only entries with level equals or higher than
118+
// this value will be logged. However, this is also capped by the global log level. That is,
119+
// messages with lower level than global-log-level will be discarded at any case.
120+
func SetSyslogLevel(logLevel LogLevel) {
121+
syslogLevel = logLevel
122+
}
123+
124+
// logFormattedEntry nicely formats and emits a log entry
125+
func logFormattedEntry(logLevel LogLevel, message string, args ...interface{}) string {
126+
if logLevel > globalLogLevel {
127+
return ""
128+
}
129+
// if TZ env variable is set, update the timestamp timezone
130+
localizedTime := time.Now()
131+
tzLocation := os.Getenv("TZ")
132+
if tzLocation != "" {
133+
location, err := time.LoadLocation(tzLocation)
134+
if err == nil { // if invalid tz location was provided, just leave it as the default
135+
localizedTime = time.Now().In(location)
136+
}
137+
}
138+
139+
msgArgs := fmt.Sprintf(message, args...)
140+
entryString := fmt.Sprintf("%s %s %s", localizedTime.Format(TimeFormat), logLevel, msgArgs)
141+
fmt.Fprintln(os.Stderr, entryString)
142+
143+
if syslogWriter != nil {
144+
go func() error {
145+
if logLevel > syslogLevel {
146+
return nil
147+
}
148+
switch logLevel {
149+
case FATAL:
150+
return syslogWriter.Emerg(msgArgs)
151+
case CRITICAL:
152+
return syslogWriter.Crit(msgArgs)
153+
case ERROR:
154+
return syslogWriter.Err(msgArgs)
155+
case WARNING:
156+
return syslogWriter.Warning(msgArgs)
157+
case NOTICE:
158+
return syslogWriter.Notice(msgArgs)
159+
case INFO:
160+
return syslogWriter.Info(msgArgs)
161+
case DEBUG:
162+
return syslogWriter.Debug(msgArgs)
163+
}
164+
return nil
165+
}()
166+
}
167+
return entryString
168+
}
169+
170+
// logEntry emits a formatted log entry
171+
func logEntry(logLevel LogLevel, message string, args ...interface{}) string {
172+
entryString := message
173+
for _, s := range args {
174+
entryString += fmt.Sprintf(" %s", s)
175+
}
176+
return logFormattedEntry(logLevel, entryString)
177+
}
178+
179+
// logErrorEntry emits a log entry based on given error object
180+
func logErrorEntry(logLevel LogLevel, err error) error {
181+
if err == nil {
182+
// No error
183+
return nil
184+
}
185+
entryString := fmt.Sprintf("%+v", err)
186+
logEntry(logLevel, entryString)
187+
if printStackTrace {
188+
debug.PrintStack()
189+
}
190+
return err
191+
}
192+
193+
func Debug(message string, args ...interface{}) string {
194+
return logEntry(DEBUG, message, args...)
195+
}
196+
197+
func Debugf(message string, args ...interface{}) string {
198+
return logFormattedEntry(DEBUG, message, args...)
199+
}
200+
201+
func Info(message string, args ...interface{}) string {
202+
return logEntry(INFO, message, args...)
203+
}
204+
205+
func Infof(message string, args ...interface{}) string {
206+
return logFormattedEntry(INFO, message, args...)
207+
}
208+
209+
func Notice(message string, args ...interface{}) string {
210+
return logEntry(NOTICE, message, args...)
211+
}
212+
213+
func Noticef(message string, args ...interface{}) string {
214+
return logFormattedEntry(NOTICE, message, args...)
215+
}
216+
217+
func Warning(message string, args ...interface{}) error {
218+
return errors.New(logEntry(WARNING, message, args...))
219+
}
220+
221+
func Warningf(message string, args ...interface{}) error {
222+
return errors.New(logFormattedEntry(WARNING, message, args...))
223+
}
224+
225+
func Error(message string, args ...interface{}) error {
226+
return errors.New(logEntry(ERROR, message, args...))
227+
}
228+
229+
func Errorf(message string, args ...interface{}) error {
230+
return errors.New(logFormattedEntry(ERROR, message, args...))
231+
}
232+
233+
func Errore(err error) error {
234+
return logErrorEntry(ERROR, err)
235+
}
236+
237+
func Critical(message string, args ...interface{}) error {
238+
return errors.New(logEntry(CRITICAL, message, args...))
239+
}
240+
241+
func Criticalf(message string, args ...interface{}) error {
242+
return errors.New(logFormattedEntry(CRITICAL, message, args...))
243+
}
244+
245+
func Criticale(err error) error {
246+
return logErrorEntry(CRITICAL, err)
247+
}
248+
249+
// Fatal emits a FATAL level entry and exists the program
250+
func Fatal(message string, args ...interface{}) error {
251+
logEntry(FATAL, message, args...)
252+
os.Exit(1)
253+
return errors.New(logEntry(CRITICAL, message, args...))
254+
}
255+
256+
// Fatalf emits a FATAL level entry and exists the program
257+
func Fatalf(message string, args ...interface{}) error {
258+
logFormattedEntry(FATAL, message, args...)
259+
os.Exit(1)
260+
return errors.New(logFormattedEntry(CRITICAL, message, args...))
261+
}
262+
263+
// Fatale emits a FATAL level entry and exists the program
264+
func Fatale(err error) error {
265+
logErrorEntry(FATAL, err)
266+
os.Exit(1)
267+
return err
268+
}

0 commit comments

Comments
 (0)