/
span.go
141 lines (119 loc) · 3.01 KB
/
span.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
package trace
import (
"fmt"
"time"
protogen "github.com/djienet/kratos/pkg/net/trace/proto"
)
const (
_maxChilds = 1024
_maxTags = 128
_maxLogs = 256
)
var _ Trace = &Span{}
// Span is a trace span.
type Span struct {
dapper *dapper
context spanContext
operationName string
startTime time.Time
duration time.Duration
tags []Tag
logs []*protogen.Log
childs int
}
func (s *Span) ServiceName() string {
return s.dapper.serviceName
}
func (s *Span) OperationName() string {
return s.operationName
}
func (s *Span) StartTime() time.Time {
return s.startTime
}
func (s *Span) Duration() time.Duration {
return s.duration
}
func (s *Span) TraceID() string {
return s.context.String()
}
func (s *Span) Context() spanContext {
return s.context
}
func (s *Span) Tags() []Tag {
return s.tags
}
func (s *Span) Logs() []*protogen.Log {
return s.logs
}
func (s *Span) Fork(serviceName, operationName string) Trace {
if s.childs > _maxChilds {
// if child span more than max childs set return noopspan
return noopspan{}
}
s.childs++
// 为了兼容临时为 New 的 Span 设置 span.kind
return s.dapper.newSpanWithContext(operationName, s.context).SetTag(TagString(TagSpanKind, "client"))
}
func (s *Span) Follow(serviceName, operationName string) Trace {
return s.Fork(serviceName, operationName).SetTag(TagString(TagSpanKind, "producer"))
}
func (s *Span) Finish(perr *error) {
s.duration = time.Since(s.startTime)
if perr != nil && *perr != nil {
err := *perr
s.SetTag(TagBool(TagError, true))
s.SetLog(Log(LogMessage, err.Error()))
if err, ok := err.(stackTracer); ok {
s.SetLog(Log(LogStack, fmt.Sprintf("%+v", err.StackTrace())))
}
}
s.dapper.report(s)
}
func (s *Span) SetTag(tags ...Tag) Trace {
if !s.context.isSampled() && !s.context.isDebug() {
return s
}
if len(s.tags) < _maxTags {
s.tags = append(s.tags, tags...)
}
if len(s.tags) == _maxTags {
s.tags = append(s.tags, Tag{Key: "trace.error", Value: "too many tags"})
}
return s
}
// LogFields is an efficient and type-checked way to record key:value
// NOTE current unsupport
func (s *Span) SetLog(logs ...LogField) Trace {
if !s.context.isSampled() && !s.context.isDebug() {
return s
}
if len(s.logs) < _maxLogs {
s.setLog(logs...)
}
if len(s.logs) == _maxLogs {
s.setLog(LogField{Key: "trace.error", Value: "too many logs"})
}
return s
}
func (s *Span) setLog(logs ...LogField) Trace {
protoLog := &protogen.Log{
Timestamp: time.Now().UnixNano(),
Fields: make([]*protogen.Field, len(logs)),
}
for i := range logs {
protoLog.Fields[i] = &protogen.Field{Key: logs[i].Key, Value: []byte(logs[i].Value)}
}
s.logs = append(s.logs, protoLog)
return s
}
// Visit visits the k-v pair in trace, calling fn for each.
func (s *Span) Visit(fn func(k, v string)) {
fn(KratosTraceID, s.context.String())
}
// SetTitle reset trace title
func (s *Span) SetTitle(operationName string) {
s.operationName = operationName
}
func (s *Span) String() string {
return s.context.String()
}