Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Fix handling of traces in the Jaeger plugin #129

Merged
merged 1 commit into from
Sep 2, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -35,13 +35,14 @@ NOTE: As semantic versioning states all 0.y.z releases can contain breaking chan
- [#120](https://github.com/kobsio/kobs/pull/120): Fix reconcilation of Flux resources.
- [#123](https://github.com/kobsio/kobs/pull/123): Fix fields handling in ClickHouse and Elasticsearch plugin.
- [#125](https://github.com/kobsio/kobs/pull/125): Fix missing `return` statement in ClickHouse panel.
- [#129](https://github.com/kobsio/kobs/pull/129): Fix handling of traces in the Jaeger plugin by using some function from the [jaegertracing/jaeger-ui](https://github.com/jaegertracing/jaeger-ui).

### Changed

- [#106](https://github.com/kobsio/kobs/pull/106): :warning: *Breaking change:* :warning: Change Prometheus sparkline chart to allow the usage of labels.
- [#107](https://github.com/kobsio/kobs/pull/107): Add new option for Prometheus chart legend and change formatting of values.
- [#108](https://github.com/kobsio/kobs/pull/108): Improve tooltip position in all nivo charts.
- [#121](https://github.com/kobsio/kobs/pull/121): :warning: *Breaking change:* Allow multiple queries in the panel options for the Elasticsearch plugin.
- [#121](https://github.com/kobsio/kobs/pull/121): :warning: *Breaking change:* :warning: Allow multiple queries in the panel options for the Elasticsearch plugin.

## [v0.5.0](https://github.com/kobsio/kobs/releases/tag/v0.5.0) (2021-08-03)

1 change: 1 addition & 0 deletions plugins/jaeger/package.json
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@
"react-dropzone": "^11.3.4",
"react-query": "^3.17.2",
"react-router-dom": "^5.2.0",
"react-virtuoso": "^1.11.1",
"typescript": "^4.3.4"
}
}
13 changes: 9 additions & 4 deletions plugins/jaeger/src/components/page/Trace.tsx
Original file line number Diff line number Diff line change
@@ -3,7 +3,8 @@ import React, { useEffect, useState } from 'react';
import { useHistory, useLocation, useParams } from 'react-router-dom';

import { ITrace } from '../../utils/interfaces';
import TraceCompare from './TraceCompare';
import TraceCompareData from './TraceCompareData';
import TraceCompareID from './TraceCompareID';
import TraceSelect from './TraceSelect';

interface IJaegerPageCompareParams {
@@ -40,7 +41,7 @@ const JaegerPageCompare: React.FunctionComponent<IJaegerPageCompareProps> = ({ n

// handleUpload handles the upload of a JSON file, which contains a trace. When the file upload is finished we parse
// the content of the file and set the uploadedTrace state. This state (trace) is then passed to the first
// TraceCompare so that the trace can be viewed.
// TraceCompareID so that the trace can be viewed.
const handleUpload = (trace: ITrace): void => {
setUploadedTrace(trace);
history.push({
@@ -66,12 +67,16 @@ const JaegerPageCompare: React.FunctionComponent<IJaegerPageCompareProps> = ({ n
return (
<Grid>
<GridItem sm={12} md={12} lg={compareTrace ? 6 : 12} xl={compareTrace ? 6 : 12} xl2={compareTrace ? 6 : 12}>
<TraceCompare name={name} traceID={params.traceID} trace={uploadedTrace} />
{uploadedTrace ? (
<TraceCompareData name={name} traceData={uploadedTrace} />
) : (
<TraceCompareID name={name} traceID={params.traceID} />
)}
</GridItem>

{compareTrace ? (
<GridItem sm={12} md={12} lg={compareTrace ? 6 : 12} xl={compareTrace ? 6 : 12} xl2={compareTrace ? 6 : 12}>
<TraceCompare name={name} traceID={compareTrace} />
<TraceCompareID name={name} traceID={compareTrace} />
</GridItem>
) : null}
</Grid>
98 changes: 8 additions & 90 deletions plugins/jaeger/src/components/page/TraceCompare.tsx
Original file line number Diff line number Diff line change
@@ -1,126 +1,44 @@
import {
Alert,
AlertActionLink,
AlertVariant,
Grid,
GridItem,
PageSection,
PageSectionVariants,
Spinner,
Title,
} from '@patternfly/react-core';
import { QueryObserverResult, useQuery } from 'react-query';
import { Grid, GridItem, PageSection, PageSectionVariants, Title } from '@patternfly/react-core';
import React from 'react';
import { useHistory } from 'react-router-dom';

import { addColorForProcesses, getRootSpan } from '../../utils/helpers';
import { ITrace } from '../../utils/interfaces';
import Spans from '../panel/details/Spans';
import TraceActions from '../panel/details/TraceActions';
import TraceHeader from '../panel/details/TraceHeader';

interface ITraceCompareProps {
name: string;
traceID: string;
trace?: ITrace;
trace: ITrace;
}

const TraceCompare: React.FunctionComponent<ITraceCompareProps> = ({ name, traceID, trace }: ITraceCompareProps) => {
const history = useHistory();

const { isError, isLoading, error, data, refetch } = useQuery<ITrace, Error>(
['jaeger/trace', name, traceID, trace],
async () => {
try {
if (trace && trace.traceID === traceID) {
return addColorForProcesses([trace])[0];
}

const response = await fetch(`/api/plugins/jaeger/trace/${name}?traceID=${traceID}`, {
method: 'get',
});
const json = await response.json();

if (response.status >= 200 && response.status < 300) {
return addColorForProcesses(json.data)[0];
} else {
if (json.error) {
throw new Error(json.error);
} else {
throw new Error('An unknown error occured');
}
}
} catch (err) {
throw err;
}
},
);

if (isLoading) {
return (
<div className="pf-u-text-align-center">
<Spinner />
</div>
);
}

if (isError) {
return (
<PageSection variant={PageSectionVariants.default}>
<Alert
variant={AlertVariant.danger}
title="Could not get trace"
actionLinks={
<React.Fragment>
<AlertActionLink onClick={(): void => history.push('/')}>Home</AlertActionLink>
<AlertActionLink onClick={(): Promise<QueryObserverResult<ITrace, Error>> => refetch()}>
Retry
</AlertActionLink>
</React.Fragment>
}
>
<p>{error?.message}</p>
</Alert>
</PageSection>
);
}

if (!data) {
return null;
}

const rootSpan = data && data.spans.length > 0 ? getRootSpan(data.spans) : undefined;
if (!rootSpan) {
return null;
}

const TraceCompare: React.FunctionComponent<ITraceCompareProps> = ({ name, trace }: ITraceCompareProps) => {
return (
<React.Fragment>
<Grid>
<GridItem sm={11} md={11} lg={11} xl={11} xl2={11}>
<PageSection style={{ height: '100%' }} variant={PageSectionVariants.light}>
<Title className="pf-u-text-nowrap pf-u-text-truncate" headingLevel="h6" size="xl">
{data.processes[rootSpan.processID].serviceName}: {rootSpan.operationName}
<span className="pf-u-pl-sm pf-u-font-size-sm pf-u-color-400">{data.traceID}</span>
{trace.traceName}
<span className="pf-u-pl-sm pf-u-font-size-sm pf-u-color-400">{trace.traceID}</span>
</Title>
<p>
<TraceHeader trace={data} rootSpan={rootSpan} />
<TraceHeader trace={trace} />
</p>
</PageSection>
</GridItem>

<GridItem sm={1} md={1} lg={1} xl={1} xl2={1}>
<PageSection style={{ height: '100%' }} variant={PageSectionVariants.light}>
<div style={{ float: 'right', textAlign: 'right' }}>
<TraceActions name={name} trace={data} />
<TraceActions name={name} trace={trace} />
</div>
</PageSection>
</GridItem>

<GridItem sm={12} md={12} lg={12} xl={12} xl2={12}>
<PageSection variant={PageSectionVariants.default}>
<div style={{ position: 'relative' }}>
<Spans name={name} trace={data} />
<Spans name={name} trace={trace} />
</div>
</PageSection>
</GridItem>
26 changes: 26 additions & 0 deletions plugins/jaeger/src/components/page/TraceCompareData.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';

import { ITrace } from '../../utils/interfaces';
import TraceCompare from './TraceCompare';
import { addColorForProcesses } from '../../utils/colors';
import { transformTraceData } from '../../utils/helpers';

interface ITraceCompareDataProps {
name: string;
traceData: ITrace;
}

const TraceCompareData: React.FunctionComponent<ITraceCompareDataProps> = ({
name,
traceData,
}: ITraceCompareDataProps) => {
const trace = transformTraceData(addColorForProcesses([traceData])[0]);

if (!trace) {
return null;
}

return <TraceCompare name={name} trace={trace} />;
};

export default TraceCompareData;
86 changes: 86 additions & 0 deletions plugins/jaeger/src/components/page/TraceCompareID.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {
Alert,
AlertActionLink,
AlertVariant,
PageSection,
PageSectionVariants,
Spinner,
} from '@patternfly/react-core';
import { QueryObserverResult, useQuery } from 'react-query';
import React from 'react';
import { useHistory } from 'react-router-dom';

import { ITrace } from '../../utils/interfaces';
import TraceCompare from './TraceCompare';
import { addColorForProcesses } from '../../utils/colors';
import { transformTraceData } from '../../utils/helpers';

interface ITraceCompareIDProps {
name: string;
traceID: string;
}

const TraceCompareID: React.FunctionComponent<ITraceCompareIDProps> = ({ name, traceID }: ITraceCompareIDProps) => {
const history = useHistory();

const { isError, isLoading, error, data, refetch } = useQuery<ITrace | null, Error>(
['jaeger/trace', name, traceID],
async () => {
try {
const response = await fetch(`/api/plugins/jaeger/trace/${name}?traceID=${traceID}`, {
method: 'get',
});
const json = await response.json();

if (response.status >= 200 && response.status < 300) {
return transformTraceData(addColorForProcesses(json.data)[0]);
} else {
if (json.error) {
throw new Error(json.error);
} else {
throw new Error('An unknown error occured');
}
}
} catch (err) {
throw err;
}
},
);

if (isLoading) {
return (
<div className="pf-u-text-align-center">
<Spinner />
</div>
);
}

if (isError) {
return (
<PageSection variant={PageSectionVariants.default}>
<Alert
variant={AlertVariant.danger}
title="Could not get trace"
actionLinks={
<React.Fragment>
<AlertActionLink onClick={(): void => history.push('/')}>Home</AlertActionLink>
<AlertActionLink onClick={(): Promise<QueryObserverResult<ITrace | null, Error>> => refetch()}>
Retry
</AlertActionLink>
</React.Fragment>
}
>
<p>{error?.message}</p>
</Alert>
</PageSection>
);
}

if (!data) {
return null;
}

return <TraceCompare name={name} trace={data} />;
};

export default TraceCompareID;
15 changes: 13 additions & 2 deletions plugins/jaeger/src/components/panel/Traces.tsx
Original file line number Diff line number Diff line change
@@ -3,11 +3,12 @@ import { QueryObserverResult, useQuery } from 'react-query';
import React from 'react';

import { IOptions, ITrace } from '../../utils/interfaces';
import { addColorForProcesses, encodeTags } from '../../utils/helpers';
import { encodeTags, transformTraceData } from '../../utils/helpers';
import { PluginCard } from '@kobsio/plugin-core';
import TracesActions from './TracesActions';
import TracesChart from './TracesChart';
import TracesList from './TracesList';
import { addColorForProcesses } from '../../utils/colors';

interface ITracesProps extends IOptions {
name: string;
@@ -46,7 +47,17 @@ const Traces: React.FunctionComponent<ITracesProps> = ({
const json = await response.json();

if (response.status >= 200 && response.status < 300) {
return addColorForProcesses(json.data);
const traceData = addColorForProcesses(json.data);
const traces: ITrace[] = [];

for (const trace of traceData) {
const transformedTrace = transformTraceData(trace);
if (transformedTrace) {
traces.push(transformedTrace);
}
}

return traces;
} else {
if (json.error) {
throw new Error(json.error);
31 changes: 8 additions & 23 deletions plugins/jaeger/src/components/panel/TracesChart.tsx
Original file line number Diff line number Diff line change
@@ -5,7 +5,6 @@ import { SquareIcon } from '@patternfly/react-icons';
import { TooltipWrapper } from '@nivo/tooltip';
import Trace from './details/Trace';

import { getDuration, getRootSpan } from '../../utils/helpers';
import { ITrace } from '../../utils/interfaces';

interface IDatum extends Datum {
@@ -39,27 +38,13 @@ const TracesChart: React.FunctionComponent<ITracesChartProps> = ({ name, traces,
maximalSpans = trace.spans.length;
}

const rootSpan = getRootSpan(trace.spans);
if (!rootSpan) {
result.push({
label: `${trace.traceID}`,
size: trace.spans.length,
trace,
x: new Date(Math.floor(trace.spans[0].startTime / 1000)),
y: getDuration(trace.spans),
});
} else {
const rootSpanProcess = trace.processes[rootSpan.processID];
const rootSpanService = rootSpanProcess.serviceName;

result.push({
label: `${rootSpanService}: ${rootSpan.operationName}`,
size: trace.spans.length,
trace,
x: new Date(Math.floor(trace.spans[0].startTime / 1000)),
y: getDuration(trace.spans),
});
}
result.push({
label: trace.traceName,
size: trace.spans.length,
trace,
x: new Date(Math.floor(trace.spans[0].startTime / 1000)),
y: trace.duration / 1000,
});
});

return {
@@ -90,7 +75,7 @@ const TracesChart: React.FunctionComponent<ITracesChartProps> = ({ name, traces,
enableGridX={false}
enableGridY={false}
margin={{ bottom: 25, left: 0, right: 0, top: 0 }}
nodeSize={{ key: 'size', sizes: [15, 20], values: [min, max] }}
nodeSize={{ key: 'size', sizes: [15, 75], values: [min, max] }}
theme={{
background: '#ffffff',
fontFamily: 'RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif',
Loading