+
+
+ {capitalize(status)}
+
+ }
+ titleSize="xxxs"
+ textAlign="left"
+ className="monSummaryStatusNoWrap__stat"
+ description={i18n.translate('xpack.monitoring.summaryStatus.statusDescription', {
+ defaultMessage: 'Status',
+ })}
+ />
);
};
export function SummaryStatus({
- metrics,
+ StatusIndicator = DefaultStatusIndicator,
status,
- alerts,
isOnline,
IconComponent = DefaultIconComponent,
+ alerts,
+ metrics,
...props
}) {
return (
-
+
+
+
{alerts ? (
> = {
ccs: {
enabled: true,
},
+ kibana: {
+ reporting: {
+ stale_status_threshold_seconds: true,
+ },
+ },
},
kibana: true,
},
diff --git a/x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.ts b/x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.ts
index 9b939ad83c089..b338829c9e48f 100644
--- a/x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.ts
+++ b/x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.ts
@@ -6,27 +6,26 @@
*/
import { merge } from 'lodash';
-// @ts-ignore
-import { MissingRequiredError } from '../error_missing_required';
-// @ts-ignore
-import { calculateAvailability } from '../calculate_availability';
-import { LegacyRequest } from '../../types';
import { ElasticsearchResponse } from '../../../common/types/es';
-import { getNewIndexPatterns } from '../cluster/get_index_patterns';
import { Globals } from '../../static_globals';
+import { LegacyRequest } from '../../types';
+import { getNewIndexPatterns } from '../cluster/get_index_patterns';
+import { MissingRequiredError } from '../error_missing_required';
import { buildKibanaInfo } from './build_kibana_info';
+import { isKibanaStatusStale } from './is_kibana_status_stale';
export function handleResponse(resp: ElasticsearchResponse) {
const hit = resp.hits?.hits[0];
const legacySource = hit?._source.kibana_stats;
const mbSource = hit?._source.kibana?.stats;
- const availabilityTimestamp = hit?._source['@timestamp'] ?? legacySource?.timestamp;
- if (!availabilityTimestamp) {
+ const lastSeenTimestamp = hit?._source['@timestamp'] ?? legacySource?.timestamp;
+ if (!lastSeenTimestamp) {
throw new MissingRequiredError('timestamp');
}
return merge(buildKibanaInfo(hit!), {
- availability: calculateAvailability(availabilityTimestamp),
+ statusIsStale: isKibanaStatusStale(lastSeenTimestamp),
+ lastSeenTimestamp,
os_memory_free: mbSource?.os?.memory?.free_in_bytes ?? legacySource?.os?.memory?.free_in_bytes,
uptime: mbSource?.process?.uptime?.ms ?? legacySource?.process?.uptime_in_millis,
});
diff --git a/x-pack/plugins/monitoring/server/lib/kibana/get_kibanas.ts b/x-pack/plugins/monitoring/server/lib/kibana/get_kibanas.ts
index 7f6f45cc98500..69cda62ae852c 100644
--- a/x-pack/plugins/monitoring/server/lib/kibana/get_kibanas.ts
+++ b/x-pack/plugins/monitoring/server/lib/kibana/get_kibanas.ts
@@ -6,17 +6,14 @@
*/
import moment from 'moment';
-// @ts-ignore
-import { createQuery } from '../create_query';
-// @ts-ignore
-import { calculateAvailability } from '../calculate_availability';
-// @ts-ignore
-import { KibanaMetric } from '../metrics';
+import { ElasticsearchResponse, ElasticsearchResponseHit } from '../../../common/types/es';
+import { Globals } from '../../static_globals';
import { LegacyRequest } from '../../types';
import { getNewIndexPatterns } from '../cluster/get_index_patterns';
-import { Globals } from '../../static_globals';
-import { ElasticsearchResponse, ElasticsearchResponseHit } from '../../../common/types/es';
-import { KibanaInfo, buildKibanaInfo } from './build_kibana_info';
+import { createQuery } from '../create_query';
+import { KibanaMetric } from '../metrics';
+import { buildKibanaInfo, KibanaInfo } from './build_kibana_info';
+import { isKibanaStatusStale } from './is_kibana_status_stale';
interface Kibana {
process?: {
@@ -38,7 +35,8 @@ interface Kibana {
};
concurrent_connections?: number;
kibana?: KibanaInfo;
- availability: boolean;
+ statusIsStale: boolean;
+ lastSeenTimestamp: string;
}
/*
@@ -120,6 +118,8 @@ export async function getKibanas(req: LegacyRequest, { clusterUuid }: { clusterU
const legacyStats = hit._source.kibana_stats;
const mbStats = hit._source.kibana?.stats;
+ const lastSeenTimestamp = hit._source['@timestamp'] ?? hit._source.timestamp;
+
const kibana: Kibana = {
kibana: buildKibanaInfo(hit),
concurrent_connections:
@@ -143,7 +143,8 @@ export async function getKibanas(req: LegacyRequest, { clusterUuid }: { clusterU
requests: {
total: mbStats?.request?.total ?? legacyStats?.requests?.total,
},
- availability: calculateAvailability(hit._source['@timestamp'] ?? hit._source.timestamp),
+ statusIsStale: isKibanaStatusStale(lastSeenTimestamp),
+ lastSeenTimestamp,
};
return kibana;
});
diff --git a/x-pack/plugins/monitoring/server/lib/kibana/get_kibanas_for_clusters.ts b/x-pack/plugins/monitoring/server/lib/kibana/get_kibanas_for_clusters.ts
index 4b3acd4e937db..17ccff4a9ea7d 100644
--- a/x-pack/plugins/monitoring/server/lib/kibana/get_kibanas_for_clusters.ts
+++ b/x-pack/plugins/monitoring/server/lib/kibana/get_kibanas_for_clusters.ts
@@ -6,11 +6,12 @@
*/
import { chain, find } from 'lodash';
-import { LegacyRequest, Cluster, Bucket } from '../../types';
+import { Globals } from '../../static_globals';
+import { Bucket, Cluster, LegacyRequest } from '../../types';
+import { getNewIndexPatterns } from '../cluster/get_index_patterns';
import { createQuery } from '../create_query';
import { KibanaClusterMetric } from '../metrics';
-import { getNewIndexPatterns } from '../cluster/get_index_patterns';
-import { Globals } from '../../static_globals';
+import { isKibanaStatusStale } from './is_kibana_status_stale';
/*
* Get high-level info for Kibanas in a set of clusters
@@ -74,6 +75,11 @@ export function getKibanasForClusters(req: LegacyRequest, clusters: Cluster[], c
},
},
aggs: {
+ last_seen: {
+ max: {
+ field: 'kibana_stats.timestamp',
+ },
+ },
response_time_max: {
max: {
field: 'kibana_stats.response_times.max',
@@ -185,6 +191,7 @@ export function getKibanasForClusters(req: LegacyRequest, clusters: Cluster[], c
let responseTime = 0;
let memorySize = 0;
let memoryLimit = 0;
+ let someStatusIsStale = true;
// if the cluster has kibana instances at all
if (kibanaUuids.length) {
@@ -204,6 +211,8 @@ export function getKibanasForClusters(req: LegacyRequest, clusters: Cluster[], c
responseTime = aggregations.response_time_max?.value;
memorySize = aggregations.memory_rss?.value;
memoryLimit = aggregations.memory_heap_size_limit?.value;
+
+ someStatusIsStale = kibanaUuids.some(hasStaleStatus);
}
return {
@@ -211,6 +220,7 @@ export function getKibanasForClusters(req: LegacyRequest, clusters: Cluster[], c
stats: {
uuids: kibanaUuids.map(({ key }: Bucket) => key),
status,
+ some_status_is_stale: someStatusIsStale,
requests_total: requestsTotal,
concurrent_connections: connections,
response_time_max: responseTime,
@@ -223,3 +233,19 @@ export function getKibanasForClusters(req: LegacyRequest, clusters: Cluster[], c
})
);
}
+
+function hasStaleStatus(kibana: any) {
+ const buckets: any[] = kibana?.latest_report?.buckets ?? [];
+
+ if (buckets.length === 0) {
+ return true;
+ }
+
+ const lastSeenTimestamp: string | null = buckets[0]?.last_seen?.value_as_string ?? null;
+
+ if (lastSeenTimestamp === null) {
+ return true;
+ }
+
+ return isKibanaStatusStale(lastSeenTimestamp);
+}
diff --git a/x-pack/plugins/monitoring/server/lib/kibana/is_kibana_status_stale.ts b/x-pack/plugins/monitoring/server/lib/kibana/is_kibana_status_stale.ts
new file mode 100644
index 0000000000000..841fd61ad65f4
--- /dev/null
+++ b/x-pack/plugins/monitoring/server/lib/kibana/is_kibana_status_stale.ts
@@ -0,0 +1,18 @@
+/*
+ * 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 moment from 'moment';
+import { Globals } from '../../static_globals';
+
+export function isKibanaStatusStale(lastSeenTimestamp: string) {
+ const lastSeen = moment(lastSeenTimestamp);
+ const staleThreshold = moment().subtract(
+ Globals.app.config.ui.kibana.reporting.stale_status_threshold_seconds,
+ 'seconds'
+ );
+ return staleThreshold.isAfter(lastSeen);
+}