diff --git a/x-pack/legacy/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap b/x-pack/legacy/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap index 04b759ac007d9..a7c61d1c79da4 100644 --- a/x-pack/legacy/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap +++ b/x-pack/legacy/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap @@ -10,6 +10,8 @@ exports[`Error ERROR_GROUP_ID 1`] = `"grouping key"`; exports[`Error ERROR_LOG_MESSAGE 1`] = `undefined`; +exports[`Error ERROR_PAGE_URL 1`] = `undefined`; + exports[`Error HTTP_REQUEST_METHOD 1`] = `undefined`; exports[`Error METRIC_JAVA_HEAP_MEMORY_COMMITTED 1`] = `undefined`; @@ -72,6 +74,8 @@ exports[`Error TRANSACTION_ID 1`] = `"transaction id"`; exports[`Error TRANSACTION_NAME 1`] = `undefined`; +exports[`Error TRANSACTION_PAGE_URL 1`] = `undefined`; + exports[`Error TRANSACTION_RESULT 1`] = `undefined`; exports[`Error TRANSACTION_SAMPLED 1`] = `undefined`; @@ -92,6 +96,8 @@ exports[`Span ERROR_GROUP_ID 1`] = `undefined`; exports[`Span ERROR_LOG_MESSAGE 1`] = `undefined`; +exports[`Span ERROR_PAGE_URL 1`] = `undefined`; + exports[`Span HTTP_REQUEST_METHOD 1`] = `undefined`; exports[`Span METRIC_JAVA_HEAP_MEMORY_COMMITTED 1`] = `undefined`; @@ -154,6 +160,8 @@ exports[`Span TRANSACTION_ID 1`] = `"transaction id"`; exports[`Span TRANSACTION_NAME 1`] = `undefined`; +exports[`Span TRANSACTION_PAGE_URL 1`] = `undefined`; + exports[`Span TRANSACTION_RESULT 1`] = `undefined`; exports[`Span TRANSACTION_SAMPLED 1`] = `undefined`; @@ -174,6 +182,8 @@ exports[`Transaction ERROR_GROUP_ID 1`] = `undefined`; exports[`Transaction ERROR_LOG_MESSAGE 1`] = `undefined`; +exports[`Transaction ERROR_PAGE_URL 1`] = `undefined`; + exports[`Transaction HTTP_REQUEST_METHOD 1`] = `"GET"`; exports[`Transaction METRIC_JAVA_HEAP_MEMORY_COMMITTED 1`] = `undefined`; @@ -236,6 +246,8 @@ exports[`Transaction TRANSACTION_ID 1`] = `"transaction id"`; exports[`Transaction TRANSACTION_NAME 1`] = `"transaction name"`; +exports[`Transaction TRANSACTION_PAGE_URL 1`] = `undefined`; + exports[`Transaction TRANSACTION_RESULT 1`] = `"transaction result"`; exports[`Transaction TRANSACTION_SAMPLED 1`] = `true`; diff --git a/x-pack/legacy/plugins/apm/common/elasticsearch_fieldnames.ts b/x-pack/legacy/plugins/apm/common/elasticsearch_fieldnames.ts index 1dbfa0592ec8e..a5d057817893c 100644 --- a/x-pack/legacy/plugins/apm/common/elasticsearch_fieldnames.ts +++ b/x-pack/legacy/plugins/apm/common/elasticsearch_fieldnames.ts @@ -21,6 +21,7 @@ export const TRANSACTION_NAME = 'transaction.name'; export const TRANSACTION_ID = 'transaction.id'; export const TRANSACTION_SAMPLED = 'transaction.sampled'; export const TRANSACTION_BREAKDOWN_COUNT = 'transaction.breakdown.count'; +export const TRANSACTION_PAGE_URL = 'transaction.page.url'; export const TRACE_ID = 'trace.id'; @@ -40,6 +41,7 @@ export const ERROR_CULPRIT = 'error.culprit'; export const ERROR_LOG_MESSAGE = 'error.log.message'; export const ERROR_EXC_MESSAGE = 'error.exception.message'; // only to be used in es queries, since error.exception is now an array export const ERROR_EXC_HANDLED = 'error.exception.handled'; // only to be used in es queries, since error.exception is now an array +export const ERROR_PAGE_URL = 'error.page.url'; // METRICS export const METRIC_SYSTEM_FREE_MEMORY = 'system.memory.actual.free'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/StickyErrorProperties.test.tsx b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/StickyErrorProperties.test.tsx index 7fbc72bf54325..4d4991f161f6e 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/StickyErrorProperties.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/StickyErrorProperties.test.tsx @@ -4,12 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { shallow } from 'enzyme'; +import { shallow, ShallowWrapper } from 'enzyme'; import React from 'react'; import { APMError } from '../../../../../typings/es_schemas/ui/APMError'; import { Transaction } from '../../../../../typings/es_schemas/ui/Transaction'; import { IStickyProperty } from '../../../shared/StickyProperties'; import { StickyErrorProperties } from './StickyErrorProperties'; +import { + ERROR_PAGE_URL, + URL_FULL +} from '../../../../../common/elasticsearch_fieldnames'; describe('StickyErrorProperties', () => { it('should render StickyProperties', () => { @@ -28,6 +32,7 @@ describe('StickyErrorProperties', () => { const error = { '@timestamp': 'myTimestamp', + agent: { name: 'nodejs' }, http: { request: { method: 'GET' } }, url: { full: 'myUrl' }, service: { name: 'myService' }, @@ -43,36 +48,70 @@ describe('StickyErrorProperties', () => { expect(wrapper).toMatchSnapshot(); }); - describe('error.exception.handled', () => { - function getIsHandledValue(error: APMError) { - const wrapper = shallow( - - ); + it('url.full', () => { + const error = { + agent: { name: 'nodejs' }, + url: { full: 'myFullUrl' } + } as APMError; - const stickyProps = wrapper.prop('stickyProperties') as IStickyProperty[]; - const handledProp = stickyProps.find( - prop => prop.fieldName === 'error.exception.handled' - ); + const wrapper = shallow( + + ); + const urlValue = getValueByFieldName(wrapper, URL_FULL); + expect(urlValue).toBe('myFullUrl'); + }); - return handledProp && handledProp.val; - } + it('error.page.url', () => { + const error = { + agent: { name: 'rum-js' }, + error: { page: { url: 'myPageUrl' } } + } as APMError; + const wrapper = shallow( + + ); + const urlValue = getValueByFieldName(wrapper, ERROR_PAGE_URL); + expect(urlValue).toBe('myPageUrl'); + }); + + describe('error.exception.handled', () => { it('should should render "true"', () => { - const error = { error: { exception: [{ handled: true }] } } as APMError; - const isHandledValue = getIsHandledValue(error); - expect(isHandledValue).toBe('true'); + const error = { + agent: { name: 'nodejs' }, + error: { exception: [{ handled: true }] } + } as APMError; + const wrapper = shallow( + + ); + const value = getValueByFieldName(wrapper, 'error.exception.handled'); + expect(value).toBe('true'); }); it('should should render "false"', () => { - const error = { error: { exception: [{ handled: false }] } } as APMError; - const isHandledValue = getIsHandledValue(error); - expect(isHandledValue).toBe('false'); + const error = { + agent: { name: 'nodejs' }, + error: { exception: [{ handled: false }] } + } as APMError; + const wrapper = shallow( + + ); + const value = getValueByFieldName(wrapper, 'error.exception.handled'); + expect(value).toBe('false'); }); it('should should render "N/A"', () => { - const error = {} as APMError; - const isHandledValue = getIsHandledValue(error); - expect(isHandledValue).toBe('N/A'); + const error = { agent: { name: 'nodejs' } } as APMError; + const wrapper = shallow( + + ); + const value = getValueByFieldName(wrapper, 'error.exception.handled'); + expect(value).toBe('N/A'); }); }); }); + +function getValueByFieldName(wrapper: ShallowWrapper, fieldName: string) { + const stickyProps = wrapper.prop('stickyProperties') as IStickyProperty[]; + const prop = stickyProps.find(p => p.fieldName === fieldName); + return prop && prop.val; +} diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/StickyErrorProperties.tsx b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/StickyErrorProperties.tsx index 783eb4e85fb80..bdf122ca52c49 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/StickyErrorProperties.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/StickyErrorProperties.tsx @@ -13,13 +13,15 @@ import { HTTP_REQUEST_METHOD, TRANSACTION_ID, URL_FULL, - USER_ID + USER_ID, + ERROR_PAGE_URL } from '../../../../../common/elasticsearch_fieldnames'; import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; import { APMError } from '../../../../../typings/es_schemas/ui/APMError'; import { Transaction } from '../../../../../typings/es_schemas/ui/Transaction'; import { StickyProperties } from '../../../shared/StickyProperties'; import { TransactionLink } from '../../../shared/Links/apm/TransactionLink'; +import { isRumAgentName } from '../../../../../common/agent_name'; interface Props { error: APMError; @@ -49,6 +51,18 @@ function TransactionLinkWrapper({ export function StickyErrorProperties({ error, transaction }: Props) { const isHandled = idx(error, _ => _.error.exception[0].handled); + const isRumAgent = isRumAgentName(error.agent.name); + + const { urlFieldName, urlValue } = isRumAgent + ? { + urlFieldName: ERROR_PAGE_URL, + urlValue: idx(error, _ => _.error.page.url) + } + : { + urlFieldName: URL_FULL, + urlValue: idx(error, _ => _.url.full) + }; + const stickyProperties = [ { fieldName: '@timestamp', @@ -59,12 +73,9 @@ export function StickyErrorProperties({ error, transaction }: Props) { width: '50%' }, { - fieldName: URL_FULL, + fieldName: urlFieldName, label: 'URL', - val: - idx(error, _ => _.context.page.url) || - idx(error, _ => _.url.full) || - NOT_AVAILABLE_LABEL, + val: urlValue || NOT_AVAILABLE_LABEL, truncated: true, width: '50%' }, diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/StickyTransactionProperties.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/StickyTransactionProperties.tsx index e533d7c026bbf..a11086ef23734 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/StickyTransactionProperties.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/StickyTransactionProperties.tsx @@ -12,7 +12,8 @@ import { TRANSACTION_DURATION, TRANSACTION_RESULT, URL_FULL, - USER_ID + USER_ID, + TRANSACTION_PAGE_URL } from '../../../../../common/elasticsearch_fieldnames'; import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; import { Transaction } from '../../../../../typings/es_schemas/ui/Transaction'; @@ -22,6 +23,7 @@ import { StickyProperties } from '../../../shared/StickyProperties'; import { ErrorCountBadge } from './ErrorCountBadge'; +import { isRumAgentName } from '../../../../../common/agent_name'; interface Props { transaction: Transaction; @@ -35,10 +37,18 @@ export function StickyTransactionProperties({ errorCount }: Props) { const timestamp = transaction['@timestamp']; - const url = - idx(transaction, _ => _.context.page.url) || - idx(transaction, _ => _.url.full) || - NOT_AVAILABLE_LABEL; + + const isRumAgent = isRumAgentName(transaction.agent.name); + const { urlFieldName, urlValue } = isRumAgent + ? { + urlFieldName: TRANSACTION_PAGE_URL, + urlValue: idx(transaction, _ => _.transaction.page.url) + } + : { + urlFieldName: URL_FULL, + urlValue: idx(transaction, _ => _.url.full) + }; + const duration = transaction.transaction.duration.us; const noErrorsText = i18n.translate( @@ -59,9 +69,9 @@ export function StickyTransactionProperties({ width: '50%' }, { - fieldName: URL_FULL, + fieldName: urlFieldName, label: 'URL', - val: url, + val: urlValue || NOT_AVAILABLE_LABEL, truncated: true, width: '50%' }, diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/ErrorRaw.ts b/x-pack/legacy/plugins/apm/typings/es_schemas/raw/ErrorRaw.ts index 8b917c7f5f343..2388556348f79 100644 --- a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/ErrorRaw.ts +++ b/x-pack/legacy/plugins/apm/typings/es_schemas/raw/ErrorRaw.ts @@ -6,10 +6,10 @@ import { APMBaseDoc } from './APMBaseDoc'; import { Container } from './fields/Container'; -import { Context } from './fields/Context'; import { Host } from './fields/Host'; import { Http } from './fields/Http'; import { Kubernetes } from './fields/Kubernetes'; +import { Page } from './fields/Page'; import { Process } from './fields/Process'; import { Service } from './fields/Service'; import { IStackframe } from './fields/Stackframe'; @@ -47,12 +47,12 @@ export interface ErrorRaw extends APMBaseDoc { grouping_key: string; // either exception or log are given exception?: Exception[]; + page?: Page; // special property for RUM: shared by error and transaction log?: Log; }; // Shared by errors and transactions container?: Container; - context?: Context; host?: Host; http?: Http; kubernetes?: Kubernetes; diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/TransactionRaw.ts b/x-pack/legacy/plugins/apm/typings/es_schemas/raw/TransactionRaw.ts index 735efe73aed10..e72870f2197a0 100644 --- a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/TransactionRaw.ts +++ b/x-pack/legacy/plugins/apm/typings/es_schemas/raw/TransactionRaw.ts @@ -6,10 +6,10 @@ import { APMBaseDoc } from './APMBaseDoc'; import { Container } from './fields/Container'; -import { Context } from './fields/Context'; import { Host } from './fields/Host'; import { Http } from './fields/Http'; import { Kubernetes } from './fields/Kubernetes'; +import { Page } from './fields/Page'; import { Process } from './fields/Process'; import { Service } from './fields/Service'; import { Url } from './fields/Url'; @@ -34,6 +34,7 @@ export interface TransactionRaw extends APMBaseDoc { }; }; name?: string; + page?: Page; // special property for RUM: shared by error and transaction result?: string; sampled: boolean; span_count?: { @@ -45,7 +46,6 @@ export interface TransactionRaw extends APMBaseDoc { // Shared by errors and transactions container?: Container; - context?: Context; host?: Host; http?: Http; kubernetes?: Kubernetes; diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Context.ts b/x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Page.ts similarity index 72% rename from x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Context.ts rename to x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Page.ts index eed626865868a..02498dd731de5 100644 --- a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Context.ts +++ b/x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Page.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export interface Context { - page?: { url: string }; // only for RUM agent +// only for RUM agent: shared by error and transaction +export interface Page { + url: string; }