github.com/divyam234/rclone@v1.64.1/fs/log/log.go (about)

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