This repository has been archived by the owner on Oct 29, 2021. It is now read-only.
/
span_helper.go
217 lines (188 loc) · 5.88 KB
/
span_helper.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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
package spanhelper
import (
"context"
"encoding/json"
"fmt"
"runtime/debug"
"github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/log"
"github.com/sirupsen/logrus"
"github.com/networkservicemesh/networkservicemesh/pkg/tools/jaeger"
)
type spanHelperKeyType string
const (
spanHelperTraceDepth spanHelperKeyType = "spanHelperTraceDepth"
)
func withTraceDepth(parent context.Context, value int) context.Context {
return context.WithValue(parent, spanHelperTraceDepth, value)
}
func traceDepth(ctx context.Context) int {
if rv, ok := ctx.Value(spanHelperTraceDepth).(int); ok {
return rv
}
return 0
}
// LogFromSpan - return a logger that has a TraceHook to also log messages to the span
func LogFromSpan(span opentracing.Span) logrus.FieldLogger {
if span != nil {
logger := logrus.New().WithField("span", span)
logger.Logger.AddHook(NewTraceHook(span))
return logger
}
return logrus.New()
}
type traceHook struct {
index int
span opentracing.Span
}
// NewTraceHook - create a TraceHook for also logging to a span
func NewTraceHook(span opentracing.Span) logrus.Hook {
return &traceHook{
span: span,
}
}
func (h *traceHook) Levels() []logrus.Level {
return logrus.AllLevels
}
func (h *traceHook) Fire(entry *logrus.Entry) error {
msg, err := entry.String()
if err != nil {
return err
}
h.span.LogFields(log.String(fmt.Sprintf("log[%d]", h.index), msg))
h.index++
return nil
}
// SpanHelper - wrap span if specified to simplify workflow
type SpanHelper interface {
Finish()
Context() context.Context
Logger() logrus.FieldLogger
LogObject(attribute string, value interface{})
LogValue(attribute string, value interface{})
LogError(err error)
Span() opentracing.Span
}
type spanHelper struct {
operation string
span opentracing.Span
ctx context.Context
logger logrus.FieldLogger
}
func (s *spanHelper) Span() opentracing.Span {
return s.span
}
func (s *spanHelper) LogError(err error) {
if s.span != nil && err != nil {
otgrpc.SetSpanTags(s.span, err, false)
s.span.LogFields(log.String("event", "error"), log.String("message", fmt.Sprintf("%+v", err)), log.String("stacktrace", string(debug.Stack())))
logrus.Errorf(">><<%s %s=%v span=%v", getPrefix("--", traceDepth(s.ctx)), "error", fmt.Sprintf("%+v", err), s.span)
}
}
func (s *spanHelper) LogObject(attribute string, value interface{}) {
cc, err := json.Marshal(value)
msg := value
if err == nil {
msg = string(cc)
}
if s.span != nil {
s.span.LogFields(log.Object(attribute, msg), log.String("stacktrace", string(debug.Stack())))
}
logrus.Infof(">><<%s %s=%v span=%v", getPrefix("--", traceDepth(s.ctx)), attribute, msg, s.span)
}
func (s *spanHelper) LogValue(attribute string, value interface{}) {
if s.span != nil {
s.span.LogFields(log.Object(attribute, value), log.String("stacktrace", string(debug.Stack())))
}
logrus.Infof(">><<%s %s=%v span=%v", getPrefix("--", traceDepth(s.ctx)), attribute, value, s.span)
}
func (s *spanHelper) Finish() {
if s.span != nil {
s.span.Finish()
s.span = nil
}
}
func (s *spanHelper) Logger() logrus.FieldLogger {
if s.logger == nil {
s.logger = LogFromSpan(s.span)
if s.operation != "" {
s.logger = s.logger.WithField("operation", s.operation)
}
}
return s.logger
}
// NewSpanHelper - constructs a span helper from context/snap and opertaion name.
func NewSpanHelper(ctx context.Context, span opentracing.Span, operation string) SpanHelper {
return &spanHelper{
ctx: withTraceDepth(ctx, traceDepth(ctx)+1),
span: span,
operation: operation,
}
}
func (s *spanHelper) startSpan(operation string) (result SpanHelper) {
if s.ctx != nil && jaeger.IsOpentracingEnabled() {
newSpan, newCtx := opentracing.StartSpanFromContext(s.ctx, operation)
result = NewSpanHelper(newCtx, newSpan, operation)
} else {
result = NewSpanHelper(context.Background(), nil, operation)
}
result.Logger().Infof("===> %v()", operation)
return result
}
func (s *spanHelper) Context() context.Context {
return s.ctx
}
// FromContext - return span helper from context and if opentracing is enabled start new span
func FromContext(ctx context.Context, operation string) (result SpanHelper) {
if jaeger.IsOpentracingEnabled() {
newSpan, newCtx := opentracing.StartSpanFromContext(ctx, operation)
result = NewSpanHelper(newCtx, newSpan, operation)
} else {
// return just context
result = NewSpanHelper(ctx, nil, operation)
}
printStart(result, operation)
return result
}
func printStart(result SpanHelper, operation string) {
prefix := getPrefix("--", traceDepth(result.Context()))
logrus.Infof("==%s> %v() span:%v", prefix, operation, result.Span())
}
func getPrefix(c string, depth int) string {
prefix := ""
for i := 0; i < depth; i++ {
prefix += c
}
return prefix
}
// GetSpanHelper - construct a span helper object from current context span
func GetSpanHelper(ctx context.Context) SpanHelper {
if jaeger.IsOpentracingEnabled() {
span := opentracing.SpanFromContext(ctx)
return NewSpanHelper(ctx, span, "")
}
// return just context
return &spanHelper{
span: nil,
ctx: ctx,
}
}
//CopySpan - construct span helper object with ctx and copy span from spanContext
// Will start new operation on span
func CopySpan(ctx context.Context, spanContext SpanHelper, operation string) SpanHelper {
return WithSpan(ctx, spanContext.Span(), operation)
}
// WithSpan - construct span helper object with ctx and copy spanid from span
// Will start new operation on span
func WithSpan(ctx context.Context, span opentracing.Span, operation string) (result SpanHelper) {
if jaeger.IsOpentracingEnabled() && span != nil {
ctx = opentracing.ContextWithSpan(ctx, span)
newSpan, newCtx := opentracing.StartSpanFromContext(ctx, operation)
result = NewSpanHelper(newCtx, newSpan, operation)
} else {
result = NewSpanHelper(ctx, nil, operation)
}
printStart(result, operation)
return result
}