Skip to content

Commit

Permalink
Fix jaeger spans key names for filtering (opensearch-project#1428)
Browse files Browse the repository at this point in the history
Signed-off-by: Joshua Li <[email protected]>
  • Loading branch information
joshuali925 authored Feb 19, 2024
1 parent 9750c82 commit 2c1497b
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 45 deletions.
14 changes: 14 additions & 0 deletions common/types/trace_analytics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export type SpanField =
| 'SPAN_ID'
| 'PARENT_SPAN_ID'
| 'SERVICE'
| 'OPERATION'
| 'DURATION'
| 'START_TIME'
| 'END_TIME'
| 'ERRORS';
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import React, { useState } from 'react';
interface FlyoutListItemProps {
title: React.ReactNode;
description: React.ReactNode;
addSpanFilter: () => void;
addSpanFilter?: () => void;
}

export function FlyoutListItem(props: FlyoutListItemProps) {
Expand All @@ -27,12 +27,15 @@ export function FlyoutListItem(props: FlyoutListItemProps) {
props.description !== '-' ? (
<EuiFlexGroup gutterSize="none">
<EuiFlexItem>
<EuiText size="s" style={{ wordBreak: 'break-all', wordWrap: 'break-word', whiteSpace: 'pre-line' }}>
<EuiText
size="s"
style={{ wordBreak: 'break-all', wordWrap: 'break-word', whiteSpace: 'pre-line' }}
>
<b>{props.description}</b>
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
{hover && (
{hover && props.addSpanFilter && (
<EuiToolTip position="top" content="Filter spans on this value">
<EuiButtonIcon
aria-label="span-flyout-filter-icon"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,43 @@ import moment from 'moment';
import React, { useEffect, useState } from 'react';
import { HttpSetup } from '../../../../../../../src/core/public';
import { TRACE_ANALYTICS_DATE_FORMAT } from '../../../../../common/constants/trace_analytics';
import { SpanField } from '../../../../../common/types/trace_analytics';
import { TraceAnalyticsMode } from '../../home';
import { handleSpansFlyoutRequest } from '../../requests/traces_request_handler';
import { microToMilliSec, nanoToMilliSec } from '../common/helper_functions';
import { FlyoutListItem } from './flyout_list_item';

const MODE_TO_FIELDS: Record<TraceAnalyticsMode, Record<SpanField, string | undefined>> = {
data_prepper: {
SPAN_ID: 'spanId',
PARENT_SPAN_ID: 'parentSpanId',
SERVICE: 'serviceName',
OPERATION: 'name',
DURATION: 'durationInNanos',
START_TIME: 'startTime',
END_TIME: 'endTime',
ERRORS: 'status.code',
},
jaeger: {
SPAN_ID: 'spanID',
PARENT_SPAN_ID: undefined,
SERVICE: 'process.serviceName',
OPERATION: 'operationName',
DURATION: 'duration',
START_TIME: 'startTime',
END_TIME: undefined,
ERRORS: 'tag.error',
},
};

const getSpanFieldKey = (mode: TraceAnalyticsMode, field: SpanField) => MODE_TO_FIELDS[mode][field];

const getSpanValue = (span: object, mode: TraceAnalyticsMode, field: SpanField) => {
const fieldKey = getSpanFieldKey(mode, field);
if (fieldKey === undefined) return undefined;
return _.get(span, fieldKey);
};

export function SpanDetailFlyout(props: {
http: HttpSetup;
spanId: string;
Expand All @@ -42,13 +74,19 @@ export function SpanDetailFlyout(props: {
handleSpansFlyoutRequest(props.http, props.spanId, setSpan, mode);
}, [props.spanId]);

const getListItem = (field: string, title: React.ReactNode, description: React.ReactNode) => {
const getListItem = (
fieldKey: string | undefined,
title: React.ReactNode,
description: React.ReactNode
) => {
return (
<FlyoutListItem
title={title}
description={description}
key={`list-item-${title}`}
addSpanFilter={() => props.addSpanFilter(field, span[field])}
addSpanFilter={
fieldKey ? () => props.addSpanFilter(fieldKey, _.get(span, fieldKey)) : undefined
}
/>
);
};
Expand All @@ -65,27 +103,27 @@ export function SpanDetailFlyout(props: {
if (!span || _.isEmpty(span)) return '-';
const overviewList = [
getListItem(
'spanId',
getSpanFieldKey(mode, 'SPAN_ID'),
'Span ID',
(mode === 'data_prepper' ? span.spanId : span.spanID) ? (
getSpanValue(span, mode, 'SPAN_ID') ? (
<EuiFlexGroup gutterSize="xs" style={{ marginTop: -4, marginBottom: -4 }}>
<EuiFlexItem grow={false}>
<EuiCopy textToCopy={mode === 'data_prepper' ? span.spanId : span.spanID}>
<EuiCopy textToCopy={getSpanValue(span, mode, 'SPAN_ID')}>
{(copy) => (
<EuiButtonIcon aria-label="copy-button" onClick={copy} iconType="copyClipboard" />
)}
</EuiCopy>
</EuiFlexItem>
<EuiFlexItem>{mode === 'data_prepper' ? span.spanId : span.spanID}</EuiFlexItem>
<EuiFlexItem>{getSpanValue(span, mode, 'SPAN_ID')}</EuiFlexItem>
</EuiFlexGroup>
) : (
'-'
)
),
getListItem(
'parentSpanId',
getSpanFieldKey(mode, 'PARENT_SPAN_ID'),
'Parent span ID',
(mode === 'data_prepper' ? span.parentSpanId : span.references.length) ? (
getSpanValue(span, mode, 'PARENT_SPAN_ID') ? (
<EuiFlexGroup gutterSize="xs" style={{ marginTop: -4, marginBottom: -4 }}>
<EuiFlexItem grow={false}>
<EuiCopy
Expand All @@ -105,17 +143,17 @@ export function SpanDetailFlyout(props: {
)
),
getListItem(
'serviceName',
getSpanFieldKey(mode, 'SERVICE'),
'Service',
(mode === 'data_prepper' ? span.serviceName : span.process.serviceName) || '-'
getSpanValue(span, mode, 'SERVICE') || '-'
),
getListItem(
'name',
getSpanFieldKey(mode, 'OPERATION'),
'Operation',
(mode === 'data_prepper' ? span.name : span.operationName) || '-'
getSpanValue(span, mode, 'OPERATION') || '-'
),
getListItem(
'durationInNanos',
getSpanFieldKey(mode, 'DURATION'),
'Duration',
`${
mode === 'data_prepper'
Expand All @@ -124,7 +162,7 @@ export function SpanDetailFlyout(props: {
} ms`
),
getListItem(
'startTime',
getSpanFieldKey(mode, 'START_TIME'),
'Start time',
mode === 'data_prepper'
? moment(span.startTime).format(TRACE_ANALYTICS_DATE_FORMAT)
Expand All @@ -133,7 +171,7 @@ export function SpanDetailFlyout(props: {
)
),
getListItem(
'endTime',
getSpanFieldKey(mode, 'END_TIME'),
'End time',
mode === 'data_prepper'
? moment(span.endTime).format(TRACE_ANALYTICS_DATE_FORMAT)
Expand All @@ -142,9 +180,9 @@ export function SpanDetailFlyout(props: {
)
),
getListItem(
'status.code',
getSpanFieldKey(mode, 'ERRORS'),
'Errors',
(mode === 'data_prepper' ? span['status.code'] === 2 : span.tag.error) ? (
(mode === 'data_prepper' ? span['status.code'] === 2 : span.tag?.error) ? (
<EuiText color="danger" size="s" style={{ fontWeight: 700 }}>
Yes
</EuiText>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
/* eslint-disable no-console */

import { BarOrientation } from '../../../../common/constants/shared';
import _ from 'lodash';
import moment from 'moment';
import { v1 as uuid } from 'uuid';
import { BarOrientation } from '../../../../common/constants/shared';
import { HttpSetup } from '../../../../../../src/core/public';
import { TRACE_ANALYTICS_DATE_FORMAT } from '../../../../common/constants/trace_analytics';
import { microToMilliSec, nanoToMilliSec } from '../components/common/helper_functions';
Expand Down Expand Up @@ -38,7 +37,7 @@ export const handleTracesRequest = async (
items: any,
setItems: (items: any) => void,
mode: TraceAnalyticsMode,
sort?: any,
sort?: any
) => {
const binarySearch = (arr: number[], target: number) => {
if (!arr) return Number.NaN;
Expand Down Expand Up @@ -73,7 +72,7 @@ export const handleTracesRequest = async (
.then((response) => {
return Promise.all(
response.aggregations.traces.buckets.map((bucket: any) => {
if (mode === 'data_prepper') {
if (mode === 'data_prepper') {
return {
trace_id: bucket.key,
trace_group: bucket.trace_group.buckets[0]?.key,
Expand All @@ -86,15 +85,14 @@ export const handleTracesRequest = async (
),
actions: '#',
};
}
return {
trace_id: bucket.key,
latency: bucket.latency.value,
last_updated: moment(bucket.last_updated.value).format(TRACE_ANALYTICS_DATE_FORMAT),
error_count: bucket.error_count.doc_count,
actions: '#',
};

}
return {
trace_id: bucket.key,
latency: bucket.latency.value,
last_updated: moment(bucket.last_updated.value).format(TRACE_ANALYTICS_DATE_FORMAT),
error_count: bucket.error_count.doc_count,
actions: '#',
};
})
);
})
Expand All @@ -109,7 +107,7 @@ export const handleTraceViewRequest = (
http: HttpSetup,
fields: {},
setFields: (fields: any) => void,
mode: TraceAnalyticsMode,
mode: TraceAnalyticsMode
) => {
handleDslRequest(http, null, getTracesQuery(mode, traceId), mode)
.then(async (response) => {
Expand Down Expand Up @@ -138,7 +136,7 @@ export const handleServicesPieChartRequest = async (
http: HttpSetup,
setServiceBreakdownData: (serviceBreakdownData: any) => void,
setColorMap: (colorMap: any) => void,
mode: TraceAnalyticsMode,
mode: TraceAnalyticsMode
) => {
const colors = [
'#7492e7',
Expand Down Expand Up @@ -201,7 +199,7 @@ export const handleSpansGanttRequest = (
setSpanDetailData: (spanDetailData: any) => void,
colorMap: any,
spanFiltersDSL: any,
mode: TraceAnalyticsMode,
mode: TraceAnalyticsMode
) => {
handleDslRequest(http, spanFiltersDSL, getSpanDetailQuery(mode, traceId), mode)
.then((response) => hitsToSpanDetailData(response.hits.hits, colorMap, mode))
Expand All @@ -213,7 +211,7 @@ export const handleSpansFlyoutRequest = (
http: HttpSetup,
spanId: string,
setItems: (items: any) => void,
mode: TraceAnalyticsMode,
mode: TraceAnalyticsMode
) => {
handleDslRequest(http, null, getSpanFlyoutQuery(mode, spanId), mode)
.then((response) => {
Expand All @@ -230,15 +228,35 @@ const hitsToSpanDetailData = async (hits: any, colorMap: any, mode: TraceAnalyti
};
if (hits.length === 0) return data;

const minStartTime = mode === 'jaeger' ? microToMilliSec(hits[hits.length - 1].sort[0]) : nanoToMilliSec(hits[hits.length - 1].sort[0]);
const minStartTime =
mode === 'jaeger'
? microToMilliSec(hits[hits.length - 1].sort[0])
: nanoToMilliSec(hits[hits.length - 1].sort[0]);
let maxEndTime = 0;

hits.forEach((hit: any) => {
const startTime = mode === 'jaeger' ? microToMilliSec(hit.sort[0]) - minStartTime : nanoToMilliSec(hit.sort[0]) - minStartTime;
const duration = mode === 'jaeger' ? _.round(microToMilliSec(hit._source.duration), 2) : _.round(nanoToMilliSec(hit._source.durationInNanos), 2);;
const serviceName = mode === 'jaeger'? _.get(hit, ['_source', 'process'])['serviceName'] : _.get(hit, ['_source', 'serviceName']);
const name = mode === 'jaeger' ? _.get(hit, '_source.operationName') : _.get(hit, '_source.name');
const error = mode === 'jaeger' ? (hit._source['tag']['error'] === true ? ' \u26a0 Error' : '') : (hit._source['status.code'] === 2 ? ' \u26a0 Error' : '');
const startTime =
mode === 'jaeger'
? microToMilliSec(hit.sort[0]) - minStartTime
: nanoToMilliSec(hit.sort[0]) - minStartTime;
const duration =
mode === 'jaeger'
? _.round(microToMilliSec(hit._source.duration), 2)
: _.round(nanoToMilliSec(hit._source.durationInNanos), 2);
const serviceName =
mode === 'jaeger'
? _.get(hit, ['_source', 'process']).serviceName
: _.get(hit, ['_source', 'serviceName']);
const name =
mode === 'jaeger' ? _.get(hit, '_source.operationName') : _.get(hit, '_source.name');
const error =
mode === 'jaeger'
? hit._source.tag?.['error'] === true
? ' \u26a0 Error'
: ''
: hit._source['status.code'] === 2
? ' \u26a0 Error'
: '';
const uniqueLabel = `${serviceName} <br>${name} ` + uuid();
maxEndTime = Math.max(maxEndTime, startTime + duration);

Expand Down Expand Up @@ -292,7 +310,7 @@ export const handlePayloadRequest = (
http: HttpSetup,
payloadData: any,
setPayloadData: (payloadData: any) => void,
mode: TraceAnalyticsMode,
mode: TraceAnalyticsMode
) => {
handleDslRequest(http, null, getPayloadQuery(mode, traceId), mode)
.then((response) => setPayloadData(JSON.stringify(response.hits.hits, null, 2)))
Expand All @@ -305,7 +323,7 @@ export const handleSpansRequest = (
setTotal: (total: number) => void,
spanSearchParams: SpanSearchParams,
DSL: any,
mode: TraceAnalyticsMode,
mode: TraceAnalyticsMode
) => {
handleDslRequest(http, DSL, getSpansQuery(spanSearchParams), mode)
.then((response) => {
Expand Down

0 comments on commit 2c1497b

Please sign in to comment.