diff --git a/x-pack/legacy/plugins/infra/common/graphql/types.ts b/x-pack/legacy/plugins/infra/common/graphql/types.ts
index 7843a54b93bed..a9c50e97f4348 100644
--- a/x-pack/legacy/plugins/infra/common/graphql/types.ts
+++ b/x-pack/legacy/plugins/infra/common/graphql/types.ts
@@ -36,6 +36,8 @@ export interface InfraSource {
logEntryHighlights: InfraLogEntryInterval[];
/** A consecutive span of summary buckets within an interval */
logSummaryBetween: InfraLogSummaryInterval;
+ /** Spans of summary highlight buckets within an interval */
+ logSummaryHighlightsBetween: InfraLogSummaryHighlightInterval[];
logItem: InfraLogItem;
/** A snapshot of nodes */
@@ -224,6 +226,30 @@ export interface InfraLogSummaryBucket {
/** The number of entries inside the bucket */
entriesCount: number;
}
+/** A consecutive sequence of log summary highlight buckets */
+export interface InfraLogSummaryHighlightInterval {
+ /** The millisecond timestamp corresponding to the start of the interval covered by the summary */
+ start?: number | null;
+ /** The millisecond timestamp corresponding to the end of the interval covered by the summary */
+ end?: number | null;
+ /** The query the log entries were filtered by */
+ filterQuery?: string | null;
+ /** The query the log entries were highlighted with */
+ highlightQuery?: string | null;
+ /** A list of the log entries */
+ buckets: InfraLogSummaryHighlightBucket[];
+}
+/** A log summary highlight bucket */
+export interface InfraLogSummaryHighlightBucket {
+ /** The start timestamp of the bucket */
+ start: number;
+ /** The end timestamp of the bucket */
+ end: number;
+ /** The number of highlighted entries inside the bucket */
+ entriesCount: number;
+ /** The time key of a representative of the highlighted log entries in this bucket */
+ representativeKey: InfraTimeKey;
+}
export interface InfraLogItem {
/** The ID of the document */
@@ -446,6 +472,18 @@ export interface LogSummaryBetweenInfraSourceArgs {
/** The query to filter the log entries by */
filterQuery?: string | null;
}
+export interface LogSummaryHighlightsBetweenInfraSourceArgs {
+ /** The millisecond timestamp that corresponds to the start of the interval */
+ start: number;
+ /** The millisecond timestamp that corresponds to the end of the interval */
+ end: number;
+ /** The size of each bucket in milliseconds */
+ bucketSize: number;
+ /** The query to filter the log entries by */
+ filterQuery?: string | null;
+ /** The highlighting to apply to the log entries */
+ highlightQueries: string[];
+}
export interface LogItemInfraSourceArgs {
id: string;
}
@@ -657,6 +695,55 @@ export namespace LogEntryHighlightsQuery {
export type Entries = InfraLogEntryHighlightFields.Fragment;
}
+export namespace LogSummaryHighlightsQuery {
+ export type Variables = {
+ sourceId?: string | null;
+ start: number;
+ end: number;
+ bucketSize: number;
+ highlightQueries: string[];
+ filterQuery?: string | null;
+ };
+
+ export type Query = {
+ __typename?: 'Query';
+
+ source: Source;
+ };
+
+ export type Source = {
+ __typename?: 'InfraSource';
+
+ id: string;
+
+ logSummaryHighlightsBetween: LogSummaryHighlightsBetween[];
+ };
+
+ export type LogSummaryHighlightsBetween = {
+ __typename?: 'InfraLogSummaryHighlightInterval';
+
+ start?: number | null;
+
+ end?: number | null;
+
+ buckets: Buckets[];
+ };
+
+ export type Buckets = {
+ __typename?: 'InfraLogSummaryHighlightBucket';
+
+ start: number;
+
+ end: number;
+
+ entriesCount: number;
+
+ representativeKey: RepresentativeKey;
+ };
+
+ export type RepresentativeKey = InfraTimeKeyFields.Fragment;
+}
+
export namespace LogSummary {
export type Variables = {
sourceId?: string | null;
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx
index 988458f30b3d1..f84fe6b713b89 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx
+++ b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx
@@ -9,12 +9,11 @@ import * as React from 'react';
import euiStyled from '../../../../../../common/eui_styled_components';
import { LogEntryTime } from '../../../../common/log_entry';
-// import { SearchSummaryBucket } from '../../../../common/log_search_summary';
import { DensityChart } from './density_chart';
import { HighlightedInterval } from './highlighted_interval';
-// import { SearchMarkers } from './search_markers';
+import { SearchMarkers } from './search_markers';
import { TimeRuler } from './time_ruler';
-import { SummaryBucket } from './types';
+import { SummaryBucket, SummaryHighlightBucket } from './types';
interface LogMinimapProps {
className?: string;
@@ -26,7 +25,7 @@ interface LogMinimapProps {
jumpToTarget: (params: LogEntryTime) => any;
intervalSize: number;
summaryBuckets: SummaryBucket[];
- // searchSummaryBuckets?: SearchSummaryBucket[];
+ summaryHighlightBuckets?: SummaryHighlightBucket[];
target: number | null;
width: number;
}
@@ -81,9 +80,9 @@ export class LogMinimap extends React.Component
) : null}
-
- {/*
+
- */}
+
+
);
}
@@ -145,6 +144,7 @@ const MinimapBorder = euiStyled.line`
`;
const TimeCursor = euiStyled.line`
+ pointer-events: none;
stroke-width: 1px;
stroke: ${props =>
props.theme.darkMode
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/search_marker.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/search_marker.tsx
index 8a3292312de18..5b661562a451e 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/search_marker.tsx
+++ b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/search_marker.tsx
@@ -9,11 +9,11 @@ import * as React from 'react';
import euiStyled, { keyframes } from '../../../../../../common/eui_styled_components';
import { LogEntryTime } from '../../../../common/log_entry';
-import { SearchSummaryBucket } from '../../../../common/log_search_summary';
import { SearchMarkerTooltip } from './search_marker_tooltip';
+import { SummaryHighlightBucket } from './types';
interface SearchMarkerProps {
- bucket: SearchSummaryBucket;
+ bucket: SummaryHighlightBucket;
height: number;
width: number;
jumpToTarget: (target: LogEntryTime) => void;
@@ -31,7 +31,7 @@ export class SearchMarker extends React.PureComponent = evt => {
evt.stopPropagation();
- this.props.jumpToTarget(this.props.bucket.representative.fields);
+ this.props.jumpToTarget(this.props.bucket.representativeKey);
};
public handleMouseEnter: React.MouseEventHandler = evt => {
@@ -51,7 +51,7 @@ export class SearchMarker extends React.PureComponent 1 ? (
+ bucket.entriesCount > 1 ? (
) : (
<>
@@ -73,9 +73,9 @@ export class SearchMarker extends React.PureComponent
@@ -107,15 +107,16 @@ const SearchMarkerGroup = euiStyled.g`
`;
const SearchMarkerBackgroundRect = euiStyled.rect`
- fill: ${props => props.theme.eui.euiColorSecondary};
+ fill: ${props => props.theme.eui.euiColorAccent};
opacity: 0;
transition: opacity ${props => props.theme.eui.euiAnimSpeedNormal} ease-in;
+ cursor: pointer;
${SearchMarkerGroup}:hover & {
- opacity: 0.2;
+ opacity: 0.3;
}
`;
const SearchMarkerForegroundRect = euiStyled.rect`
- fill: ${props => props.theme.eui.euiColorSecondary};
+ fill: ${props => props.theme.eui.euiColorAccent};
`;
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/search_markers.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/search_markers.tsx
index 8ad3947cc5a23..5007ce8863e70 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/search_markers.tsx
+++ b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/search_markers.tsx
@@ -9,11 +9,11 @@ import { scaleTime } from 'd3-scale';
import * as React from 'react';
import { LogEntryTime } from '../../../../common/log_entry';
-import { SearchSummaryBucket } from '../../../../common/log_search_summary';
import { SearchMarker } from './search_marker';
+import { SummaryHighlightBucket } from './types';
interface SearchMarkersProps {
- buckets: SearchSummaryBucket[];
+ buckets: SummaryHighlightBucket[];
className?: string;
end: number;
start: number;
@@ -38,7 +38,10 @@ export class SearchMarkers extends React.PureComponent {
return (
{buckets.map(bucket => (
-
+
props.theme.eui.euiFontSizeXS};
line-height: ${props => props.theme.eui.euiLineHeight};
fill: ${props => props.theme.eui.textColors.subdued};
+ pointer-events: none;
`;
const TimeRulerGridLine = euiStyled.line`
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/types.ts b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/types.ts
index ac3ea48bc4b16..d8197935dafa7 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/types.ts
+++ b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/types.ts
@@ -4,8 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { TimeKey } from '../../../../common/time';
+
export interface SummaryBucket {
start: number;
end: number;
entriesCount: number;
}
+
+export interface SummaryHighlightBucket extends SummaryBucket {
+ representativeKey: TimeKey;
+}
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_highlights.gql_query.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.gql_query.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_highlights.gql_query.ts
rename to x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.gql_query.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/data_fetching.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx
similarity index 71%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/data_fetching.tsx
rename to x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx
index 2efe6bbb5bd6a..6ead866fb960a 100644
--- a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/data_fetching.tsx
+++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx
@@ -11,11 +11,11 @@ import { LogEntryHighlightsQuery } from '../../../graphql/types';
import { DependencyError, useApolloClient } from '../../../utils/apollo_context';
import { LogEntryHighlightsMap } from '../../../utils/log_entry';
import { useTrackedPromise } from '../../../utils/use_tracked_promise';
-import { logEntryHighlightsQuery } from './log_highlights.gql_query';
+import { logEntryHighlightsQuery } from './log_entry_highlights.gql_query';
export type LogEntryHighlights = LogEntryHighlightsQuery.Query['source']['logEntryHighlights'];
-export const useHighlightsFetcher = (
+export const useLogEntryHighlights = (
sourceId: string,
sourceVersion: string | undefined,
startKey: TimeKey | null,
@@ -24,9 +24,7 @@ export const useHighlightsFetcher = (
highlightTerms: string[]
) => {
const apolloClient = useApolloClient();
- const [logEntryHighlights, setLogEntryHighlights] = useState(
- undefined
- );
+ const [logEntryHighlights, setLogEntryHighlights] = useState([]);
const [loadLogEntryHighlightsRequest, loadLogEntryHighlights] = useTrackedPromise(
{
cancelPreviousOn: 'resolution',
@@ -35,7 +33,7 @@ export const useHighlightsFetcher = (
throw new DependencyError('Failed to load source: No apollo client available.');
}
if (!startKey || !endKey || !highlightTerms.length) {
- throw new Error();
+ throw new Error('Skipping request: Insufficient parameters');
}
return await apolloClient.query<
@@ -51,9 +49,7 @@ export const useHighlightsFetcher = (
filterQuery,
highlights: [
{
- query: JSON.stringify({
- multi_match: { query: highlightTerms[0], type: 'phrase', lenient: true },
- }),
+ query: highlightTerms[0],
countBefore: 1,
countAfter: 1,
},
@@ -69,7 +65,7 @@ export const useHighlightsFetcher = (
);
useEffect(() => {
- setLogEntryHighlights(undefined);
+ setLogEntryHighlights([]);
}, [highlightTerms]);
useEffect(() => {
@@ -80,29 +76,24 @@ export const useHighlightsFetcher = (
) {
loadLogEntryHighlights();
} else {
- setLogEntryHighlights(undefined);
+ setLogEntryHighlights([]);
}
}, [highlightTerms, startKey, endKey, filterQuery, sourceVersion]);
const logEntryHighlightsById = useMemo(
() =>
- logEntryHighlights
- ? logEntryHighlights.reduce(
- (accumulatedLogEntryHighlightsById, { entries }) => {
- return entries.reduce(
- (singleHighlightLogEntriesById, entry) => {
- const highlightsForId = singleHighlightLogEntriesById[entry.gid] || [];
- return {
- ...singleHighlightLogEntriesById,
- [entry.gid]: [...highlightsForId, entry],
- };
- },
- accumulatedLogEntryHighlightsById
- );
- },
- {}
- )
- : {},
+ logEntryHighlights.reduce(
+ (accumulatedLogEntryHighlightsById, { entries }) => {
+ return entries.reduce((singleHighlightLogEntriesById, entry) => {
+ const highlightsForId = singleHighlightLogEntriesById[entry.gid] || [];
+ return {
+ ...singleHighlightLogEntriesById,
+ [entry.gid]: [...highlightsForId, entry],
+ };
+ }, accumulatedLogEntryHighlightsById);
+ },
+ {}
+ ),
[logEntryHighlights]
);
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx
index 5c7bdad139b04..537dd5c0070f5 100644
--- a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx
+++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx
@@ -5,11 +5,14 @@
*/
import createContainer from 'constate-latest';
-import { useState } from 'react';
+import { useState, useContext } from 'react';
-import { useHighlightsFetcher } from './data_fetching';
+import { useLogEntryHighlights } from './log_entry_highlights';
+import { useLogSummaryHighlights } from './log_summary_highlights';
import { useNextAndPrevious } from './next_and_previous';
import { useReduxBridgeSetters } from './redux_bridge_setters';
+import { useLogSummaryBufferInterval } from '../log_summary';
+import { LogViewConfiguration } from '../log_view_configuration';
export const useLogHighlightsState = ({
sourceId,
@@ -33,11 +36,31 @@ export const useLogHighlightsState = ({
setJumpToTarget,
} = useReduxBridgeSetters();
+ const { intervalSize: summaryIntervalSize } = useContext(LogViewConfiguration.Context);
+ const {
+ start: summaryStart,
+ end: summaryEnd,
+ bucketSize: summaryBucketSize,
+ } = useLogSummaryBufferInterval(
+ visibleMidpoint ? visibleMidpoint.time : null,
+ summaryIntervalSize
+ );
+
const {
logEntryHighlights,
logEntryHighlightsById,
loadLogEntryHighlightsRequest,
- } = useHighlightsFetcher(sourceId, sourceVersion, startKey, endKey, filterQuery, highlightTerms);
+ } = useLogEntryHighlights(sourceId, sourceVersion, startKey, endKey, filterQuery, highlightTerms);
+
+ const { logSummaryHighlights, loadLogSummaryHighlightsRequest } = useLogSummaryHighlights(
+ sourceId,
+ sourceVersion,
+ summaryStart,
+ summaryEnd,
+ summaryBucketSize,
+ filterQuery,
+ highlightTerms
+ );
const {
currentHighlightKey,
@@ -60,7 +83,9 @@ export const useLogHighlightsState = ({
setFilterQuery,
logEntryHighlights,
logEntryHighlightsById,
+ logSummaryHighlights,
loadLogEntryHighlightsRequest,
+ loadLogSummaryHighlightsRequest,
setVisibleMidpoint,
currentHighlightKey,
hasPreviousHighlight,
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.gql_query.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.gql_query.ts
new file mode 100644
index 0000000000000..4d2c4075b50e7
--- /dev/null
+++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.gql_query.ts
@@ -0,0 +1,44 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import gql from 'graphql-tag';
+
+import { sharedFragments } from '../../../../common/graphql/shared';
+
+export const logSummaryHighlightsQuery = gql`
+ query LogSummaryHighlightsQuery(
+ $sourceId: ID = "default"
+ $start: Float!
+ $end: Float!
+ $bucketSize: Float!
+ $highlightQueries: [String!]!
+ $filterQuery: String
+ ) {
+ source(id: $sourceId) {
+ id
+ logSummaryHighlightsBetween(
+ start: $start
+ end: $end
+ bucketSize: $bucketSize
+ highlightQueries: $highlightQueries
+ filterQuery: $filterQuery
+ ) {
+ start
+ end
+ buckets {
+ start
+ end
+ entriesCount
+ representativeKey {
+ ...InfraTimeKeyFields
+ }
+ }
+ }
+ }
+ }
+
+ ${sharedFragments.InfraTimeKey}
+`;
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts
new file mode 100644
index 0000000000000..34c66afda010e
--- /dev/null
+++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts
@@ -0,0 +1,83 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { useEffect, useMemo, useState } from 'react';
+import { debounce } from 'lodash';
+
+import { LogSummaryHighlightsQuery } from '../../../graphql/types';
+import { DependencyError, useApolloClient } from '../../../utils/apollo_context';
+import { useTrackedPromise } from '../../../utils/use_tracked_promise';
+import { logSummaryHighlightsQuery } from './log_summary_highlights.gql_query';
+
+export type LogSummaryHighlights = LogSummaryHighlightsQuery.Query['source']['logSummaryHighlightsBetween'];
+
+export const useLogSummaryHighlights = (
+ sourceId: string,
+ sourceVersion: string | undefined,
+ start: number | null,
+ end: number | null,
+ bucketSize: number,
+ filterQuery: string | null,
+ highlightTerms: string[]
+) => {
+ const apolloClient = useApolloClient();
+ const [logSummaryHighlights, setLogSummaryHighlights] = useState([]);
+
+ const [loadLogSummaryHighlightsRequest, loadLogSummaryHighlights] = useTrackedPromise(
+ {
+ cancelPreviousOn: 'resolution',
+ createPromise: async () => {
+ if (!apolloClient) {
+ throw new DependencyError('Failed to load source: No apollo client available.');
+ }
+ if (!start || !end || !highlightTerms.length) {
+ throw new Error('Skipping request: Insufficient parameters');
+ }
+
+ return await apolloClient.query<
+ LogSummaryHighlightsQuery.Query,
+ LogSummaryHighlightsQuery.Variables
+ >({
+ fetchPolicy: 'no-cache',
+ query: logSummaryHighlightsQuery,
+ variables: {
+ sourceId,
+ start,
+ end,
+ bucketSize,
+ highlightQueries: [highlightTerms[0]],
+ filterQuery,
+ },
+ });
+ },
+ onResolve: response => {
+ setLogSummaryHighlights(response.data.source.logSummaryHighlightsBetween);
+ },
+ },
+ [apolloClient, sourceId, start, end, bucketSize, filterQuery, highlightTerms]
+ );
+
+ const debouncedLoadSummaryHighlights = useMemo(() => debounce(loadLogSummaryHighlights, 275), [
+ loadLogSummaryHighlights,
+ ]);
+
+ useEffect(() => {
+ setLogSummaryHighlights([]);
+ }, [highlightTerms]);
+
+ useEffect(() => {
+ if (highlightTerms.filter(highlightTerm => highlightTerm.length > 0).length && start && end) {
+ debouncedLoadSummaryHighlights();
+ } else {
+ setLogSummaryHighlights([]);
+ }
+ }, [highlightTerms, start, end, bucketSize, filterQuery, sourceVersion]);
+
+ return {
+ logSummaryHighlights,
+ loadLogSummaryHighlightsRequest,
+ };
+};
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/next_and_previous.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/next_and_previous.tsx
index 6cf602a4a701e..95ead50119eb4 100644
--- a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/next_and_previous.tsx
+++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/next_and_previous.tsx
@@ -13,7 +13,7 @@ import {
getLogEntryIndexBeforeTime,
getUniqueLogEntryKey,
} from '../../../utils/log_entry';
-import { LogEntryHighlights } from './data_fetching';
+import { LogEntryHighlights } from './log_entry_highlights';
export const useNextAndPrevious = ({
highlightTerms,
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/index.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/index.ts
index c62fd0379de58..20c4267000a25 100644
--- a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/index.ts
+++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/index.ts
@@ -4,5 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
-export * from './with_summary';
export * from './log_summary';
+export * from './use_log_summary_buffer_interval';
+export * from './with_summary';
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.tsx
index b5c765c2c2a27..a188e698991a0 100644
--- a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.tsx
+++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.tsx
@@ -4,14 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { useMemo, useState } from 'react';
+import { useState } from 'react';
import { LogSummary as LogSummaryQuery } from '../../../graphql/types';
import { useApolloClient } from '../../../utils/apollo_context';
import { useCancellableEffect } from '../../../utils/cancellable_effect';
import { logSummaryQuery } from './log_summary.gql_query';
+import { useLogSummaryBufferInterval } from './use_log_summary_buffer_interval';
-const LOAD_BUCKETS_PER_PAGE = 100;
export type LogSummaryBetween = LogSummaryQuery.Query['source']['logSummaryBetween'];
export type LogSummaryBuckets = LogSummaryBetween['buckets'];
@@ -24,17 +24,10 @@ export const useLogSummary = (
const [logSummaryBetween, setLogSummaryBetween] = useState({ buckets: [] });
const apolloClient = useApolloClient();
- const [bufferStart, bufferEnd] = useMemo(() => {
- if (midpointTime === null || intervalSize <= 0) {
- return [null, null];
- }
-
- const halfIntervalSize = intervalSize / 2;
- return [
- (Math.floor((midpointTime - halfIntervalSize) / intervalSize) - 0.5) * intervalSize,
- (Math.ceil((midpointTime + halfIntervalSize) / intervalSize) + 0.5) * intervalSize,
- ];
- }, [midpointTime, intervalSize]);
+ const { start: bufferStart, end: bufferEnd, bucketSize } = useLogSummaryBufferInterval(
+ midpointTime,
+ intervalSize
+ );
useCancellableEffect(
getIsCancelled => {
@@ -51,7 +44,7 @@ export const useLogSummary = (
sourceId,
start: bufferStart,
end: bufferEnd,
- bucketSize: intervalSize / LOAD_BUCKETS_PER_PAGE,
+ bucketSize,
},
})
.then(response => {
@@ -60,10 +53,12 @@ export const useLogSummary = (
}
});
},
- [apolloClient, sourceId, filterQuery, bufferStart, bufferEnd, intervalSize]
+ [apolloClient, sourceId, filterQuery, bufferStart, bufferEnd, bucketSize]
);
return {
buckets: logSummaryBetween.buckets,
+ start: bufferStart,
+ end: bufferEnd,
};
};
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/use_log_summary_buffer_interval.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/use_log_summary_buffer_interval.ts
new file mode 100644
index 0000000000000..27af76b70f47a
--- /dev/null
+++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/use_log_summary_buffer_interval.ts
@@ -0,0 +1,30 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { useMemo } from 'react';
+
+const LOAD_BUCKETS_PER_PAGE = 100;
+const UNKNOWN_BUFFER_INTERVAL = {
+ start: null,
+ end: null,
+ bucketSize: 0,
+};
+
+export const useLogSummaryBufferInterval = (midpointTime: number | null, intervalSize: number) => {
+ return useMemo(() => {
+ if (midpointTime === null || intervalSize <= 0) {
+ return UNKNOWN_BUFFER_INTERVAL;
+ }
+
+ const halfIntervalSize = intervalSize / 2;
+
+ return {
+ start: (Math.floor((midpointTime - halfIntervalSize) / intervalSize) - 0.5) * intervalSize,
+ end: (Math.ceil((midpointTime + halfIntervalSize) / intervalSize) + 0.5) * intervalSize,
+ bucketSize: intervalSize / LOAD_BUCKETS_PER_PAGE,
+ };
+ }, [midpointTime, intervalSize]);
+};
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/with_summary.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/with_summary.ts
index 5df603f0d92b6..61c603130df52 100644
--- a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/with_summary.ts
+++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/with_summary.ts
@@ -22,15 +22,24 @@ export const WithSummary = connect((state: State) => ({
filterQuery,
visibleMidpointTime,
}: {
- children: RendererFunction<{ buckets: LogSummaryBuckets }>;
+ children: RendererFunction<{
+ buckets: LogSummaryBuckets;
+ start: number | null;
+ end: number | null;
+ }>;
filterQuery: string | null;
visibleMidpointTime: number | null;
}) => {
const { intervalSize } = useContext(LogViewConfiguration.Context);
const { sourceId } = useContext(Source.Context);
- const { buckets } = useLogSummary(sourceId, visibleMidpointTime, intervalSize, filterQuery);
+ const { buckets, start, end } = useLogSummary(
+ sourceId,
+ visibleMidpointTime,
+ intervalSize,
+ filterQuery
+ );
- return children({ buckets });
+ return children({ buckets, start, end });
}
);
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/with_stream_items.ts b/x-pack/legacy/plugins/infra/public/containers/logs/with_stream_items.ts
index 51fee075b6443..6a79d7b8e4ac9 100644
--- a/x-pack/legacy/plugins/infra/public/containers/logs/with_stream_items.ts
+++ b/x-pack/legacy/plugins/infra/public/containers/logs/with_stream_items.ts
@@ -27,7 +27,6 @@ export const withStreamItems = connect(
entries: logEntriesSelectors.selectEntries(state),
entriesStart: logEntriesSelectors.selectEntriesStart(state),
entriesEnd: logEntriesSelectors.selectEntriesEnd(state),
- // items: selectItems(state),
}),
bindPlainActionCreators({
loadNewerEntries: logEntriesActions.loadNewerEntries,
@@ -77,27 +76,6 @@ export const WithStreamItems = withStreamItems(
}
);
-// export const WithStreamItemsOld = asChildFunctionRenderer(withStreamItems, {
-// onInitialize: props => {
-// if (!props.isReloading && !props.isLoadingMore) {
-// props.reloadEntries();
-// }
-// },
-// });
-
-// const selectItems = createSelector(
-// logEntriesSelectors.selectEntries,
-// logEntriesSelectors.selectIsReloadingEntries,
-// logPositionSelectors.selectIsAutoReloading,
-// // searchResultsSelectors.selectSearchResultsById,
-// (logEntries, isReloading, isAutoReloading /* , searchResults */) =>
-// isReloading && !isAutoReloading
-// ? []
-// : logEntries.map(logEntry =>
-// createLogEntryStreamItem(logEntry /* , searchResults[logEntry.gid] || null */)
-// )
-// );
-
const createLogEntryStreamItem = (
logEntry: LogEntry,
highlights: LogEntryHighlight[]
diff --git a/x-pack/legacy/plugins/infra/public/graphql/introspection.json b/x-pack/legacy/plugins/infra/public/graphql/introspection.json
index 055fac61cb93d..22e8ca519e513 100644
--- a/x-pack/legacy/plugins/infra/public/graphql/introspection.json
+++ b/x-pack/legacy/plugins/infra/public/graphql/introspection.json
@@ -335,6 +335,85 @@
"isDeprecated": false,
"deprecationReason": null
},
+ {
+ "name": "logSummaryHighlightsBetween",
+ "description": "Spans of summary highlight buckets within an interval",
+ "args": [
+ {
+ "name": "start",
+ "description": "The millisecond timestamp that corresponds to the start of the interval",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "end",
+ "description": "The millisecond timestamp that corresponds to the end of the interval",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "bucketSize",
+ "description": "The size of each bucket in milliseconds",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "filterQuery",
+ "description": "The query to filter the log entries by",
+ "type": { "kind": "SCALAR", "name": "String", "ofType": null },
+ "defaultValue": null
+ },
+ {
+ "name": "highlightQueries",
+ "description": "The highlighting to apply to the log entries",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": { "kind": "SCALAR", "name": "String", "ofType": null }
+ }
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "InfraLogSummaryHighlightInterval",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
{
"name": "logItem",
"description": "",
@@ -1676,6 +1755,132 @@
"enumValues": null,
"possibleTypes": null
},
+ {
+ "kind": "OBJECT",
+ "name": "InfraLogSummaryHighlightInterval",
+ "description": "A consecutive sequence of log summary highlight buckets",
+ "fields": [
+ {
+ "name": "start",
+ "description": "The millisecond timestamp corresponding to the start of the interval covered by the summary",
+ "args": [],
+ "type": { "kind": "SCALAR", "name": "Float", "ofType": null },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "end",
+ "description": "The millisecond timestamp corresponding to the end of the interval covered by the summary",
+ "args": [],
+ "type": { "kind": "SCALAR", "name": "Float", "ofType": null },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "filterQuery",
+ "description": "The query the log entries were filtered by",
+ "args": [],
+ "type": { "kind": "SCALAR", "name": "String", "ofType": null },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "highlightQuery",
+ "description": "The query the log entries were highlighted with",
+ "args": [],
+ "type": { "kind": "SCALAR", "name": "String", "ofType": null },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "buckets",
+ "description": "A list of the log entries",
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "InfraLogSummaryHighlightBucket",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "InfraLogSummaryHighlightBucket",
+ "description": "A log summary highlight bucket",
+ "fields": [
+ {
+ "name": "start",
+ "description": "The start timestamp of the bucket",
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "end",
+ "description": "The end timestamp of the bucket",
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "entriesCount",
+ "description": "The number of highlighted entries inside the bucket",
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": { "kind": "SCALAR", "name": "Int", "ofType": null }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "representativeKey",
+ "description": "The time key of a representative of the highlighted log entries in this bucket",
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": { "kind": "OBJECT", "name": "InfraTimeKey", "ofType": null }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [],
+ "enumValues": null,
+ "possibleTypes": null
+ },
{
"kind": "OBJECT",
"name": "InfraLogItem",
diff --git a/x-pack/legacy/plugins/infra/public/graphql/types.ts b/x-pack/legacy/plugins/infra/public/graphql/types.ts
index 7843a54b93bed..a9c50e97f4348 100644
--- a/x-pack/legacy/plugins/infra/public/graphql/types.ts
+++ b/x-pack/legacy/plugins/infra/public/graphql/types.ts
@@ -36,6 +36,8 @@ export interface InfraSource {
logEntryHighlights: InfraLogEntryInterval[];
/** A consecutive span of summary buckets within an interval */
logSummaryBetween: InfraLogSummaryInterval;
+ /** Spans of summary highlight buckets within an interval */
+ logSummaryHighlightsBetween: InfraLogSummaryHighlightInterval[];
logItem: InfraLogItem;
/** A snapshot of nodes */
@@ -224,6 +226,30 @@ export interface InfraLogSummaryBucket {
/** The number of entries inside the bucket */
entriesCount: number;
}
+/** A consecutive sequence of log summary highlight buckets */
+export interface InfraLogSummaryHighlightInterval {
+ /** The millisecond timestamp corresponding to the start of the interval covered by the summary */
+ start?: number | null;
+ /** The millisecond timestamp corresponding to the end of the interval covered by the summary */
+ end?: number | null;
+ /** The query the log entries were filtered by */
+ filterQuery?: string | null;
+ /** The query the log entries were highlighted with */
+ highlightQuery?: string | null;
+ /** A list of the log entries */
+ buckets: InfraLogSummaryHighlightBucket[];
+}
+/** A log summary highlight bucket */
+export interface InfraLogSummaryHighlightBucket {
+ /** The start timestamp of the bucket */
+ start: number;
+ /** The end timestamp of the bucket */
+ end: number;
+ /** The number of highlighted entries inside the bucket */
+ entriesCount: number;
+ /** The time key of a representative of the highlighted log entries in this bucket */
+ representativeKey: InfraTimeKey;
+}
export interface InfraLogItem {
/** The ID of the document */
@@ -446,6 +472,18 @@ export interface LogSummaryBetweenInfraSourceArgs {
/** The query to filter the log entries by */
filterQuery?: string | null;
}
+export interface LogSummaryHighlightsBetweenInfraSourceArgs {
+ /** The millisecond timestamp that corresponds to the start of the interval */
+ start: number;
+ /** The millisecond timestamp that corresponds to the end of the interval */
+ end: number;
+ /** The size of each bucket in milliseconds */
+ bucketSize: number;
+ /** The query to filter the log entries by */
+ filterQuery?: string | null;
+ /** The highlighting to apply to the log entries */
+ highlightQueries: string[];
+}
export interface LogItemInfraSourceArgs {
id: string;
}
@@ -657,6 +695,55 @@ export namespace LogEntryHighlightsQuery {
export type Entries = InfraLogEntryHighlightFields.Fragment;
}
+export namespace LogSummaryHighlightsQuery {
+ export type Variables = {
+ sourceId?: string | null;
+ start: number;
+ end: number;
+ bucketSize: number;
+ highlightQueries: string[];
+ filterQuery?: string | null;
+ };
+
+ export type Query = {
+ __typename?: 'Query';
+
+ source: Source;
+ };
+
+ export type Source = {
+ __typename?: 'InfraSource';
+
+ id: string;
+
+ logSummaryHighlightsBetween: LogSummaryHighlightsBetween[];
+ };
+
+ export type LogSummaryHighlightsBetween = {
+ __typename?: 'InfraLogSummaryHighlightInterval';
+
+ start?: number | null;
+
+ end?: number | null;
+
+ buckets: Buckets[];
+ };
+
+ export type Buckets = {
+ __typename?: 'InfraLogSummaryHighlightBucket';
+
+ start: number;
+
+ end: number;
+
+ entriesCount: number;
+
+ representativeKey: RepresentativeKey;
+ };
+
+ export type RepresentativeKey = InfraTimeKeyFields.Fragment;
+}
+
export namespace LogSummary {
export type Variables = {
sourceId?: string | null;
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_logs_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_logs_content.tsx
index 2ead89f94f812..00c52eecdbe34 100644
--- a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_logs_content.tsx
+++ b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_logs_content.tsx
@@ -28,7 +28,7 @@ import { ReduxSourceIdBridge, WithStreamItems } from '../../../containers/logs/w
import { Source } from '../../../containers/source';
import { LogsToolbar } from './page_toolbar';
-import { LogHighlightsBridge } from '../../../containers/logs/log_highlights';
+import { LogHighlightsBridge, LogHighlightsState } from '../../../containers/logs/log_highlights';
export const LogsPageLogsContent: React.FunctionComponent = () => {
const { createDerivedIndexPattern, source, sourceId, version } = useContext(Source.Context);
@@ -42,7 +42,7 @@ export const LogsPageLogsContent: React.FunctionComponent = () => {
flyoutItem,
isLoading,
} = useContext(LogFlyoutState.Context);
-
+ const { logSummaryHighlights } = useContext(LogHighlightsState.Context);
const derivedIndexPattern = createDerivedIndexPattern('logs');
return (
@@ -129,6 +129,9 @@ export const LogsPageLogsContent: React.FunctionComponent = () => {
intervalSize={intervalSize}
jumpToTarget={jumpToTargetPosition}
summaryBuckets={buckets}
+ summaryHighlightBuckets={
+ logSummaryHighlights.length > 0 ? logSummaryHighlights[0].buckets : []
+ }
target={visibleMidpointTime}
/>
)}
diff --git a/x-pack/legacy/plugins/infra/server/graphql/log_entries/resolvers.ts b/x-pack/legacy/plugins/infra/server/graphql/log_entries/resolvers.ts
index 31fd19a6e125d..f63d09807336a 100644
--- a/x-pack/legacy/plugins/infra/server/graphql/log_entries/resolvers.ts
+++ b/x-pack/legacy/plugins/infra/server/graphql/log_entries/resolvers.ts
@@ -6,11 +6,9 @@
import { failure } from 'io-ts/lib/PathReporter';
-import { JsonObject } from '../../../common/typed_json';
import {
InfraLogEntryColumn,
InfraLogEntryFieldColumn,
- InfraLogEntryHighlightInput,
InfraLogEntryMessageColumn,
InfraLogEntryTimestampColumn,
InfraLogMessageConstantSegment,
@@ -45,6 +43,11 @@ export type InfraSourceLogSummaryBetweenResolver = ChildResolverOf<
QuerySourceResolver
>;
+export type InfraSourceLogSummaryHighlightsBetweenResolver = ChildResolverOf<
+ InfraResolverOf,
+ QuerySourceResolver
+>;
+
export type InfraSourceLogItem = ChildResolverOf<
InfraResolverOf,
QuerySourceResolver
@@ -58,6 +61,7 @@ export const createLogEntriesResolvers = (libs: {
logEntriesBetween: InfraSourceLogEntriesBetweenResolver;
logEntryHighlights: InfraSourceLogEntryHighlightsResolver;
logSummaryBetween: InfraSourceLogSummaryBetweenResolver;
+ logSummaryHighlightsBetween: InfraSourceLogSummaryHighlightsBetweenResolver;
logItem: InfraSourceLogItem;
};
InfraLogEntryColumn: {
@@ -130,7 +134,7 @@ export const createLogEntriesResolvers = (libs: {
source.id,
args.startKey,
args.endKey,
- parseHighlightInputs(args.highlights),
+ args.highlights.filter(highlightInput => !!highlightInput.query),
parseFilterQuery(args.filterQuery)
);
@@ -160,6 +164,23 @@ export const createLogEntriesResolvers = (libs: {
buckets,
};
},
+ async logSummaryHighlightsBetween(source, args, { req }) {
+ const summaryHighlightSets = await libs.logEntries.getLogSummaryHighlightBucketsBetween(
+ req,
+ source.id,
+ args.start,
+ args.end,
+ args.bucketSize,
+ args.highlightQueries.filter(highlightQuery => !!highlightQuery),
+ parseFilterQuery(args.filterQuery)
+ );
+
+ return summaryHighlightSets.map(buckets => ({
+ start: buckets.length > 0 ? buckets[0].start : null,
+ end: buckets.length > 0 ? buckets[buckets.length - 1].end : null,
+ buckets,
+ }));
+ },
async logItem(source, args, { req }) {
const sourceConfiguration = SourceConfigurationRuntimeType.decode(
source.configuration
@@ -217,24 +238,3 @@ const isConstantSegment = (
const isFieldSegment = (segment: InfraLogMessageSegment): segment is InfraLogMessageFieldSegment =>
'field' in segment && 'value' in segment && 'highlights' in segment;
-
-const parseHighlightInputs = (highlightInputs: InfraLogEntryHighlightInput[]) =>
- highlightInputs
- ? highlightInputs.reduce>(
- (parsedHighlightInputs, highlightInput) => {
- const parsedQuery = parseFilterQuery(highlightInput.query);
- if (parsedQuery) {
- return [
- ...parsedHighlightInputs,
- {
- ...highlightInput,
- query: parsedQuery,
- },
- ];
- } else {
- return parsedHighlightInputs;
- }
- },
- []
- )
- : [];
diff --git a/x-pack/legacy/plugins/infra/server/graphql/log_entries/schema.gql.ts b/x-pack/legacy/plugins/infra/server/graphql/log_entries/schema.gql.ts
index 0e5a6203519b3..0ef896e8edfa4 100644
--- a/x-pack/legacy/plugins/infra/server/graphql/log_entries/schema.gql.ts
+++ b/x-pack/legacy/plugins/infra/server/graphql/log_entries/schema.gql.ts
@@ -92,6 +92,18 @@ export const logEntriesSchema = gql`
entriesCount: Int!
}
+ "A log summary highlight bucket"
+ type InfraLogSummaryHighlightBucket {
+ "The start timestamp of the bucket"
+ start: Float!
+ "The end timestamp of the bucket"
+ end: Float!
+ "The number of highlighted entries inside the bucket"
+ entriesCount: Int!
+ "The time key of a representative of the highlighted log entries in this bucket"
+ representativeKey: InfraTimeKey!
+ }
+
"A consecutive sequence of log entries"
type InfraLogEntryInterval {
"The key corresponding to the start of the interval covered by the entries"
@@ -122,6 +134,20 @@ export const logEntriesSchema = gql`
buckets: [InfraLogSummaryBucket!]!
}
+ "A consecutive sequence of log summary highlight buckets"
+ type InfraLogSummaryHighlightInterval {
+ "The millisecond timestamp corresponding to the start of the interval covered by the summary"
+ start: Float
+ "The millisecond timestamp corresponding to the end of the interval covered by the summary"
+ end: Float
+ "The query the log entries were filtered by"
+ filterQuery: String
+ "The query the log entries were highlighted with"
+ highlightQuery: String
+ "A list of the log entries"
+ buckets: [InfraLogSummaryHighlightBucket!]!
+ }
+
type InfraLogItemField {
"The flattened field name"
field: String!
@@ -183,6 +209,19 @@ export const logEntriesSchema = gql`
"The query to filter the log entries by"
filterQuery: String
): InfraLogSummaryInterval!
+ "Spans of summary highlight buckets within an interval"
+ logSummaryHighlightsBetween(
+ "The millisecond timestamp that corresponds to the start of the interval"
+ start: Float!
+ "The millisecond timestamp that corresponds to the end of the interval"
+ end: Float!
+ "The size of each bucket in milliseconds"
+ bucketSize: Float!
+ "The query to filter the log entries by"
+ filterQuery: String
+ "The highlighting to apply to the log entries"
+ highlightQueries: [String!]!
+ ): [InfraLogSummaryHighlightInterval!]!
logItem(id: ID!): InfraLogItem!
}
`;
diff --git a/x-pack/legacy/plugins/infra/server/graphql/types.ts b/x-pack/legacy/plugins/infra/server/graphql/types.ts
index e223ac7b334a2..0853d1f64472f 100644
--- a/x-pack/legacy/plugins/infra/server/graphql/types.ts
+++ b/x-pack/legacy/plugins/infra/server/graphql/types.ts
@@ -64,6 +64,8 @@ export interface InfraSource {
logEntryHighlights: InfraLogEntryInterval[];
/** A consecutive span of summary buckets within an interval */
logSummaryBetween: InfraLogSummaryInterval;
+ /** Spans of summary highlight buckets within an interval */
+ logSummaryHighlightsBetween: InfraLogSummaryHighlightInterval[];
logItem: InfraLogItem;
/** A snapshot of nodes */
@@ -252,6 +254,30 @@ export interface InfraLogSummaryBucket {
/** The number of entries inside the bucket */
entriesCount: number;
}
+/** A consecutive sequence of log summary highlight buckets */
+export interface InfraLogSummaryHighlightInterval {
+ /** The millisecond timestamp corresponding to the start of the interval covered by the summary */
+ start?: number | null;
+ /** The millisecond timestamp corresponding to the end of the interval covered by the summary */
+ end?: number | null;
+ /** The query the log entries were filtered by */
+ filterQuery?: string | null;
+ /** The query the log entries were highlighted with */
+ highlightQuery?: string | null;
+ /** A list of the log entries */
+ buckets: InfraLogSummaryHighlightBucket[];
+}
+/** A log summary highlight bucket */
+export interface InfraLogSummaryHighlightBucket {
+ /** The start timestamp of the bucket */
+ start: number;
+ /** The end timestamp of the bucket */
+ end: number;
+ /** The number of highlighted entries inside the bucket */
+ entriesCount: number;
+ /** The time key of a representative of the highlighted log entries in this bucket */
+ representativeKey: InfraTimeKey;
+}
export interface InfraLogItem {
/** The ID of the document */
@@ -474,6 +500,18 @@ export interface LogSummaryBetweenInfraSourceArgs {
/** The query to filter the log entries by */
filterQuery?: string | null;
}
+export interface LogSummaryHighlightsBetweenInfraSourceArgs {
+ /** The millisecond timestamp that corresponds to the start of the interval */
+ start: number;
+ /** The millisecond timestamp that corresponds to the end of the interval */
+ end: number;
+ /** The size of each bucket in milliseconds */
+ bucketSize: number;
+ /** The query to filter the log entries by */
+ filterQuery?: string | null;
+ /** The highlighting to apply to the log entries */
+ highlightQueries: string[];
+}
export interface LogItemInfraSourceArgs {
id: string;
}
@@ -650,6 +688,12 @@ export namespace InfraSourceResolvers {
logEntryHighlights?: LogEntryHighlightsResolver;
/** A consecutive span of summary buckets within an interval */
logSummaryBetween?: LogSummaryBetweenResolver;
+ /** Spans of summary highlight buckets within an interval */
+ logSummaryHighlightsBetween?: LogSummaryHighlightsBetweenResolver<
+ InfraLogSummaryHighlightInterval[],
+ TypeParent,
+ Context
+ >;
logItem?: LogItemResolver;
/** A snapshot of nodes */
@@ -750,6 +794,24 @@ export namespace InfraSourceResolvers {
filterQuery?: string | null;
}
+ export type LogSummaryHighlightsBetweenResolver<
+ R = InfraLogSummaryHighlightInterval[],
+ Parent = InfraSource,
+ Context = InfraContext
+ > = Resolver;
+ export interface LogSummaryHighlightsBetweenArgs {
+ /** The millisecond timestamp that corresponds to the start of the interval */
+ start: number;
+ /** The millisecond timestamp that corresponds to the end of the interval */
+ end: number;
+ /** The size of each bucket in milliseconds */
+ bucketSize: number;
+ /** The query to filter the log entries by */
+ filterQuery?: string | null;
+ /** The highlighting to apply to the log entries */
+ highlightQueries: string[];
+ }
+
export type LogItemResolver<
R = InfraLogItem,
Parent = InfraSource,
@@ -1356,6 +1418,84 @@ export namespace InfraLogSummaryBucketResolvers {
Context = InfraContext
> = Resolver;
}
+/** A consecutive sequence of log summary highlight buckets */
+export namespace InfraLogSummaryHighlightIntervalResolvers {
+ export interface Resolvers<
+ Context = InfraContext,
+ TypeParent = InfraLogSummaryHighlightInterval
+ > {
+ /** The millisecond timestamp corresponding to the start of the interval covered by the summary */
+ start?: StartResolver;
+ /** The millisecond timestamp corresponding to the end of the interval covered by the summary */
+ end?: EndResolver;
+ /** The query the log entries were filtered by */
+ filterQuery?: FilterQueryResolver;
+ /** The query the log entries were highlighted with */
+ highlightQuery?: HighlightQueryResolver;
+ /** A list of the log entries */
+ buckets?: BucketsResolver;
+ }
+
+ export type StartResolver<
+ R = number | null,
+ Parent = InfraLogSummaryHighlightInterval,
+ Context = InfraContext
+ > = Resolver;
+ export type EndResolver<
+ R = number | null,
+ Parent = InfraLogSummaryHighlightInterval,
+ Context = InfraContext
+ > = Resolver;
+ export type FilterQueryResolver<
+ R = string | null,
+ Parent = InfraLogSummaryHighlightInterval,
+ Context = InfraContext
+ > = Resolver;
+ export type HighlightQueryResolver<
+ R = string | null,
+ Parent = InfraLogSummaryHighlightInterval,
+ Context = InfraContext
+ > = Resolver;
+ export type BucketsResolver<
+ R = InfraLogSummaryHighlightBucket[],
+ Parent = InfraLogSummaryHighlightInterval,
+ Context = InfraContext
+ > = Resolver;
+}
+/** A log summary highlight bucket */
+export namespace InfraLogSummaryHighlightBucketResolvers {
+ export interface Resolvers {
+ /** The start timestamp of the bucket */
+ start?: StartResolver;
+ /** The end timestamp of the bucket */
+ end?: EndResolver;
+ /** The number of highlighted entries inside the bucket */
+ entriesCount?: EntriesCountResolver;
+ /** The time key of a representative of the highlighted log entries in this bucket */
+ representativeKey?: RepresentativeKeyResolver;
+ }
+
+ export type StartResolver<
+ R = number,
+ Parent = InfraLogSummaryHighlightBucket,
+ Context = InfraContext
+ > = Resolver;
+ export type EndResolver<
+ R = number,
+ Parent = InfraLogSummaryHighlightBucket,
+ Context = InfraContext
+ > = Resolver;
+ export type EntriesCountResolver<
+ R = number,
+ Parent = InfraLogSummaryHighlightBucket,
+ Context = InfraContext
+ > = Resolver;
+ export type RepresentativeKeyResolver<
+ R = InfraTimeKey,
+ Parent = InfraLogSummaryHighlightBucket,
+ Context = InfraContext
+ > = Resolver;
+}
export namespace InfraLogItemResolvers {
export interface Resolvers {
diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/framework/adapter_types.ts b/x-pack/legacy/plugins/infra/server/lib/adapters/framework/adapter_types.ts
index 999d95c1b52c8..9e60abf58f4a1 100644
--- a/x-pack/legacy/plugins/infra/server/lib/adapters/framework/adapter_types.ts
+++ b/x-pack/legacy/plugins/infra/server/lib/adapters/framework/adapter_types.ts
@@ -152,15 +152,21 @@ export interface SortedSearchHit extends SearchHit {
};
}
-export interface InfraDateRangeAggregationBucket {
+export type InfraDateRangeAggregationBucket = {
from?: number;
to?: number;
doc_count: number;
key: string;
+} & NestedAggregation;
+
+export interface InfraDateRangeAggregationResponse {
+ buckets: Array>;
}
-export interface InfraDateRangeAggregationResponse {
- buckets: InfraDateRangeAggregationBucket[];
+export interface InfraTopHitsAggregationResponse {
+ hits: {
+ hits: [];
+ };
}
export interface InfraMetadataAggregationBucket {
diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts b/x-pack/legacy/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts
index 17f2d850f1217..7f3108836aad6 100644
--- a/x-pack/legacy/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts
+++ b/x-pack/legacy/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts
@@ -4,7 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
+/* eslint-disable @typescript-eslint/no-empty-interface */
+
import { timeMilliseconds } from 'd3-time';
+import * as runtimeTypes from 'io-ts';
import first from 'lodash/fp/first';
import get from 'lodash/fp/get';
import has from 'lodash/fp/has';
@@ -16,14 +19,10 @@ import {
LogEntriesAdapter,
LogEntryDocument,
LogEntryQuery,
+ LogSummaryBucket,
} from '../../domains/log_entries_domain';
import { InfraSourceConfiguration } from '../../sources';
-import {
- InfraDateRangeAggregationBucket,
- InfraDateRangeAggregationResponse,
- InfraFrameworkRequest,
- SortedSearchHit,
-} from '../framework';
+import { InfraFrameworkRequest, SortedSearchHit } from '../framework';
import { InfraBackendFrameworkAdapter } from '../framework';
const DAY_MILLIS = 24 * 60 * 60 * 1000;
@@ -111,7 +110,7 @@ export class InfraKibanaLogEntriesAdapter implements LogEntriesAdapter {
end: number,
bucketSize: number,
filterQuery?: LogEntryQuery
- ): Promise {
+ ): Promise {
const bucketIntervalStarts = timeMilliseconds(new Date(start), new Date(end), bucketSize);
const query = {
@@ -129,6 +128,18 @@ export class InfraKibanaLogEntriesAdapter implements LogEntriesAdapter {
to: bucketIntervalStart.getTime() + bucketSize,
})),
},
+ aggregations: {
+ top_hits_by_key: {
+ top_hits: {
+ size: 1,
+ sort: [
+ { [sourceConfiguration.fields.timestamp]: 'asc' },
+ { [sourceConfiguration.fields.tiebreaker]: 'asc' },
+ ],
+ _source: false,
+ },
+ },
+ },
},
},
query: {
@@ -148,17 +159,19 @@ export class InfraKibanaLogEntriesAdapter implements LogEntriesAdapter {
},
},
size: 0,
+ track_total_hits: false,
},
};
- const response = await this.framework.callWithRequest<
- any,
- { count_by_date?: InfraDateRangeAggregationResponse }
- >(request, 'search', query);
+ const response = await this.framework.callWithRequest(request, 'search', query);
- return response.aggregations && response.aggregations.count_by_date
- ? response.aggregations.count_by_date.buckets
- : [];
+ return LogSummaryResponseRuntimeType.decode(response)
+ .map(logSummaryResponse =>
+ logSummaryResponse.aggregations.count_by_date.buckets.map(
+ convertDateRangeBucketToSummaryBucket
+ )
+ )
+ .getOrElse([]);
}
public async getLogItem(
@@ -322,5 +335,51 @@ const convertHitToLogEntryDocument = (fields: string[]) => (
},
});
+const convertDateRangeBucketToSummaryBucket = (
+ bucket: LogSummaryDateRangeBucket
+): LogSummaryBucket => ({
+ entriesCount: bucket.doc_count,
+ start: bucket.from || 0,
+ end: bucket.to || 0,
+ topEntryKeys: bucket.top_hits_by_key.hits.hits.map(hit => ({
+ tiebreaker: hit.sort[1],
+ time: hit.sort[0],
+ })),
+});
+
const createQueryFilterClauses = (filterQuery: LogEntryQuery | undefined) =>
filterQuery ? [filterQuery] : [];
+
+const LogSummaryDateRangeBucketRuntimeType = runtimeTypes.intersection([
+ runtimeTypes.type({
+ doc_count: runtimeTypes.number,
+ key: runtimeTypes.string,
+ top_hits_by_key: runtimeTypes.type({
+ hits: runtimeTypes.type({
+ hits: runtimeTypes.array(
+ runtimeTypes.type({
+ sort: runtimeTypes.tuple([runtimeTypes.number, runtimeTypes.number]),
+ })
+ ),
+ }),
+ }),
+ }),
+ runtimeTypes.partial({
+ from: runtimeTypes.number,
+ to: runtimeTypes.number,
+ }),
+]);
+
+export interface LogSummaryDateRangeBucket
+ extends runtimeTypes.TypeOf {}
+
+const LogSummaryResponseRuntimeType = runtimeTypes.type({
+ aggregations: runtimeTypes.type({
+ count_by_date: runtimeTypes.type({
+ buckets: runtimeTypes.array(LogSummaryDateRangeBucketRuntimeType),
+ }),
+ }),
+});
+
+export interface LogSummaryResponse
+ extends runtimeTypes.TypeOf {}
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts b/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts
index d4e7c51d59669..0127f80b31357 100644
--- a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts
+++ b/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts
@@ -14,8 +14,9 @@ import {
InfraLogItem,
InfraLogMessageSegment,
InfraLogSummaryBucket,
+ InfraLogSummaryHighlightBucket,
} from '../../../graphql/types';
-import { InfraDateRangeAggregationBucket, InfraFrameworkRequest } from '../../adapters/framework';
+import { InfraFrameworkRequest } from '../../adapters/framework';
import {
InfraSourceConfiguration,
InfraSources,
@@ -133,7 +134,7 @@ export class InfraLogEntriesDomain {
startKey: TimeKey,
endKey: TimeKey,
highlights: Array<{
- query: JsonObject;
+ query: string;
countBefore: number;
countAfter: number;
}>,
@@ -147,13 +148,14 @@ export class InfraLogEntriesDomain {
const documentSets = await Promise.all(
highlights.map(async highlight => {
+ const highlightQuery = createHighlightQueryDsl(highlight.query, requiredFields);
const query = filterQuery
? {
bool: {
- filter: [filterQuery, highlight.query],
+ filter: [filterQuery, highlightQuery],
},
}
- : highlight.query;
+ : highlightQuery;
const [documentsBefore, documents, documentsAfter] = await Promise.all([
this.adapter.getAdjacentLogEntryDocuments(
request,
@@ -163,7 +165,7 @@ export class InfraLogEntriesDomain {
'desc',
highlight.countBefore,
query,
- highlight.query
+ highlightQuery
),
this.adapter.getContainedLogEntryDocuments(
request,
@@ -172,7 +174,7 @@ export class InfraLogEntriesDomain {
startKey,
endKey,
query,
- highlight.query
+ highlightQuery
),
this.adapter.getAdjacentLogEntryDocuments(
request,
@@ -182,7 +184,7 @@ export class InfraLogEntriesDomain {
'asc',
highlight.countAfter,
query,
- highlight.query
+ highlightQuery
),
]);
const entries = [...documentsBefore, ...documents, ...documentsAfter].map(
@@ -217,8 +219,50 @@ export class InfraLogEntriesDomain {
bucketSize,
filterQuery
);
- const buckets = dateRangeBuckets.map(convertDateRangeBucketToSummaryBucket);
- return buckets;
+ return dateRangeBuckets;
+ }
+
+ public async getLogSummaryHighlightBucketsBetween(
+ request: InfraFrameworkRequest,
+ sourceId: string,
+ start: number,
+ end: number,
+ bucketSize: number,
+ highlightQueries: string[],
+ filterQuery?: LogEntryQuery
+ ): Promise {
+ const { configuration } = await this.libs.sources.getSourceConfiguration(request, sourceId);
+ const messageFormattingRules = compileFormattingRules(
+ getBuiltinRules(configuration.fields.message)
+ );
+ const requiredFields = getRequiredFields(configuration, messageFormattingRules);
+
+ const summaries = await Promise.all(
+ highlightQueries.map(async highlightQueryPhrase => {
+ const highlightQuery = createHighlightQueryDsl(highlightQueryPhrase, requiredFields);
+ const query = filterQuery
+ ? {
+ bool: {
+ must: [filterQuery, highlightQuery],
+ },
+ }
+ : highlightQuery;
+ const summaryBuckets = await this.adapter.getContainedLogSummaryBuckets(
+ request,
+ configuration,
+ start,
+ end,
+ bucketSize,
+ query
+ );
+ const summaryHighlightBuckets = summaryBuckets
+ .filter(logSummaryBucketHasEntries)
+ .map(convertLogSummaryBucketToSummaryHighlightBucket);
+ return summaryHighlightBuckets;
+ })
+ );
+
+ return summaries;
}
public async getLogItem(
@@ -283,7 +327,7 @@ export interface LogEntriesAdapter {
end: number,
bucketSize: number,
filterQuery?: LogEntryQuery
- ): Promise;
+ ): Promise;
getLogItem(
request: InfraFrameworkRequest,
@@ -301,6 +345,13 @@ export interface LogEntryDocument {
key: TimeKey;
}
+export interface LogSummaryBucket {
+ entriesCount: number;
+ start: number;
+ end: number;
+ topEntryKeys: TimeKey[];
+}
+
const convertLogDocumentToEntry = (
sourceId: string,
logColumns: InfraSourceConfiguration['logColumns'],
@@ -331,12 +382,16 @@ const convertLogDocumentToEntry = (
}),
});
-const convertDateRangeBucketToSummaryBucket = (
- bucket: InfraDateRangeAggregationBucket
-): InfraLogSummaryBucket => ({
- entriesCount: bucket.doc_count,
- start: bucket.from || 0,
- end: bucket.to || 0,
+const logSummaryBucketHasEntries = (bucket: LogSummaryBucket) =>
+ bucket.entriesCount > 0 && bucket.topEntryKeys.length > 0;
+
+const convertLogSummaryBucketToSummaryHighlightBucket = (
+ bucket: LogSummaryBucket
+): InfraLogSummaryHighlightBucket => ({
+ entriesCount: bucket.entriesCount,
+ start: bucket.start,
+ end: bucket.end,
+ representativeKey: bucket.topEntryKeys[0],
});
const getRequiredFields = (
@@ -356,3 +411,12 @@ const getRequiredFields = (
return Array.from(new Set([...fieldsFromCustomColumns, ...fieldsFromFormattingRules]));
};
+
+const createHighlightQueryDsl = (phrase: string, fields: string[]) => ({
+ multi_match: {
+ fields,
+ lenient: true,
+ query: phrase,
+ type: 'phrase',
+ },
+});
diff --git a/x-pack/test/api_integration/apis/infra/log_entry_highlights.ts b/x-pack/test/api_integration/apis/infra/log_entry_highlights.ts
index efc1173f9fb90..53ece86b92483 100644
--- a/x-pack/test/api_integration/apis/infra/log_entry_highlights.ts
+++ b/x-pack/test/api_integration/apis/infra/log_entry_highlights.ts
@@ -55,9 +55,7 @@ export default function({ getService }: FtrProviderContext) {
endKey: KEY_AFTER_END,
highlights: [
{
- query: JSON.stringify({
- multi_match: { query: 'message of document 0', type: 'phrase', lenient: true },
- }),
+ query: 'message of document 0',
countBefore: 0,
countAfter: 0,
},
@@ -103,13 +101,7 @@ export default function({ getService }: FtrProviderContext) {
endKey: KEY_AFTER_END,
highlights: [
{
- query: JSON.stringify({
- multi_match: {
- query: 'generate_test_data/simple_logs',
- type: 'phrase',
- lenient: true,
- },
- }),
+ query: 'generate_test_data/simple_logs',
countBefore: 0,
countAfter: 0,
},
@@ -155,9 +147,7 @@ export default function({ getService }: FtrProviderContext) {
}),
highlights: [
{
- query: JSON.stringify({
- multi_match: { query: 'message', type: 'phrase', lenient: true },
- }),
+ query: 'message',
countBefore: 0,
countAfter: 0,
},
@@ -201,9 +191,7 @@ export default function({ getService }: FtrProviderContext) {
endKey: KEY_BEFORE_END,
highlights: [
{
- query: JSON.stringify({
- multi_match: { query: 'message of document 0', type: 'phrase', lenient: true },
- }),
+ query: 'message of document 0',
countBefore: 2,
countAfter: 2,
},