github.com/decred/politeia@v1.4.0/politeiawww/cmd/politeiavoter/log.go (about) 1 // Copyright (c) 2017-2019 The Decred developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "fmt" 9 "os" 10 "path/filepath" 11 12 "github.com/decred/slog" 13 "github.com/jrick/logrotate/rotator" 14 ) 15 16 // logWriter implements an io.Writer that outputs to both standard output and 17 // the write-end pipe of an initialized log rotator. 18 type logWriter struct{} 19 20 func (logWriter) Write(p []byte) (n int, err error) { 21 os.Stdout.Write(p) 22 return logRotator.Write(p) 23 } 24 25 // Loggers per subsystem. A single backend logger is created and all subsytem 26 // loggers created from it will write to the backend. When adding new 27 // subsystems, add the subsystem logger variable here and to the 28 // subsystemLoggers map. 29 // 30 // Loggers can not be used before the log rotator has been initialized with a 31 // log file. This must be performed early during application startup by calling 32 // initLogRotator. 33 var ( 34 // backendLog is the logging backend used to create all subsystem loggers. 35 // The backend must not be used before the log rotator has been initialized, 36 // or data races and/or nil pointer dereferences will occur. 37 backendLog = slog.NewBackend(logWriter{}) 38 39 // logRotator is one of the logging outputs. It should be closed on 40 // application shutdown. 41 logRotator *rotator.Rotator 42 43 log = backendLog.Logger("POLV") 44 ) 45 46 // subsystemLoggers maps each subsystem identifier to its associated logger. 47 var subsystemLoggers = map[string]slog.Logger{ 48 "POLV": log, 49 } 50 51 // initLogRotator initializes the logging rotater to write logs to logFile and 52 // create roll files in the same directory. It must be called before the 53 // package-global log rotater variables are used. 54 func initLogRotator(logFile string) { 55 logDir, _ := filepath.Split(logFile) 56 err := os.MkdirAll(logDir, 0700) 57 if err != nil { 58 fmt.Fprintf(os.Stderr, "failed to create log directory: %v\n", err) 59 os.Exit(1) 60 } 61 r, err := rotator.New(logFile, 10*1024, false, 3) 62 if err != nil { 63 fmt.Fprintf(os.Stderr, "failed to create file rotator: %v\n", err) 64 os.Exit(1) 65 } 66 67 logRotator = r 68 } 69 70 // setLogLevel sets the logging level for provided subsystem. Invalid 71 // subsystems are ignored. Uninitialized subsystems are dynamically created as 72 // needed. 73 func setLogLevel(subsystemID string, logLevel string) { 74 // Ignore invalid subsystems. 75 logger, ok := subsystemLoggers[subsystemID] 76 if !ok { 77 return 78 } 79 80 // Defaults to info if the log level is invalid. 81 level, _ := slog.LevelFromString(logLevel) 82 logger.SetLevel(level) 83 } 84 85 // setLogLevels sets the log level for all subsystem loggers to the passed 86 // level. It also dynamically creates the subsystem loggers as needed, so it 87 // can be used to initialize the logging system. 88 func setLogLevels(logLevel string) { 89 // Configure all sub-systems with the new logging level. Dynamically 90 // create loggers as needed. 91 for subsystemID := range subsystemLoggers { 92 setLogLevel(subsystemID, logLevel) 93 } 94 } 95 96 // LogClosure is a closure that can be printed with %v to be used to 97 // generate expensive-to-create data for a detailed log level and avoid doing 98 // the work if the data isn't printed. 99 type logClosure func() string 100 101 func (c logClosure) String() string { 102 return c() 103 }