mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Jaeger: Add stack trace to span detail row (#26427)
* Add stack trace to span detail row * Modify accordian text not to have a whitespace * Modify stackTrace to stackTraces * Modify AccordianText ti get text component as prop * Fix typecheck and test failure * Span details text area do not wrap line
This commit is contained in:
parent
fabd879cbe
commit
ba50e96544
@ -57,6 +57,7 @@ export type TraceSpanData = {
|
|||||||
tags?: TraceKeyValuePair[];
|
tags?: TraceKeyValuePair[];
|
||||||
references?: TraceSpanReference[];
|
references?: TraceSpanReference[];
|
||||||
warnings?: string[] | null;
|
warnings?: string[] | null;
|
||||||
|
stackTraces?: string[];
|
||||||
flags: number;
|
flags: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@grafana/data": "7.2.0-pre.0",
|
"@grafana/data": "7.2.0-pre.0",
|
||||||
|
"@grafana/ui": "7.2.0-pre.0",
|
||||||
"@types/classnames": "^2.2.7",
|
"@types/classnames": "^2.2.7",
|
||||||
"@types/deep-freeze": "^0.1.1",
|
"@types/deep-freeze": "^0.1.1",
|
||||||
"@types/hoist-non-react-statics": "^3.3.1",
|
"@types/hoist-non-react-statics": "^3.3.1",
|
||||||
@ -22,6 +23,7 @@
|
|||||||
"@types/moment": "^2.13.0",
|
"@types/moment": "^2.13.0",
|
||||||
"@types/react-icons": "2.2.7",
|
"@types/react-icons": "2.2.7",
|
||||||
"@types/recompose": "^0.30.7",
|
"@types/recompose": "^0.30.7",
|
||||||
|
"@types/slate-react": "0.22.5",
|
||||||
"chance": "^1.0.10",
|
"chance": "^1.0.10",
|
||||||
"classnames": "^2.2.5",
|
"classnames": "^2.2.5",
|
||||||
"combokeys": "^3.0.0",
|
"combokeys": "^3.0.0",
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { shallow } from 'enzyme';
|
import { mount } from 'enzyme';
|
||||||
import AccordianText from './AccordianText';
|
import AccordianText from './AccordianText';
|
||||||
import TextList from './TextList';
|
import TextList from './TextList';
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ describe('<AccordianText>', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
wrapper = shallow(<AccordianText {...props} />);
|
wrapper = mount(<AccordianText {...props} />);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders without exploding', () => {
|
it('renders without exploding', () => {
|
||||||
|
@ -40,17 +40,31 @@ const getStyles = createStyle((theme: Theme) => {
|
|||||||
|
|
||||||
type AccordianTextProps = {
|
type AccordianTextProps = {
|
||||||
className?: string | TNil;
|
className?: string | TNil;
|
||||||
data: string[];
|
|
||||||
headerClassName?: string | TNil;
|
headerClassName?: string | TNil;
|
||||||
|
data: string[];
|
||||||
highContrast?: boolean;
|
highContrast?: boolean;
|
||||||
interactive?: boolean;
|
interactive?: boolean;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
label: React.ReactNode;
|
label: React.ReactNode | string;
|
||||||
onToggle?: null | (() => void);
|
onToggle?: null | (() => void);
|
||||||
|
TextComponent?: React.ElementType<{ data: string[] }>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function DefaultTextComponent({ data }: { data: string[] }) {
|
||||||
|
return <TextList data={data} />;
|
||||||
|
}
|
||||||
|
|
||||||
export default function AccordianText(props: AccordianTextProps) {
|
export default function AccordianText(props: AccordianTextProps) {
|
||||||
const { className, data, headerClassName, interactive, isOpen, label, onToggle } = props;
|
const {
|
||||||
|
className,
|
||||||
|
data,
|
||||||
|
headerClassName,
|
||||||
|
interactive,
|
||||||
|
isOpen,
|
||||||
|
label,
|
||||||
|
onToggle,
|
||||||
|
TextComponent = DefaultTextComponent,
|
||||||
|
} = props;
|
||||||
const isEmpty = !Array.isArray(data) || !data.length;
|
const isEmpty = !Array.isArray(data) || !data.length;
|
||||||
const accordianKeyValuesStyles = getAccordianKeyValuesStyles(useTheme());
|
const accordianKeyValuesStyles = getAccordianKeyValuesStyles(useTheme());
|
||||||
const iconCls = cx(uAlignIcon, { [accordianKeyValuesStyles.emptyIcon]: isEmpty });
|
const iconCls = cx(uAlignIcon, { [accordianKeyValuesStyles.emptyIcon]: isEmpty });
|
||||||
@ -68,9 +82,10 @@ export default function AccordianText(props: AccordianTextProps) {
|
|||||||
return (
|
return (
|
||||||
<div className={className || ''}>
|
<div className={className || ''}>
|
||||||
<div className={cx(styles.header, headerClassName)} {...headerProps} data-test-id="AccordianText--header">
|
<div className={cx(styles.header, headerClassName)} {...headerProps} data-test-id="AccordianText--header">
|
||||||
{arrow} <strong>{label}</strong> ({data.length})
|
{arrow}
|
||||||
|
<strong>{label}</strong> ({data.length})
|
||||||
</div>
|
</div>
|
||||||
{isOpen && <TextList data={data} />}
|
{isOpen && <TextComponent data={data} />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ export default class DetailState {
|
|||||||
isProcessOpen: boolean;
|
isProcessOpen: boolean;
|
||||||
logs: { isOpen: boolean; openedItems: Set<TraceLog> };
|
logs: { isOpen: boolean; openedItems: Set<TraceLog> };
|
||||||
isWarningsOpen: boolean;
|
isWarningsOpen: boolean;
|
||||||
|
isStackTracesOpen: boolean;
|
||||||
isReferencesOpen: boolean;
|
isReferencesOpen: boolean;
|
||||||
|
|
||||||
constructor(oldState?: DetailState) {
|
constructor(oldState?: DetailState) {
|
||||||
@ -30,12 +31,14 @@ export default class DetailState {
|
|||||||
isProcessOpen,
|
isProcessOpen,
|
||||||
isReferencesOpen,
|
isReferencesOpen,
|
||||||
isWarningsOpen,
|
isWarningsOpen,
|
||||||
|
isStackTracesOpen,
|
||||||
logs,
|
logs,
|
||||||
}: DetailState | Record<string, undefined> = oldState || {};
|
}: DetailState | Record<string, undefined> = oldState || {};
|
||||||
this.isTagsOpen = Boolean(isTagsOpen);
|
this.isTagsOpen = Boolean(isTagsOpen);
|
||||||
this.isProcessOpen = Boolean(isProcessOpen);
|
this.isProcessOpen = Boolean(isProcessOpen);
|
||||||
this.isReferencesOpen = Boolean(isReferencesOpen);
|
this.isReferencesOpen = Boolean(isReferencesOpen);
|
||||||
this.isWarningsOpen = Boolean(isWarningsOpen);
|
this.isWarningsOpen = Boolean(isWarningsOpen);
|
||||||
|
this.isStackTracesOpen = Boolean(isStackTracesOpen);
|
||||||
this.logs = {
|
this.logs = {
|
||||||
isOpen: Boolean(logs && logs.isOpen),
|
isOpen: Boolean(logs && logs.isOpen),
|
||||||
openedItems: logs && logs.openedItems ? new Set(logs.openedItems) : new Set(),
|
openedItems: logs && logs.openedItems ? new Set(logs.openedItems) : new Set(),
|
||||||
@ -66,6 +69,12 @@ export default class DetailState {
|
|||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleStackTraces() {
|
||||||
|
const next = new DetailState(this);
|
||||||
|
next.isStackTracesOpen = !this.isStackTracesOpen;
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
toggleLogs() {
|
toggleLogs() {
|
||||||
const next = new DetailState(this);
|
const next = new DetailState(this);
|
||||||
next.logs.isOpen = !this.logs.isOpen;
|
next.logs.isOpen = !this.logs.isOpen;
|
||||||
|
@ -30,6 +30,7 @@ import AccordianReferences from './AccordianReferences';
|
|||||||
import { autoColor, createStyle, Theme, useTheme } from '../../Theme';
|
import { autoColor, createStyle, Theme, useTheme } from '../../Theme';
|
||||||
import { UIDivider } from '../../uiElementsContext';
|
import { UIDivider } from '../../uiElementsContext';
|
||||||
import { ubFlex, ubFlexAuto, ubItemsCenter, ubM0, ubMb1, ubMy1, ubTxRightAlign } from '../../uberUtilityStyles';
|
import { ubFlex, ubFlexAuto, ubItemsCenter, ubM0, ubMb1, ubMy1, ubTxRightAlign } from '../../uberUtilityStyles';
|
||||||
|
import { TextArea } from '@grafana/ui';
|
||||||
|
|
||||||
const getStyles = createStyle((theme: Theme) => {
|
const getStyles = createStyle((theme: Theme) => {
|
||||||
return {
|
return {
|
||||||
@ -94,6 +95,10 @@ const getStyles = createStyle((theme: Theme) => {
|
|||||||
label: AccordianWarningsLabel;
|
label: AccordianWarningsLabel;
|
||||||
color: ${autoColor(theme, '#d36c08')};
|
color: ${autoColor(theme, '#d36c08')};
|
||||||
`,
|
`,
|
||||||
|
Textarea: css`
|
||||||
|
word-break: break-all;
|
||||||
|
white-space: pre;
|
||||||
|
`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -107,6 +112,7 @@ type SpanDetailProps = {
|
|||||||
tagsToggle: (spanID: string) => void;
|
tagsToggle: (spanID: string) => void;
|
||||||
traceStartTime: number;
|
traceStartTime: number;
|
||||||
warningsToggle: (spanID: string) => void;
|
warningsToggle: (spanID: string) => void;
|
||||||
|
stackTracesToggle: (spanID: string) => void;
|
||||||
referencesToggle: (spanID: string) => void;
|
referencesToggle: (spanID: string) => void;
|
||||||
focusSpan: (uiFind: string) => void;
|
focusSpan: (uiFind: string) => void;
|
||||||
};
|
};
|
||||||
@ -122,11 +128,30 @@ export default function SpanDetail(props: SpanDetailProps) {
|
|||||||
tagsToggle,
|
tagsToggle,
|
||||||
traceStartTime,
|
traceStartTime,
|
||||||
warningsToggle,
|
warningsToggle,
|
||||||
|
stackTracesToggle,
|
||||||
referencesToggle,
|
referencesToggle,
|
||||||
focusSpan,
|
focusSpan,
|
||||||
} = props;
|
} = props;
|
||||||
const { isTagsOpen, isProcessOpen, logs: logsState, isWarningsOpen, isReferencesOpen } = detailState;
|
const {
|
||||||
const { operationName, process, duration, relativeStartTime, spanID, logs, tags, warnings, references } = span;
|
isTagsOpen,
|
||||||
|
isProcessOpen,
|
||||||
|
logs: logsState,
|
||||||
|
isWarningsOpen,
|
||||||
|
isReferencesOpen,
|
||||||
|
isStackTracesOpen,
|
||||||
|
} = detailState;
|
||||||
|
const {
|
||||||
|
operationName,
|
||||||
|
process,
|
||||||
|
duration,
|
||||||
|
relativeStartTime,
|
||||||
|
spanID,
|
||||||
|
logs,
|
||||||
|
tags,
|
||||||
|
warnings,
|
||||||
|
references,
|
||||||
|
stackTraces,
|
||||||
|
} = span;
|
||||||
const overviewItems = [
|
const overviewItems = [
|
||||||
{
|
{
|
||||||
key: 'svc',
|
key: 'svc',
|
||||||
@ -195,6 +220,24 @@ export default function SpanDetail(props: SpanDetailProps) {
|
|||||||
onToggle={() => warningsToggle(spanID)}
|
onToggle={() => warningsToggle(spanID)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{stackTraces && stackTraces.length && (
|
||||||
|
<AccordianText
|
||||||
|
label="Stack trace"
|
||||||
|
data={stackTraces}
|
||||||
|
isOpen={isStackTracesOpen}
|
||||||
|
TextComponent={textComponentProps => (
|
||||||
|
<TextArea
|
||||||
|
className={styles.Textarea}
|
||||||
|
style={{ cursor: 'unset' }}
|
||||||
|
readOnly
|
||||||
|
cols={10}
|
||||||
|
rows={10}
|
||||||
|
value={textComponentProps.data}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
onToggle={() => stackTracesToggle(spanID)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{references && references.length > 1 && (
|
{references && references.length > 1 && (
|
||||||
<AccordianReferences
|
<AccordianReferences
|
||||||
data={references}
|
data={references}
|
||||||
|
@ -76,6 +76,7 @@ type SpanDetailRowProps = {
|
|||||||
processToggle: (spanID: string) => void;
|
processToggle: (spanID: string) => void;
|
||||||
referencesToggle: (spanID: string) => void;
|
referencesToggle: (spanID: string) => void;
|
||||||
warningsToggle: (spanID: string) => void;
|
warningsToggle: (spanID: string) => void;
|
||||||
|
stackTracesToggle: (spanID: string) => void;
|
||||||
span: TraceSpan;
|
span: TraceSpan;
|
||||||
tagsToggle: (spanID: string) => void;
|
tagsToggle: (spanID: string) => void;
|
||||||
traceStartTime: number;
|
traceStartTime: number;
|
||||||
@ -106,6 +107,7 @@ export class UnthemedSpanDetailRow extends React.PureComponent<SpanDetailRowProp
|
|||||||
processToggle,
|
processToggle,
|
||||||
referencesToggle,
|
referencesToggle,
|
||||||
warningsToggle,
|
warningsToggle,
|
||||||
|
stackTracesToggle,
|
||||||
span,
|
span,
|
||||||
tagsToggle,
|
tagsToggle,
|
||||||
traceStartTime,
|
traceStartTime,
|
||||||
@ -147,6 +149,7 @@ export class UnthemedSpanDetailRow extends React.PureComponent<SpanDetailRowProp
|
|||||||
processToggle={processToggle}
|
processToggle={processToggle}
|
||||||
referencesToggle={referencesToggle}
|
referencesToggle={referencesToggle}
|
||||||
warningsToggle={warningsToggle}
|
warningsToggle={warningsToggle}
|
||||||
|
stackTracesToggle={stackTracesToggle}
|
||||||
span={span}
|
span={span}
|
||||||
tagsToggle={tagsToggle}
|
tagsToggle={tagsToggle}
|
||||||
traceStartTime={traceStartTime}
|
traceStartTime={traceStartTime}
|
||||||
|
@ -68,6 +68,7 @@ type TVirtualizedTraceViewOwnProps = {
|
|||||||
detailLogItemToggle: (spanID: string, log: TraceLog) => void;
|
detailLogItemToggle: (spanID: string, log: TraceLog) => void;
|
||||||
detailLogsToggle: (spanID: string) => void;
|
detailLogsToggle: (spanID: string) => void;
|
||||||
detailWarningsToggle: (spanID: string) => void;
|
detailWarningsToggle: (spanID: string) => void;
|
||||||
|
detailStackTracesToggle: (spanID: string) => void;
|
||||||
detailReferencesToggle: (spanID: string) => void;
|
detailReferencesToggle: (spanID: string) => void;
|
||||||
detailProcessToggle: (spanID: string) => void;
|
detailProcessToggle: (spanID: string) => void;
|
||||||
detailTagsToggle: (spanID: string) => void;
|
detailTagsToggle: (spanID: string) => void;
|
||||||
@ -392,6 +393,7 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
|
|||||||
detailProcessToggle,
|
detailProcessToggle,
|
||||||
detailReferencesToggle,
|
detailReferencesToggle,
|
||||||
detailWarningsToggle,
|
detailWarningsToggle,
|
||||||
|
detailStackTracesToggle,
|
||||||
detailStates,
|
detailStates,
|
||||||
detailTagsToggle,
|
detailTagsToggle,
|
||||||
detailToggle,
|
detailToggle,
|
||||||
@ -423,6 +425,7 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
|
|||||||
processToggle={detailProcessToggle}
|
processToggle={detailProcessToggle}
|
||||||
referencesToggle={detailReferencesToggle}
|
referencesToggle={detailReferencesToggle}
|
||||||
warningsToggle={detailWarningsToggle}
|
warningsToggle={detailWarningsToggle}
|
||||||
|
stackTracesToggle={detailStackTracesToggle}
|
||||||
span={span}
|
span={span}
|
||||||
tagsToggle={detailTagsToggle}
|
tagsToggle={detailTagsToggle}
|
||||||
traceStartTime={trace.startTime}
|
traceStartTime={trace.startTime}
|
||||||
|
@ -89,6 +89,7 @@ type TProps = TExtractUiFindFromStateReturn & {
|
|||||||
detailLogItemToggle: (spanID: string, log: TraceLog) => void;
|
detailLogItemToggle: (spanID: string, log: TraceLog) => void;
|
||||||
detailLogsToggle: (spanID: string) => void;
|
detailLogsToggle: (spanID: string) => void;
|
||||||
detailWarningsToggle: (spanID: string) => void;
|
detailWarningsToggle: (spanID: string) => void;
|
||||||
|
detailStackTracesToggle: (spanID: string) => void;
|
||||||
detailReferencesToggle: (spanID: string) => void;
|
detailReferencesToggle: (spanID: string) => void;
|
||||||
detailProcessToggle: (spanID: string) => void;
|
detailProcessToggle: (spanID: string) => void;
|
||||||
detailTagsToggle: (spanID: string) => void;
|
detailTagsToggle: (spanID: string) => void;
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": "node_modules/@types",
|
"baseUrl": "node_modules/@types",
|
||||||
|
"paths": {
|
||||||
|
"@grafana/slate-react": ["slate-react"]
|
||||||
|
},
|
||||||
"typeRoots": ["node_modules/@types"]
|
"typeRoots": ["node_modules/@types"]
|
||||||
},
|
},
|
||||||
"extends": "@grafana/tsconfig",
|
"extends": "@grafana/tsconfig",
|
||||||
"include": ["src/**/*.ts*", "typings", "../../public/app/types/jquery/*.ts"]
|
"include": ["src/**/*.ts*", "typings", "../../public/app/types/jquery/*.ts", "../../public/app/types/svg.d.ts"]
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ export function TraceView(props: Props) {
|
|||||||
detailReferencesToggle,
|
detailReferencesToggle,
|
||||||
detailTagsToggle,
|
detailTagsToggle,
|
||||||
detailWarningsToggle,
|
detailWarningsToggle,
|
||||||
|
detailStackTracesToggle,
|
||||||
} = useDetailState();
|
} = useDetailState();
|
||||||
const { removeHoverIndentGuideId, addHoverIndentGuideId, hoverIndentGuideIds } = useHoverIndentGuide();
|
const { removeHoverIndentGuideId, addHoverIndentGuideId, hoverIndentGuideIds } = useHoverIndentGuide();
|
||||||
const { viewRange, updateViewRangeTime, updateNextViewRangeTime } = useViewRange();
|
const { viewRange, updateViewRangeTime, updateNextViewRangeTime } = useViewRange();
|
||||||
@ -126,6 +127,7 @@ export function TraceView(props: Props) {
|
|||||||
detailLogItemToggle={detailLogItemToggle}
|
detailLogItemToggle={detailLogItemToggle}
|
||||||
detailLogsToggle={detailLogsToggle}
|
detailLogsToggle={detailLogsToggle}
|
||||||
detailWarningsToggle={detailWarningsToggle}
|
detailWarningsToggle={detailWarningsToggle}
|
||||||
|
detailStackTracesToggle={detailStackTracesToggle}
|
||||||
detailReferencesToggle={detailReferencesToggle}
|
detailReferencesToggle={detailReferencesToggle}
|
||||||
detailProcessToggle={detailProcessToggle}
|
detailProcessToggle={detailProcessToggle}
|
||||||
detailTagsToggle={detailTagsToggle}
|
detailTagsToggle={detailTagsToggle}
|
||||||
|
@ -44,6 +44,9 @@ export function useDetailState() {
|
|||||||
detailWarningsToggle: useCallback(makeDetailSubsectionToggle('warnings', detailStates, setDetailStates), [
|
detailWarningsToggle: useCallback(makeDetailSubsectionToggle('warnings', detailStates, setDetailStates), [
|
||||||
detailStates,
|
detailStates,
|
||||||
]),
|
]),
|
||||||
|
detailStackTracesToggle: useCallback(makeDetailSubsectionToggle('stackTraces', detailStates, setDetailStates), [
|
||||||
|
detailStates,
|
||||||
|
]),
|
||||||
detailReferencesToggle: useCallback(makeDetailSubsectionToggle('references', detailStates, setDetailStates), [
|
detailReferencesToggle: useCallback(makeDetailSubsectionToggle('references', detailStates, setDetailStates), [
|
||||||
detailStates,
|
detailStates,
|
||||||
]),
|
]),
|
||||||
@ -55,7 +58,7 @@ export function useDetailState() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function makeDetailSubsectionToggle(
|
function makeDetailSubsectionToggle(
|
||||||
subSection: 'tags' | 'process' | 'logs' | 'warnings' | 'references',
|
subSection: 'tags' | 'process' | 'logs' | 'warnings' | 'references' | 'stackTraces',
|
||||||
detailStates: Map<string, DetailState>,
|
detailStates: Map<string, DetailState>,
|
||||||
setDetailStates: (detailStates: Map<string, DetailState>) => void
|
setDetailStates: (detailStates: Map<string, DetailState>) => void
|
||||||
) {
|
) {
|
||||||
@ -73,6 +76,8 @@ function makeDetailSubsectionToggle(
|
|||||||
detailState = old.toggleWarnings();
|
detailState = old.toggleWarnings();
|
||||||
} else if (subSection === 'references') {
|
} else if (subSection === 'references') {
|
||||||
detailState = old.toggleReferences();
|
detailState = old.toggleReferences();
|
||||||
|
} else if (subSection === 'stackTraces') {
|
||||||
|
detailState = old.toggleStackTraces();
|
||||||
} else {
|
} else {
|
||||||
detailState = old.toggleLogs();
|
detailState = old.toggleLogs();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user