github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/logger/standard.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package logger 5 6 import ( 7 "fmt" 8 "os" 9 "path/filepath" 10 "strings" 11 "sync" 12 13 "github.com/keybase/go-framed-msgpack-rpc/rpc" 14 logging "github.com/keybase/go-logging" 15 "golang.org/x/net/context" 16 17 keybase1 "github.com/keybase/client/go/protocol/keybase1" 18 ) 19 20 const permDir os.FileMode = 0700 21 22 // Map from module name to whether SetLevel() has been called for that 23 // module. 24 var initLoggingSetLevelCalled = map[string]struct{}{} 25 26 // Protects access to initLoggingSetLevelCalled and the actual 27 // SetLevel call. 28 var initLoggingSetLevelMutex sync.Mutex 29 30 // CtxStandardLoggerKey is a type defining context keys used by the 31 // Standard logger. 32 type CtxStandardLoggerKey int 33 34 const ( 35 // CtxLogTagsKey defines a context key that can associate with a map of 36 // context keys (key -> descriptive-name), the mapped values of which should 37 // be logged by a Standard logger if one of those keys is seen in a context 38 // during a log call. 39 CtxLogTagsKey CtxStandardLoggerKey = iota 40 ) 41 42 type CtxLogTags map[interface{}]string 43 44 // NewContext returns a new Context that carries adds the given log 45 // tag mappings (context key -> display string). 46 func NewContextWithLogTags( 47 ctx context.Context, logTagsToAdd CtxLogTags) context.Context { 48 currTags, _ := LogTagsFromContext(ctx) 49 newTags := make(CtxLogTags) 50 // Make a copy to avoid races 51 for key, tag := range currTags { 52 newTags[key] = tag 53 } 54 for key, tag := range logTagsToAdd { 55 newTags[key] = tag 56 } 57 return context.WithValue(ctx, CtxLogTagsKey, newTags) 58 } 59 60 // LogTagsFromContext returns the log tags being passed along with the 61 // given context. 62 func LogTagsFromContext(ctx context.Context) (CtxLogTags, bool) { 63 logTags, ok := ctx.Value(CtxLogTagsKey).(CtxLogTags) 64 return logTags, ok 65 } 66 67 // LogTagsFromContextRPC is a wrapper around LogTagsFromContext 68 // that simply casts the result to the type expected by 69 // rpc.Connection. 70 func LogTagsFromContextRPC(ctx context.Context) (map[interface{}]string, bool) { 71 tags, ok := LogTagsFromContext(ctx) 72 return map[interface{}]string(tags), ok 73 } 74 75 type rpcTagKey string 76 77 // ConvertRPCTagsToLogTags takes any RPC tags in the context and makes 78 // them log tags. It uses the string representation of the tag key, 79 // rather than the original uniquely typed key, since the latter isn't 80 // available in the RPC tags. 81 func ConvertRPCTagsToLogTags(ctx context.Context) context.Context { 82 rpcTags, ok := rpc.RpcTagsFromContext(ctx) 83 if !ok { 84 return ctx 85 } 86 87 tags := make(CtxLogTags) 88 for key, value := range rpcTags { 89 // The map key should be a proper unique type, but that's not 90 // passed along in the RPC so just use our own string-like type. 91 tags[rpcTagKey(key)] = key 92 ctx = context.WithValue(ctx, rpcTagKey(key), value) 93 } 94 ctx = context.WithValue(ctx, rpc.CtxRpcTagsKey, nil) 95 return NewContextWithLogTags(ctx, tags) 96 } 97 98 type ExternalLogger interface { 99 Log(level keybase1.LogLevel, format string, args []interface{}) 100 } 101 102 type Standard struct { 103 internal *logging.Logger 104 filename string 105 configureMutex sync.Mutex 106 module string 107 108 externalHandler ExternalHandler 109 } 110 111 // Verify Standard fully implements the Logger interface. 112 var _ Logger = (*Standard)(nil) 113 114 // New creates a new Standard logger for module. 115 func New(module string) *Standard { 116 return NewWithCallDepth(module, 0) 117 } 118 119 // NewWithCallDepth creates a new Standard logger for module, and when 120 // printing file names and line numbers, it goes extraCallDepth up the 121 // stack from where logger was invoked. 122 func NewWithCallDepth(module string, extraCallDepth int) *Standard { 123 log := logging.MustGetLogger(module) 124 log.ExtraCalldepth = 1 + extraCallDepth 125 126 ret := &Standard{ 127 internal: log, 128 module: module, 129 } 130 ret.setLogLevelInfo() 131 return ret 132 } 133 134 func (log *Standard) setLogLevelInfo() { 135 initLoggingSetLevelMutex.Lock() 136 defer initLoggingSetLevelMutex.Unlock() 137 138 if _, found := initLoggingSetLevelCalled[log.module]; !found { 139 logging.SetLevel(logging.INFO, log.module) 140 initLoggingSetLevelCalled[log.module] = struct{}{} 141 } 142 } 143 144 func prepareString( 145 ctx context.Context, fmts string) string { 146 if ctx == nil { 147 return fmts 148 } 149 logTags, ok := LogTagsFromContext(ctx) 150 if !ok || len(logTags) == 0 { 151 return fmts 152 } 153 var tags []string 154 for key, tag := range logTags { 155 if v := ctx.Value(key); v != nil { 156 tags = append(tags, fmt.Sprintf("%s=%s", tag, v)) 157 } 158 } 159 return fmts + " [tags:" + strings.Join(tags, ",") + "]" 160 } 161 162 func (log *Standard) Debug(fmt string, arg ...interface{}) { 163 log.internal.Debugf(fmt, arg...) 164 if log.externalHandler != nil { 165 log.externalHandler.Log(keybase1.LogLevel_DEBUG, fmt, arg) 166 } 167 } 168 169 func (log *Standard) CDebugf(ctx context.Context, fmt string, 170 arg ...interface{}) { 171 if log.internal.IsEnabledFor(logging.DEBUG) { 172 log.CloneWithAddedDepth(1).Debug(prepareString(ctx, fmt), arg...) 173 } 174 } 175 176 func (log *Standard) Info(fmt string, arg ...interface{}) { 177 log.internal.Infof(fmt, arg...) 178 if log.externalHandler != nil { 179 log.externalHandler.Log(keybase1.LogLevel_INFO, fmt, arg) 180 } 181 } 182 183 func (log *Standard) CInfof(ctx context.Context, fmt string, 184 arg ...interface{}) { 185 if log.internal.IsEnabledFor(logging.INFO) { 186 log.CloneWithAddedDepth(1).Info(prepareString(ctx, fmt), arg...) 187 } 188 } 189 190 func (log *Standard) Notice(fmt string, arg ...interface{}) { 191 log.internal.Noticef(fmt, arg...) 192 if log.externalHandler != nil { 193 log.externalHandler.Log(keybase1.LogLevel_NOTICE, fmt, arg) 194 } 195 } 196 197 func (log *Standard) CNoticef(ctx context.Context, fmt string, 198 arg ...interface{}) { 199 if log.internal.IsEnabledFor(logging.NOTICE) { 200 log.CloneWithAddedDepth(1).Notice(prepareString(ctx, fmt), arg...) 201 } 202 } 203 204 func (log *Standard) Warning(fmt string, arg ...interface{}) { 205 log.internal.Warningf(fmt, arg...) 206 if log.externalHandler != nil { 207 log.externalHandler.Log(keybase1.LogLevel_WARN, fmt, arg) 208 } 209 } 210 211 func (log *Standard) CWarningf(ctx context.Context, fmt string, 212 arg ...interface{}) { 213 if log.internal.IsEnabledFor(logging.WARNING) { 214 log.CloneWithAddedDepth(1).Warning(prepareString(ctx, fmt), arg...) 215 } 216 } 217 218 func (log *Standard) Error(fmt string, arg ...interface{}) { 219 log.internal.Errorf(fmt, arg...) 220 if log.externalHandler != nil { 221 log.externalHandler.Log(keybase1.LogLevel_ERROR, fmt, arg) 222 } 223 } 224 225 func (log *Standard) Errorf(fmt string, arg ...interface{}) { 226 log.CloneWithAddedDepth(1).Error(fmt, arg...) 227 } 228 229 func (log *Standard) CErrorf(ctx context.Context, fmt string, 230 arg ...interface{}) { 231 if log.internal.IsEnabledFor(logging.ERROR) { 232 log.CloneWithAddedDepth(1).Error(prepareString(ctx, fmt), arg...) 233 } 234 } 235 236 func (log *Standard) Critical(fmt string, arg ...interface{}) { 237 log.internal.Criticalf(fmt, arg...) 238 if log.externalHandler != nil { 239 log.externalHandler.Log(keybase1.LogLevel_CRITICAL, fmt, arg) 240 } 241 } 242 243 func (log *Standard) CCriticalf(ctx context.Context, fmt string, 244 arg ...interface{}) { 245 if log.internal.IsEnabledFor(logging.CRITICAL) { 246 log.CloneWithAddedDepth(1).Critical(prepareString(ctx, fmt), arg...) 247 } 248 } 249 250 func (log *Standard) Fatalf(fmt string, arg ...interface{}) { 251 log.internal.Fatalf(fmt, arg...) 252 if log.externalHandler != nil { 253 log.externalHandler.Log(keybase1.LogLevel_FATAL, fmt, arg) 254 } 255 } 256 257 func (log *Standard) CFatalf(ctx context.Context, fmt string, 258 arg ...interface{}) { 259 log.CloneWithAddedDepth(1).Fatalf(prepareString(ctx, fmt), arg...) 260 } 261 262 func (log *Standard) Profile(fmts string, arg ...interface{}) { 263 log.CloneWithAddedDepth(1).Debug(fmts, arg...) 264 } 265 266 // Configure sets the style of the log file, whether debugging (verbose) 267 // is enabled and a filename. If a filename is provided here it will 268 // be used for logging straight away (this is a new feature). 269 // SetLogFileConfig provides a way to set the log file with more control on rotation. 270 func (log *Standard) Configure(style string, debug bool, filename string) { 271 log.configureMutex.Lock() 272 defer log.configureMutex.Unlock() 273 274 log.filename = filename 275 276 var logfmt string 277 278 globalLock.Lock() 279 isTerm := stderrIsTerminal 280 globalLock.Unlock() 281 282 // TODO: how should setting the log file after a Configure be handled? 283 if isTerm { 284 if debug { 285 logfmt = fancyFormat 286 } else { 287 logfmt = defaultFormat 288 } 289 } else { 290 logfmt = fileFormat 291 } 292 293 // Override the format above if an explicit style was specified. 294 switch style { 295 case "default": 296 logfmt = defaultFormat // Default 297 case "plain": 298 logfmt = plainFormat // Plain 299 case "file": 300 logfmt = fileFormat // Good for logging to files 301 case "fancy": 302 logfmt = fancyFormat // Fancy, good for terminals with color 303 } 304 305 if debug { 306 logging.SetLevel(logging.DEBUG, log.module) 307 } 308 309 logging.SetFormatter(logging.MustStringFormatter(logfmt)) 310 311 } 312 313 func OpenLogFile(filename string) (name string, file *os.File, err error) { 314 name = filename 315 if err = MakeParentDirs(name); err != nil { 316 return 317 } 318 file, err = os.OpenFile(name, (os.O_APPEND | os.O_WRONLY | os.O_CREATE), 0600) 319 if err != nil { 320 return 321 } 322 return 323 } 324 325 func FileExists(path string) (bool, error) { 326 _, err := os.Stat(path) 327 if err == nil { 328 return true, nil 329 } 330 if os.IsNotExist(err) { 331 return false, nil 332 } 333 return false, err 334 } 335 336 func MakeParentDirs(filename string) error { 337 dir, _ := filepath.Split(filename) 338 // If passed a plain file name as a path 339 if dir == "" { 340 return nil 341 } 342 exists, err := FileExists(dir) 343 if err != nil { 344 return err 345 } 346 347 if !exists { 348 err = os.MkdirAll(dir, permDir) 349 if err != nil { 350 return err 351 } 352 } 353 return nil 354 } 355 356 func (log *Standard) CloneWithAddedDepth(depth int) Logger { 357 clone := Standard{ 358 filename: log.filename, 359 module: log.module, 360 externalHandler: log.externalHandler, 361 } 362 cloneInternal := *log.internal 363 clone.internal = &cloneInternal 364 clone.internal.ExtraCalldepth = log.internal.ExtraCalldepth + depth 365 366 return &clone 367 } 368 369 func (log *Standard) SetExternalHandler(handler ExternalHandler) { 370 log.externalHandler = handler 371 } 372 373 type UnforwardedLogger Standard 374 375 func (log *Standard) GetUnforwardedLogger() *UnforwardedLogger { 376 return (*UnforwardedLogger)(log) 377 } 378 379 func (log *UnforwardedLogger) Debug(s string, args ...interface{}) { 380 log.internal.Debugf(s, args...) 381 } 382 383 func (log *UnforwardedLogger) Error(s string, args ...interface{}) { 384 log.internal.Errorf(s, args...) 385 } 386 387 func (log *UnforwardedLogger) Errorf(s string, args ...interface{}) { 388 log.internal.Errorf(s, args...) 389 } 390 391 func (log *UnforwardedLogger) Warning(s string, args ...interface{}) { 392 log.internal.Warningf(s, args...) 393 } 394 395 func (log *UnforwardedLogger) Info(s string, args ...interface{}) { 396 log.internal.Infof(s, args...) 397 } 398 399 func (log *UnforwardedLogger) Profile(s string, args ...interface{}) { 400 log.internal.Debugf(s, args...) 401 }