2019-07-26 00:54:09 +00:00
|
|
|
package log
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"runtime"
|
|
|
|
"strings"
|
|
|
|
|
2024-07-09 17:42:01 +00:00
|
|
|
"github.com/rclone/rclone/fs"
|
2019-07-26 00:54:09 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
2024-07-09 17:42:01 +00:00
|
|
|
var loggerInstalled = false
|
|
|
|
|
|
|
|
// InstallJSONLogger installs the JSON logger at the specified log level
|
|
|
|
func InstallJSONLogger(logLevel fs.LogLevel) {
|
|
|
|
if !loggerInstalled {
|
|
|
|
logrus.AddHook(NewCallerHook())
|
|
|
|
loggerInstalled = true
|
|
|
|
}
|
|
|
|
logrus.SetFormatter(&logrus.JSONFormatter{
|
|
|
|
TimestampFormat: "2006-01-02T15:04:05.999999-07:00",
|
|
|
|
})
|
|
|
|
logrus.SetLevel(logrus.DebugLevel)
|
|
|
|
switch logLevel {
|
|
|
|
case fs.LogLevelEmergency, fs.LogLevelAlert:
|
|
|
|
logrus.SetLevel(logrus.PanicLevel)
|
|
|
|
case fs.LogLevelCritical:
|
|
|
|
logrus.SetLevel(logrus.FatalLevel)
|
|
|
|
case fs.LogLevelError:
|
|
|
|
logrus.SetLevel(logrus.ErrorLevel)
|
|
|
|
case fs.LogLevelWarning, fs.LogLevelNotice:
|
|
|
|
logrus.SetLevel(logrus.WarnLevel)
|
|
|
|
case fs.LogLevelInfo:
|
|
|
|
logrus.SetLevel(logrus.InfoLevel)
|
|
|
|
case fs.LogLevelDebug:
|
|
|
|
logrus.SetLevel(logrus.DebugLevel)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// install hook in fs to call to avoid circular dependency
|
|
|
|
func init() {
|
|
|
|
fs.InstallJSONLogger = InstallJSONLogger
|
|
|
|
}
|
|
|
|
|
2019-07-26 00:54:09 +00:00
|
|
|
// CallerHook for log the calling file and line of the fine
|
|
|
|
type CallerHook struct {
|
|
|
|
Field string
|
|
|
|
Skip int
|
|
|
|
levels []logrus.Level
|
|
|
|
}
|
|
|
|
|
2020-05-20 10:39:20 +00:00
|
|
|
// NewCallerHook use to make a hook
|
2019-07-26 00:54:09 +00:00
|
|
|
func NewCallerHook(levels ...logrus.Level) logrus.Hook {
|
|
|
|
hook := CallerHook{
|
|
|
|
Field: "source",
|
|
|
|
Skip: 7,
|
|
|
|
levels: levels,
|
|
|
|
}
|
|
|
|
if len(hook.levels) == 0 {
|
|
|
|
hook.levels = logrus.AllLevels
|
|
|
|
}
|
|
|
|
return &hook
|
|
|
|
}
|
|
|
|
|
|
|
|
// Levels implement applied hook to which levels
|
|
|
|
func (h *CallerHook) Levels() []logrus.Level {
|
|
|
|
return logrus.AllLevels
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fire logs the information of context (filename and line)
|
|
|
|
func (h *CallerHook) Fire(entry *logrus.Entry) error {
|
|
|
|
entry.Data[h.Field] = findCaller(h.Skip)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-05-20 10:39:20 +00:00
|
|
|
// findCaller ignores the caller relevant to logrus or fslog then find out the exact caller
|
2019-07-26 00:54:09 +00:00
|
|
|
func findCaller(skip int) string {
|
|
|
|
file := ""
|
|
|
|
line := 0
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
file, line = getCaller(skip + i)
|
2022-06-08 20:25:17 +00:00
|
|
|
if !strings.HasPrefix(file, "logrus") && !strings.Contains(file, "log.go") {
|
2019-07-26 00:54:09 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%s:%d", file, line)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getCaller(skip int) (string, int) {
|
|
|
|
_, file, line, ok := runtime.Caller(skip)
|
|
|
|
// fmt.Println(file,":",line)
|
|
|
|
if !ok {
|
|
|
|
return "", 0
|
|
|
|
}
|
|
|
|
n := 0
|
|
|
|
for i := len(file) - 1; i > 0; i-- {
|
|
|
|
if file[i] == '/' {
|
|
|
|
n++
|
|
|
|
if n >= 2 {
|
|
|
|
file = file[i+1:]
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return file, line
|
|
|
|
}
|