forked from rclone/rclone
-
Notifications
You must be signed in to change notification settings - Fork 3
/
log.go
executable file
·142 lines (130 loc) · 3.42 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
// Package log provides logging for rclone
package log
import (
"io"
"log"
"os"
"reflect"
"runtime"
"strings"
"github.com/artpar/rclone/fs"
"github.com/sirupsen/logrus"
)
// Options contains options for the remote control server
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, eg KERN,USER,...
}
// 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.Config.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.Config.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, ",longfile,") {
flags |= log.Llongfile
}
if strings.Contains(flagsStr, ",shortfile,") {
flags |= log.Lshortfile
}
if strings.Contains(flagsStr, ",UTC,") {
flags |= log.LUTC
}
log.SetFlags(flags)
// 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.Printf("Failed to open log file: %v", err)
return
}
_, 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.Printf("Can't use --syslog and --log-file together")
return
}
startSysLog()
}
}
// Redirected returns true if the log has been redirected from stdout
func Redirected() bool {
return Opt.UseSyslog || Opt.File != ""
}