mirror of
https://github.com/SigNoz/signoz.git
synced 2026-06-11 11:20:32 +01:00
Compare commits
5 Commits
main
...
feat/flame
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6f1a98728 | ||
|
|
4c52989f5c | ||
|
|
f9fc66c7a4 | ||
|
|
4a522e542c | ||
|
|
5ed9602b6b |
@@ -36,6 +36,7 @@ export const REACT_QUERY_KEY = {
|
||||
GET_TRACE_V4_WATERFALL: 'GET_TRACE_V4_WATERFALL',
|
||||
GET_TRACE_AGGREGATIONS: 'GET_TRACE_AGGREGATIONS',
|
||||
GET_TRACE_V2_FLAMEGRAPH: 'GET_TRACE_V2_FLAMEGRAPH',
|
||||
GET_TRACE_V3_FLAMEGRAPH: 'GET_TRACE_V3_FLAMEGRAPH',
|
||||
GET_POD_LIST: 'GET_POD_LIST',
|
||||
GET_NODE_LIST: 'GET_NODE_LIST',
|
||||
GET_DEPLOYMENT_LIST: 'GET_DEPLOYMENT_LIST',
|
||||
|
||||
49
frontend/src/hooks/trace/useGetTraceFlamegraphV3.tsx
Normal file
49
frontend/src/hooks/trace/useGetTraceFlamegraphV3.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import { getFlamegraph } from 'api/generated/services/tracedetail';
|
||||
import {
|
||||
SpantypesGettableFlamegraphTraceDTO,
|
||||
TelemetrytypesTelemetryFieldKeyDTO,
|
||||
} from 'api/generated/services/sigNoz.schemas';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import { useQuery, UseQueryResult } from 'react-query';
|
||||
import { TelemetryFieldKey } from 'types/api/v5/queryRange';
|
||||
|
||||
export interface GetTraceFlamegraphV3Props {
|
||||
traceId: string;
|
||||
selectedSpanId?: string;
|
||||
selectFields?: TelemetryFieldKey[];
|
||||
}
|
||||
|
||||
const useGetTraceFlamegraphV3 = (
|
||||
props: GetTraceFlamegraphV3Props,
|
||||
): UseQueryResult<SpantypesGettableFlamegraphTraceDTO, unknown> =>
|
||||
useQuery({
|
||||
queryFn: () =>
|
||||
getFlamegraph(
|
||||
{ traceID: props.traceId },
|
||||
{
|
||||
selectedSpanId: props.selectedSpanId,
|
||||
// v5 TelemetryFieldKey and the generated DTO are runtime-identical; only
|
||||
// the literal-union vs enum nominal types differ
|
||||
selectFields: props.selectFields as TelemetrytypesTelemetryFieldKeyDTO[],
|
||||
},
|
||||
).then((res) => ({
|
||||
...res.data,
|
||||
// v3 returns span.timestamp in nanoseconds, but the flamegraph render
|
||||
// pipeline (and the shared v2 page) treat it as milliseconds, matching
|
||||
// startTimestampMillis. Normalise once here at the data boundary.
|
||||
spans: (res.data.spans ?? []).map((level) =>
|
||||
level.map((span) => ({ ...span, timestamp: span.timestamp / 1e6 })),
|
||||
),
|
||||
})),
|
||||
queryKey: [
|
||||
REACT_QUERY_KEY.GET_TRACE_V3_FLAMEGRAPH,
|
||||
props.traceId,
|
||||
props.selectedSpanId,
|
||||
props.selectFields,
|
||||
],
|
||||
enabled: !!props.traceId,
|
||||
keepPreviousData: true,
|
||||
refetchOnWindowFocus: false,
|
||||
});
|
||||
|
||||
export default useGetTraceFlamegraphV3;
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useHistory, useLocation, useParams } from 'react-router-dom';
|
||||
import { Skeleton } from 'antd';
|
||||
import useGetTraceFlamegraph from 'hooks/trace/useGetTraceFlamegraph';
|
||||
import useGetTraceFlamegraphV3 from 'hooks/trace/useGetTraceFlamegraphV3';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import { TraceDetailFlamegraphURLProps } from 'types/api/trace/getTraceFlamegraph';
|
||||
import { SpanV3 } from 'types/api/trace/getTraceV3';
|
||||
@@ -70,17 +70,13 @@ function TraceFlamegraph({
|
||||
data,
|
||||
isFetching,
|
||||
error: fetchError,
|
||||
} = useGetTraceFlamegraph({
|
||||
} = useGetTraceFlamegraphV3({
|
||||
traceId,
|
||||
selectedSpanId: selectedSpanIdForFetch,
|
||||
limit: FLAMEGRAPH_SPAN_LIMIT,
|
||||
selectFields: flamegraphSelectFields,
|
||||
});
|
||||
|
||||
const spans = useMemo(
|
||||
() => data?.payload?.spans || [],
|
||||
[data?.payload?.spans],
|
||||
);
|
||||
const spans = useMemo(() => data?.spans || [], [data?.spans]);
|
||||
|
||||
const {
|
||||
layout,
|
||||
@@ -99,8 +95,8 @@ function TraceFlamegraph({
|
||||
setFirstSpanAtFetchLevel={setFirstSpanAtFetchLevel}
|
||||
onSpanClick={handleSpanClick}
|
||||
traceMetadata={{
|
||||
startTime: data?.payload?.startTimestampMillis || 0,
|
||||
endTime: data?.payload?.endTimestampMillis || 0,
|
||||
startTime: data?.startTimestampMillis || 0,
|
||||
endTime: data?.endTimestampMillis || 0,
|
||||
}}
|
||||
filteredSpanIds={filteredSpanIds}
|
||||
isFilterActive={isFilterActive}
|
||||
@@ -124,7 +120,7 @@ function TraceFlamegraph({
|
||||
if (fetchError || workerError) {
|
||||
return <Error error={(fetchError || workerError) as any} />;
|
||||
}
|
||||
if (data?.payload?.spans && data.payload.spans.length === 0) {
|
||||
if (data?.spans && data.spans.length === 0) {
|
||||
return <div>No data found for trace {traceId}</div>;
|
||||
}
|
||||
return (
|
||||
@@ -134,17 +130,17 @@ function TraceFlamegraph({
|
||||
setFirstSpanAtFetchLevel={setFirstSpanAtFetchLevel}
|
||||
onSpanClick={handleSpanClick}
|
||||
traceMetadata={{
|
||||
startTime: data?.payload?.startTimestampMillis || 0,
|
||||
endTime: data?.payload?.endTimestampMillis || 0,
|
||||
startTime: data?.startTimestampMillis || 0,
|
||||
endTime: data?.endTimestampMillis || 0,
|
||||
}}
|
||||
filteredSpanIds={filteredSpanIds}
|
||||
isFilterActive={isFilterActive}
|
||||
/>
|
||||
);
|
||||
}, [
|
||||
data?.payload?.endTimestampMillis,
|
||||
data?.payload?.startTimestampMillis,
|
||||
data?.payload?.spans,
|
||||
data?.endTimestampMillis,
|
||||
data?.startTimestampMillis,
|
||||
data?.spans,
|
||||
fetchError,
|
||||
filteredSpanIds,
|
||||
firstSpanAtFetchLevel,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import useGetTraceFlamegraph from 'hooks/trace/useGetTraceFlamegraph';
|
||||
import useGetTraceFlamegraphV3 from 'hooks/trace/useGetTraceFlamegraphV3';
|
||||
import { AllTheProviders } from 'tests/test-utils';
|
||||
import { SpanV3 } from 'types/api/trace/getTraceV3';
|
||||
|
||||
import { FLAMEGRAPH_SPAN_LIMIT } from '../constants';
|
||||
import TraceFlamegraph from '../TraceFlamegraph';
|
||||
|
||||
jest.mock('hooks/trace/useGetTraceFlamegraph');
|
||||
jest.mock('hooks/trace/useGetTraceFlamegraphV3');
|
||||
|
||||
// Short-circuit the worker so the test doesn't depend on layout computation.
|
||||
jest.mock('../hooks/useVisualLayoutWorker', () => ({
|
||||
@@ -17,9 +17,8 @@ jest.mock('../hooks/useVisualLayoutWorker', () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
const mockUseGetTraceFlamegraph = useGetTraceFlamegraph as jest.MockedFunction<
|
||||
typeof useGetTraceFlamegraph
|
||||
>;
|
||||
const mockUseGetTraceFlamegraph =
|
||||
useGetTraceFlamegraphV3 as jest.MockedFunction<typeof useGetTraceFlamegraphV3>;
|
||||
|
||||
function renderFlamegraph(props: {
|
||||
selectedSpan: SpanV3 | undefined;
|
||||
@@ -45,7 +44,7 @@ describe('TraceFlamegraph - selectedSpanId pass-through', () => {
|
||||
beforeEach(() => {
|
||||
mockUseGetTraceFlamegraph.mockReset();
|
||||
mockUseGetTraceFlamegraph.mockReturnValue({
|
||||
data: { payload: { spans: [] } },
|
||||
data: { spans: [] },
|
||||
isFetching: false,
|
||||
error: null,
|
||||
} as never);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FlamegraphSpan } from 'types/api/trace/getTraceFlamegraph';
|
||||
import { SpantypesFlamegraphSpanDTO as FlamegraphSpan } from 'api/generated/services/sigNoz.schemas';
|
||||
|
||||
import {
|
||||
computeVisualLayout,
|
||||
@@ -14,12 +14,12 @@ function makeSpan(
|
||||
): FlamegraphSpan {
|
||||
return {
|
||||
parentSpanId: '',
|
||||
traceId: 'trace-1',
|
||||
hasError: false,
|
||||
serviceName: 'svc',
|
||||
name: 'op',
|
||||
level: 0,
|
||||
event: [],
|
||||
resource: {},
|
||||
attributes: {},
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FlamegraphSpan } from 'types/api/trace/getTraceFlamegraph';
|
||||
import { SpantypesFlamegraphSpanDTO as FlamegraphSpan } from 'api/generated/services/sigNoz.schemas';
|
||||
|
||||
/** Minimal FlamegraphSpan for unit tests */
|
||||
export const MOCK_SPAN: FlamegraphSpan = {
|
||||
@@ -6,12 +6,12 @@ export const MOCK_SPAN: FlamegraphSpan = {
|
||||
durationNano: 50_000_000, // 50ms
|
||||
spanId: 'span-1',
|
||||
parentSpanId: '',
|
||||
traceId: 'trace-1',
|
||||
hasError: false,
|
||||
serviceName: 'test-service',
|
||||
name: 'test-span',
|
||||
level: 0,
|
||||
event: [],
|
||||
resource: {},
|
||||
attributes: {},
|
||||
};
|
||||
|
||||
/** Nested spans structure for findSpanById tests */
|
||||
|
||||
@@ -65,37 +65,25 @@ describe('Presentation / Styling Utils', () => {
|
||||
describe('getFlamegraphSpanGroupValue', () => {
|
||||
it('returns resource[field.name] when present', () => {
|
||||
const value = getFlamegraphSpanGroupValue(
|
||||
{
|
||||
serviceName: 'legacy',
|
||||
resource: { 'service.name': 'svc-from-resource' },
|
||||
},
|
||||
{ resource: { 'service.name': 'svc-from-resource' } },
|
||||
SERVICE_FIELD,
|
||||
);
|
||||
expect(value).toBe('svc-from-resource');
|
||||
});
|
||||
|
||||
it('falls back to top-level serviceName for service.name when resource is empty', () => {
|
||||
const value = getFlamegraphSpanGroupValue(
|
||||
{ serviceName: 'svc-legacy', resource: {} },
|
||||
SERVICE_FIELD,
|
||||
);
|
||||
expect(value).toBe('svc-legacy');
|
||||
it('returns "unknown" for service.name when resource is empty', () => {
|
||||
const value = getFlamegraphSpanGroupValue({ resource: {} }, SERVICE_FIELD);
|
||||
expect(value).toBe('unknown');
|
||||
});
|
||||
|
||||
it('returns "unknown" for non-service fields when resource is missing', () => {
|
||||
const value = getFlamegraphSpanGroupValue(
|
||||
{ serviceName: 'svc', resource: {} },
|
||||
HOST_FIELD,
|
||||
);
|
||||
const value = getFlamegraphSpanGroupValue({ resource: {} }, HOST_FIELD);
|
||||
expect(value).toBe('unknown');
|
||||
});
|
||||
|
||||
it('reads host.name from resource when present', () => {
|
||||
const value = getFlamegraphSpanGroupValue(
|
||||
{
|
||||
serviceName: 'svc',
|
||||
resource: { 'host.name': 'host-1' },
|
||||
},
|
||||
{ resource: { 'host.name': 'host-1' } },
|
||||
HOST_FIELD,
|
||||
);
|
||||
expect(value).toBe('host-1');
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
/* eslint-disable sonarjs/cognitive-complexity */
|
||||
import { FlamegraphSpan } from 'types/api/trace/getTraceFlamegraph';
|
||||
import { SpantypesFlamegraphSpanDTO as FlamegraphSpan } from 'api/generated/services/sigNoz.schemas';
|
||||
|
||||
export interface ConnectorLine {
|
||||
parentRow: number;
|
||||
childRow: number;
|
||||
timestampMs: number;
|
||||
serviceName: string;
|
||||
// Snapshot of the child span's resource so draw-time can resolve the
|
||||
// `colorByField` group value without crossing the worker boundary.
|
||||
resource?: Record<string, string>;
|
||||
@@ -159,24 +158,8 @@ export function computeVisualLayout(spans: FlamegraphSpan[][]): VisualLayout {
|
||||
}
|
||||
}
|
||||
|
||||
// Extract parentSpanId — the field may be missing at runtime when the API
|
||||
// returns `references` instead. Fall back to the first CHILD_OF reference.
|
||||
function getParentId(span: FlamegraphSpan): string {
|
||||
if (span.parentSpanId) {
|
||||
return span.parentSpanId;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const refs = (span as any).references as
|
||||
| Array<{ spanId?: string; refType?: string }>
|
||||
| undefined;
|
||||
if (refs) {
|
||||
for (const ref of refs) {
|
||||
if (ref.refType === 'CHILD_OF' && ref.spanId) {
|
||||
return ref.spanId;
|
||||
}
|
||||
}
|
||||
}
|
||||
return '';
|
||||
return span.parentSpanId || '';
|
||||
}
|
||||
|
||||
// Build children map and identify roots
|
||||
@@ -480,7 +463,6 @@ export function computeVisualLayout(spans: FlamegraphSpan[][]): VisualLayout {
|
||||
parentRow,
|
||||
childRow,
|
||||
timestampMs: child.timestamp,
|
||||
serviceName: child.serviceName,
|
||||
resource: child.resource,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { RefObject, useCallback, useMemo, useRef } from 'react';
|
||||
import { generateColorPair } from 'pages/TraceDetailsV3/utils/generateColorPair';
|
||||
import { useTraceStore } from 'pages/TraceDetailsV3/stores/traceStore';
|
||||
import { FlamegraphSpan } from 'types/api/trace/getTraceFlamegraph';
|
||||
import { SpantypesFlamegraphSpanDTO as FlamegraphSpan } from 'api/generated/services/sigNoz.schemas';
|
||||
import { TelemetryFieldKey } from 'types/api/v5/queryRange';
|
||||
|
||||
import { ConnectorLine } from '../computeVisualLayout';
|
||||
@@ -200,7 +200,7 @@ function drawConnectorLines(args: DrawConnectorLinesArgs): void {
|
||||
}
|
||||
|
||||
const groupValue = getFlamegraphSpanGroupValue(
|
||||
{ serviceName: conn.serviceName, resource: conn.resource },
|
||||
{ resource: conn.resource },
|
||||
colorByField,
|
||||
);
|
||||
const pair = generateColorPair(groupValue);
|
||||
|
||||
@@ -11,10 +11,9 @@ import {
|
||||
import { useTraceStore } from 'pages/TraceDetailsV3/stores/traceStore';
|
||||
import { RESERVED_PREVIEW_KEYS } from 'pages/TraceDetailsV3/SpanHoverCard/SpanHoverCard';
|
||||
import { getSpanAttribute } from 'pages/TraceDetailsV3/utils';
|
||||
import { FlamegraphSpan } from 'types/api/trace/getTraceFlamegraph';
|
||||
import { SpantypesFlamegraphSpanDTO as FlamegraphSpan } from 'api/generated/services/sigNoz.schemas';
|
||||
|
||||
import { EventRect, SpanRect } from '../types';
|
||||
import { ITraceMetadata } from '../types';
|
||||
import { EventRect, ITraceMetadata, SpanRect } from '../types';
|
||||
import {
|
||||
getFlamegraphServiceName,
|
||||
getFlamegraphSpanGroupValue,
|
||||
@@ -200,7 +199,7 @@ export function useFlamegraphHover(
|
||||
|
||||
if (eventRect) {
|
||||
const { event, span } = eventRect;
|
||||
const eventTimeMs = event.timeUnixNano / 1e6;
|
||||
const eventTimeMs = (event.timeUnixNano ?? 0) / 1e6;
|
||||
setHoveredEventKey(`${span.spanId}-${event.name}-${event.timeUnixNano}`);
|
||||
setHoveredSpanId(span.spanId);
|
||||
setTooltipContent({
|
||||
@@ -220,10 +219,10 @@ export function useFlamegraphHover(
|
||||
return isDarkMode ? pair.color : pair.colorDark;
|
||||
})(),
|
||||
event: {
|
||||
name: event.name,
|
||||
name: event.name ?? '',
|
||||
timeOffsetMs: eventTimeMs - span.timestamp,
|
||||
isError: event.isError,
|
||||
attributeMap: event.attributeMap || {},
|
||||
isError: event.isError ?? false,
|
||||
attributeMap: (event.attributeMap as Record<string, string>) ?? {},
|
||||
},
|
||||
});
|
||||
updateCursor(canvas, eventRect.span);
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
SetStateAction,
|
||||
useEffect,
|
||||
} from 'react';
|
||||
import { FlamegraphSpan } from 'types/api/trace/getTraceFlamegraph';
|
||||
import { SpantypesFlamegraphSpanDTO as FlamegraphSpan } from 'api/generated/services/sigNoz.schemas';
|
||||
|
||||
import { MIN_VISIBLE_SPAN_MS } from '../constants';
|
||||
import { ITraceMetadata } from '../types';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { FlamegraphSpan } from 'types/api/trace/getTraceFlamegraph';
|
||||
import { SpantypesFlamegraphSpanDTO as FlamegraphSpan } from 'api/generated/services/sigNoz.schemas';
|
||||
|
||||
import { computeVisualLayout, VisualLayout } from '../computeVisualLayout';
|
||||
import { LayoutWorkerResponse } from '../visualLayoutWorkerTypes';
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import {
|
||||
SpantypesEventDTO as FlamegraphEvent,
|
||||
SpantypesFlamegraphSpanDTO as FlamegraphSpan,
|
||||
} from 'api/generated/services/sigNoz.schemas';
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
import { Event, FlamegraphSpan } from 'types/api/trace/getTraceFlamegraph';
|
||||
|
||||
import { VisualLayout } from './computeVisualLayout';
|
||||
|
||||
@@ -28,7 +31,7 @@ export interface SpanRect {
|
||||
}
|
||||
|
||||
export interface EventRect {
|
||||
event: Event;
|
||||
event: FlamegraphEvent;
|
||||
span: FlamegraphSpan;
|
||||
cx: number;
|
||||
cy: number;
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
generateColorPair,
|
||||
RESERVED_ERROR,
|
||||
} from 'pages/TraceDetailsV3/utils/generateColorPair';
|
||||
import { FlamegraphSpan } from 'types/api/trace/getTraceFlamegraph';
|
||||
import { SpantypesFlamegraphSpanDTO as FlamegraphSpan } from 'api/generated/services/sigNoz.schemas';
|
||||
import { TelemetryFieldKey } from 'types/api/v5/queryRange';
|
||||
|
||||
import {
|
||||
@@ -74,34 +74,25 @@ export function getFlamegraphRowMetrics(
|
||||
|
||||
/**
|
||||
* Resolve the displayed service.name for a flamegraph span. Used by tooltips
|
||||
* (service identity, independent of the active colour-by field). Prefers
|
||||
* `resource['service.name']` with legacy top-level `serviceName` fallback.
|
||||
* (service identity, independent of the active colour-by field). Reads
|
||||
* `resource['service.name']`.
|
||||
*/
|
||||
export function getFlamegraphServiceName(
|
||||
span: Pick<FlamegraphSpan, 'serviceName' | 'resource' | 'attributes'>,
|
||||
span: Partial<Pick<FlamegraphSpan, 'resource' | 'attributes'>>,
|
||||
): string {
|
||||
return getSpanAttribute(span, 'service.name') || span.serviceName || '';
|
||||
return getSpanAttribute(span, 'service.name') || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the value used to bucket a flamegraph span by colour for the given
|
||||
* field. Prefers `resource[field.name]` (new contract from `selectFields`).
|
||||
* For `service.name`, falls back to the legacy top-level `serviceName` when
|
||||
* resource is empty (backward-compat with backends that haven't shipped
|
||||
* `selectFields` yet). For other fields, falls back to `'unknown'`.
|
||||
* field. Prefers `resource[field.name]` (contract from `selectFields`), falling
|
||||
* back to `'unknown'`.
|
||||
*/
|
||||
export function getFlamegraphSpanGroupValue(
|
||||
span: Pick<FlamegraphSpan, 'serviceName' | 'resource' | 'attributes'>,
|
||||
span: Partial<Pick<FlamegraphSpan, 'resource' | 'attributes'>>,
|
||||
field: TelemetryFieldKey,
|
||||
): string {
|
||||
const fromAttribute = getSpanAttribute(span, field.name);
|
||||
if (fromAttribute) {
|
||||
return fromAttribute;
|
||||
}
|
||||
if (field.name === 'service.name') {
|
||||
return span.serviceName || 'unknown';
|
||||
}
|
||||
return 'unknown';
|
||||
return getSpanAttribute(span, field.name) || 'unknown';
|
||||
}
|
||||
|
||||
interface GetSpanColorArgs {
|
||||
@@ -296,7 +287,7 @@ export function drawSpanBar(args: DrawSpanBarArgs): void {
|
||||
return;
|
||||
}
|
||||
|
||||
const eventTimeMs = event.timeUnixNano / 1e6;
|
||||
const eventTimeMs = (event.timeUnixNano ?? 0) / 1e6;
|
||||
const eventOffsetPercent =
|
||||
((eventTimeMs - span.timestamp) / spanDurationMs) * 100;
|
||||
const clampedOffset = clamp(eventOffsetPercent, 1, 99);
|
||||
@@ -306,7 +297,11 @@ export function drawSpanBar(args: DrawSpanBarArgs): void {
|
||||
// Event dots derive from the effective bar color so they track the
|
||||
// light/dark variant the bar is rendered with.
|
||||
const parentBarColor = isDarkMode ? color : colorDark;
|
||||
const dotColor = getEventDotColor(parentBarColor, event.isError, isDarkMode);
|
||||
const dotColor = getEventDotColor(
|
||||
parentBarColor,
|
||||
event.isError ?? false,
|
||||
isDarkMode,
|
||||
);
|
||||
const eventKey = `${span.spanId}-${event.name}-${event.timeUnixNano}`;
|
||||
const isEventHovered = hoveredEventKey === eventKey;
|
||||
const dotSize = isEventHovered
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FlamegraphSpan } from 'types/api/trace/getTraceFlamegraph';
|
||||
import { SpantypesFlamegraphSpanDTO as FlamegraphSpan } from 'api/generated/services/sigNoz.schemas';
|
||||
|
||||
import { VisualLayout } from './computeVisualLayout';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user