mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 01:13:23 -04:00
[i18n] APM translations for Error Group (#28314)
* Translations for ErrorGroup * Make occurrencesCount required * Update message ids
This commit is contained in:
parent
2937145f0f
commit
38163f2517
6 changed files with 165 additions and 40 deletions
|
@ -104,42 +104,61 @@ export function DetailView({ errorGroup, urlParams, location }: Props) {
|
|||
}
|
||||
|
||||
const transactionLink = getTransactionLink(error, transaction);
|
||||
const notAvailableLabel = i18n.translate(
|
||||
'xpack.apm.errorGroupDetails.notAvailableLabel',
|
||||
{
|
||||
defaultMessage: 'N/A'
|
||||
}
|
||||
);
|
||||
const stickyProperties = [
|
||||
{
|
||||
fieldName: '@timestamp',
|
||||
label: 'Timestamp',
|
||||
label: i18n.translate('xpack.apm.errorGroupDetails.timestampLabel', {
|
||||
defaultMessage: 'Timestamp'
|
||||
}),
|
||||
val: error['@timestamp'],
|
||||
width: '50%'
|
||||
},
|
||||
{
|
||||
fieldName: REQUEST_URL_FULL,
|
||||
label: 'URL',
|
||||
val: get(error, REQUEST_URL_FULL, 'N/A'),
|
||||
val: get(error, REQUEST_URL_FULL, notAvailableLabel),
|
||||
truncated: true,
|
||||
width: '50%'
|
||||
},
|
||||
{
|
||||
fieldName: REQUEST_METHOD,
|
||||
label: 'Request method',
|
||||
val: get(error, REQUEST_METHOD, 'N/A'),
|
||||
label: i18n.translate('xpack.apm.errorGroupDetails.requestMethodLabel', {
|
||||
defaultMessage: 'Request method'
|
||||
}),
|
||||
val: get(error, REQUEST_METHOD, notAvailableLabel),
|
||||
width: '25%'
|
||||
},
|
||||
{
|
||||
fieldName: ERROR_EXC_HANDLED,
|
||||
label: 'Handled',
|
||||
val: String(get(error, ERROR_EXC_HANDLED, 'N/A')),
|
||||
label: i18n.translate('xpack.apm.errorGroupDetails.handledLabel', {
|
||||
defaultMessage: 'Handled'
|
||||
}),
|
||||
val: String(get(error, ERROR_EXC_HANDLED, notAvailableLabel)),
|
||||
width: '25%'
|
||||
},
|
||||
{
|
||||
fieldName: TRANSACTION_ID,
|
||||
label: 'Transaction sample ID',
|
||||
val: transactionLink || 'N/A',
|
||||
label: i18n.translate(
|
||||
'xpack.apm.errorGroupDetails.transactionSampleIdLabel',
|
||||
{
|
||||
defaultMessage: 'Transaction sample ID'
|
||||
}
|
||||
),
|
||||
val: transactionLink || notAvailableLabel,
|
||||
width: '25%'
|
||||
},
|
||||
{
|
||||
fieldName: USER_ID,
|
||||
label: 'User ID',
|
||||
val: get(error, USER_ID, 'N/A'),
|
||||
label: i18n.translate('xpack.apm.errorGroupDetails.userIdLabel', {
|
||||
defaultMessage: 'User ID'
|
||||
}),
|
||||
val: get(error, USER_ID, notAvailableLabel),
|
||||
width: '25%'
|
||||
}
|
||||
];
|
||||
|
@ -151,11 +170,25 @@ export function DetailView({ errorGroup, urlParams, location }: Props) {
|
|||
<Container>
|
||||
<HeaderContainer>
|
||||
<EuiTitle size="s">
|
||||
<h3>Error occurrence</h3>
|
||||
<h3>
|
||||
{i18n.translate(
|
||||
'xpack.apm.errorGroupDetails.errorOccurrenceTitle',
|
||||
{
|
||||
defaultMessage: 'Error occurrence'
|
||||
}
|
||||
)}
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<DiscoverErrorButton error={error} kuery={urlParams.kuery}>
|
||||
<EuiButtonEmpty iconType="discoverApp">
|
||||
{`View ${occurrencesCount} occurrences in Discover`}
|
||||
{i18n.translate(
|
||||
'xpack.apm.errorGroupDetails.viewOccurrencesInDiscoverButtonLabel',
|
||||
{
|
||||
defaultMessage:
|
||||
'View {occurrencesCount} occurrences in Discover',
|
||||
values: { occurrencesCount }
|
||||
}
|
||||
)}
|
||||
</EuiButtonEmpty>
|
||||
</DiscoverErrorButton>
|
||||
</HeaderContainer>
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import { EuiTitle } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import Histogram from '../../../shared/charts/Histogram';
|
||||
import { EmptyMessage } from '../../../shared/EmptyMessage';
|
||||
|
@ -23,7 +24,12 @@ export function getFormattedBuckets(buckets, bucketSize) {
|
|||
});
|
||||
}
|
||||
|
||||
function Distribution({ distribution, title = 'Occurrences' }) {
|
||||
function Distribution({
|
||||
distribution,
|
||||
title = i18n.translate('xpack.apm.errorGroupDetails.occurrencesChartLabel', {
|
||||
defaultMessage: 'Occurrences'
|
||||
})
|
||||
}) {
|
||||
const buckets = getFormattedBuckets(
|
||||
distribution.buckets,
|
||||
distribution.bucketSize
|
||||
|
@ -32,7 +38,13 @@ function Distribution({ distribution, title = 'Occurrences' }) {
|
|||
const isEmpty = distribution.totalHits === 0;
|
||||
|
||||
if (isEmpty) {
|
||||
return <EmptyMessage heading="No errors were found" />;
|
||||
return (
|
||||
<EmptyMessage
|
||||
heading={i18n.translate('xpack.apm.errorGroupDetails.noErrorsLabel', {
|
||||
defaultMessage: 'No errors were found'
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -45,8 +57,18 @@ function Distribution({ distribution, title = 'Occurrences' }) {
|
|||
xType="time"
|
||||
buckets={buckets}
|
||||
bucketSize={distribution.bucketSize}
|
||||
formatYShort={value => `${value} occ.`}
|
||||
formatYLong={value => `${value} occurrences`}
|
||||
formatYShort={value =>
|
||||
i18n.translate('xpack.apm.errorGroupDetails.occurrencesShortLabel', {
|
||||
defaultMessage: '{occCount} occ.',
|
||||
values: { occCount: value }
|
||||
})
|
||||
}
|
||||
formatYLong={value =>
|
||||
i18n.translate('xpack.apm.errorGroupDetails.occurrencesLongLabel', {
|
||||
defaultMessage: '{occCount} occurrences',
|
||||
values: { occCount: value }
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import { EuiBadge, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { get } from 'lodash';
|
||||
import React, { Fragment } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
@ -57,9 +58,16 @@ const Culprit = styled.div`
|
|||
font-family: ${fontFamilyCode};
|
||||
`;
|
||||
|
||||
const notAvailableLabel = i18n.translate(
|
||||
'xpack.apm.errorGroupDetails.notAvailableLabel',
|
||||
{
|
||||
defaultMessage: 'N/A'
|
||||
}
|
||||
);
|
||||
|
||||
function getShortGroupId(errorGroupId?: string) {
|
||||
if (!errorGroupId) {
|
||||
return 'N/A';
|
||||
return notAvailableLabel;
|
||||
}
|
||||
|
||||
return errorGroupId.slice(0, 5);
|
||||
|
@ -87,9 +95,21 @@ export function ErrorGroupDetails({ urlParams, location }: Props) {
|
|||
<div>
|
||||
<EuiTitle>
|
||||
<span>
|
||||
Error group {getShortGroupId(urlParams.errorGroupId)}
|
||||
{i18n.translate('xpack.apm.errorGroupDetails.errorGroupTitle', {
|
||||
defaultMessage: 'Error group {errorGroupId}',
|
||||
values: {
|
||||
errorGroupId: getShortGroupId(urlParams.errorGroupId)
|
||||
}
|
||||
})}
|
||||
{isUnhandled && (
|
||||
<UnhandledBadge color="warning">Unhandled</UnhandledBadge>
|
||||
<UnhandledBadge color="warning">
|
||||
{i18n.translate(
|
||||
'xpack.apm.errorGroupDetails.unhandledLabel',
|
||||
{
|
||||
defaultMessage: 'Unhandled'
|
||||
}
|
||||
)}
|
||||
</UnhandledBadge>
|
||||
)}
|
||||
</span>
|
||||
</EuiTitle>
|
||||
|
@ -105,14 +125,35 @@ export function ErrorGroupDetails({ urlParams, location }: Props) {
|
|||
<EuiText>
|
||||
{logMessage && (
|
||||
<Fragment>
|
||||
<Label>Log message</Label>
|
||||
<Label>
|
||||
{i18n.translate(
|
||||
'xpack.apm.errorGroupDetails.logMessageLabel',
|
||||
{
|
||||
defaultMessage: 'Log message'
|
||||
}
|
||||
)}
|
||||
</Label>
|
||||
<Message>{logMessage}</Message>
|
||||
</Fragment>
|
||||
)}
|
||||
<Label>Exception message</Label>
|
||||
<Message>{excMessage || 'N/A'}</Message>
|
||||
<Label>Culprit</Label>
|
||||
<Culprit>{culprit || 'N/A'}</Culprit>
|
||||
<Label>
|
||||
{i18n.translate(
|
||||
'xpack.apm.errorGroupDetails.exceptionMessageLabel',
|
||||
{
|
||||
defaultMessage: 'Exception message'
|
||||
}
|
||||
)}
|
||||
</Label>
|
||||
<Message>{excMessage || notAvailableLabel}</Message>
|
||||
<Label>
|
||||
{i18n.translate(
|
||||
'xpack.apm.errorGroupDetails.culpritLabel',
|
||||
{
|
||||
defaultMessage: 'Culprit'
|
||||
}
|
||||
)}
|
||||
</Label>
|
||||
<Culprit>{culprit || notAvailableLabel}</Culprit>
|
||||
</EuiText>
|
||||
</Titles>
|
||||
)}
|
||||
|
|
|
@ -24,6 +24,7 @@ import {
|
|||
fontSizes,
|
||||
truncate
|
||||
} from '../../../../style/variables';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
function paginateItems({ items, pageIndex, pageSize }) {
|
||||
return items.slice(pageIndex * pageSize, (pageIndex + 1) * pageSize);
|
||||
|
@ -47,6 +48,13 @@ const Culprit = styled.div`
|
|||
font-family: ${fontFamilyCode};
|
||||
`;
|
||||
|
||||
const notAvailableLabel = i18n.translate(
|
||||
'xpack.apm.errorsTable.notAvailableLabel',
|
||||
{
|
||||
defaultMessage: 'N/A'
|
||||
}
|
||||
);
|
||||
|
||||
class List extends Component {
|
||||
state = {
|
||||
page: {
|
||||
|
@ -82,33 +90,40 @@ class List extends Component {
|
|||
|
||||
const columns = [
|
||||
{
|
||||
name: 'Group ID',
|
||||
name: i18n.translate('xpack.apm.errorsTable.groupIdColumnLabel', {
|
||||
defaultMessage: 'Group ID'
|
||||
}),
|
||||
field: 'groupId',
|
||||
sortable: false,
|
||||
width: px(unit * 6),
|
||||
render: groupId => {
|
||||
return (
|
||||
<GroupIdLink path={`/${serviceName}/errors/${groupId}`}>
|
||||
{groupId.slice(0, 5) || 'N/A'}
|
||||
{groupId.slice(0, 5) || notAvailableLabel}
|
||||
</GroupIdLink>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Error message and culprit',
|
||||
name: i18n.translate(
|
||||
'xpack.apm.errorsTable.errorMessageAndCulpritColumnLabel',
|
||||
{
|
||||
defaultMessage: 'Error message and culprit'
|
||||
}
|
||||
),
|
||||
field: 'message',
|
||||
sortable: false,
|
||||
width: '50%',
|
||||
render: (message, item) => {
|
||||
return (
|
||||
<MessageAndCulpritCell>
|
||||
<TooltipOverlay content={message || 'N/A'}>
|
||||
<TooltipOverlay content={message || notAvailableLabel}>
|
||||
<MessageLink path={`/${serviceName}/errors/${item.groupId}`}>
|
||||
{message || 'N/A'}
|
||||
{message || notAvailableLabel}
|
||||
</MessageLink>
|
||||
</TooltipOverlay>
|
||||
<TooltipOverlay content={item.culprit || 'N/A'}>
|
||||
<Culprit>{item.culprit || 'N/A'}</Culprit>
|
||||
<TooltipOverlay content={item.culprit || notAvailableLabel}>
|
||||
<Culprit>{item.culprit || notAvailableLabel}</Culprit>
|
||||
</TooltipOverlay>
|
||||
</MessageAndCulpritCell>
|
||||
);
|
||||
|
@ -121,28 +136,42 @@ class List extends Component {
|
|||
align: 'right',
|
||||
render: isUnhandled =>
|
||||
isUnhandled === false && (
|
||||
<EuiBadge color="warning">Unhandled</EuiBadge>
|
||||
<EuiBadge color="warning">
|
||||
{i18n.translate('xpack.apm.errorsTable.unhandledLabel', {
|
||||
defaultMessage: 'Unhandled'
|
||||
})}
|
||||
</EuiBadge>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: 'Occurrences',
|
||||
name: i18n.translate('xpack.apm.errorsTable.occurrencesColumnLabel', {
|
||||
defaultMessage: 'Occurrences'
|
||||
}),
|
||||
field: 'occurrenceCount',
|
||||
sortable: true,
|
||||
dataType: 'number',
|
||||
render: value => (value ? numeral(value).format('0.[0]a') : 'N/A')
|
||||
render: value =>
|
||||
value ? numeral(value).format('0.[0]a') : notAvailableLabel
|
||||
},
|
||||
{
|
||||
field: 'latestOccurrenceAt',
|
||||
sortable: true,
|
||||
name: 'Latest occurrence',
|
||||
name: i18n.translate(
|
||||
'xpack.apm.errorsTable.latestOccurrenceColumnLabel',
|
||||
{
|
||||
defaultMessage: 'Latest occurrence'
|
||||
}
|
||||
),
|
||||
align: 'right',
|
||||
render: value => (value ? moment(value).fromNow() : 'N/A')
|
||||
render: value => (value ? moment(value).fromNow() : notAvailableLabel)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<EuiBasicTable
|
||||
noItemsMessage="No errors were found"
|
||||
noItemsMessage={i18n.translate('xpack.apm.errorsTable.noErrorsLabel', {
|
||||
defaultMessage: 'No errors were found'
|
||||
})}
|
||||
items={paginatedItems}
|
||||
columns={columns}
|
||||
pagination={{
|
||||
|
|
|
@ -14,7 +14,7 @@ import { IUrlParams } from '../urlParams';
|
|||
import { createInitialDataSelector } from './helpers';
|
||||
|
||||
const ID = 'errorGroupDetails';
|
||||
const INITIAL_DATA: ErrorGroupAPIResponse = {};
|
||||
const INITIAL_DATA: ErrorGroupAPIResponse = { occurrencesCount: 0 };
|
||||
const withInitialData = createInitialDataSelector(INITIAL_DATA);
|
||||
|
||||
export function getErrorGroupDetails(state: IReduxState) {
|
||||
|
|
|
@ -20,7 +20,7 @@ import { getTransaction } from '../transactions/get_transaction';
|
|||
export interface ErrorGroupAPIResponse {
|
||||
transaction?: Transaction;
|
||||
error?: APMError;
|
||||
occurrencesCount?: number;
|
||||
occurrencesCount: number;
|
||||
}
|
||||
|
||||
// TODO: rename from "getErrorGroup" to "getErrorGroupSample" (since a single error is returned, not an errorGroup)
|
||||
|
@ -82,6 +82,6 @@ export async function getErrorGroup({
|
|||
return {
|
||||
transaction,
|
||||
error,
|
||||
occurrencesCount: oc(resp).hits.total()
|
||||
occurrencesCount: resp.hits.total
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue