Tempo: Support standard span convention (#88268)

This commit is contained in:
Fabrizio 2024-05-27 10:10:04 +02:00 committed by GitHub
parent e28a34f126
commit e7f05f4ff2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 108 additions and 0 deletions

View File

@ -0,0 +1,80 @@
import { TraceSpan } from '../types';
import { findHeaderTags } from './trace-viewer';
describe('findHeaderTags()', () => {
it('return empty object when no spans are provided', () => {
const spans: TraceSpan[] = [];
expect(findHeaderTags(spans)).toEqual({});
});
it('return header tags when spans follow the OTEL semantic convention', () => {
const spans: TraceSpan[] = [
// @ts-ignore
{
tags: [
{ key: 'http.request.method', value: 'GET' },
{ key: 'http.response.status_code', value: '200' },
{ key: 'http.route', value: '/api/users' },
],
},
];
expect(findHeaderTags(spans)).toEqual({
method: [{ key: 'http.request.method', value: 'GET' }],
status: [{ key: 'http.response.status_code', value: '200' }],
url: [{ key: 'http.route', value: '/api/users' }],
});
});
it('return header tags when spans follow the alternative convention', () => {
const spans: TraceSpan[] = [
// @ts-ignore
{
tags: [
{ key: 'http.method', value: 'GET' },
{ key: 'http.status_code', value: '200' },
{ key: 'http.path', value: '/api/users' },
],
},
// @ts-ignore
{
tags: [
{ key: 'http.method', value: 'POST' },
{ key: 'http.status_code', value: '404' },
{ key: 'http.path', value: '/api/posts' },
],
},
];
expect(findHeaderTags(spans)).toEqual({
method: [{ key: 'http.method', value: 'GET' }],
status: [{ key: 'http.status_code', value: '200' }],
url: [{ key: 'http.path', value: '/api/users' }],
});
});
it('return header tags, prioritizing the spans that follow the OTEL semantinc convention', () => {
const spans: TraceSpan[] = [
// @ts-ignore
{
tags: [
{ key: 'http.method', value: 'GET' },
{ key: 'http.status', value: '200' },
{ key: 'http.path', value: '/api/users' },
],
},
// @ts-ignore
{
tags: [
{ key: 'http.request.method', value: 'POST' },
{ key: 'http.response.status_code', value: '404' },
{ key: 'http.route', value: '/api/users' },
],
},
];
expect(findHeaderTags(spans)).toEqual({
method: [{ key: 'http.request.method', value: 'POST' }],
status: [{ key: 'http.response.status_code', value: '404' }],
url: [{ key: 'http.route', value: '/api/users' }],
});
});
});

View File

@ -56,7 +56,34 @@ export const getTraceName = memoize(_getTraceNameImpl, (spans: TraceSpan[]) => {
return spans[0].traceID;
});
// Find header tags according to either old standard (e..g, `http.method`) or the
// standard OTEL semantic convention, as per https://opentelemetry.io/docs/specs/semconv/http/http-spans
// (e.g., `http.request.method`). Spans following the OTEL semantic convention are prioritized.
//
// Note that we are ignoring these cases:
// - conventions are mixed, e.g., a span with method in `http.method` but status code in `http.response.status_code`
// - tags are not in the same span, e.g., method in spans[0] but status in spans[1]
export function findHeaderTags(spans: TraceSpan[]) {
// OTEL semantic convention
for (let i = 0; i < spans.length; i++) {
const method = spans[i].tags.filter((tag) => {
return tag.key === 'http.request.method';
});
const status = spans[i].tags.filter((tag) => {
return tag.key === 'http.response.status_code';
});
const url = spans[i].tags.filter((tag) => {
return tag.key === 'http.route';
});
if (method.length > 0 || status.length > 0 || url.length > 0) {
return { method, status, url };
}
}
// Non-standard convention
for (let i = 0; i < spans.length; i++) {
const method = spans[i].tags.filter((tag) => {
return tag.key === 'http.method';
@ -74,6 +101,7 @@ export function findHeaderTags(spans: TraceSpan[]) {
return { method, status, url };
}
}
return {};
}