1package gocbcore 2 3import ( 4 "fmt" 5 "log" 6 "os" 7 "strings" 8) 9 10// LogLevel specifies the severity of a log message. 11type LogLevel int 12 13// Various logging levels (or subsystems) which can categorize the message. 14// Currently these are ordered in decreasing severity. 15const ( 16 LogError LogLevel = iota 17 LogWarn 18 LogInfo 19 LogDebug 20 LogTrace 21 LogSched 22 LogMaxVerbosity 23) 24 25func redactUserData(v interface{}) string { 26 return fmt.Sprintf("<ud>%v<ud>", v) 27} 28 29func redactMetaData(v interface{}) string { 30 return fmt.Sprintf("<md>%v<md>", v) 31} 32 33func redactSystemData(v interface{}) string { 34 return fmt.Sprintf("<sd>%v<sd>", v) 35} 36 37// LogRedactLevel specifies the degree with which to redact the logs. 38type LogRedactLevel int 39 40const ( 41 // RedactNone indicates to perform no redactions 42 RedactNone LogRedactLevel = iota 43 44 // RedactPartial indicates to redact all possible user-identifying information from logs. 45 RedactPartial 46 47 // RedactFull indicates to fully redact all possible identifying information from logs. 48 RedactFull 49) 50 51// SetLogRedactionLevel specifies the level with which logs should be redacted. 52func SetLogRedactionLevel(level LogRedactLevel) { 53 globalLogRedactionLevel = level 54} 55 56func isLogRedactionLevelNone() bool { 57 return globalLogRedactionLevel == RedactNone 58} 59 60func isLogRedactionLevelPartial() bool { 61 return globalLogRedactionLevel == RedactPartial 62} 63 64func isLogRedactionLevelFull() bool { 65 return globalLogRedactionLevel == RedactFull 66} 67 68func logLevelToString(level LogLevel) string { 69 switch level { 70 case LogError: 71 return "error" 72 case LogWarn: 73 return "warn" 74 case LogInfo: 75 return "info" 76 case LogDebug: 77 return "debug" 78 case LogTrace: 79 return "trace" 80 case LogSched: 81 return "sched" 82 } 83 84 return fmt.Sprintf("unknown (%d)", level) 85} 86 87// Logger defines a logging interface. You can either use one of the default loggers 88// (DefaultStdioLogger(), VerboseStdioLogger()) or implement your own. 89type Logger interface { 90 // Outputs logging information: 91 // level is the verbosity level 92 // offset is the position within the calling stack from which the message 93 // originated. This is useful for contextual loggers which retrieve file/line 94 // information. 95 Log(level LogLevel, offset int, format string, v ...interface{}) error 96} 97 98type defaultLogger struct { 99 Level LogLevel 100 GoLogger *log.Logger 101} 102 103func (l *defaultLogger) Log(level LogLevel, offset int, format string, v ...interface{}) error { 104 if level > l.Level { 105 return nil 106 } 107 s := fmt.Sprintf(format, v...) 108 return l.GoLogger.Output(offset+2, s) 109} 110 111var ( 112 globalDefaultLogger = defaultLogger{ 113 GoLogger: log.New(os.Stderr, "GOCB ", log.Lmicroseconds|log.Lshortfile), Level: LogDebug, 114 } 115 116 globalVerboseLogger = defaultLogger{ 117 GoLogger: globalDefaultLogger.GoLogger, Level: LogMaxVerbosity, 118 } 119 120 globalLogger Logger 121 globalLogRedactionLevel LogRedactLevel 122) 123 124// DefaultStdioLogger gets the default standard I/O logger. 125// gocbcore.SetLogger(gocbcore.DefaultStdioLogger()) 126func DefaultStdioLogger() Logger { 127 return &globalDefaultLogger 128} 129 130// VerboseStdioLogger is a more verbose level of DefaultStdioLogger(). Messages 131// pertaining to the scheduling of ordinary commands (and their responses) will 132// also be emitted. 133// gocbcore.SetLogger(gocbcore.VerboseStdioLogger()) 134func VerboseStdioLogger() Logger { 135 return &globalVerboseLogger 136} 137 138// SetLogger sets a logger to be used by the library. A logger can be obtained via 139// the DefaultStdioLogger() or VerboseStdioLogger() functions. You can also implement 140// your own logger using the Logger interface. 141func SetLogger(logger Logger) { 142 globalLogger = logger 143} 144 145type redactableLogValue interface { 146 redacted() interface{} 147} 148 149func logExf(level LogLevel, offset int, format string, v ...interface{}) { 150 if globalLogger != nil { 151 if level <= LogInfo && !isLogRedactionLevelNone() { 152 // We only redact at info level or below. 153 for i, iv := range v { 154 if redactable, ok := iv.(redactableLogValue); ok { 155 v[i] = redactable.redacted() 156 } 157 } 158 } 159 160 err := globalLogger.Log(level, offset+1, format, v...) 161 if err != nil { 162 log.Printf("Logger error occurred (%s)\n", err) 163 } 164 } 165} 166 167func logDebugf(format string, v ...interface{}) { 168 logExf(LogDebug, 1, format, v...) 169} 170 171func logSchedf(format string, v ...interface{}) { 172 logExf(LogSched, 1, format, v...) 173} 174 175func logWarnf(format string, v ...interface{}) { 176 logExf(LogWarn, 1, format, v...) 177} 178 179func logErrorf(format string, v ...interface{}) { 180 logExf(LogError, 1, format, v...) 181} 182 183func logInfof(format string, v ...interface{}) { 184 logExf(LogInfo, 1, format, v...) 185} 186 187func reindentLog(indent, message string) string { 188 reindentedMessage := strings.Replace(message, "\n", "\n"+indent, -1) 189 return fmt.Sprintf("%s%s", indent, reindentedMessage) 190} 191