-
Notifications
You must be signed in to change notification settings - Fork 49
/
logging.go
153 lines (124 loc) · 3.43 KB
/
logging.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
package gateway
import (
"fmt"
"strings"
"github.com/sirupsen/logrus"
"github.com/vektah/gqlparser/ast"
)
// Logger handles the logging in the gateway library
type Logger struct {
fields logrus.Fields
}
// LoggerFields is a wrapper over a map of key,value pairs to associate with the log
type LoggerFields map[string]interface{}
// Debug should be used for any logging that would be useful for debugging
func (l *Logger) Debug(args ...interface{}) {
entry := newLogEntry()
// if there are fields
if l.fields != nil {
entry = entry.WithFields(l.fields)
}
// finally log
entry.Debug(args...)
}
// Info should be used for any logging that doesn't necessarily need attention but is nice to see by default
func (l *Logger) Info(args ...interface{}) {
entry := newLogEntry()
// if there are fields
if l.fields != nil {
entry = entry.WithFields(l.fields)
}
// finally log
entry.Info(args...)
}
// Warn should be used for logging that needs attention
func (l *Logger) Warn(args ...interface{}) {
entry := newLogEntry()
// if there are fields
if l.fields != nil {
entry = entry.WithFields(l.fields)
}
// finally log
entry.Warn(args...)
}
// WithFields adds the provided fields to the Log
func (l *Logger) WithFields(fields LoggerFields) *Logger {
// build up the logrus fields
logrusFields := logrus.Fields{}
for key, value := range fields {
logrusFields[key] = value
}
return &Logger{fields: logrusFields}
}
// QueryPlanStep formats and logs a query plan step for human consumption
func (l *Logger) QueryPlanStep(step *QueryPlanStep) {
log.WithFields(LoggerFields{
"id": step.ParentID,
"insertion point": step.InsertionPoint,
}).Info(step.ParentType)
log.Info(l.FormatSelectionSet(step.SelectionSet))
}
func (l *Logger) indentPrefix(level int) string {
acc := "\n"
// build up the prefix
for i := 0; i <= level; i++ {
acc += " "
}
return acc
}
func (l *Logger) selectionSelectionSet(level int, selectionSet ast.SelectionSet) string {
acc := " {"
// and any sub selection
acc += l.selection(level+1, selectionSet)
acc += l.indentPrefix(level) + "}"
return acc
}
func (l *Logger) selection(level int, selectionSet ast.SelectionSet) string {
acc := ""
for _, selection := range selectionSet {
acc += l.indentPrefix(level)
switch selection := selection.(type) {
case *ast.Field:
// add the field name
acc += selection.Name
if len(selection.SelectionSet) > 0 {
acc += l.selectionSelectionSet(level, selection.SelectionSet)
}
case *ast.InlineFragment:
// print the fragment name
acc += fmt.Sprintf("... on %v", selection.TypeCondition) +
l.selectionSelectionSet(level, selection.SelectionSet)
case *ast.FragmentSpread:
// print the fragment name
acc += "..." + selection.Name
}
}
return acc
}
// FormatSelectionSet returns a pretty printed version of a selection set
func (l *Logger) FormatSelectionSet(selection ast.SelectionSet) string {
acc := "{"
insides := l.selection(0, selection)
if strings.TrimSpace(insides) != "" {
acc += insides + "\n}"
} else {
acc += "}"
}
return acc
}
var log *Logger
func newLogEntry() *logrus.Entry {
entry := logrus.New()
// only log the warning severity or above.
entry.SetLevel(logrus.WarnLevel)
// configure the formatter
entry.SetFormatter(&logrus.TextFormatter{
DisableTimestamp: true,
ForceColors: true,
DisableLevelTruncation: true,
})
return logrus.NewEntry(entry)
}
func init() {
log = &Logger{}
}