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)

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.

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 and mykolaharmash committed Dec 13, 2024
1 parent e061b4c commit 41dbdb5
Show file tree
Hide file tree
Showing 12 changed files with 128 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
KIBANA_BASE_URL = "http://localhost:5601/ftw"
ELASTICSEARCH_HOST = "http://localhost:9200"
KIBANA_USERNAME = "elastic"
KIBANA_PASSWORD = "changeme"

# Comment this variable if running against a cloud environment
CLUSTER_ENVIRONMENT = local
# Folder that will contain all the playwright test results
# along with artifacts like code snippets that are
# picked up by Ensemble story on the CI
ARTIFACTS_FOLDER = ./.playwright
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"cookies": [
{
"name": "sid",
"value": "Fe26.2**0f6c7b5841cd939b4f7e6042e8e548a61396f6137a16a80468967d75a3c4a15d*3aO-W92cmioNifwGvMIe7w*Js9nTdolngZrFhPjTAihnf4ND08FtNYrODG00EvZrd193yWRvEVQy__7L5FZQtS1sD5LntsQMZO2gy5wPFuBKog0AXKOVIj-kxzIKtIbiUiTdLVZl2kqvmMqpYkhfFwK0SDubaMrypI14odSGRxYzAfGTL7czxDatfNdndIPTYlpkUCKKvJdTBZOQS2H3HbDlzPb71pFl7YPvQSSbdFe0frFIOytodO8JxPj2dsqIpGbZZ9mBfriiF3S6SU1ayR1DjLRMpyL32MLyOrlzJKy_w**b2cea11d4b35aa93dc79e42a09484d199a1fdac7234a01438f7dd2c084387c44*hj1VDxXhtXc4m9sC_vq_Xck3R3E8za4ramNZLcIL5pM",
"domain": "localhost",
"path": "/ftw",
"expires": -1,
"httpOnly": true,
"secure": false,
"sameSite": "Lax"
}
],
"origins": [
{
"origin": "http://localhost:5601",
"localStorage": [
{
"name": "newsfeed.kibana.readStatus",
"value": "{\"235b3e1b48\":false,\"99c40b0db0\":false,\"297e88cb8d\":false}"
},
{
"name": "analytics",
"value": "{\"reportVersion\":3,\"userAgent\":{\"kibana-user_agent\":{\"key\":\"kibana-user_agent\",\"appName\":\"kibana\",\"type\":\"user_agent\",\"userAgent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36\"}},\"uiCounter\":{\"ebt_counters.client-enqueued_enqueued-Loaded Kibana\":{\"key\":\"ebt_counters.client-enqueued_enqueued-Loaded Kibana\",\"appName\":\"ebt_counters.client\",\"eventName\":\"Loaded Kibana\",\"type\":\"enqueued_enqueued\",\"total\":1},\"ebt_counters.client-enqueued_enqueued-performance_metric\":{\"key\":\"ebt_counters.client-enqueued_enqueued-performance_metric\",\"appName\":\"ebt_counters.client\",\"eventName\":\"performance_metric\",\"type\":\"enqueued_enqueued\",\"total\":2},\"ebt_counters.client-sent_to_shipper_OK-Loaded Kibana\":{\"key\":\"ebt_counters.client-sent_to_shipper_OK-Loaded Kibana\",\"appName\":\"ebt_counters.client\",\"eventName\":\"Loaded Kibana\",\"type\":\"sent_to_shipper_OK\",\"total\":1},\"ebt_counters.client-sent_to_shipper_OK-performance_metric\":{\"key\":\"ebt_counters.client-sent_to_shipper_OK-performance_metric\",\"appName\":\"ebt_counters.client\",\"eventName\":\"performance_metric\",\"type\":\"sent_to_shipper_OK\",\"total\":2}}}"
},
{
"name": "kibana.security.userAuthType",
"value": "{\"signature\":\"dd4be0630945f1a8be8e10baabf6536c937d053481c09e30a0703b5ce17e1fcd\",\"timestamp\":1733926278035}"
},
{
"name": "newsfeed.kibana.lastFetch",
"value": "1733926278001"
},
{
"name": "eui-experimental-theme-enabled",
"value": "true"
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"status": "passed",
"failedTests": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
kubectl kustomize https://github.com/elastic/elastic-agent/deploy/kubernetes/elastic-agent-kustomize/default/elastic-agent-standalone\?ref\=v8.16.1 | sed -e 's/JUFQSV9LRVkl/enN3UHRwTUJRbjdEVk1laFpoZXA6ZEtrai1lRnFSUEcwYVhKbHREM2tIUQ==/g' -e "s/%ES_HOST%/http:\/\/localhost:9200/g" -e "s/%ONBOARDING_ID%/af9c9709-52da-45a8-904f-228aa2e61621/g" -e "s/\(docker.elastic.co\/beats\/elastic-agent:\).*$/\18.16.1/g" -e "/{CA_TRUSTED}/c\ " | kubectl apply -f-

0 comments on commit 41dbdb5

Please sign in to comment.