github.com/Synthesix/Sia@v1.3.3-0.20180413141344-f863baeed3ca/persist/log.go (about) 1 package persist 2 3 import ( 4 "fmt" 5 "io" 6 "log" 7 "os" 8 "sync" 9 10 "github.com/Synthesix/Sia/build" 11 ) 12 13 // Logger is a wrapper for the standard library logger that enforces logging 14 // with the Sia-standard settings. It also supports a Close method, which 15 // attempts to close the underlying io.Writer. 16 type Logger struct { 17 *log.Logger 18 w io.Writer 19 } 20 21 // Close logs a shutdown message and closes the Logger's underlying io.Writer, 22 // if it is also an io.Closer. 23 func (l *Logger) Close() error { 24 l.Output(2, "SHUTDOWN: Logging has terminated.") 25 if c, ok := l.w.(io.Closer); ok { 26 return c.Close() 27 } 28 return nil 29 } 30 31 // Critical logs a message with a CRITICAL prefix that guides the user to the 32 // Sia github tracker. If debug mode is enabled, it will also write the message 33 // to os.Stderr and panic. Critical should only be called if there has been a 34 // developer error, otherwise Severe should be called. 35 func (l *Logger) Critical(v ...interface{}) { 36 l.Output(2, "CRITICAL: "+fmt.Sprintln(v...)) 37 build.Critical(v...) 38 } 39 40 // Debug is equivalent to Logger.Print when build.DEBUG is true. Otherwise it 41 // is a no-op. 42 func (l *Logger) Debug(v ...interface{}) { 43 if build.DEBUG { 44 l.Output(2, fmt.Sprint(v...)) 45 } 46 } 47 48 // Debugf is equivalent to Logger.Printf when build.DEBUG is true. Otherwise it 49 // is a no-op. 50 func (l *Logger) Debugf(format string, v ...interface{}) { 51 if build.DEBUG { 52 l.Output(2, fmt.Sprintf(format, v...)) 53 } 54 } 55 56 // Debugln is equivalent to Logger.Println when build.DEBUG is true. Otherwise 57 // it is a no-op. 58 func (l *Logger) Debugln(v ...interface{}) { 59 if build.DEBUG { 60 l.Output(2, "[DEBUG] "+fmt.Sprintln(v...)) 61 } 62 } 63 64 // Severe logs a message with a SEVERE prefix. If debug mode is enabled, it 65 // will also write the message to os.Stderr and panic. Severe should be called 66 // if there is a severe problem with the user's machine or setup that should be 67 // addressed ASAP but does not necessarily require that the machine crash or 68 // exit. 69 func (l *Logger) Severe(v ...interface{}) { 70 l.Output(2, "SEVERE: "+fmt.Sprintln(v...)) 71 build.Severe(v...) 72 } 73 74 // NewLogger returns a logger that can be closed. Calls should not be made to 75 // the logger after 'Close' has been called. 76 func NewLogger(w io.Writer) *Logger { 77 l := log.New(w, "", log.Ldate|log.Ltime|log.Lmicroseconds|log.Lshortfile|log.LUTC) 78 l.Output(3, "STARTUP: Logging has started. Siad Version "+build.Version) // Call depth is 3 because NewLogger is usually called by NewFileLogger 79 return &Logger{l, w} 80 } 81 82 // closeableFile wraps an os.File to perform sanity checks on its Write and 83 // Close methods. When the checks are enabled, calls to Write or Close will 84 // panic if they are called after the file has already been closed. 85 type closeableFile struct { 86 *os.File 87 closed bool 88 mu sync.RWMutex 89 } 90 91 // Close closes the file and sets the closed flag. 92 func (cf *closeableFile) Close() error { 93 cf.mu.Lock() 94 defer cf.mu.Unlock() 95 // Sanity check - close should not have been called yet. 96 if cf.closed { 97 build.Critical("cannot close the file; already closed") 98 } 99 100 // Ensure that all data has actually hit the disk. 101 if err := cf.Sync(); err != nil { 102 return err 103 } 104 cf.closed = true 105 return cf.File.Close() 106 } 107 108 // Write takes the input data and writes it to the file. 109 func (cf *closeableFile) Write(b []byte) (int, error) { 110 cf.mu.RLock() 111 defer cf.mu.RUnlock() 112 // Sanity check - close should not have been called yet. 113 if cf.closed { 114 build.Critical("cannot write to the file after it has been closed") 115 } 116 return cf.File.Write(b) 117 } 118 119 // NewFileLogger returns a logger that logs to logFilename. The file is opened 120 // in append mode, and created if it does not exist. 121 func NewFileLogger(logFilename string) (*Logger, error) { 122 logFile, err := os.OpenFile(logFilename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0660) 123 if err != nil { 124 return nil, err 125 } 126 cf := &closeableFile{File: logFile} 127 return NewLogger(cf), nil 128 }