Skip to content

Commit 4c07be5

Browse files
Adrian Coleadriancole
Adrian Cole
authored andcommitted
Completes javascript port of v2 -> v1 span conversion
There are a number of edge cases that our Java code handles which the Javascript does not. This will complete the port as a part of migrating the UI to use v2 endpoints.
1 parent a40ebcb commit 4c07be5

File tree

4 files changed

+1032
-678
lines changed

4 files changed

+1032
-678
lines changed

zipkin-ui/js/spanConverter.js

Lines changed: 123 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,17 @@ function toV1Endpoint(endpoint) {
1818
}
1919

2020
function toV1Annotation(ann, endpoint) {
21-
return {
21+
const res = {
2222
value: ann.value,
2323
timestamp: ann.timestamp,
24-
endpoint
2524
};
25+
if (endpoint) {
26+
res.endpoint = endpoint;
27+
}
28+
return res;
2629
}
2730

28-
// Copied from https://github.com/openzipkin/zipkin-js/blob/8018e441d01804b02d0d217f10cd82759e71e02a/packages/zipkin/src/jsonEncoder.js#L25
29-
// Modified to correct assumption that 'annotations' always exist and ensure
30-
// that 'beginAnnotation' comes first timestamp/duration should always be copied over
31+
// ported from zipkin2.v1.V1SpanConverter
3132
function convertV1(span) {
3233
const res = {
3334
traceId: span.traceId,
@@ -37,65 +38,137 @@ function convertV1(span) {
3738
}
3839
res.id = span.id;
3940
res.name = span.name || ''; // undefined is not allowed in v1
41+
if (span.debug) {
42+
res.debug = true;
43+
}
44+
45+
// Don't report timestamp and duration on shared spans (should be server, but not necessarily)
4046
if (!span.shared) {
4147
if (span.timestamp) res.timestamp = span.timestamp;
4248
if (span.duration) res.duration = span.duration;
4349
}
4450

45-
const jsonEndpoint = toV1Endpoint(span.localEndpoint);
51+
let startTs = span.timestamp || 0;
52+
let endTs = startTs && span.duration ? startTs + span.duration : 0;
53+
let msTs = 0;
54+
let wsTs = 0;
55+
let wrTs = 0;
56+
let mrTs = 0;
57+
58+
let begin;
59+
let end;
60+
61+
let kind = span.kind;
4662

47-
let beginAnnotation;
48-
let endAnnotation;
49-
let addressKey;
50-
let local;
51-
switch (span.kind) {
63+
// scan annotations in case there are better timestamps, or inferred kind
64+
(span.annotations || []).forEach((a) => {
65+
switch (a.value) {
66+
case 'cs':
67+
kind = 'CLIENT';
68+
if (a.timestamp < startTs) startTs = a.timestamp;
69+
break;
70+
case 'sr':
71+
kind = 'SERVER';
72+
if (a.timestamp < startTs) startTs = a.timestamp;
73+
break;
74+
case 'ss':
75+
kind = 'SERVER';
76+
if (a.timestamp > endTs) endTs = a.timestamp;
77+
break;
78+
case 'cr':
79+
kind = 'CLIENT';
80+
if (a.timestamp > endTs) endTs = a.timestamp;
81+
break;
82+
case 'ms':
83+
kind = 'PRODUCER';
84+
msTs = a.timestamp;
85+
break;
86+
case 'mr':
87+
kind = 'CONSUMER';
88+
mrTs = a.timestamp;
89+
break;
90+
case 'ws':
91+
wsTs = a.timestamp;
92+
break;
93+
case 'wr':
94+
wrTs = a.timestamp;
95+
break;
96+
default:
97+
}
98+
});
99+
100+
let addr = 'sa'; // default which will be unset later if needed
101+
102+
switch (kind) {
52103
case 'CLIENT':
53-
beginAnnotation = span.timestamp ? 'cs' : undefined;
54-
endAnnotation = 'cr';
55-
addressKey = 'sa';
104+
addr = 'sa';
105+
begin = 'cs';
106+
end = 'cr';
56107
break;
57108
case 'SERVER':
58-
beginAnnotation = span.timestamp ? 'sr' : undefined;
59-
endAnnotation = 'ss';
60-
addressKey = 'ca';
109+
addr = 'ca';
110+
begin = 'sr';
111+
end = 'ss';
61112
break;
62113
case 'PRODUCER':
63-
beginAnnotation = span.timestamp ? 'ms' : undefined;
64-
endAnnotation = 'ws';
65-
addressKey = 'ma';
114+
addr = 'ma';
115+
begin = 'ms';
116+
end = 'ws';
117+
if (startTs === 0 || (msTs !== 0 && msTs < startTs)) {
118+
startTs = msTs;
119+
}
120+
if (endTs === 0 || (wsTs !== 0 && wsTs > endTs)) {
121+
endTs = wsTs;
122+
}
66123
break;
67124
case 'CONSUMER':
68-
if (span.timestamp && span.duration) {
69-
beginAnnotation = 'wr';
70-
endAnnotation = 'mr';
71-
} else if (span.timestamp) {
72-
beginAnnotation = 'mr';
125+
addr = 'ma';
126+
if (startTs === 0 || (wrTs !== 0 && wrTs < startTs)) {
127+
startTs = wrTs;
128+
}
129+
if (endTs === 0 || (mrTs !== 0 && mrTs > endTs)) {
130+
endTs = mrTs;
131+
}
132+
if (endTs !== 0 || wrTs !== 0) {
133+
begin = 'wr';
134+
end = 'mr';
135+
} else {
136+
begin = 'mr';
73137
}
74-
addressKey = 'ma';
75138
break;
76139
default:
77-
local = true;
78140
}
79141

142+
// If we didn't find a span kind, directly or indirectly, unset the addr
143+
if (!span.remoteEndpoint) addr = undefined;
144+
145+
const beginAnnotation = startTs && begin;
146+
const endAnnotation = endTs && end;
147+
const ep = toV1Endpoint(span.localEndpoint);
148+
80149
res.annotations = []; // prefer empty to undefined for arrays
150+
151+
let annotationCount = (span.annotations || []).length;
81152
if (beginAnnotation) {
82-
res.annotations.push({
83-
value: beginAnnotation,
84-
timestamp: span.timestamp,
85-
endpoint: jsonEndpoint
86-
});
153+
annotationCount++;
154+
res.annotations.push(toV1Annotation({
155+
value: begin,
156+
timestamp: startTs
157+
}, ep));
87158
}
88159

89-
(span.annotations || []).forEach((ann) =>
90-
res.annotations.push(toV1Annotation(ann, jsonEndpoint))
91-
);
160+
(span.annotations || []).forEach((a) => {
161+
if (beginAnnotation && a.value === begin) return;
162+
if (endAnnotation && a.value === end) return;
163+
res.annotations.push(toV1Annotation(a, ep));
164+
});
92165

93-
if (beginAnnotation && span.duration) {
94-
res.annotations.push({
95-
value: endAnnotation,
96-
timestamp: span.timestamp + span.duration,
97-
endpoint: jsonEndpoint
98-
});
166+
if (endAnnotation) {
167+
annotationCount++;
168+
res.annotations.push(toV1Annotation({
169+
value: end,
170+
timestamp: endTs
171+
}, ep));
99172
}
100173

101174
res.binaryAnnotations = []; // prefer empty to undefined for arrays
@@ -104,27 +177,26 @@ function convertV1(span) {
104177
res.binaryAnnotations = keys.map(key => ({
105178
key,
106179
value: span.tags[key],
107-
endpoint: jsonEndpoint
180+
endpoint: ep
108181
}));
109182
}
110183

111-
// worst case, make a dummy local component annotation, so the span can be looked up
112-
if (res.annotations.length === 0 && res.binaryAnnotations.length === 0 && local) {
113-
res.binaryAnnotations.push({key: 'lc', value: '', endpoint: jsonEndpoint});
114-
}
184+
const writeLocalComponent = annotationCount === 0 && ep && keys.length === 0;
185+
const hasRemoteEndpoint = addr && span.remoteEndpoint;
115186

116-
if (span.remoteEndpoint) {
187+
// write an empty "lc" annotation to avoid missing the localEndpoint in an in-process span
188+
if (writeLocalComponent) {
189+
res.binaryAnnotations.push({key: 'lc', value: '', endpoint: ep});
190+
}
191+
if (hasRemoteEndpoint) {
117192
const address = {
118-
key: addressKey,
193+
key: addr,
119194
value: true,
120195
endpoint: toV1Endpoint(span.remoteEndpoint)
121196
};
122197
res.binaryAnnotations.push(address);
123198
}
124199

125-
if (span.debug) {
126-
res.debug = true;
127-
}
128200
return res;
129201
}
130202

0 commit comments

Comments
 (0)