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