Skip to content

Commit

Permalink
[APM] Mobile crashes & errors (#165892)
Browse files Browse the repository at this point in the history
## Summary

This PR adds back the `Errors` tab to mobile apm services under the
title `Errors & Crashes`. This new page is split into too sections:
errors, and crashes.

Error Tab:
<img width="1456" alt="Screenshot 2023-10-25 at 10 57 00"
src="https://github.com/elastic/kibana/assets/75274611/20277c31-d88c-44ae-b896-1da4223cb392">

Crashes Tab:
<img width="1454" alt="Screenshot 2023-10-25 at 10 57 35"
src="https://github.com/elastic/kibana/assets/75274611/2b0dea23-cbab-4e68-a14a-c3b14d4bd860">


### Checklist

Delete any items that are not applicable to this PR.

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [x] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [x] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)


### Risk Matrix

Delete this section if it is not applicable to this PR.

Before closing this PR, invite QA, stakeholders, and other developers to
identify risks that should be tested prior to the change/feature
release.

When forming the risk matrix, consider some of the following examples
and how they may potentially impact the change:

### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: kibanamachine <[email protected]>
Co-authored-by: Katerina <[email protected]>
  • Loading branch information
3 people authored Dec 1, 2023
1 parent eac36e8 commit 33c74ae
Show file tree
Hide file tree
Showing 71 changed files with 5,539 additions and 71 deletions.
3 changes: 3 additions & 0 deletions docs/apm/how-to-guides.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Learn how to perform common APM app tasks.
* <<correlations>>
* <<agent-explorer>>
* <<machine-learning-integration>>
* <<mobile-session-explorer>>
* <<apm-lambda>>
* <<advanced-queries>>
* <<storage-explorer>>
Expand All @@ -35,6 +36,8 @@ include::agent-explorer.asciidoc[]

include::machine-learning.asciidoc[]

include::mobile-session-explorer.asciidoc[]

include::lambda.asciidoc[]

include::advanced-queries.asciidoc[]
Expand Down
Binary file added docs/apm/images/mobile-session-error-details.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/apm/images/mobile-session-explorer-apm.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/apm/images/mobile-session-explorer-nav.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions docs/apm/mobile-errors.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[role="xpack"]
[[mobile-errors-crashes]]
=== Mobile errors and crashes

TIP: {apm-guide-ref}/data-model-errors.html[Errors] are groups of exceptions with a similar exception or log message.

The *Errors & Crashes* overview provides a high-level view of errors and crashes that APM mobile agents catch,
or that users manually report with APM agent APIs. Errors and crashes are separated into two tabs for easy differentiation.
Like errors are grouped together to make it easy to quickly see which errors are affecting your services,
and to take actions to rectify them.





[role="screenshot"]
image::apm/images/mobile-errors-overview.png[Mobile Errors overview]

Selecting an error group ID or error message brings you to the *Error group*.

[role="screenshot"]
image::apm/images/mobile-error-group.png[Mobile Error group]

The error group details page visualizes the number of error occurrences over time and compared to a recent time range.
This allows you to quickly determine if the error rate is changing or remaining constant.
You'll also see the "most affected" chart which can be oriented to 'by device' or 'by app version'.

Further down, you'll see an Error sample.
The error shown is always the most recent to occur.
The sample includes the exception message, culprit, stack trace where the error occurred (when available),
and additional contextual information to help debug the issue--all of which can be copied with the click of a button.

In some cases, you might also see a Transaction sample ID.
This feature allows you to make a connection between the errors and transactions,
by linking you to the specific transaction where the error occurred.
This allows you to see the whole trace, including which services the request went through.
4 changes: 3 additions & 1 deletion docs/apm/mobile-service.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ to make data-driven decisions about how to improve your user experience.

For example, see:

* Crash Rate (Crashes per minute) -- coming soon
* Crash Rate (Crashes per session)
* Slowest App load time -- coming soon
* Number of sessions
* Number of HTTP requests
Expand All @@ -28,6 +28,8 @@ of their mobile application environment and the impact of backend errors and bot
Understand the impact of slow application load times and variations in application crash rate on user traffic (coming soon).
Visualize session and HTTP trends, and see where your users are located--enabling you to optimize your infrastructure deployment and routing topology.

Note: due to the way crash rate is calculated (crashes per session) it is possible to have greater than 100% rate, due to fact that a session may contain multiple crashes.

[role="screenshot"]
image::apm/images/mobile-location.png[mobile service overview centered on location map]

