forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
diagnostic.go
180 lines (162 loc) · 5.42 KB
/
diagnostic.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
169
170
171
172
173
174
175
176
177
178
179
180
package types
import (
"fmt"
"runtime"
"strings"
"sync"
"github.com/golang/glog"
"github.com/openshift/origin/pkg/diagnostics/log"
)
// Diagnostic provides the interface for building diagnostics that can execute as part of the diagnostic framework.
// The Name and Description methods are used to identify which diagnostic is running in the output.
// The CanRun() method provides a pre-execution check for whether the diagnostic is relevant and runnable as constructed.
// If not, a user-facing reason for skipping the diagnostic can be given.
// Finally, the Check() method runs the diagnostic with the resulting messages and errors returned in a result object.
// It should be assumed a Diagnostic can run in parallel with other Diagnostics.
type Diagnostic interface {
Name() string
Description() string
CanRun() (canRun bool, reason error)
Check() DiagnosticResult
}
// DiagnosticResult provides a result object for diagnostics, accumulating the messages and errors
// that the diagnostic generates as it runs.
type DiagnosticResult interface {
// Failure is true if there are any errors entered.
Failure() bool
// Logs/Warnings/Errors entered into the result object
Logs() []log.Entry
Warnings() []DiagnosticError
Errors() []DiagnosticError
// <Level> just takes a plain string, no formatting
// <Level>f provides format string params
// <Level>t interface{} should be a log.Hash for a template
// Error and Warning add an entry to both logs and corresponding list of errors/warnings.
Error(id string, err error, text string)
Warn(id string, err error, text string)
Info(id string, text string)
Debug(id string, text string)
}
type diagnosticResultImpl struct {
lock sync.Mutex
failure bool
origin string // origin of the results, usually the diagnostic name; included in log Entries
logs []log.Entry
warnings []DiagnosticError
errors []DiagnosticError
}
// NewDiagnosticResult generates an internally-implemented DiagnosticResult.
// The origin may be output with some log messages to help identify where in code it originated.
func NewDiagnosticResult(origin string) DiagnosticResult {
return &diagnosticResultImpl{
origin: origin,
errors: []DiagnosticError{},
warnings: []DiagnosticError{},
logs: []log.Entry{},
}
}
func (r *diagnosticResultImpl) appendLogs(stackDepth int, entry ...log.Entry) {
if r.logs == nil {
r.logs = make([]log.Entry, 0)
}
r.logs = append(r.logs, entry...)
// glog immediately for debugging when a diagnostic silently chokes
for _, entry := range entry {
if glog.V(glog.Level(6 - entry.Level.Level)) {
glog.InfoDepth(stackDepth, entry.Message)
}
}
}
func (r *diagnosticResultImpl) Failure() bool {
r.lock.Lock()
defer r.lock.Unlock()
return r.failure
}
func (r *diagnosticResultImpl) Logs() []log.Entry {
r.lock.Lock()
defer r.lock.Unlock()
if r.logs == nil {
return make([]log.Entry, 0)
}
return r.logs
}
func (r *diagnosticResultImpl) appendWarnings(warn ...DiagnosticError) {
if r.warnings == nil {
r.warnings = make([]DiagnosticError, 0)
}
r.warnings = append(r.warnings, warn...)
}
func (r *diagnosticResultImpl) Warnings() []DiagnosticError {
r.lock.Lock()
defer r.lock.Unlock()
if r.warnings == nil {
return make([]DiagnosticError, 0)
}
return r.warnings
}
func (r *diagnosticResultImpl) appendErrors(err ...DiagnosticError) {
if r.errors == nil {
r.errors = make([]DiagnosticError, 0)
}
r.failure = true
r.errors = append(r.errors, err...)
}
func (r *diagnosticResultImpl) Errors() []DiagnosticError {
r.lock.Lock()
defer r.lock.Unlock()
if r.errors == nil {
return make([]DiagnosticError, 0)
}
return r.errors
}
// basic ingress functions (private)
func (r *diagnosticResultImpl) caller(depth int) string {
if _, file, line, ok := runtime.Caller(depth + 1); ok {
paths := strings.SplitAfter(file, "github.com/")
return fmt.Sprintf("diagnostic %s@%s:%d", r.origin, paths[len(paths)-1], line)
}
return "diagnostic " + r.origin
}
func (r *diagnosticResultImpl) logError(id string, err error, msg string) {
r.appendLogs(2, log.Entry{ID: id, Origin: r.caller(2), Level: log.ErrorLevel, Message: msg})
if de, ok := err.(DiagnosticError); ok {
r.appendErrors(de)
} else {
r.appendErrors(DiagnosticError{id, msg, err})
}
}
func (r *diagnosticResultImpl) logWarning(id string, err error, msg string) {
r.appendLogs(2, log.Entry{ID: id, Origin: r.caller(2), Level: log.WarnLevel, Message: msg})
if de, ok := err.(DiagnosticError); ok {
r.appendWarnings(de)
} else {
r.appendWarnings(DiagnosticError{id, msg, err})
}
}
func (r *diagnosticResultImpl) logMessage(id string, level log.Level, msg string) {
r.appendLogs(2, log.Entry{ID: id, Origin: r.caller(2), Level: level, Message: msg})
}
// Public ingress functions
// Errors are recorded in the result as errors plus log entries
func (r *diagnosticResultImpl) Error(id string, err error, text string) {
r.lock.Lock()
defer r.lock.Unlock()
r.logError(id, err, text)
}
// Warnings are recorded in the result as warnings plus log entries
func (r *diagnosticResultImpl) Warn(id string, err error, text string) {
r.lock.Lock()
defer r.lock.Unlock()
r.logWarning(id, err, text)
}
// Info/Debug are just recorded as log entries.
func (r *diagnosticResultImpl) Info(id string, text string) {
r.lock.Lock()
defer r.lock.Unlock()
r.logMessage(id, log.InfoLevel, text)
}
func (r *diagnosticResultImpl) Debug(id string, text string) {
r.lock.Lock()
defer r.lock.Unlock()
r.logMessage(id, log.DebugLevel, text)
}