github.com/westcoastroms/westcoastroms-build@v0.0.0-20190928114312-2350e5a73030/build/soong/ui/logger/logger.go (about)

     1  // Copyright 2017 Google Inc. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package logger implements a logging package designed for command line
    16  // utilities.  It uses the standard 'log' package and function, but splits
    17  // output between stderr and a rotating log file.
    18  //
    19  // In addition to the standard logger functions, Verbose[f|ln] calls only go to
    20  // the log file by default, unless SetVerbose(true) has been called.
    21  //
    22  // The log file also includes extended date/time/source information, which are
    23  // omitted from the stderr output for better readability.
    24  //
    25  // In order to better handle resource cleanup after a Fatal error, the Fatal
    26  // functions panic instead of calling os.Exit(). To actually do the cleanup,
    27  // and prevent the printing of the panic, call defer logger.Cleanup() at the
    28  // beginning of your main function.
    29  package logger
    30  
    31  import (
    32  	"errors"
    33  	"fmt"
    34  	"io"
    35  	"io/ioutil"
    36  	"log"
    37  	"os"
    38  	"path/filepath"
    39  	"strconv"
    40  	"sync"
    41  	"syscall"
    42  )
    43  
    44  type Logger interface {
    45  	// Print* prints to both stderr and the file log.
    46  	// Arguments to Print are handled in the manner of fmt.Print.
    47  	Print(v ...interface{})
    48  	// Arguments to Printf are handled in the manner of fmt.Printf
    49  	Printf(format string, v ...interface{})
    50  	// Arguments to Println are handled in the manner of fmt.Println
    51  	Println(v ...interface{})
    52  
    53  	// Verbose* is equivalent to Print*, but skips stderr unless the
    54  	// logger has been configured in verbose mode.
    55  	Verbose(v ...interface{})
    56  	Verbosef(format string, v ...interface{})
    57  	Verboseln(v ...interface{})
    58  
    59  	// Fatal* is equivalent to Print* followed by a call to panic that
    60  	// can be converted to an error using Recover, or will be converted
    61  	// to a call to os.Exit(1) with a deferred call to Cleanup()
    62  	Fatal(v ...interface{})
    63  	Fatalf(format string, v ...interface{})
    64  	Fatalln(v ...interface{})
    65  
    66  	// Panic is equivalent to Print* followed by a call to panic.
    67  	Panic(v ...interface{})
    68  	Panicf(format string, v ...interface{})
    69  	Panicln(v ...interface{})
    70  
    71  	// Output writes the string to both stderr and the file log.
    72  	Output(calldepth int, str string) error
    73  }
    74  
    75  // fatalLog is the type used when Fatal[f|ln]
    76  type fatalLog error
    77  
    78  func fileRotation(from, baseName, ext string, cur, max int) error {
    79  	newName := baseName + "." + strconv.Itoa(cur) + ext
    80  
    81  	if _, err := os.Lstat(newName); err == nil {
    82  		if cur+1 <= max {
    83  			fileRotation(newName, baseName, ext, cur+1, max)
    84  		}
    85  	}
    86  
    87  	if err := os.Rename(from, newName); err != nil {
    88  		return fmt.Errorf("Failed to rotate %s to %s. %s", from, newName, err)
    89  	}
    90  	return nil
    91  }
    92  
    93  // CreateFileWithRotation returns a new os.File using os.Create, renaming any
    94  // existing files to <filename>.#.<ext>, keeping up to maxCount files.
    95  // <filename>.1.<ext> is the most recent backup, <filename>.2.<ext> is the
    96  // second most recent backup, etc.
    97  func CreateFileWithRotation(filename string, maxCount int) (*os.File, error) {
    98  	lockFileName := filepath.Join(filepath.Dir(filename), ".lock_"+filepath.Base(filename))
    99  	lockFile, err := os.OpenFile(lockFileName, os.O_RDWR|os.O_CREATE, 0666)
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  	defer lockFile.Close()
   104  
   105  	err = syscall.Flock(int(lockFile.Fd()), syscall.LOCK_EX)
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  
   110  	if _, err := os.Lstat(filename); err == nil {
   111  		ext := filepath.Ext(filename)
   112  		basename := filename[:len(filename)-len(ext)]
   113  		if err = fileRotation(filename, basename, ext, 1, maxCount); err != nil {
   114  			return nil, err
   115  		}
   116  	}
   117  
   118  	return os.Create(filename)
   119  }
   120  
   121  // Recover can be used with defer in a GoRoutine to convert a Fatal panics to
   122  // an error that can be handled.
   123  func Recover(fn func(err error)) {
   124  	p := recover()
   125  
   126  	if p == nil {
   127  		return
   128  	} else if log, ok := p.(fatalLog); ok {
   129  		fn(error(log))
   130  	} else {
   131  		panic(p)
   132  	}
   133  }
   134  
   135  type stdLogger struct {
   136  	stderr  *log.Logger
   137  	verbose bool
   138  
   139  	fileLogger *log.Logger
   140  	mutex      sync.Mutex
   141  	file       *os.File
   142  }
   143  
   144  var _ Logger = &stdLogger{}
   145  
   146  // New creates a new Logger. The out variable sets the destination, commonly
   147  // os.Stderr, but it may be a buffer for tests, or a separate log file if
   148  // the user doesn't need to see the output.
   149  func New(out io.Writer) *stdLogger {
   150  	return &stdLogger{
   151  		stderr:     log.New(out, "", log.Ltime),
   152  		fileLogger: log.New(ioutil.Discard, "", log.Ldate|log.Lmicroseconds|log.Llongfile),
   153  	}
   154  }
   155  
   156  // SetVerbose controls whether Verbose[f|ln] logs to stderr as well as the
   157  // file-backed log.
   158  func (s *stdLogger) SetVerbose(v bool) *stdLogger {
   159  	s.verbose = v
   160  	return s
   161  }
   162  
   163  // SetOutput controls where the file-backed log will be saved. It will keep
   164  // some number of backups of old log files.
   165  func (s *stdLogger) SetOutput(path string) *stdLogger {
   166  	if f, err := CreateFileWithRotation(path, 5); err == nil {
   167  		s.mutex.Lock()
   168  		defer s.mutex.Unlock()
   169  
   170  		if s.file != nil {
   171  			s.file.Close()
   172  		}
   173  		s.file = f
   174  		s.fileLogger.SetOutput(f)
   175  	} else {
   176  		s.Fatal(err.Error())
   177  	}
   178  	return s
   179  }
   180  
   181  // Close disables logging to the file and closes the file handle.
   182  func (s *stdLogger) Close() {
   183  	s.mutex.Lock()
   184  	defer s.mutex.Unlock()
   185  	if s.file != nil {
   186  		s.fileLogger.SetOutput(ioutil.Discard)
   187  		s.file.Close()
   188  		s.file = nil
   189  	}
   190  }
   191  
   192  // Cleanup should be used with defer in your main function. It will close the
   193  // log file and convert any Fatal panics back to os.Exit(1)
   194  func (s *stdLogger) Cleanup() {
   195  	fatal := false
   196  	p := recover()
   197  
   198  	if _, ok := p.(fatalLog); ok {
   199  		fatal = true
   200  		p = nil
   201  	} else if p != nil {
   202  		s.Println(p)
   203  	}
   204  
   205  	s.Close()
   206  
   207  	if p != nil {
   208  		panic(p)
   209  	} else if fatal {
   210  		os.Exit(1)
   211  	}
   212  }
   213  
   214  // Output writes string to both stderr and the file log.
   215  func (s *stdLogger) Output(calldepth int, str string) error {
   216  	s.stderr.Output(calldepth+1, str)
   217  	return s.fileLogger.Output(calldepth+1, str)
   218  }
   219  
   220  // VerboseOutput is equivalent to Output, but only goes to the file log
   221  // unless SetVerbose(true) has been called.
   222  func (s *stdLogger) VerboseOutput(calldepth int, str string) error {
   223  	if s.verbose {
   224  		s.stderr.Output(calldepth+1, str)
   225  	}
   226  	return s.fileLogger.Output(calldepth+1, str)
   227  }
   228  
   229  // Print prints to both stderr and the file log.
   230  // Arguments are handled in the manner of fmt.Print.
   231  func (s *stdLogger) Print(v ...interface{}) {
   232  	output := fmt.Sprint(v...)
   233  	s.Output(2, output)
   234  }
   235  
   236  // Printf prints to both stderr and the file log.
   237  // Arguments are handled in the manner of fmt.Printf.
   238  func (s *stdLogger) Printf(format string, v ...interface{}) {
   239  	output := fmt.Sprintf(format, v...)
   240  	s.Output(2, output)
   241  }
   242  
   243  // Println prints to both stderr and the file log.
   244  // Arguments are handled in the manner of fmt.Println.
   245  func (s *stdLogger) Println(v ...interface{}) {
   246  	output := fmt.Sprintln(v...)
   247  	s.Output(2, output)
   248  }
   249  
   250  // Verbose is equivalent to Print, but only goes to the file log unless
   251  // SetVerbose(true) has been called.
   252  func (s *stdLogger) Verbose(v ...interface{}) {
   253  	output := fmt.Sprint(v...)
   254  	s.VerboseOutput(2, output)
   255  }
   256  
   257  // Verbosef is equivalent to Printf, but only goes to the file log unless
   258  // SetVerbose(true) has been called.
   259  func (s *stdLogger) Verbosef(format string, v ...interface{}) {
   260  	output := fmt.Sprintf(format, v...)
   261  	s.VerboseOutput(2, output)
   262  }
   263  
   264  // Verboseln is equivalent to Println, but only goes to the file log unless
   265  // SetVerbose(true) has been called.
   266  func (s *stdLogger) Verboseln(v ...interface{}) {
   267  	output := fmt.Sprintln(v...)
   268  	s.VerboseOutput(2, output)
   269  }
   270  
   271  // Fatal is equivalent to Print() followed by a call to panic() that
   272  // Cleanup will convert to a os.Exit(1).
   273  func (s *stdLogger) Fatal(v ...interface{}) {
   274  	output := fmt.Sprint(v...)
   275  	s.Output(2, output)
   276  	panic(fatalLog(errors.New(output)))
   277  }
   278  
   279  // Fatalf is equivalent to Printf() followed by a call to panic() that
   280  // Cleanup will convert to a os.Exit(1).
   281  func (s *stdLogger) Fatalf(format string, v ...interface{}) {
   282  	output := fmt.Sprintf(format, v...)
   283  	s.Output(2, output)
   284  	panic(fatalLog(errors.New(output)))
   285  }
   286  
   287  // Fatalln is equivalent to Println() followed by a call to panic() that
   288  // Cleanup will convert to a os.Exit(1).
   289  func (s *stdLogger) Fatalln(v ...interface{}) {
   290  	output := fmt.Sprintln(v...)
   291  	s.Output(2, output)
   292  	panic(fatalLog(errors.New(output)))
   293  }
   294  
   295  // Panic is equivalent to Print() followed by a call to panic().
   296  func (s *stdLogger) Panic(v ...interface{}) {
   297  	output := fmt.Sprint(v...)
   298  	s.Output(2, output)
   299  	panic(output)
   300  }
   301  
   302  // Panicf is equivalent to Printf() followed by a call to panic().
   303  func (s *stdLogger) Panicf(format string, v ...interface{}) {
   304  	output := fmt.Sprintf(format, v...)
   305  	s.Output(2, output)
   306  	panic(output)
   307  }
   308  
   309  // Panicln is equivalent to Println() followed by a call to panic().
   310  func (s *stdLogger) Panicln(v ...interface{}) {
   311  	output := fmt.Sprintln(v...)
   312  	s.Output(2, output)
   313  	panic(output)
   314  }