github.com/decred/politeia@v1.4.0/politeiad/log.go (about) 1 // Copyright (c) 2017-2021 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/politeia/politeiad/backend/gitbe" 13 "github.com/decred/politeia/politeiad/backendv2/tstorebe" 14 "github.com/decred/politeia/politeiad/backendv2/tstorebe/plugins/comments" 15 "github.com/decred/politeia/politeiad/backendv2/tstorebe/plugins/dcrdata" 16 "github.com/decred/politeia/politeiad/backendv2/tstorebe/plugins/pi" 17 "github.com/decred/politeia/politeiad/backendv2/tstorebe/plugins/ticketvote" 18 "github.com/decred/politeia/politeiad/backendv2/tstorebe/plugins/usermd" 19 "github.com/decred/politeia/politeiad/backendv2/tstorebe/store/localdb" 20 "github.com/decred/politeia/politeiad/backendv2/tstorebe/store/mysql" 21 "github.com/decred/politeia/politeiad/backendv2/tstorebe/tlog" 22 "github.com/decred/politeia/politeiad/backendv2/tstorebe/tstore" 23 "github.com/decred/politeia/politeiawww/wsdcrdata" 24 "github.com/decred/slog" 25 "github.com/jrick/logrotate/rotator" 26 ) 27 28 // logWriter implements an io.Writer that outputs to both standard output and 29 // the write-end pipe of an initialized log rotator. 30 type logWriter struct{} 31 32 func (logWriter) Write(p []byte) (n int, err error) { 33 os.Stdout.Write(p) 34 return logRotator.Write(p) 35 } 36 37 // Loggers per subsystem. A single backend logger is created and all subsytem 38 // loggers created from it will write to the backend. When adding new 39 // subsystems, add the subsystem logger variable here and to the 40 // subsystemLoggers map. 41 // 42 // Loggers can not be used before the log rotator has been initialized with a 43 // log file. This must be performed early during application startup by calling 44 // initLogRotator. 45 var ( 46 // backendLog is the logging backend used to create all subsystem loggers. 47 // The backend must not be used before the log rotator has been initialized, 48 // or data races and/or nil pointer dereferences will occur. 49 backendLog = slog.NewBackend(logWriter{}) 50 51 // logRotator is one of the logging outputs. It should be closed on 52 // application shutdown. 53 logRotator *rotator.Rotator 54 55 log = backendLog.Logger("POLI") 56 gitbeLog = backendLog.Logger("GITB") 57 tstorebeLog = backendLog.Logger("BACK") 58 tstoreLog = backendLog.Logger("TSTR") 59 kvstoreLog = backendLog.Logger("STOR") 60 wsdcrdataLog = backendLog.Logger("WSDD") 61 pluginLog = backendLog.Logger("PLUG") 62 tlogLog = backendLog.Logger("TLOG") 63 ) 64 65 // Initialize package-global logger variables. 66 func init() { 67 // Git backend loggers 68 gitbe.UseLogger(gitbeLog) 69 70 // Tstore backend loggers 71 tstorebe.UseLogger(tstorebeLog) 72 tstore.UseLogger(tstoreLog) 73 localdb.UseLogger(kvstoreLog) 74 mysql.UseLogger(kvstoreLog) 75 tlog.UseLogger(tlogLog) 76 77 // Plugin loggers 78 comments.UseLogger(pluginLog) 79 dcrdata.UseLogger(pluginLog) 80 ticketvote.UseLogger(pluginLog) 81 usermd.UseLogger(pluginLog) 82 pi.UseLogger(pluginLog) 83 84 // Other loggers 85 wsdcrdata.UseLogger(wsdcrdataLog) 86 } 87 88 // subsystemLoggers maps each subsystem identifier to its associated logger. 89 var subsystemLoggers = map[string]slog.Logger{ 90 "POLI": log, 91 "GITB": gitbeLog, 92 "BACK": tstorebeLog, 93 "TSTR": tstoreLog, 94 "STOR": kvstoreLog, 95 "WSDD": wsdcrdataLog, 96 "PLUG": pluginLog, 97 "TLOG": tlogLog, 98 } 99 100 // initLogRotator initializes the logging rotater to write logs to logFile and 101 // create roll files in the same directory. It must be called before the 102 // package-global log rotater variables are used. 103 func initLogRotator(logFile string) { 104 logDir, _ := filepath.Split(logFile) 105 err := os.MkdirAll(logDir, 0700) 106 if err != nil { 107 fmt.Fprintf(os.Stderr, "failed to create log directory: %v\n", err) 108 os.Exit(1) 109 } 110 r, err := rotator.New(logFile, 10*1024, false, 3) 111 if err != nil { 112 fmt.Fprintf(os.Stderr, "failed to create file rotator: %v\n", err) 113 os.Exit(1) 114 } 115 116 logRotator = r 117 } 118 119 // setLogLevel sets the logging level for provided subsystem. Invalid 120 // subsystems are ignored. Uninitialized subsystems are dynamically created as 121 // needed. 122 func setLogLevel(subsystemID string, logLevel string) { 123 // Ignore invalid subsystems. 124 logger, ok := subsystemLoggers[subsystemID] 125 if !ok { 126 return 127 } 128 129 // Defaults to info if the log level is invalid. 130 level, _ := slog.LevelFromString(logLevel) 131 logger.SetLevel(level) 132 } 133 134 // setLogLevels sets the log level for all subsystem loggers to the passed 135 // level. It also dynamically creates the subsystem loggers as needed, so it 136 // can be used to initialize the logging system. 137 func setLogLevels(logLevel string) { 138 // Configure all sub-systems with the new logging level. Dynamically 139 // create loggers as needed. 140 for subsystemID := range subsystemLoggers { 141 setLogLevel(subsystemID, logLevel) 142 } 143 } 144 145 // LogClosure is a closure that can be printed with %v to be used to 146 // generate expensive-to-create data for a detailed log level and avoid doing 147 // the work if the data isn't printed. 148 type logClosure func() string 149 150 func (c logClosure) String() string { 151 return c() 152 } 153 154 func newLogClosure(c func() string) logClosure { 155 return logClosure(c) 156 }