Expand Down
43 changes: 43 additions & 0 deletions docs/apm/mobile-session-explorer.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
[role="xpack]
[[mobile-session-explorer]]
=== Exploring mobile sessions with Discover
Elastic Mobile APM provides session tracking by attaching a `session.id`, a guid, to every span and event.
This allows for the recall of the activities of a specific user during a specific period of time. The best way recall
these data points is using the xref:document-explorer[Discover document explorer]. This guide will explain how to do that.

=== Viewing sessions with Discover

The first step is to find the relevant `session.id`. In this example, we'll walk through investigating a crash.
Since all events and spans have `session.id` attributes, a crash is no different.

The steps to follow are:

* copy the `session.id` from the relevant document.
* Open the Discover page.
* Select the appropriate data view (use `APM` to search all datastreams)
* set filter to the copied `session.id`

Here we can see the `session.id` guid in the metadata viewer in the error detail view:
[role="screenshot"]
image::images/mobile-session-error-details.png[Example of session.id in error details]

Copy this value and open the Discover page:

[role="screenshot"]
image::images/mobile-session-explorer-nav.png[Example view of navigation to Discover]


set the data view. `APM` selected in the example:

[role="screenshot"]
image::images/mobile-session-explorer-apm.png[Example view of Explorer selecting APM data view]

filter using the `session.id`: `session.id: "<copied session id guid>"`:

[role="screenshot"]
image::images/mobile-session-filter-discover.png[Filter Explor using session.id]

explore all the documents associated with that session id including crashes, lifecycle events, network requests, errors, and other custom events!



8 changes: 8 additions & 0 deletions packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ export class Instance extends Entity<ApmFields> {
});
}

