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  }