github.com/atlassian/git-lob@v0.0.0-20150806085256-2386a5ed291a/util/log.go (about)

     1  package util
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"log"
     9  	"os"
    10  	"path/filepath"
    11  	"runtime"
    12  	"runtime/debug"
    13  	"strings"
    14  
    15  	"github.com/atlassian/git-lob/Godeps/_workspace/src/github.com/mitchellh/go-homedir"
    16  )
    17  
    18  var (
    19  	// Console output (can be overridden by changing)
    20  	consoleErr io.Writer = os.Stderr
    21  	consoleOut io.Writer = os.Stdout
    22  	// Loggers for file output
    23  	debugLog  *log.Logger
    24  	errorLog  *log.Logger
    25  	outputLog *log.Logger
    26  	logFile   *os.File
    27  )
    28  
    29  // Always send all console output to stderr, including info/debug messages
    30  // This is mostly useful when stdout is reserved for piping content
    31  func LogAllConsoleOutputToStdErr() {
    32  	consoleOut = os.Stderr
    33  	consoleErr = os.Stderr
    34  }
    35  
    36  // Suppress all console output
    37  func LogSuppressAllConsoleOutput() {
    38  	consoleOut = ioutil.Discard
    39  	consoleErr = ioutil.Discard
    40  	errorLog = log.New(ioutil.Discard, "", 0)
    41  	debugLog = log.New(ioutil.Discard, "", 0)
    42  	outputLog = log.New(ioutil.Discard, "", 0)
    43  }
    44  
    45  func writeToLog(log *log.Logger, addNewline bool, includeStack bool, msgs ...interface{}) {
    46  	if log != nil {
    47  		// Prefix message with repo root (this is cached for efficiency)
    48  		// We don't add this to the Logger prefix in New() because this prefixes before the timestamp & other
    49  		// flag-based fields, which means things don't line up nicely in the log
    50  		root, _, _ := GetRepoRoot() // ignore failure, just use blank string
    51  		buf := bytes.NewBufferString(fmt.Sprintf("[%v]: ", root))
    52  		fmt.Fprint(buf, msgs...)
    53  		if addNewline {
    54  			buf.WriteString("\n")
    55  		}
    56  		log.Print(buf.String())
    57  		if includeStack {
    58  			log.Println(string(debug.Stack()))
    59  		}
    60  	}
    61  
    62  }
    63  
    64  // Log error to console and log with format (no implicit newline)
    65  func LogErrorf(format string, v ...interface{}) {
    66  	msg := fmt.Sprintf(format, v...)
    67  	fmt.Fprint(consoleErr, msg)
    68  	writeToLog(errorLog, false, true, msg)
    69  }
    70  
    71  // Log debug message to console and log with format (if verbose)
    72  func LogDebugf(format string, v ...interface{}) {
    73  	if GlobalOptions.Verbose {
    74  		fmt.Fprintf(consoleOut, format, v...)
    75  	}
    76  
    77  	if GlobalOptions.VerboseLog {
    78  		writeToLog(debugLog, false, false, fmt.Sprintf(format, v...))
    79  	}
    80  
    81  }
    82  
    83  // Log output message to console and log with format (if not quiet)
    84  // You probably don't want to use this since most info messages are for
    85  // an interactive user only; see LogConsolef instead for that
    86  func Logf(format string, v ...interface{}) {
    87  	if !GlobalOptions.Quiet {
    88  		msg := fmt.Sprintf(format, v...)
    89  		fmt.Fprint(consoleOut, msg)
    90  		writeToLog(outputLog, false, false, msg)
    91  	}
    92  }
    93  
    94  // Log error message to console and log with newline & spaces in between
    95  func LogError(msgs ...interface{}) {
    96  	fmt.Fprintln(consoleErr, msgs...)
    97  	writeToLog(errorLog, true, true, msgs...)
    98  }
    99  
   100  // Log debug message to console and log with newline (if verbose)
   101  func LogDebug(msgs ...interface{}) {
   102  	if GlobalOptions.Verbose {
   103  		fmt.Fprintln(consoleOut, msgs...)
   104  	}
   105  
   106  	if GlobalOptions.VerboseLog {
   107  		writeToLog(debugLog, true, false, msgs...)
   108  	}
   109  }
   110  
   111  // Log output message to console and log with newline (if not quiet)
   112  // You probably don't want to use this since most info messages are for
   113  // an interactive user only; see util.LogConsole instead for that
   114  func Log(msgs ...interface{}) {
   115  	if !GlobalOptions.Quiet {
   116  		fmt.Fprintln(consoleOut, msgs...)
   117  		writeToLog(outputLog, true, false, msgs...)
   118  	}
   119  }
   120  
   121  // Write an informational message to the console with newline (if not quiet), and not the log
   122  func LogConsole(msgs ...interface{}) {
   123  	if !GlobalOptions.Quiet {
   124  		fmt.Fprintln(consoleOut, msgs...)
   125  	}
   126  }
   127  
   128  // Overwrite the current line in the console (e.g. for progressive update), if not quiet
   129  // Requires the previous line length so that it can clear it with spaces
   130  // Does not add a newline after writing
   131  func LogConsoleOverwrite(newString string, lastLineLength int) {
   132  	if len(newString) < lastLineLength {
   133  		LogConsolef("\r%v%v", newString, strings.Repeat(" ", lastLineLength-len(newString)))
   134  	} else {
   135  		LogConsolef("\r%v", newString)
   136  	}
   137  
   138  }
   139  
   140  // Write an informational message to the console (if not quiet), and not the log
   141  func LogConsolef(format string, v ...interface{}) {
   142  	if !GlobalOptions.Quiet {
   143  		fmt.Fprintf(consoleOut, format, v...)
   144  	}
   145  }
   146  
   147  // Write an error message to the console with newline and not the log
   148  func LogConsoleError(msgs ...interface{}) {
   149  	fmt.Fprintln(consoleErr, msgs...)
   150  }
   151  
   152  // Write an error message to the console and not the log
   153  func LogConsoleErrorf(format string, v ...interface{}) {
   154  	fmt.Fprintf(consoleErr, format, v...)
   155  }
   156  
   157  // Write a debug message to the console with newline (if verbose), and not the log
   158  func LogConsoleDebug(msgs ...interface{}) {
   159  	if GlobalOptions.Verbose {
   160  		fmt.Fprintln(consoleOut, msgs...)
   161  	}
   162  }
   163  
   164  // Write a debug message to the console (if verbose), and not the log
   165  func LogConsoleDebugf(format string, v ...interface{}) {
   166  	if GlobalOptions.Verbose {
   167  		fmt.Fprintf(consoleOut, format, v...)
   168  	}
   169  }
   170  
   171  func getLogFileHandle() *os.File {
   172  	var logFileName string
   173  	if GlobalOptions.LogFile != "" {
   174  		logFileName = GlobalOptions.LogFile
   175  	} else {
   176  		home, err := homedir.Dir()
   177  		if err != nil {
   178  			log.Fatal(err)
   179  		}
   180  		logFileName = filepath.Join(home, "git-lob.log")
   181  	}
   182  	var err error
   183  	logFile, err = os.OpenFile(logFileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
   184  	if err != nil {
   185  		log.Fatal(err)
   186  	}
   187  	return logFile
   188  }
   189  
   190  // Initialise logging, make sure GlobalOptions is initialised
   191  func InitLogging() {
   192  
   193  	if GlobalOptions.LogEnabled {
   194  		const logFlags = log.Ldate | log.Ltime
   195  		f := getLogFileHandle()
   196  		outputLog = log.New(f, "", logFlags)
   197  		errorLog = log.New(f, "ERROR: ", logFlags)
   198  		debugLog = log.New(f, "", logFlags)
   199  	}
   200  }
   201  func ShutDownLogging() {
   202  	if logFile != nil {
   203  		logFile.Close()
   204  	}
   205  
   206  }
   207  
   208  var spinnerCycle = 0
   209  var spinnerChars = []byte{'|', '/', '-', '\\'}
   210  
   211  // Render a 'spinner' in the console, with optional prefix (which must stay constant between calls unless you clear the line)
   212  func LogConsoleSpinner(prefix string) {
   213  	LogConsoleOverwrite(fmt.Sprintf("%v%c", prefix, spinnerChars[spinnerCycle]), len(prefix)+1)
   214  	spinnerCycle = (spinnerCycle + 1) % len(spinnerChars)
   215  }
   216  
   217  // Finish a spinner progress with a check mark and a newline
   218  func LogConsoleSpinnerFinish(prefix string) {
   219  	if runtime.GOOS == "windows" {
   220  		// Windows console sucks, can't do nice check mark except in ConEmu (not cmd or git bash)
   221  		// So play it safe & boring
   222  		LogConsoleOverwrite(fmt.Sprintf("%v%v\n", prefix, "Done"), len(prefix)+1)
   223  	} else {
   224  		LogConsoleOverwrite(fmt.Sprintf("%v%c\n", prefix, '\u2714'), len(prefix)+1)
   225  	}
   226  	spinnerCycle = 0
   227  }