github.com/johnathanhowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/persist/log.go (about) 1 package persist 2 3 import ( 4 "fmt" 5 "io" 6 "log" 7 "os" 8 "sync" 9 10 "github.com/NebulousLabs/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 // Critical logs a message with a CRITICAL prefix. If debug mode is enabled, 22 // it will also write the message to os.Stderr and panic. 23 func (l *Logger) Critical(v ...interface{}) { 24 l.Output(2, "CRITICAL: "+fmt.Sprintln(v...)) 25 build.Critical(v...) 26 } 27 28 // Debug is equivalent to Logger.Print when build.DEBUG is true. Otherwise it 29 // is a no-op. 30 func (l *Logger) Debug(v ...interface{}) { 31 if build.DEBUG { 32 l.Output(2, fmt.Sprint(v...)) 33 } 34 } 35 36 // Debugf is equivalent to Logger.Printf when build.DEBUG is true. Otherwise it 37 // is a no-op. 38 func (l *Logger) Debugf(format string, v ...interface{}) { 39 if build.DEBUG { 40 l.Output(2, fmt.Sprintf(format, v...)) 41 } 42 } 43 44 // Debugln is equivalent to Logger.Println when build.DEBUG is true. Otherwise 45 // it is a no-op. 46 func (l *Logger) Debugln(v ...interface{}) { 47 if build.DEBUG { 48 l.Output(2, fmt.Sprintln(v...)) 49 } 50 } 51 52 // Close logs a shutdown message and closes the Logger's underlying io.Writer, 53 // if it is also an io.Closer. 54 func (l *Logger) Close() error { 55 l.Output(2, "SHUTDOWN: Logging has terminated.") 56 if c, ok := l.w.(io.Closer); ok { 57 return c.Close() 58 } 59 return nil 60 } 61 62 // NewLogger returns a logger that can be closed. Calls should not be made to 63 // the logger after 'Close' has been called. 64 func NewLogger(w io.Writer) *Logger { 65 l := log.New(w, "", log.Ldate|log.Ltime|log.Lmicroseconds|log.Lshortfile|log.LUTC) 66 l.Output(3, "STARTUP: Logging has started.") // Call depth is 3 because NewLogger is usually called by NewFileLogger 67 return &Logger{l, w} 68 } 69 70 // closeableFile wraps an os.File to perform sanity checks on its Write and 71 // Close methods. When the checks are enabled, calls to Write or Close will 72 // panic if they are called after the file has already been closed. 73 type closeableFile struct { 74 *os.File 75 closed bool 76 mu sync.RWMutex 77 } 78 79 // Close closes the file and sets the closed flag. 80 func (cf *closeableFile) Close() error { 81 cf.mu.Lock() 82 defer cf.mu.Unlock() 83 // Sanity check - close should not have been called yet. 84 if cf.closed { 85 build.Critical("cannot close the file; already closed") 86 } 87 88 // Ensure that all data has actually hit the disk. 89 if err := cf.Sync(); err != nil { 90 return err 91 } 92 cf.closed = true 93 return cf.File.Close() 94 } 95 96 // Write takes the input data and writes it to the file. 97 func (cf *closeableFile) Write(b []byte) (int, error) { 98 cf.mu.RLock() 99 defer cf.mu.RUnlock() 100 // Sanity check - close should not have been called yet. 101 if cf.closed { 102 build.Critical("cannot write to the file after it has been closed") 103 } 104 return cf.File.Write(b) 105 } 106 107 // NewFileLogger returns a logger that logs to logFilename. The file is opened 108 // in append mode, and created if it does not exist. 109 func NewFileLogger(logFilename string) (*Logger, error) { 110 logFile, err := os.OpenFile(logFilename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0660) 111 if err != nil { 112 return nil, err 113 } 114 cf := &closeableFile{File: logFile} 115 return NewLogger(cf), nil 116 }