Skip to content

Commit

Permalink
[Stack Monitoring] Migrate logs-related components to TypeScript (ela…
Browse files Browse the repository at this point in the history
…stic#203536)

## Summary
A recent [bug](elastic#199902) that
affected some of the pages in Stack Monitoring was caused by changes
related to the locators of the logs-related apps.

The issue wasn't caught by type checks as the affected area in the
monitoring plugin was written in JavaScript.

The goal of this PR is to migrate the logs-related components to
TypeScript.

### Testing
The stateful environment deployed by this PR includes logs and metrics
for stack monitoring. Please make sure to select a larger time range
(e.g. last 14 days).
  • Loading branch information
gbamparop authored Dec 13, 2024
1 parent e061b4c commit 46a1535
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface ExternalConfig {
renderReactApp: boolean;
staleStatusThresholdSeconds: number;
isCcsEnabled: boolean;
logsIndices: string;
}

export const ExternalConfigContext = createContext({} as ExternalConfig);
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const sharePlugin = {
},
},
},
};
} as unknown as ReturnType<typeof sharePluginMock.createStartContract>;

const logs = {
enabled: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,42 @@
import React, { PureComponent, useContext } from 'react';
import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app';
import { upperFirst } from 'lodash';
import { Legacy } from '../../legacy_shims';
import { EuiBasicTable, EuiTitle, EuiSpacer, EuiText, EuiCallOut, EuiLink } from '@elastic/eui';
import { formatDateTimeLocal } from '../../../common/formatting';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { Reason } from './reason';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { SharePluginStart } from '@kbn/share-plugin/public';
import { Reason, type IReason } from './reason';
import { formatDateTimeLocal } from '../../../common/formatting';
import { Legacy } from '../../legacy_shims';
import { ExternalConfigContext } from '../../application/contexts/external_config_context';
import { MonitoringStartServices } from '../../types';

interface LogsProps {
logs: {
logs?: Array<{
timestamp: string;
component: string;
level: string;
type: string;
node: string;
message: string;
}>;
enabled: boolean;
limit: number;
reason?: IReason;
};
nodeId?: string;
indexUuid?: string;
clusterUuid?: string;
}

const getFormattedDateTimeLocal = (timestamp) => {
interface LogsContentProps extends LogsProps {
sharePlugin: SharePluginStart;
logsIndices: string;
}

const getFormattedDateTimeLocal = (timestamp: number | Date) => {
const timezone = Legacy.shims.uiSettings?.get('dateFormat:tz');
return formatDateTimeLocal(timestamp, timezone);
};
Expand Down Expand Up @@ -51,7 +77,7 @@ const columns = [
field: 'timestamp',
name: columnTimestampTitle,
width: '12%',
render: (timestamp) => getFormattedDateTimeLocal(timestamp),
render: (timestamp: number | Date) => getFormattedDateTimeLocal(timestamp),
},
{
field: 'level',
Expand All @@ -62,7 +88,7 @@ const columns = [
field: 'type',
name: columnTypeTitle,
width: '10%',
render: (type) => upperFirst(type),
render: (type: string) => upperFirst(type),
},
{
field: 'message',
Expand All @@ -81,7 +107,7 @@ const clusterColumns = [
field: 'timestamp',
name: columnTimestampTitle,
width: '12%',
render: (timestamp) => getFormattedDateTimeLocal(timestamp),
render: (timestamp: number | Date) => getFormattedDateTimeLocal(timestamp),
},
{
field: 'level',
Expand All @@ -92,7 +118,7 @@ const clusterColumns = [
field: 'type',
name: columnTypeTitle,
width: '10%',
render: (type) => upperFirst(type),
render: (type: string) => upperFirst(type),
},
{
field: 'message',
Expand All @@ -111,7 +137,13 @@ const clusterColumns = [
},
];

function getDiscoverLink(clusterUuid, nodeId, indexUuid, sharePlugin, logsIndices) {
function getDiscoverLink(
clusterUuid?: string,
nodeId?: string,
indexUuid?: string,
sharePlugin?: SharePluginStart,
logsIndices?: string
) {
const params = [];
if (clusterUuid) {
params.push(`elasticsearch.cluster.uuid:${clusterUuid}`);
Expand All @@ -124,13 +156,9 @@ function getDiscoverLink(clusterUuid, nodeId, indexUuid, sharePlugin, logsIndice
}

const filter = params.join(' and ');
const discoverLocator = sharePlugin.url.locators.get('DISCOVER_APP_LOCATOR');
const discoverLocator = sharePlugin?.url.locators.get('DISCOVER_APP_LOCATOR');

if (!discoverLocator) {
return;
}

const base = discoverLocator.getRedirectUrl({
const base = discoverLocator?.getRedirectUrl({
dataViewSpec: {
id: logsIndices,
title: logsIndices,
Expand All @@ -144,14 +172,15 @@ function getDiscoverLink(clusterUuid, nodeId, indexUuid, sharePlugin, logsIndice
return base;
}

export const Logs = (props) => {
export const Logs = (props: LogsProps) => {
const {
services: { share },
} = useKibana();
} = useKibana<MonitoringStartServices>();
const externalConfig = useContext(ExternalConfigContext);
return <LogsContent sharePlugin={share} logsIndices={externalConfig.logsIndices} {...props} />;
};
export class LogsContent extends PureComponent {

export class LogsContent extends PureComponent<LogsContentProps> {
renderLogs() {
const {
logs: { enabled, logs },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,19 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { Legacy } from '../../legacy_shims';
import { Monospace } from '../metricbeat_migration/instruction_steps/components/monospace/monospace';

export const Reason = ({ reason }) => {
export interface IReason {
indexPatternExists?: boolean;
indexPatternInTimeRangeExists?: boolean;
typeExists?: boolean;
typeExistsAtAnyTime?: boolean;
usingStructuredLogs?: boolean;
clusterExists?: boolean;
nodeExists?: boolean | null;
indexExists?: boolean;
correctIndexName?: boolean;
}

export const Reason = ({ reason }: { reason?: IReason }) => {
const filebeatUrl = Legacy.shims.docLinks.links.filebeat.installation;
const elasticsearchUrl = Legacy.shims.docLinks.links.filebeat.elasticsearchModule;
const troubleshootUrl = Legacy.shims.docLinks.links.monitoring.troubleshootKibana;
Expand All @@ -36,7 +48,7 @@ export const Reason = ({ reason }) => {
/>
);

if (false === reason.indexPatternExists) {
if (false === reason?.indexPatternExists) {
title = i18n.translate('xpack.monitoring.logs.reason.noIndexPatternTitle', {
defaultMessage: 'No log data found',
});
Expand All @@ -56,8 +68,8 @@ export const Reason = ({ reason }) => {
/>
);
} else if (
false === reason.indexPatternInTimeRangeExists ||
(false === reason.typeExists && reason.typeExistsAtAnyTime)
false === reason?.indexPatternInTimeRangeExists ||
(false === reason?.typeExists && reason.typeExistsAtAnyTime)
) {
title = i18n.translate('xpack.monitoring.logs.reason.noIndexPatternInTimePeriodTitle', {
defaultMessage: 'No logs for the selected time',
Expand All @@ -68,7 +80,7 @@ export const Reason = ({ reason }) => {
defaultMessage="Use the time filter to adjust your timeframe."
/>
);
} else if (false === reason.typeExists) {
} else if (false === reason?.typeExists) {
title = i18n.translate('xpack.monitoring.logs.reason.noTypeTitle', {
defaultMessage: 'No logs for Elasticsearch',
});
Expand All @@ -87,7 +99,7 @@ export const Reason = ({ reason }) => {
}}
/>
);
} else if (false === reason.usingStructuredLogs) {
} else if (false === reason?.usingStructuredLogs) {
title = i18n.translate('xpack.monitoring.logs.reason.notUsingStructuredLogsTitle', {
defaultMessage: 'No structured logs found',
});
Expand All @@ -107,7 +119,7 @@ export const Reason = ({ reason }) => {
}}
/>
);
} else if (false === reason.clusterExists) {
} else if (false === reason?.clusterExists) {
title = i18n.translate('xpack.monitoring.logs.reason.noClusterTitle', {
defaultMessage: 'No logs for this cluster',
});
Expand All @@ -126,7 +138,7 @@ export const Reason = ({ reason }) => {
}}
/>
);
} else if (false === reason.nodeExists) {
} else if (false === reason?.nodeExists) {
title = i18n.translate('xpack.monitoring.logs.reason.noNodeTitle', {
defaultMessage: 'No logs for this Elasticsearch node',
});
Expand All @@ -145,7 +157,7 @@ export const Reason = ({ reason }) => {
}}
/>
);
} else if (false === reason.indexExists) {
} else if (false === reason?.indexExists) {
title = i18n.translate('xpack.monitoring.logs.reason.noIndexTitle', {
defaultMessage: 'No logs for this index',
});
Expand All @@ -164,7 +176,7 @@ export const Reason = ({ reason }) => {
}}
/>
);
} else if (false === reason.correctIndexName) {
} else if (false === reason?.correctIndexName) {
title = i18n.translate('xpack.monitoring.logs.reason.correctIndexNameTitle', {
defaultMessage: 'Corrupted filebeat index',
});
Expand Down

0 comments on commit 46a1535

Please sign in to comment.