gitlab.com/jokerrs1/Sia@v1.3.2/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  // 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  }