mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
parent
e488f5d87a
commit
f385912bef
20 changed files with 1475 additions and 1169 deletions
|
@ -17,7 +17,7 @@ export interface ErrorTab {
|
|||
export const logStacktraceTab: ErrorTab = {
|
||||
key: 'log_stacktrace',
|
||||
label: i18n.translate('xpack.apm.propertiesTable.tabs.logStacktraceLabel', {
|
||||
defaultMessage: 'Log stacktrace'
|
||||
defaultMessage: 'Log stack trace'
|
||||
})
|
||||
};
|
||||
|
||||
|
@ -26,7 +26,7 @@ export const exceptionStacktraceTab: ErrorTab = {
|
|||
label: i18n.translate(
|
||||
'xpack.apm.propertiesTable.tabs.exceptionStacktraceLabel',
|
||||
{
|
||||
defaultMessage: 'Exception stacktrace'
|
||||
defaultMessage: 'Exception stack trace'
|
||||
}
|
||||
)
|
||||
};
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { ExceptionStacktrace } from './ExceptionStacktrace';
|
||||
|
||||
describe('ExceptionStacktrace', () => {
|
||||
describe('render', () => {
|
||||
it('renders', () => {
|
||||
const props = { exceptions: [] };
|
||||
|
||||
expect(() =>
|
||||
shallow(<ExceptionStacktrace {...props} />)
|
||||
).not.toThrowError();
|
||||
});
|
||||
|
||||
describe('with a stack trace', () => {
|
||||
it('renders the stack trace', () => {
|
||||
const props = { exceptions: [{}] };
|
||||
|
||||
expect(
|
||||
shallow(<ExceptionStacktrace {...props} />).find('Stacktrace')
|
||||
).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with more than one stack trace', () => {
|
||||
it('renders a cause stack trace', () => {
|
||||
const props = { exceptions: [{}, {}] };
|
||||
|
||||
expect(
|
||||
shallow(<ExceptionStacktrace {...props} />).find('CauseStacktrace')
|
||||
).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiTitle } from '@elastic/eui';
|
||||
import { idx } from '@kbn/elastic-idx/target';
|
||||
import { Exception } from '../../../../../typings/es_schemas/raw/ErrorRaw';
|
||||
import { Stacktrace } from '../../../shared/Stacktrace';
|
||||
import { CauseStacktrace } from '../../../shared/Stacktrace/CauseStacktrace';
|
||||
|
||||
interface ExceptionStacktraceProps {
|
||||
codeLanguage?: string;
|
||||
exceptions: Exception[];
|
||||
}
|
||||
|
||||
export function ExceptionStacktrace({
|
||||
codeLanguage,
|
||||
exceptions
|
||||
}: ExceptionStacktraceProps) {
|
||||
const title = idx(exceptions, _ => _[0].message);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiTitle size="xs">
|
||||
<h4>{title}</h4>
|
||||
</EuiTitle>
|
||||
{exceptions.map((ex, index) => {
|
||||
return index === 0 ? (
|
||||
<Stacktrace
|
||||
key={index}
|
||||
stackframes={ex.stacktrace}
|
||||
codeLanguage={codeLanguage}
|
||||
/>
|
||||
) : (
|
||||
<CauseStacktrace
|
||||
codeLanguage={codeLanguage}
|
||||
key={index}
|
||||
id={index.toString()}
|
||||
message={ex.message}
|
||||
stackframes={ex.stacktrace}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -46,7 +46,7 @@ exports[`DetailView should render TabContent 1`] = `
|
|||
currentTab={
|
||||
Object {
|
||||
"key": "exception_stacktrace",
|
||||
"label": "Exception stacktrace",
|
||||
"label": "Exception stack trace",
|
||||
}
|
||||
}
|
||||
error={
|
||||
|
@ -71,7 +71,7 @@ exports[`DetailView should render tabs 1`] = `
|
|||
key="exception_stacktrace"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Exception stacktrace
|
||||
Exception stack trace
|
||||
</EuiTab>
|
||||
<EuiTab
|
||||
isSelected={false}
|
||||
|
|
|
@ -40,6 +40,7 @@ import { TimestampTooltip } from '../../../shared/TimestampTooltip';
|
|||
import { HttpInfoSummaryItem } from '../../../shared/Summary/HttpInfoSummaryItem';
|
||||
import { TransactionDetailLink } from '../../../shared/Links/apm/TransactionDetailLink';
|
||||
import { UserAgentSummaryItem } from '../../../shared/Summary/UserAgentSummaryItem';
|
||||
import { ExceptionStacktrace } from './ExceptionStacktrace';
|
||||
|
||||
const HeaderContainer = styled.div`
|
||||
display: flex;
|
||||
|
@ -180,7 +181,7 @@ export function DetailView({ errorGroup, urlParams, location }: Props) {
|
|||
);
|
||||
}
|
||||
|
||||
export function TabContent({
|
||||
function TabContent({
|
||||
error,
|
||||
currentTab
|
||||
}: {
|
||||
|
@ -188,7 +189,7 @@ export function TabContent({
|
|||
currentTab: ErrorTab;
|
||||
}) {
|
||||
const codeLanguage = idx(error, _ => _.service.language.name);
|
||||
const excStackframes = idx(error, _ => _.error.exception[0].stacktrace);
|
||||
const exceptions = idx(error, _ => _.error.exception) || [];
|
||||
const logStackframes = idx(error, _ => _.error.log.stacktrace);
|
||||
|
||||
switch (currentTab.key) {
|
||||
|
@ -198,7 +199,10 @@ export function TabContent({
|
|||
);
|
||||
case exceptionStacktraceTab.key:
|
||||
return (
|
||||
<Stacktrace stackframes={excStackframes} codeLanguage={codeLanguage} />
|
||||
<ExceptionStacktrace
|
||||
codeLanguage={codeLanguage}
|
||||
exceptions={exceptions}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return <ErrorMetadata error={error} />;
|
||||
|
|
|
@ -4,12 +4,11 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiLink } from '@elastic/eui';
|
||||
import { EuiIcon, EuiLink } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { Fragment, useEffect, useRef, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { px, units } from '../../../../../../../style/variables';
|
||||
import { Ellipsis } from '../../../../../../shared/Icons';
|
||||
|
||||
const ToggleButtonContainer = styled.div`
|
||||
margin-top: ${px(units.half)};
|
||||
|
@ -55,7 +54,13 @@ export const TruncateHeightSection: React.SFC<Props> = ({
|
|||
setIsOpen(!isOpen);
|
||||
}}
|
||||
>
|
||||
<Ellipsis horizontal={!isOpen} />{' '}
|
||||
<EuiIcon
|
||||
style={{
|
||||
transition: 'transform 0.1s',
|
||||
transform: `rotate(${isOpen ? 90 : 0}deg)`
|
||||
}}
|
||||
type="arrowRight"
|
||||
/>{' '}
|
||||
{isOpen
|
||||
? i18n.translate('xpack.apm.toggleHeight.showLessButtonLabel', {
|
||||
defaultMessage: 'Show fewer lines'
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiIcon } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
|
||||
export function Ellipsis({ horizontal }: { horizontal: boolean }) {
|
||||
return (
|
||||
<EuiIcon
|
||||
style={{
|
||||
transition: 'transform 0.1s',
|
||||
transform: `rotate(${horizontal ? 90 : 0}deg)`
|
||||
}}
|
||||
type="arrowRight"
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import { CauseStacktrace } from './CauseStacktrace';
|
||||
|
||||
describe('CauseStacktrace', () => {
|
||||
describe('render', () => {
|
||||
describe('with no stack trace', () => {
|
||||
it('renders without the accordion', () => {
|
||||
const props = { id: 'testId', message: 'testMessage' };
|
||||
|
||||
expect(
|
||||
mount(<CauseStacktrace {...props} />).find('CausedBy')
|
||||
).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with no message and a stack trace', () => {
|
||||
it('says "Caused by …', () => {
|
||||
const props = {
|
||||
id: 'testId',
|
||||
stackframes: [{ filename: 'testFilename', line: { number: 1 } }]
|
||||
};
|
||||
|
||||
expect(
|
||||
mount(<CauseStacktrace {...props} />)
|
||||
.find('EuiTitle span')
|
||||
.text()
|
||||
).toEqual('…');
|
||||
});
|
||||
});
|
||||
|
||||
describe('with a message and a stack trace', () => {
|
||||
it('renders with the accordion', () => {
|
||||
const props = {
|
||||
id: 'testId',
|
||||
message: 'testMessage',
|
||||
stackframes: [{ filename: 'testFilename', line: { number: 1 } }]
|
||||
};
|
||||
|
||||
expect(
|
||||
shallow(<CauseStacktrace {...props} />).find('Styled(EuiAccordion)')
|
||||
).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import theme from '@elastic/eui/dist/eui_theme_light.json';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiAccordion, EuiTitle } from '@elastic/eui';
|
||||
import { px, unit } from '../../../style/variables';
|
||||
import { Stacktrace } from '.';
|
||||
import { IStackframe } from '../../../../typings/es_schemas/raw/fields/Stackframe';
|
||||
|
||||
// @ts-ignore Styled Components has trouble inferring the types of the default props here.
|
||||
const Accordion = styled(EuiAccordion)`
|
||||
border-top: ${theme.euiBorderThin};
|
||||
`;
|
||||
|
||||
const CausedByContainer = styled('h5')`
|
||||
padding: ${theme.spacerSizes.s} 0;
|
||||
`;
|
||||
|
||||
const CausedByHeading = styled('span')`
|
||||
color: ${theme.textColors.subdued};
|
||||
display: block;
|
||||
font-size: ${theme.euiFontSizeXS};
|
||||
font-weight: ${theme.euiFontWeightBold};
|
||||
text-transform: uppercase;
|
||||
`;
|
||||
|
||||
const FramesContainer = styled('div')`
|
||||
padding-left: ${px(unit)};
|
||||
`;
|
||||
|
||||
function CausedBy({ message }: { message: string }) {
|
||||
return (
|
||||
<CausedByContainer>
|
||||
<CausedByHeading>
|
||||
{i18n.translate(
|
||||
'xpack.apm.stacktraceTab.causedByFramesToogleButtonLabel',
|
||||
{
|
||||
defaultMessage: 'Caused By'
|
||||
}
|
||||
)}
|
||||
</CausedByHeading>
|
||||
<EuiTitle size="xxs">
|
||||
<span>{message}</span>
|
||||
</EuiTitle>
|
||||
</CausedByContainer>
|
||||
);
|
||||
}
|
||||
|
||||
interface CauseStacktraceProps {
|
||||
codeLanguage?: string;
|
||||
id: string;
|
||||
message?: string;
|
||||
stackframes?: IStackframe[];
|
||||
}
|
||||
|
||||
export function CauseStacktrace({
|
||||
codeLanguage,
|
||||
id,
|
||||
message = '…',
|
||||
stackframes = []
|
||||
}: CauseStacktraceProps) {
|
||||
if (stackframes.length === 0) {
|
||||
return <CausedBy message={message} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Accordion buttonContent={<CausedBy message={message} />} id={id}>
|
||||
<FramesContainer>
|
||||
<Stacktrace stackframes={stackframes} codeLanguage={codeLanguage} />
|
||||
</FramesContainer>
|
||||
</Accordion>
|
||||
);
|
||||
}
|
|
@ -32,7 +32,7 @@ registerLanguage('ruby', ruby);
|
|||
|
||||
const ContextContainer = styled.div`
|
||||
position: relative;
|
||||
border-radius: 0 0 ${borderRadius} ${borderRadius};
|
||||
border-radius: ${borderRadius};
|
||||
`;
|
||||
|
||||
const LINE_HEIGHT = units.eighth * 9;
|
||||
|
@ -49,7 +49,7 @@ const LineNumberContainer = styled.div<{ isLibraryFrame: boolean }>`
|
|||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-radius: 0 0 0 ${borderRadius};
|
||||
border-radius: ${borderRadius};
|
||||
background: ${props =>
|
||||
props.isLibraryFrame
|
||||
? theme.euiColorEmptyShade
|
||||
|
|
|
@ -12,16 +12,17 @@ import { IStackframe } from '../../../../typings/es_schemas/raw/fields/Stackfram
|
|||
import { fontFamilyCode, fontSize, px, units } from '../../../style/variables';
|
||||
|
||||
const FileDetails = styled.div`
|
||||
color: ${theme.euiColorMediumShade};
|
||||
padding: ${px(units.half)};
|
||||
color: ${theme.euiColorDarkShade};
|
||||
padding: ${px(units.half)} 0;
|
||||
font-family: ${fontFamilyCode};
|
||||
font-size: ${fontSize};
|
||||
`;
|
||||
|
||||
const LibraryFrameFileDetail = styled.span`
|
||||
color: ${theme.euiColorDarkShade};
|
||||
`;
|
||||
|
||||
const AppFrameFileDetail = styled.span`
|
||||
font-weight: bold;
|
||||
color: ${theme.euiColorFullShade};
|
||||
`;
|
||||
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiLink, EuiSpacer } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { Fragment } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { IStackframe } from '../../../../typings/es_schemas/raw/fields/Stackframe';
|
||||
import { Ellipsis } from '../../shared/Icons';
|
||||
import { Stackframe } from './Stackframe';
|
||||
|
||||
const LibraryFrameToggle = styled.div`
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
stackframes: IStackframe[];
|
||||
codeLanguage?: string;
|
||||
initialVisiblity: boolean;
|
||||
}
|
||||
|
||||
interface State {
|
||||
isVisible: boolean;
|
||||
}
|
||||
|
||||
export class LibraryStackFrames extends React.Component<Props, State> {
|
||||
public state = {
|
||||
isVisible: this.props.initialVisiblity
|
||||
};
|
||||
|
||||
public onClick = () => {
|
||||
this.setState(({ isVisible }) => ({ isVisible: !isVisible }));
|
||||
};
|
||||
|
||||
public render() {
|
||||
const { stackframes, codeLanguage } = this.props;
|
||||
const { isVisible } = this.state;
|
||||
if (stackframes.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (stackframes.length === 1) {
|
||||
return (
|
||||
<Stackframe
|
||||
isLibraryFrame
|
||||
codeLanguage={codeLanguage}
|
||||
stackframe={stackframes[0]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<LibraryFrameToggle>
|
||||
<EuiLink onClick={this.onClick}>
|
||||
<Ellipsis horizontal={isVisible} />{' '}
|
||||
{i18n.translate(
|
||||
'xpack.apm.stacktraceTab.libraryFramesToogleButtonLabel',
|
||||
{
|
||||
defaultMessage: '{stackframesLength} library frames',
|
||||
values: { stackframesLength: stackframes.length }
|
||||
}
|
||||
)}
|
||||
</EuiLink>
|
||||
</LibraryFrameToggle>
|
||||
|
||||
<div>
|
||||
{isVisible && (
|
||||
<Fragment>
|
||||
<EuiSpacer size="m" />
|
||||
{stackframes.map((stackframe, i) => (
|
||||
<Stackframe
|
||||
key={i}
|
||||
isLibraryFrame
|
||||
codeLanguage={codeLanguage}
|
||||
stackframe={stackframe}
|
||||
/>
|
||||
))}
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { LibraryStacktrace } from './LibraryStacktrace';
|
||||
|
||||
describe('LibraryStacktrace', () => {
|
||||
describe('render', () => {
|
||||
describe('with no stack frames', () => {
|
||||
it('renders null', () => {
|
||||
const props = { id: 'testId', stackframes: [] };
|
||||
|
||||
expect(shallow(<LibraryStacktrace {...props} />).html()).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with stack frames', () => {
|
||||
it('renders an accordion', () => {
|
||||
const props = {
|
||||
id: 'testId',
|
||||
stackframes: [{ filename: 'testFilename', line: { number: 1 } }]
|
||||
};
|
||||
|
||||
expect(
|
||||
shallow(<LibraryStacktrace {...props} />).find('EuiAccordion')
|
||||
).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiAccordion } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { IStackframe } from '../../../../typings/es_schemas/raw/fields/Stackframe';
|
||||
import { Stackframe } from './Stackframe';
|
||||
import { px, unit } from '../../../style/variables';
|
||||
|
||||
const FramesContainer = styled('div')`
|
||||
padding-left: ${px(unit)};
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
codeLanguage?: string;
|
||||
stackframes: IStackframe[];
|
||||
id: string;
|
||||
}
|
||||
|
||||
export function LibraryStacktrace({ codeLanguage, id, stackframes }: Props) {
|
||||
if (stackframes.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiAccordion
|
||||
buttonContent={i18n.translate(
|
||||
'xpack.apm.stacktraceTab.libraryFramesToogleButtonLabel',
|
||||
{
|
||||
defaultMessage:
|
||||
'{count, plural, one {# library frame} other {# library frames}}',
|
||||
values: { count: stackframes.length }
|
||||
}
|
||||
)}
|
||||
id={id}
|
||||
>
|
||||
<FramesContainer>
|
||||
{stackframes.map((stackframe, i) => (
|
||||
<Stackframe
|
||||
key={i}
|
||||
id={i.toString(10)}
|
||||
isLibraryFrame
|
||||
codeLanguage={codeLanguage}
|
||||
stackframe={stackframe}
|
||||
/>
|
||||
))}
|
||||
</FramesContainer>
|
||||
</EuiAccordion>
|
||||
);
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
import theme from '@elastic/eui/dist/eui_theme_light.json';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { EuiAccordion } from '@elastic/eui';
|
||||
import {
|
||||
IStackframe,
|
||||
IStackframeWithLineContext
|
||||
|
@ -16,16 +17,11 @@ import {
|
|||
fontFamilyCode,
|
||||
fontSize
|
||||
} from '../../../style/variables';
|
||||
import { FrameHeading } from '../Stacktrace/FrameHeading';
|
||||
import { FrameHeading } from './FrameHeading';
|
||||
import { Context } from './Context';
|
||||
import { Variables } from './Variables';
|
||||
|
||||
const CodeHeader = styled.div`
|
||||
border-bottom: 1px solid ${theme.euiColorLightShade};
|
||||
border-radius: ${borderRadius} ${borderRadius} 0 0;
|
||||
`;
|
||||
|
||||
const Container = styled.div<{ isLibraryFrame: boolean }>`
|
||||
const ContextContainer = styled.div<{ isLibraryFrame: boolean }>`
|
||||
position: relative;
|
||||
font-family: ${fontFamilyCode};
|
||||
font-size: ${fontSize};
|
||||
|
@ -40,12 +36,16 @@ const Container = styled.div<{ isLibraryFrame: boolean }>`
|
|||
interface Props {
|
||||
stackframe: IStackframe;
|
||||
codeLanguage?: string;
|
||||
id: string;
|
||||
initialIsOpen?: boolean;
|
||||
isLibraryFrame?: boolean;
|
||||
}
|
||||
|
||||
export function Stackframe({
|
||||
stackframe,
|
||||
codeLanguage,
|
||||
id,
|
||||
initialIsOpen = false,
|
||||
isLibraryFrame = false
|
||||
}: Props) {
|
||||
if (!hasLineContext(stackframe)) {
|
||||
|
@ -55,19 +55,22 @@ export function Stackframe({
|
|||
}
|
||||
|
||||
return (
|
||||
<Container isLibraryFrame={isLibraryFrame}>
|
||||
<CodeHeader>
|
||||
<EuiAccordion
|
||||
buttonContent={
|
||||
<FrameHeading stackframe={stackframe} isLibraryFrame={isLibraryFrame} />
|
||||
</CodeHeader>
|
||||
|
||||
<Context
|
||||
stackframe={stackframe}
|
||||
codeLanguage={codeLanguage}
|
||||
isLibraryFrame={isLibraryFrame}
|
||||
/>
|
||||
|
||||
}
|
||||
id={id}
|
||||
initialIsOpen={initialIsOpen}
|
||||
>
|
||||
<ContextContainer isLibraryFrame={isLibraryFrame}>
|
||||
<Context
|
||||
stackframe={stackframe}
|
||||
codeLanguage={codeLanguage}
|
||||
isLibraryFrame={isLibraryFrame}
|
||||
/>
|
||||
</ContextContainer>
|
||||
<Variables vars={stackframe.vars} />
|
||||
</Container>
|
||||
</EuiAccordion>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ import { flattenObject } from '../../../utils/flattenObject';
|
|||
|
||||
const VariablesContainer = styled.div`
|
||||
background: ${theme.euiColorEmptyShade};
|
||||
border-top: 1px solid ${theme.euiColorLightShade};
|
||||
border-radius: 0 0 ${borderRadius} ${borderRadius};
|
||||
padding: ${px(units.half)} ${px(unit)};
|
||||
`;
|
||||
|
|
|
@ -16,7 +16,7 @@ describe('Stackframe', () => {
|
|||
let wrapper: ReactWrapper;
|
||||
beforeEach(() => {
|
||||
const stackframe = stacktracesMock[0];
|
||||
wrapper = mount(<Stackframe stackframe={stackframe} />);
|
||||
wrapper = mount(<Stackframe id="test" stackframe={stackframe} />);
|
||||
});
|
||||
|
||||
it('should render correctly', () => {
|
||||
|
@ -38,7 +38,7 @@ describe('Stackframe', () => {
|
|||
let wrapper: ReactWrapper;
|
||||
beforeEach(() => {
|
||||
const stackframe = { line: {} } as IStackframe;
|
||||
wrapper = mount(<Stackframe stackframe={stackframe} />);
|
||||
wrapper = mount(<Stackframe id="test" stackframe={stackframe} />);
|
||||
});
|
||||
|
||||
it('should render only FrameHeading', () => {
|
||||
|
@ -55,7 +55,7 @@ describe('Stackframe', () => {
|
|||
it('should respect isLibraryFrame', () => {
|
||||
const stackframe = { line: {} } as IStackframe;
|
||||
const wrapper = shallow(
|
||||
<Stackframe stackframe={stackframe} isLibraryFrame />
|
||||
<Stackframe id="test" stackframe={stackframe} isLibraryFrame />
|
||||
);
|
||||
expect(wrapper.find('FrameHeading').prop('isLibraryFrame')).toBe(true);
|
||||
});
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -10,7 +10,7 @@ import { isEmpty, last } from 'lodash';
|
|||
import React, { Fragment } from 'react';
|
||||
import { IStackframe } from '../../../../typings/es_schemas/raw/fields/Stackframe';
|
||||
import { EmptyMessage } from '../../shared/EmptyMessage';
|
||||
import { LibraryStackFrames } from './LibraryStackFrames';
|
||||
import { LibraryStacktrace } from './LibraryStacktrace';
|
||||
import { Stackframe } from './Stackframe';
|
||||
|
||||
interface Props {
|
||||
|
@ -25,7 +25,7 @@ export function Stacktrace({ stackframes = [], codeLanguage }: Props) {
|
|||
heading={i18n.translate(
|
||||
'xpack.apm.stacktraceTab.noStacktraceAvailableLabel',
|
||||
{
|
||||
defaultMessage: 'No stacktrace available.'
|
||||
defaultMessage: 'No stack trace available.'
|
||||
}
|
||||
)}
|
||||
hideSubheading
|
||||
|
@ -34,24 +34,21 @@ export function Stacktrace({ stackframes = [], codeLanguage }: Props) {
|
|||
}
|
||||
|
||||
const groups = getGroupedStackframes(stackframes);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{groups.map((group, i) => {
|
||||
// library frame
|
||||
if (group.isLibraryFrame) {
|
||||
const hasMultipleStackframes = group.stackframes.length > 1;
|
||||
const hasLeadingSpacer = hasMultipleStackframes && i !== 0;
|
||||
const hasTrailingSpacer =
|
||||
hasMultipleStackframes && i !== groups.length - 1;
|
||||
if (group.isLibraryFrame && groups.length > 1) {
|
||||
return (
|
||||
<Fragment key={i}>
|
||||
{hasLeadingSpacer && <EuiSpacer size="m" />}
|
||||
<LibraryStackFrames
|
||||
initialVisiblity={!hasMultipleStackframes}
|
||||
<EuiSpacer size="m" />
|
||||
<LibraryStacktrace
|
||||
id={i.toString()}
|
||||
stackframes={group.stackframes}
|
||||
codeLanguage={codeLanguage}
|
||||
/>
|
||||
{hasTrailingSpacer && <EuiSpacer size="m" />}
|
||||
<EuiSpacer size="m" />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
@ -60,7 +57,12 @@ export function Stacktrace({ stackframes = [], codeLanguage }: Props) {
|
|||
return group.stackframes.map((stackframe, idx) => (
|
||||
<Fragment key={`${i}-${idx}`}>
|
||||
{idx > 0 && <EuiSpacer size="m" />}
|
||||
<Stackframe codeLanguage={codeLanguage} stackframe={stackframe} />
|
||||
<Stackframe
|
||||
codeLanguage={codeLanguage}
|
||||
id={`${i}-${idx}`}
|
||||
initialIsOpen={i === 0 && groups.length > 1}
|
||||
stackframe={stackframe}
|
||||
/>
|
||||
</Fragment>
|
||||
));
|
||||
})}
|
||||
|
|
|
@ -21,7 +21,7 @@ interface Processor {
|
|||
event: 'error';
|
||||
}
|
||||
|
||||
interface Exception {
|
||||
export interface Exception {
|
||||
message?: string; // either message or type are given
|
||||
type?: string;
|
||||
module?: string;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue