github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/fs/log/log.go (about)

     1  // Package log provides logging for rclone
     2  package log
     3  
     4  import (
     5  	"io"
     6  	"log"
     7  	"os"
     8  	"reflect"
     9  	"runtime"
    10  	"strings"
    11  
    12  	"github.com/ncw/rclone/fs"
    13  	"github.com/ncw/rclone/fs/config/flags"
    14  )
    15  
    16  // Flags
    17  var (
    18  	logFile        = flags.StringP("log-file", "", "", "Log everything to this file")
    19  	logFormat      = flags.StringP("log-format", "", "date,time", "Comma separated list of log format options")
    20  	useSyslog      = flags.BoolP("syslog", "", false, "Use Syslog for logging")
    21  	syslogFacility = flags.StringP("syslog-facility", "", "DAEMON", "Facility for syslog, eg KERN,USER,...")
    22  )
    23  
    24  // fnName returns the name of the calling +2 function
    25  func fnName() string {
    26  	pc, _, _, ok := runtime.Caller(2)
    27  	name := "*Unknown*"
    28  	if ok {
    29  		name = runtime.FuncForPC(pc).Name()
    30  		dot := strings.LastIndex(name, ".")
    31  		if dot >= 0 {
    32  			name = name[dot+1:]
    33  		}
    34  	}
    35  	return name
    36  }
    37  
    38  // Trace debugs the entry and exit of the calling function
    39  //
    40  // It is designed to be used in a defer statement so it returns a
    41  // function that logs the exit parameters.
    42  //
    43  // Any pointers in the exit function will be dereferenced
    44  func Trace(o interface{}, format string, a ...interface{}) func(string, ...interface{}) {
    45  	if fs.Config.LogLevel < fs.LogLevelDebug {
    46  		return func(format string, a ...interface{}) {}
    47  	}
    48  	name := fnName()
    49  	fs.LogPrintf(fs.LogLevelDebug, o, name+": "+format, a...)
    50  	return func(format string, a ...interface{}) {
    51  		for i := range a {
    52  			// read the values of the pointed to items
    53  			typ := reflect.TypeOf(a[i])
    54  			if typ.Kind() == reflect.Ptr {
    55  				value := reflect.ValueOf(a[i])
    56  				if value.IsNil() {
    57  					a[i] = nil
    58  				} else {
    59  					pointedToValue := reflect.Indirect(value)
    60  					a[i] = pointedToValue.Interface()
    61  				}
    62  			}
    63  		}
    64  		fs.LogPrintf(fs.LogLevelDebug, o, ">"+name+": "+format, a...)
    65  	}
    66  }
    67  
    68  // InitLogging start the logging as per the command line flags
    69  func InitLogging() {
    70  	flagsStr := "," + *logFormat + ","
    71  	var flags int
    72  	if strings.Contains(flagsStr, ",date,") {
    73  		flags |= log.Ldate
    74  	}
    75  	if strings.Contains(flagsStr, ",time,") {
    76  		flags |= log.Ltime
    77  	}
    78  	if strings.Contains(flagsStr, ",microseconds,") {
    79  		flags |= log.Lmicroseconds
    80  	}
    81  	if strings.Contains(flagsStr, ",longfile,") {
    82  		flags |= log.Llongfile
    83  	}
    84  	if strings.Contains(flagsStr, ",shortfile,") {
    85  		flags |= log.Lshortfile
    86  	}
    87  	if strings.Contains(flagsStr, ",UTC,") {
    88  		flags |= log.LUTC
    89  	}
    90  	log.SetFlags(flags)
    91  
    92  	// Log file output
    93  	if *logFile != "" {
    94  		f, err := os.OpenFile(*logFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640)
    95  		if err != nil {
    96  			log.Fatalf("Failed to open log file: %v", err)
    97  		}
    98  		_, err = f.Seek(0, io.SeekEnd)
    99  		if err != nil {
   100  			fs.Errorf(nil, "Failed to seek log file to end: %v", err)
   101  		}
   102  		log.SetOutput(f)
   103  		redirectStderr(f)
   104  	}
   105  
   106  	// Syslog output
   107  	if *useSyslog {
   108  		if *logFile != "" {
   109  			log.Fatalf("Can't use --syslog and --log-file together")
   110  		}
   111  		startSysLog()
   112  	}
   113  }
   114  
   115  // Redirected returns true if the log has been redirected from stdout
   116  func Redirected() bool {
   117  	return *useSyslog || *logFile != ""
   118  }