github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/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/rclone/rclone/fs"
    13  )
    14  
    15  // Options contains options for the remote control server
    16  type Options struct {
    17  	File           string // Log everything to this file
    18  	Format         string // Comma separated list of log format options
    19  	UseSyslog      bool   // Use Syslog for logging
    20  	SyslogFacility string // Facility for syslog, eg KERN,USER,...
    21  }
    22  
    23  // DefaultOpt is the default values used for Opt
    24  var DefaultOpt = Options{
    25  	Format:         "date,time",
    26  	SyslogFacility: "DAEMON",
    27  }
    28  
    29  // Opt is the options for the logger
    30  var Opt = DefaultOpt
    31  
    32  // fnName returns the name of the calling +2 function
    33  func fnName() string {
    34  	pc, _, _, ok := runtime.Caller(2)
    35  	name := "*Unknown*"
    36  	if ok {
    37  		name = runtime.FuncForPC(pc).Name()
    38  		dot := strings.LastIndex(name, ".")
    39  		if dot >= 0 {
    40  			name = name[dot+1:]
    41  		}
    42  	}
    43  	return name
    44  }
    45  
    46  // Trace debugs the entry and exit of the calling function
    47  //
    48  // It is designed to be used in a defer statement so it returns a
    49  // function that logs the exit parameters.
    50  //
    51  // Any pointers in the exit function will be dereferenced
    52  func Trace(o interface{}, format string, a ...interface{}) func(string, ...interface{}) {
    53  	if fs.Config.LogLevel < fs.LogLevelDebug {
    54  		return func(format string, a ...interface{}) {}
    55  	}
    56  	name := fnName()
    57  	fs.LogPrintf(fs.LogLevelDebug, o, name+": "+format, a...)
    58  	return func(format string, a ...interface{}) {
    59  		for i := range a {
    60  			// read the values of the pointed to items
    61  			typ := reflect.TypeOf(a[i])
    62  			if typ.Kind() == reflect.Ptr {
    63  				value := reflect.ValueOf(a[i])
    64  				if value.IsNil() {
    65  					a[i] = nil
    66  				} else {
    67  					pointedToValue := reflect.Indirect(value)
    68  					a[i] = pointedToValue.Interface()
    69  				}
    70  			}
    71  		}
    72  		fs.LogPrintf(fs.LogLevelDebug, o, ">"+name+": "+format, a...)
    73  	}
    74  }
    75  
    76  // Stack logs a stack trace of callers with the o and info passed in
    77  func Stack(o interface{}, info string) {
    78  	if fs.Config.LogLevel < fs.LogLevelDebug {
    79  		return
    80  	}
    81  	arr := [16 * 1024]byte{}
    82  	buf := arr[:]
    83  	n := runtime.Stack(buf, false)
    84  	buf = buf[:n]
    85  	fs.LogPrintf(fs.LogLevelDebug, o, "%s\nStack trace:\n%s", info, buf)
    86  }
    87  
    88  // InitLogging start the logging as per the command line flags
    89  func InitLogging() {
    90  	flagsStr := "," + Opt.Format + ","
    91  	var flags int
    92  	if strings.Contains(flagsStr, ",date,") {
    93  		flags |= log.Ldate
    94  	}
    95  	if strings.Contains(flagsStr, ",time,") {
    96  		flags |= log.Ltime
    97  	}
    98  	if strings.Contains(flagsStr, ",microseconds,") {
    99  		flags |= log.Lmicroseconds
   100  	}
   101  	if strings.Contains(flagsStr, ",longfile,") {
   102  		flags |= log.Llongfile
   103  	}
   104  	if strings.Contains(flagsStr, ",shortfile,") {
   105  		flags |= log.Lshortfile
   106  	}
   107  	if strings.Contains(flagsStr, ",UTC,") {
   108  		flags |= log.LUTC
   109  	}
   110  	log.SetFlags(flags)
   111  
   112  	// Log file output
   113  	if Opt.File != "" {
   114  		f, err := os.OpenFile(Opt.File, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640)
   115  		if err != nil {
   116  			log.Fatalf("Failed to open log file: %v", err)
   117  		}
   118  		_, err = f.Seek(0, io.SeekEnd)
   119  		if err != nil {
   120  			fs.Errorf(nil, "Failed to seek log file to end: %v", err)
   121  		}
   122  		log.SetOutput(f)
   123  		redirectStderr(f)
   124  	}
   125  
   126  	// Syslog output
   127  	if Opt.UseSyslog {
   128  		if Opt.File != "" {
   129  			log.Fatalf("Can't use --syslog and --log-file together")
   130  		}
   131  		startSysLog()
   132  	}
   133  }
   134  
   135  // Redirected returns true if the log has been redirected from stdout
   136  func Redirected() bool {
   137  	return Opt.UseSyslog || Opt.File != ""
   138  }