github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/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 "io/ioutil" 27 "log" 28 "os" 29 "strings" 30 "sync" 31 32 "github.com/snapcore/snapd/osutil" 33 "github.com/snapcore/snapd/strutil" 34 ) 35 36 // A Logger is a fairly minimal logging tool. 37 type Logger interface { 38 // Notice is for messages that the user should see 39 Notice(msg string) 40 // Debug is for messages that the user should be able to find if they're debugging something 41 Debug(msg string) 42 } 43 44 const ( 45 // DefaultFlags are passed to the default console log.Logger 46 DefaultFlags = log.Ldate | log.Ltime | log.Lmicroseconds | log.Lshortfile 47 ) 48 49 type nullLogger struct{} 50 51 func (nullLogger) Notice(string) {} 52 func (nullLogger) Debug(string) {} 53 54 // NullLogger is a logger that does nothing 55 var NullLogger = nullLogger{} 56 57 var ( 58 logger Logger = NullLogger 59 lock sync.Mutex 60 ) 61 62 // Panicf notifies the user and then panics 63 func Panicf(format string, v ...interface{}) { 64 msg := fmt.Sprintf(format, v...) 65 66 lock.Lock() 67 defer lock.Unlock() 68 69 logger.Notice("PANIC " + msg) 70 panic(msg) 71 } 72 73 // Noticef notifies the user of something 74 func Noticef(format string, v ...interface{}) { 75 msg := fmt.Sprintf(format, v...) 76 77 lock.Lock() 78 defer lock.Unlock() 79 80 logger.Notice(msg) 81 } 82 83 // Debugf records something in the debug log 84 func Debugf(format string, v ...interface{}) { 85 msg := fmt.Sprintf(format, v...) 86 87 lock.Lock() 88 defer lock.Unlock() 89 90 logger.Debug(msg) 91 } 92 93 // MockLogger replaces the exiting logger with a buffer and returns 94 // the log buffer and a restore function. 95 func MockLogger() (buf *bytes.Buffer, restore func()) { 96 buf = &bytes.Buffer{} 97 oldLogger := logger 98 l, err := New(buf, DefaultFlags) 99 if err != nil { 100 panic(err) 101 } 102 SetLogger(l) 103 return buf, func() { 104 SetLogger(oldLogger) 105 } 106 } 107 108 // WithLoggerLock invokes f with the global logger lock, useful for 109 // tests involving goroutines with MockLogger. 110 func WithLoggerLock(f func()) { 111 lock.Lock() 112 defer lock.Unlock() 113 114 f() 115 } 116 117 // SetLogger sets the global logger to the given one 118 func SetLogger(l Logger) { 119 lock.Lock() 120 defer lock.Unlock() 121 122 logger = l 123 } 124 125 type Log struct { 126 log *log.Logger 127 128 debug bool 129 } 130 131 // Debug only prints if SNAPD_DEBUG is set 132 func (l *Log) Debug(msg string) { 133 if l.debug || osutil.GetenvBool("SNAPD_DEBUG") { 134 l.log.Output(3, "DEBUG: "+msg) 135 } 136 } 137 138 // Notice alerts the user about something, as well as putting it syslog 139 func (l *Log) Notice(msg string) { 140 l.log.Output(3, msg) 141 } 142 143 // New creates a log.Logger using the given io.Writer and flag. 144 func New(w io.Writer, flag int) (Logger, error) { 145 logger := &Log{ 146 log: log.New(w, "", flag), 147 debug: debugEnabledOnKernelCmdline(), 148 } 149 return logger, nil 150 } 151 152 // SimpleSetup creates the default (console) logger 153 func SimpleSetup() error { 154 flags := log.Lshortfile 155 if term := os.Getenv("TERM"); term != "" { 156 // snapd is probably not running under systemd 157 flags = DefaultFlags 158 } 159 l, err := New(os.Stderr, flags) 160 if err == nil { 161 SetLogger(l) 162 } 163 return err 164 } 165 166 var procCmdline = "/proc/cmdline" 167 168 // TODO: consider generalizing this to snapdenv and having it used by 169 // other places that consider SNAPD_DEBUG 170 func debugEnabledOnKernelCmdline() bool { 171 buf, err := ioutil.ReadFile(procCmdline) 172 if err != nil { 173 return false 174 } 175 l := strings.Split(string(buf), " ") 176 return strutil.ListContains(l, "snapd.debug=1") 177 }