1package utils
2
3import (
4	"fmt"
5	"log"
6	"os"
7	"strings"
8	"time"
9)
10
11// LogLevel of quic-go
12type LogLevel uint8
13
14const (
15	// LogLevelNothing disables
16	LogLevelNothing LogLevel = iota
17	// LogLevelError enables err logs
18	LogLevelError
19	// LogLevelInfo enables info logs (e.g. packets)
20	LogLevelInfo
21	// LogLevelDebug enables debug logs (e.g. packet contents)
22	LogLevelDebug
23)
24
25const logEnv = "QUIC_GO_LOG_LEVEL"
26
27// A Logger logs.
28type Logger interface {
29	SetLogLevel(LogLevel)
30	SetLogTimeFormat(format string)
31	WithPrefix(prefix string) Logger
32	Debug() bool
33
34	Errorf(format string, args ...interface{})
35	Infof(format string, args ...interface{})
36	Debugf(format string, args ...interface{})
37}
38
39// DefaultLogger is used by quic-go for logging.
40var DefaultLogger Logger
41
42type defaultLogger struct {
43	prefix string
44
45	logLevel   LogLevel
46	timeFormat string
47}
48
49var _ Logger = &defaultLogger{}
50
51// SetLogLevel sets the log level
52func (l *defaultLogger) SetLogLevel(level LogLevel) {
53	l.logLevel = level
54}
55
56// SetLogTimeFormat sets the format of the timestamp
57// an empty string disables the logging of timestamps
58func (l *defaultLogger) SetLogTimeFormat(format string) {
59	log.SetFlags(0) // disable timestamp logging done by the log package
60	l.timeFormat = format
61}
62
63// Debugf logs something
64func (l *defaultLogger) Debugf(format string, args ...interface{}) {
65	if l.logLevel == LogLevelDebug {
66		l.logMessage(format, args...)
67	}
68}
69
70// Infof logs something
71func (l *defaultLogger) Infof(format string, args ...interface{}) {
72	if l.logLevel >= LogLevelInfo {
73		l.logMessage(format, args...)
74	}
75}
76
77// Errorf logs something
78func (l *defaultLogger) Errorf(format string, args ...interface{}) {
79	if l.logLevel >= LogLevelError {
80		l.logMessage(format, args...)
81	}
82}
83
84func (l *defaultLogger) logMessage(format string, args ...interface{}) {
85	var pre string
86
87	if len(l.timeFormat) > 0 {
88		pre = time.Now().Format(l.timeFormat) + " "
89	}
90	if len(l.prefix) > 0 {
91		pre += l.prefix + " "
92	}
93	log.Printf(pre+format, args...)
94}
95
96func (l *defaultLogger) WithPrefix(prefix string) Logger {
97	if len(l.prefix) > 0 {
98		prefix = l.prefix + " " + prefix
99	}
100	return &defaultLogger{
101		logLevel:   l.logLevel,
102		timeFormat: l.timeFormat,
103		prefix:     prefix,
104	}
105}
106
107// Debug returns true if the log level is LogLevelDebug
108func (l *defaultLogger) Debug() bool {
109	return l.logLevel == LogLevelDebug
110}
111
112func init() {
113	DefaultLogger = &defaultLogger{}
114	DefaultLogger.SetLogLevel(readLoggingEnv())
115}
116
117func readLoggingEnv() LogLevel {
118	switch strings.ToLower(os.Getenv(logEnv)) {
119	case "":
120		return LogLevelNothing
121	case "debug":
122		return LogLevelDebug
123	case "info":
124		return LogLevelInfo
125	case "error":
126		return LogLevelError
127	default:
128		fmt.Fprintln(os.Stderr, "invalid quic-go log level, see https://github.com/lucas-clemente/quic-go/wiki/Logging")
129		return LogLevelNothing
130	}
131}
132