crash({ message, type }: { message: string; type?: string }) {
return new ApmError({
...this.fields,
'error.type': 'crash',
'error.exception': [{ message, ...(type ? { type } : {}) }],
'error.grouping_name': getErrorGroupingKey(message),
});
}
error({ message, type }: { message: string; type?: string }) {
return new ApmError({
...this.fields,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
* 2.0.
*/

import { getKueryWithMobileFilters } from './get_kuery_with_mobile_filters';
import {
getKueryWithMobileFilters,
getKueryWithMobileCrashFilter,
getKueryWithMobileErrorFilter,
} from './get_kuery_with_mobile_filters';
describe('getKueryWithMobileFilters', () => {
it('should handle empty and undefined values', () => {
const result = getKueryWithMobileFilters({
Expand Down Expand Up @@ -70,3 +74,68 @@ describe('getKueryWithMobileFilters', () => {
);
});
});

describe('getKueryWithMobileCrashFilter', () => {
it('should handle empty and undefined values', () => {
const result = getKueryWithMobileCrashFilter({
groupId: undefined,
kuery: '',
});
expect(result).toBe('error.type: crash');
});
it('should return kuery and crash filter when groupId is empty', () => {
const result = getKueryWithMobileCrashFilter({
groupId: undefined,
kuery: 'foo.bar: test',
});
expect(result).toBe('foo.bar: test and error.type: crash');
});
it('should return crash filter and groupId when kuery is empty', () => {
const result = getKueryWithMobileCrashFilter({
groupId: '1',
kuery: '',
});
expect(result).toBe('error.type: crash and error.grouping_key: 1');
});
it('should return crash filter, groupId, and kuery in kql format', () => {
const result = getKueryWithMobileCrashFilter({
groupId: '1',
kuery: 'foo.bar: test',
});
expect(result).toBe(
'foo.bar: test and error.type: crash and error.grouping_key: 1'
);
});
});
describe('getKueryWithMobileErrorFilter', () => {
it('should handle empty and undefined values', () => {
const result = getKueryWithMobileErrorFilter({
groupId: undefined,
kuery: '',
});
expect(result).toBe('NOT error.type: crash');
});
it('should return kuery and error filter when groupId is empty', () => {
const result = getKueryWithMobileErrorFilter({
kuery: 'foo.bar: test',
groupId: undefined,
});
expect(result).toBe('foo.bar: test and NOT error.type: crash');
});
it('should return error filter and groupId when kuery is empty', () => {
const result = getKueryWithMobileErrorFilter({
groupId: '1',
kuery: '',
});
expect(result).toBe('NOT error.type: crash and error.grouping_key: 1');
});
it('should return error filter, groupId, and kuery in kql format', () => {
const result = getKueryWithMobileErrorFilter({
groupId: '1',
kuery: 'foo.bar: test',
});
expect(result).toBe(
'foo.bar: test and NOT error.type: crash and error.grouping_key: 1'
);
});
});
36 changes: 36 additions & 0 deletions x-pack/plugins/apm/common/utils/get_kuery_with_mobile_filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
DEVICE_MODEL_IDENTIFIER,
NETWORK_CONNECTION_TYPE,
SERVICE_VERSION,
ERROR_TYPE,
ERROR_GROUP_ID,
} from '../es_fields/apm';
import { fieldValuePairToKql } from './field_value_pair_to_kql';

Expand Down Expand Up @@ -38,3 +40,37 @@ export function getKueryWithMobileFilters({

return kueryWithFilters;
}

export function getKueryWithMobileCrashFilter({
groupId,
kuery,
}: {
groupId: string | undefined;
kuery: string;
}) {
const kueryWithFilters = [
kuery,
...fieldValuePairToKql(ERROR_TYPE, 'crash'),
...fieldValuePairToKql(ERROR_GROUP_ID, groupId),
]
.filter(Boolean)
.join(' and ');
return kueryWithFilters;
}

export function getKueryWithMobileErrorFilter({
groupId,
kuery,
}: {
groupId: string | undefined;
kuery: string;
}) {
const kueryWithFilters = [
kuery,
`NOT ${ERROR_TYPE}: crash`,
...fieldValuePairToKql(ERROR_GROUP_ID, groupId),
]
.filter(Boolean)
.join(' and ');
return kueryWithFilters;
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { TraceSearchType } from '../../../../../common/trace_explorer';
import { APMError } from '../../../../../typings/es_schemas/ui/apm_error';
import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context';
import { useLegacyUrlParams } from '../../../../context/url_params_context/use_url_params';
import { useApmParams } from '../../../../hooks/use_apm_params';
import { useAnyOfApmParams } from '../../../../hooks/use_apm_params';
import { useApmRouter } from '../../../../hooks/use_apm_router';
import { FETCH_STATUS, isPending } from '../../../../hooks/use_fetcher';
import { useTraceExplorerEnabledSetting } from '../../../../hooks/use_trace_explorer_enabled_setting';
Expand Down Expand Up @@ -102,7 +102,11 @@ export function ErrorSampleDetails({
const {
path: { groupId },
query,
} = useApmParams('/services/{serviceName}/errors/{groupId}');
} = useAnyOfApmParams(
'/services/{serviceName}/errors/{groupId}',
'/mobile-services/{serviceName}/errors-and-crashes/errors/{groupId}',
'/mobile-services/{serviceName}/errors-and-crashes/crashes/{groupId}'
);

const { kuery } = query;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { EuiLoadingSpinner } from '@elastic/eui';
import React from 'react';
import { useHistory } from 'react-router-dom';
import { fromQuery, toQuery } from '../../../shared/links/url_helpers';
import { useApmParams } from '../../../../hooks/use_apm_params';
import { useAnyOfApmParams } from '../../../../hooks/use_apm_params';
import {
FETCH_STATUS,
isPending,
Expand Down Expand Up @@ -36,7 +36,11 @@ export function ErrorSampler({
const {
path: { groupId },
query,
} = useApmParams('/services/{serviceName}/errors/{groupId}');
} = useAnyOfApmParams(
'/services/{serviceName}/errors/{groupId}',
'/mobile-services/{serviceName}/errors-and-crashes/errors/{groupId}',
'/mobile-services/{serviceName}/errors-and-crashes/crashes/{groupId}'
);

const { rangeFrom, rangeTo, environment, kuery, errorId } = query;

Expand Down
Original file line number Diff line number Diff line change
@@ -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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React, { useState } from 'react';
import { EuiSpacer } from '@elastic/eui';
import { TreemapSelect, TreemapTypes } from './treemap_select';
import { TreemapChart } from '../../../../shared/charts/treemap_chart';
import { useFetcher } from '../../../../../hooks/use_fetcher';
import {
DEVICE_MODEL_IDENTIFIER,
SERVICE_VERSION,
} from '../../../../../../common/es_fields/apm';

const ES_FIELD_MAPPING: Record<TreemapTypes, string> = {
[TreemapTypes.Devices]: DEVICE_MODEL_IDENTIFIER,
[TreemapTypes.Versions]: SERVICE_VERSION,
};

export function MobileErrorsAndCrashesTreemap({
kuery,
serviceName,
start,
end,
environment,
}: {
kuery: string;
serviceName: string;
start: string;
end: string;
environment: string;
}) {
const [selectedTreemap, selectTreemap] = useState(TreemapTypes.Devices);

const { data, status } = useFetcher(
(callApmApi) => {
const fieldName = ES_FIELD_MAPPING[selectedTreemap];
if (fieldName) {
return callApmApi(
'GET /internal/apm/mobile-services/{serviceName}/error_terms',
{
params: {
path: {
serviceName,
},
query: {
environment,
kuery,
start,
end,
fieldName,
size: 500,
},
},
}
);
}
},
[environment, kuery, serviceName, start, end, selectedTreemap]
);
return (
<>
<TreemapSelect
selectedTreemap={selectedTreemap}
onChange={selectTreemap}
/>
<EuiSpacer size="s" />
<TreemapChart
fetchStatus={status}
data={data?.terms ?? []}
id="device-treemap"
height={320}
/>
</>
);
}
Loading

0 comments on commit 33c74ae

Please sign in to comment.