-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Uptime] Annotate waterfall chart with additional metrics #103642
Changes from all commits
2468721
ef718ec
3127bb3
3f7e7e2
f9d9cac
72a644d
8768d00
5d89ca0
1d8290d
5248cd3
6bf295d
abc2c4f
ba51295
bd6bb5e
eba7398
e349d42
ed8b98b
1fedde6
2e30d48
8c3de17
3cf399d
cc3f0cb
db60bd4
a1b00db
0b91c4e
a8f8d5b
3021ffe
334f015
9a272ea
019185c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import { renderHook } from '@testing-library/react-hooks'; | ||
import { | ||
BROWSER_TRACE_NAME, | ||
BROWSER_TRACE_START, | ||
BROWSER_TRACE_TYPE, | ||
useStepWaterfallMetrics, | ||
} from './use_step_waterfall_metrics'; | ||
import * as reduxHooks from 'react-redux'; | ||
import * as searchHooks from '../../../../../../observability/public/hooks/use_es_search'; | ||
|
||
describe('useStepWaterfallMetrics', () => { | ||
jest | ||
.spyOn(reduxHooks, 'useSelector') | ||
.mockReturnValue({ settings: { heartbeatIndices: 'heartbeat-*' } }); | ||
|
||
it('returns result as expected', () => { | ||
// @ts-ignore | ||
const searchHook = jest.spyOn(searchHooks, 'useEsSearch').mockReturnValue({ | ||
loading: false, | ||
data: { | ||
hits: { | ||
total: { value: 2, relation: 'eq' }, | ||
hits: [ | ||
{ | ||
fields: { | ||
[BROWSER_TRACE_TYPE]: ['mark'], | ||
[BROWSER_TRACE_NAME]: ['navigationStart'], | ||
[BROWSER_TRACE_START]: [3456789], | ||
}, | ||
}, | ||
{ | ||
fields: { | ||
[BROWSER_TRACE_TYPE]: ['mark'], | ||
[BROWSER_TRACE_NAME]: ['domContentLoaded'], | ||
[BROWSER_TRACE_START]: [4456789], | ||
}, | ||
}, | ||
], | ||
}, | ||
} as any, | ||
}); | ||
|
||
const { result } = renderHook( | ||
(props) => | ||
useStepWaterfallMetrics({ | ||
checkGroup: '44D-444FFF-444-FFF-3333', | ||
hasNavigationRequest: true, | ||
stepIndex: 1, | ||
}), | ||
{} | ||
); | ||
|
||
expect(searchHook).toHaveBeenCalledWith( | ||
{ | ||
body: { | ||
_source: false, | ||
fields: ['browser.*'], | ||
query: { | ||
bool: { | ||
filter: [ | ||
{ | ||
term: { | ||
'synthetics.step.index': 1, | ||
}, | ||
}, | ||
{ | ||
term: { | ||
'monitor.check_group': '44D-444FFF-444-FFF-3333', | ||
}, | ||
}, | ||
{ | ||
term: { | ||
'synthetics.type': 'step/metrics', | ||
}, | ||
}, | ||
], | ||
}, | ||
}, | ||
size: 1000, | ||
}, | ||
index: 'heartbeat-*', | ||
}, | ||
['heartbeat-*', '44D-444FFF-444-FFF-3333', true] | ||
); | ||
expect(result.current).toEqual({ | ||
loading: false, | ||
metrics: [ | ||
{ | ||
id: 'domContentLoaded', | ||
offset: 1000, | ||
}, | ||
], | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import { useSelector } from 'react-redux'; | ||
import { createEsParams, useEsSearch } from '../../../../../../observability/public'; | ||
import { selectDynamicSettings } from '../../../../state/selectors'; | ||
import { MarkerItems } from '../waterfall/context/waterfall_chart'; | ||
|
||
export interface Props { | ||
checkGroup: string; | ||
stepIndex: number; | ||
hasNavigationRequest?: boolean; | ||
} | ||
export const BROWSER_TRACE_TYPE = 'browser.relative_trace.type'; | ||
export const BROWSER_TRACE_NAME = 'browser.relative_trace.name'; | ||
export const BROWSER_TRACE_START = 'browser.relative_trace.start.us'; | ||
export const NAVIGATION_START = 'navigationStart'; | ||
|
||
export const useStepWaterfallMetrics = ({ checkGroup, hasNavigationRequest, stepIndex }: Props) => { | ||
const { settings } = useSelector(selectDynamicSettings); | ||
|
||
const heartbeatIndices = settings?.heartbeatIndices || ''; | ||
|
||
const { data, loading } = useEsSearch( | ||
hasNavigationRequest | ||
? createEsParams({ | ||
index: heartbeatIndices!, | ||
body: { | ||
query: { | ||
bool: { | ||
filter: [ | ||
{ | ||
term: { | ||
'synthetics.step.index': stepIndex, | ||
}, | ||
}, | ||
{ | ||
term: { | ||
'monitor.check_group': checkGroup, | ||
}, | ||
}, | ||
{ | ||
term: { | ||
'synthetics.type': 'step/metrics', | ||
}, | ||
}, | ||
], | ||
}, | ||
}, | ||
fields: ['browser.*'], | ||
size: 1000, | ||
_source: false, | ||
}, | ||
}) | ||
: {}, | ||
[heartbeatIndices, checkGroup, hasNavigationRequest] | ||
); | ||
|
||
if (!hasNavigationRequest) { | ||
return { metrics: [], loading: false }; | ||
} | ||
|
||
const metrics: MarkerItems = []; | ||
|
||
if (data && hasNavigationRequest) { | ||
const metricDocs = data.hits.hits as unknown as Array<{ fields: any }>; | ||
let navigationStart = 0; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI, This is prone to errors and wold result in absurd values when I would change this logic to this.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Improved it to make sure check for existence explicitly. |
||
let navigationStartExist = false; | ||
|
||
metricDocs.forEach(({ fields }) => { | ||
if (fields[BROWSER_TRACE_TYPE]?.[0] === 'mark') { | ||
const { [BROWSER_TRACE_NAME]: metricType, [BROWSER_TRACE_START]: metricValue } = fields; | ||
if (metricType?.[0] === NAVIGATION_START) { | ||
navigationStart = metricValue?.[0]; | ||
navigationStartExist = true; | ||
} | ||
} | ||
}); | ||
|
||
if (navigationStartExist) { | ||
metricDocs.forEach(({ fields }) => { | ||
if (fields[BROWSER_TRACE_TYPE]?.[0] === 'mark') { | ||
const { [BROWSER_TRACE_NAME]: metricType, [BROWSER_TRACE_START]: metricValue } = fields; | ||
if (metricType?.[0] !== NAVIGATION_START) { | ||
metrics.push({ | ||
id: metricType?.[0], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can skip doing the undefined check here? |
||
offset: (metricValue?.[0] - navigationStart) / 1000, | ||
}); | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
|
||
return { metrics, loading }; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this whole hook depends on hasNavigation request, can be exit early if this is false and return an empty metrics array and loading false?