forked from DataDog/dd-trace-go
/
log.go
168 lines (146 loc) · 4.14 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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-2020 Datadog, Inc.
// Package log provides logging utilities for the tracer.
package log
import (
"fmt"
"log"
"os"
"strconv"
"sync"
"time"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
"gopkg.in/DataDog/dd-trace-go.v1/internal/version"
)
// Level specifies the logging level that the log package prints at.
type Level int
const (
// LevelDebug represents debug level messages.
LevelDebug Level = iota
// LevelWarn represents warning and errors.
LevelWarn
)
var prefixMsg = fmt.Sprintf("Datadog Tracer %s", version.Tag)
var (
mu sync.RWMutex // guards below fields
level = LevelWarn
logger ddtrace.Logger = &defaultLogger{l: log.New(os.Stderr, "", log.LstdFlags)}
)
// UseLogger sets l as the active logger.
func UseLogger(l ddtrace.Logger) {
mu.Lock()
defer mu.Unlock()
logger = l
}
// SetLevel sets the given lvl for logging.
func SetLevel(lvl Level) {
mu.Lock()
defer mu.Unlock()
level = lvl
}
// Debug prints the given message if the level is LevelDebug.
func Debug(fmt string, a ...interface{}) {
mu.RLock()
lvl := level
mu.RUnlock()
if lvl != LevelDebug {
return
}
printMsg("DEBUG", fmt, a...)
}
// Warn prints a warning message.
func Warn(fmt string, a ...interface{}) {
printMsg("WARN", fmt, a...)
}
var (
errmu sync.RWMutex // guards below fields
erragg = map[string]*errorReport{} // aggregated errors
errrate = time.Minute // the rate at which errors are reported
erron bool // true if errors are being aggregated
)
func init() {
if v := os.Getenv("DD_LOGGING_RATE"); v != "" {
if sec, err := strconv.ParseUint(v, 10, 64); err != nil {
Warn("Invalid value for DD_LOGGING_RATE: %v", err)
} else {
errrate = time.Duration(sec) * time.Second
}
}
}
type errorReport struct {
first time.Time // time when first error occurred
err error
count uint64
}
// Error reports an error. Errors get aggregated and logged periodically. The
// default is once per minute or once every DD_LOGGING_RATE number of seconds.
func Error(format string, a ...interface{}) {
key := format // format should 99.9% of the time be constant
if reachedLimit(key) {
// avoid too much lock contention on spammy errors
return
}
errmu.Lock()
defer errmu.Unlock()
report, ok := erragg[key]
if !ok {
erragg[key] = &errorReport{
err: fmt.Errorf(format, a...),
first: time.Now(),
}
report = erragg[key]
}
report.count++
if errrate == 0 {
flushLocked()
return
}
if !erron {
erron = true
time.AfterFunc(errrate, Flush)
}
}
// defaultErrorLimit specifies the maximum number of errors gathered in a report.
const defaultErrorLimit = 200
// reachedLimit reports whether the maximum count has been reached for this key.
func reachedLimit(key string) bool {
errmu.RLock()
e, ok := erragg[key]
confirm := ok && e.count > defaultErrorLimit
errmu.RUnlock()
return confirm
}
// Flush flushes and resets all aggregated errors to the logger.
func Flush() {
errmu.Lock()
defer errmu.Unlock()
flushLocked()
}
func flushLocked() {
for _, report := range erragg {
msg := fmt.Sprintf("%v", report.err)
if report.count > defaultErrorLimit {
msg += fmt.Sprintf(", %d+ additional messages skipped (first occurrence: %s)", defaultErrorLimit, report.first.Format(time.RFC822))
} else if report.count > 1 {
msg += fmt.Sprintf(", %d additional messages skipped (first occurrence: %s)", report.count-1, report.first.Format(time.RFC822))
} else {
msg += fmt.Sprintf(" (occurred: %s)", report.first.Format(time.RFC822))
}
printMsg("ERROR", msg)
}
for k := range erragg {
// compiler-optimized map-clearing post go1.11 (golang/go#20138)
delete(erragg, k)
}
erron = false
}
func printMsg(lvl, format string, a ...interface{}) {
msg := fmt.Sprintf("%s %s: %s", prefixMsg, lvl, fmt.Sprintf(format, a...))
mu.RLock()
logger.Log(msg)
mu.RUnlock()
}
type defaultLogger struct{ l *log.Logger }
func (p *defaultLogger) Log(msg string) { p.l.Print(msg) }