github.com/freetocompute/snapd@v0.0.0-20210618182524-2fb355d72fd9/logger/logger.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014,2015,2017 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package logger 21 22 import ( 23 "bytes" 24 "fmt" 25 "io" 26 "log" 27 "os" 28 "sync" 29 30 "github.com/snapcore/snapd/osutil" 31 ) 32 33 // A Logger is a fairly minimal logging tool. 34 type Logger interface { 35 // Notice is for messages that the user should see 36 Notice(msg string) 37 // Debug is for messages that the user should be able to find if they're debugging something 38 Debug(msg string) 39 } 40 41 const ( 42 // DefaultFlags are passed to the default console log.Logger 43 DefaultFlags = log.Ldate | log.Ltime | log.Lmicroseconds | log.Lshortfile 44 ) 45 46 type nullLogger struct{} 47 48 func (nullLogger) Notice(string) {} 49 func (nullLogger) Debug(string) {} 50 51 // NullLogger is a logger that does nothing 52 var NullLogger = nullLogger{} 53 54 var ( 55 logger Logger = NullLogger 56 lock sync.Mutex 57 ) 58 59 // Panicf notifies the user and then panics 60 func Panicf(format string, v ...interface{}) { 61 msg := fmt.Sprintf(format, v...) 62 63 lock.Lock() 64 defer lock.Unlock() 65 66 logger.Notice("PANIC " + msg) 67 panic(msg) 68 } 69 70 // Noticef notifies the user of something 71 func Noticef(format string, v ...interface{}) { 72 msg := fmt.Sprintf(format, v...) 73 74 lock.Lock() 75 defer lock.Unlock() 76 77 logger.Notice(msg) 78 } 79 80 // Debugf records something in the debug log 81 func Debugf(format string, v ...interface{}) { 82 msg := fmt.Sprintf(format, v...) 83 84 lock.Lock() 85 defer lock.Unlock() 86 87 logger.Debug(msg) 88 } 89 90 // MockLogger replaces the exiting logger with a buffer and returns 91 // the log buffer and a restore function. 92 func MockLogger() (buf *bytes.Buffer, restore func()) { 93 buf = &bytes.Buffer{} 94 oldLogger := logger 95 l, err := New(buf, DefaultFlags) 96 if err != nil { 97 panic(err) 98 } 99 SetLogger(l) 100 return buf, func() { 101 SetLogger(oldLogger) 102 } 103 } 104 105 // WithLoggerLock invokes f with the global logger lock, useful for 106 // tests involving goroutines with MockLogger. 107 func WithLoggerLock(f func()) { 108 lock.Lock() 109 defer lock.Unlock() 110 111 f() 112 } 113 114 // SetLogger sets the global logger to the given one 115 func SetLogger(l Logger) { 116 lock.Lock() 117 defer lock.Unlock() 118 119 logger = l 120 } 121 122 type Log struct { 123 log *log.Logger 124 125 debug bool 126 } 127 128 // Debug only prints if SNAPD_DEBUG is set 129 func (l *Log) Debug(msg string) { 130 if l.debug || osutil.GetenvBool("SNAPD_DEBUG") { 131 l.log.Output(3, "DEBUG: "+msg) 132 } 133 } 134 135 // Notice alerts the user about something, as well as putting it syslog 136 func (l *Log) Notice(msg string) { 137 l.log.Output(3, msg) 138 } 139 140 // New creates a log.Logger using the given io.Writer and flag. 141 func New(w io.Writer, flag int) (Logger, error) { 142 logger := &Log{ 143 log: log.New(w, "", flag), 144 debug: debugEnabledOnKernelCmdline(), 145 } 146 return logger, nil 147 } 148 149 // SimpleSetup creates the default (console) logger 150 func SimpleSetup() error { 151 flags := log.Lshortfile 152 if term := os.Getenv("TERM"); term != "" { 153 // snapd is probably not running under systemd 154 flags = DefaultFlags 155 } 156 l, err := New(os.Stderr, flags) 157 if err == nil { 158 SetLogger(l) 159 } 160 return err 161 } 162 163 // used to force testing of the kernel command line parsing 164 var procCmdlineUseDefaultMockInTests = true 165 166 // TODO: consider generalizing this to snapdenv and having it used by 167 // other places that consider SNAPD_DEBUG 168 func debugEnabledOnKernelCmdline() bool { 169 // if this is called during tests, always ignore it so we don't have to mock 170 // the /proc/cmdline for every test that ends up using a logger 171 if osutil.IsTestBinary() && procCmdlineUseDefaultMockInTests { 172 return false 173 } 174 m, _ := osutil.KernelCommandLineKeyValues("snapd.debug") 175 return m["snapd.debug"] == "1" 176 }