dnscrypt-proxy/vendor/github.com/jedisct1/dlog/dlog.go

269 lines
5.8 KiB
Go

package dlog
import (
"flag"
"fmt"
"os"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
)
type Severity int32
type globals struct {
sync.Mutex
logLevel Severity
useSyslog *bool
truncateLogFile *bool
appName string
syslogFacility string
systemLogger *systemLogger
fileName *string
outFd *os.File
lastMessage string
lastOccurrence time.Time
occurrences uint64
}
var _globals = globals{
logLevel: SeverityLast,
appName: "-",
lastMessage: "",
lastOccurrence: time.Now(),
occurrences: 0,
}
const (
SeverityDebug Severity = iota
SeverityInfo
SeverityNotice
SeverityWarning
SeverityError
SeverityCritical
SeverityFatal
SeverityLast
)
const (
floodDelay = 5 * time.Second
floodMinRepeats = 3
)
var SeverityName = []string{
SeverityDebug: "DEBUG",
SeverityInfo: "INFO",
SeverityNotice: "NOTICE",
SeverityWarning: "WARNING",
SeverityError: "ERROR",
SeverityCritical: "CRITICAL",
SeverityFatal: "FATAL",
}
func Debugf(format string, args ...interface{}) {
logf(SeverityDebug, format, args...)
}
func Infof(format string, args ...interface{}) {
logf(SeverityInfo, format, args...)
}
func Noticef(format string, args ...interface{}) {
logf(SeverityNotice, format, args...)
}
func Warnf(format string, args ...interface{}) {
logf(SeverityWarning, format, args...)
}
func Errorf(format string, args ...interface{}) {
logf(SeverityError, format, args...)
}
func Criticalf(format string, args ...interface{}) {
logf(SeverityCritical, format, args...)
}
func Fatalf(format string, args ...interface{}) {
logf(SeverityFatal, format, args...)
}
func Debug(message interface{}) {
log(SeverityDebug, message)
}
func Info(message interface{}) {
log(SeverityInfo, message)
}
func Notice(message interface{}) {
log(SeverityNotice, message)
}
func Warn(message interface{}) {
log(SeverityWarning, message)
}
func Error(message interface{}) {
log(SeverityError, message)
}
func Critical(message interface{}) {
log(SeverityCritical, message)
}
func Fatal(message interface{}) {
log(SeverityFatal, message)
}
func (s *Severity) get() Severity {
return Severity(atomic.LoadInt32((*int32)(s)))
}
func (s *Severity) set(val Severity) {
atomic.StoreInt32((*int32)(s), int32(val))
}
func (s *Severity) String() string {
return strconv.FormatInt(int64(*s), 10)
}
func (s *Severity) Get() interface{} {
return s.get()
}
func (s *Severity) Set(strVal string) error {
val, _ := strconv.Atoi(strVal)
s.set(Severity(val))
return nil
}
func Init(appName string, logLevel Severity, syslogFacility string) error {
_globals.logLevel.set(logLevel)
if len(syslogFacility) == 0 {
syslogFacility = "DAEMON"
}
_globals.appName = appName
_globals.syslogFacility = syslogFacility
_globals.useSyslog = flag.Bool("syslog", false, "Send application logs to the local system logger (Eventlog on Windows, syslog on Unix)")
_globals.fileName = flag.String("logfile", "", "Write application logs to file")
_globals.truncateLogFile = flag.Bool("logfile-truncate", false, "Truncate the application log file; keep only data from the most recent application launch")
flag.Var(&_globals.logLevel, "loglevel", fmt.Sprintf("Application log level (%d-%d)", SeverityDebug, SeverityFatal))
return nil
}
func LogLevel() Severity {
_globals.Lock()
logLevel := _globals.logLevel.get()
_globals.Unlock()
return logLevel
}
func SetLogLevel(logLevel Severity) {
_globals.Lock()
_globals.logLevel.set(logLevel)
_globals.Unlock()
}
func UseSyslog(value bool) {
_globals.Lock()
_globals.useSyslog = &value
_globals.Unlock()
}
func TruncateLogFile(value bool) {
_globals.Lock()
_globals.truncateLogFile = &value
_globals.Unlock()
}
func UseLogFile(fileName string) {
_globals.Lock()
_globals.fileName = &fileName
_globals.Unlock()
}
func GetFileDescriptor() *os.File {
_globals.Lock()
createFileDescriptor()
_globals.Unlock()
return _globals.outFd
}
func SetFileDescriptor(fd *os.File) {
_globals.Lock()
_globals.outFd = fd
_globals.Unlock()
}
func createFileDescriptor() {
if _globals.fileName != nil && len(*_globals.fileName) > 0 && _globals.outFd == nil {
mode := os.O_WRONLY | os.O_CREATE
if _globals.truncateLogFile != nil && *_globals.truncateLogFile {
mode |= os.O_TRUNC
} else {
mode |= os.O_APPEND
}
outFd, err := os.OpenFile(*_globals.fileName, mode, 0o644)
if err == nil {
_globals.outFd = outFd
}
}
}
func logf(severity Severity, format string, args ...interface{}) {
if severity < _globals.logLevel.get() {
return
}
now := time.Now().Local()
year, month, day := now.Date()
hour, minute, second := now.Clock()
message := fmt.Sprintf(format, args...)
message = strings.TrimSpace(strings.TrimSuffix(message, "\n"))
if len(message) <= 0 {
return
}
_globals.Lock()
defer _globals.Unlock()
if _globals.lastMessage == message {
if time.Since(_globals.lastOccurrence) < floodDelay {
_globals.occurrences++
if _globals.occurrences > floodMinRepeats {
return
}
}
} else {
_globals.occurrences = 0
_globals.lastMessage = message
}
_globals.lastOccurrence = now
if *_globals.useSyslog && _globals.systemLogger == nil {
systemLogger, err := newSystemLogger(_globals.appName, _globals.syslogFacility)
if err == nil {
_globals.systemLogger = systemLogger
}
}
createFileDescriptor()
if _globals.systemLogger != nil {
(*_globals.systemLogger).writeString(severity, message)
} else {
line := fmt.Sprintf("[%d-%02d-%02d %02d:%02d:%02d] [%s] %s\n", year, int(month), day, hour, minute, second, SeverityName[severity], message)
if _globals.outFd != nil {
_globals.outFd.WriteString(line)
_globals.outFd.Sync()
} else {
os.Stderr.WriteString(line)
}
}
if severity >= SeverityFatal {
os.Exit(255)
}
}
func log(severity Severity, args interface{}) {
logf(severity, "%v", args)
}