mirror of
https://github.com/gilbertchen/duplicacy
synced 2025-12-06 00:03:38 +00:00
* Restore/check should report an error instead of a success at the end if there were any errors and -persist is specified * Don't compute the file hash before passing the file to the chunk maker; this is redundant as the chunk maker will produce the file hash * Add a LOG_WERROR function to switch between LOG_WARN and LOG_ERROR dynamically
239 lines
4.6 KiB
Go
239 lines
4.6 KiB
Go
// Copyright (c) Acrosync LLC. All rights reserved.
|
|
// Free for personal use and commercial trial
|
|
// Commercial use requires per-user licenses available from https://duplicacy.com
|
|
|
|
package duplicacy
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"log"
|
|
"runtime/debug"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
"regexp"
|
|
)
|
|
|
|
const (
|
|
DEBUG = -2
|
|
TRACE = -1
|
|
INFO = 0
|
|
WARN = 1
|
|
ERROR = 2
|
|
FATAL = 3
|
|
ASSERT = 4
|
|
)
|
|
|
|
var LogFunction func(level int, logID string, message string)
|
|
|
|
var printLogHeader = false
|
|
|
|
func EnableLogHeader() {
|
|
printLogHeader = true
|
|
}
|
|
|
|
var printStackTrace = false
|
|
|
|
func EnableStackTrace() {
|
|
printStackTrace = true
|
|
}
|
|
|
|
var testingT *testing.T
|
|
|
|
func setTestingT(t *testing.T) {
|
|
testingT = t
|
|
}
|
|
|
|
// Contains the ids of logs that won't be displayed
|
|
var suppressedLogs map[string]bool = map[string]bool{}
|
|
|
|
func SuppressLog(id string) {
|
|
suppressedLogs[id] = true
|
|
}
|
|
|
|
func getLevelName(level int) string {
|
|
switch level {
|
|
case DEBUG:
|
|
return "DEBUG"
|
|
case TRACE:
|
|
return "TRACE"
|
|
case INFO:
|
|
return "INFO"
|
|
case WARN:
|
|
return "WARN"
|
|
case ERROR:
|
|
return "ERROR"
|
|
case FATAL:
|
|
return "FATAL"
|
|
case ASSERT:
|
|
return "ASSERT"
|
|
default:
|
|
return fmt.Sprintf("[%d]", level)
|
|
}
|
|
}
|
|
|
|
var loggingLevel int
|
|
|
|
func IsDebugging() bool {
|
|
return loggingLevel <= DEBUG
|
|
}
|
|
|
|
func IsTracing() bool {
|
|
return loggingLevel <= TRACE
|
|
}
|
|
|
|
func SetLoggingLevel(level int) {
|
|
loggingLevel = level
|
|
}
|
|
|
|
func LOG_DEBUG(logID string, format string, v ...interface{}) {
|
|
logf(DEBUG, logID, format, v...)
|
|
}
|
|
|
|
func LOG_TRACE(logID string, format string, v ...interface{}) {
|
|
logf(TRACE, logID, format, v...)
|
|
}
|
|
|
|
func LOG_INFO(logID string, format string, v ...interface{}) {
|
|
logf(INFO, logID, format, v...)
|
|
}
|
|
|
|
func LOG_WARN(logID string, format string, v ...interface{}) {
|
|
logf(WARN, logID, format, v...)
|
|
}
|
|
|
|
func LOG_ERROR(logID string, format string, v ...interface{}) {
|
|
logf(ERROR, logID, format, v...)
|
|
}
|
|
|
|
func LOG_WERROR(isWarning bool, logID string, format string, v ...interface{}) {
|
|
if isWarning {
|
|
logf(WARN, logID, format, v...)
|
|
} else {
|
|
logf(ERROR, logID, format, v...)
|
|
}
|
|
}
|
|
|
|
|
|
func LOG_FATAL(logID string, format string, v ...interface{}) {
|
|
logf(FATAL, logID, format, v...)
|
|
}
|
|
|
|
func LOG_ASSERT(logID string, format string, v ...interface{}) {
|
|
logf(ASSERT, logID, format, v...)
|
|
}
|
|
|
|
type Exception struct {
|
|
Level int
|
|
LogID string
|
|
Message string
|
|
}
|
|
|
|
var logMutex sync.Mutex
|
|
|
|
func logf(level int, logID string, format string, v ...interface{}) {
|
|
|
|
message := fmt.Sprintf(format, v...)
|
|
|
|
if LogFunction != nil {
|
|
LogFunction(level, logID, message)
|
|
return
|
|
}
|
|
|
|
now := time.Now()
|
|
|
|
// Uncomment this line to enable unbufferred logging for tests
|
|
// fmt.Printf("%s %s %s %s\n", now.Format("2006-01-02 15:04:05.000"), getLevelName(level), logID, message)
|
|
|
|
if testingT != nil {
|
|
if level <= WARN {
|
|
if level >= loggingLevel {
|
|
testingT.Logf("%s %s %s %s\n",
|
|
now.Format("2006-01-02 15:04:05.000"), getLevelName(level), logID, message)
|
|
}
|
|
} else {
|
|
testingT.Errorf("%s %s %s %s\n",
|
|
now.Format("2006-01-02 15:04:05.000"), getLevelName(level), logID, message)
|
|
}
|
|
} else {
|
|
logMutex.Lock()
|
|
defer logMutex.Unlock()
|
|
|
|
if level >= loggingLevel {
|
|
if level <= ERROR && len(suppressedLogs) > 0 {
|
|
if _, found := suppressedLogs[logID]; found {
|
|
return
|
|
}
|
|
}
|
|
|
|
if printLogHeader {
|
|
fmt.Printf("%s %s %s %s\n",
|
|
now.Format("2006-01-02 15:04:05.000"), getLevelName(level), logID, message)
|
|
} else {
|
|
fmt.Printf("%s\n", message)
|
|
}
|
|
}
|
|
}
|
|
|
|
if level > WARN {
|
|
panic(Exception{
|
|
Level: level,
|
|
LogID: logID,
|
|
Message: message,
|
|
})
|
|
}
|
|
}
|
|
|
|
// Set up logging for libraries that Duplicacy depends on. They can call 'log.Printf("[ID] message")'
|
|
// to produce logs in Duplicacy's format
|
|
type Logger struct {
|
|
formatRegex *regexp.Regexp
|
|
}
|
|
|
|
func (logger *Logger) Write(line []byte) (n int, err error) {
|
|
n = len(line)
|
|
for len(line) > 0 && line[len(line) - 1] == '\n' {
|
|
line = line[:len(line) - 1]
|
|
}
|
|
matched := logger.formatRegex.FindStringSubmatch(string(line))
|
|
if matched != nil {
|
|
LOG_INFO(matched[1], "%s", matched[2])
|
|
} else {
|
|
LOG_INFO("LOG_DEFAULT", "%s", line)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func init() {
|
|
log.SetFlags(0)
|
|
log.SetOutput(&Logger{ formatRegex: regexp.MustCompile(`^\[(.+)\]\s*(.+)`) })
|
|
}
|
|
|
|
const (
|
|
duplicacyExitCode = 100
|
|
otherExitCode = 101
|
|
)
|
|
|
|
// This is the function to be called before exiting when an error occurs.
|
|
var RunAtError func() = func() {}
|
|
|
|
func CatchLogException() {
|
|
if r := recover(); r != nil {
|
|
switch e := r.(type) {
|
|
case Exception:
|
|
if printStackTrace {
|
|
debug.PrintStack()
|
|
}
|
|
RunAtError()
|
|
os.Exit(duplicacyExitCode)
|
|
default:
|
|
fmt.Fprintf(os.Stderr, "%v\n", e)
|
|
debug.PrintStack()
|
|
RunAtError()
|
|
os.Exit(otherExitCode)
|
|
}
|
|
}
|
|
}
|