From 2d0f48b3bf8f15bbb78db44ab78fb0c77b2db788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 8 Jan 2020 11:23:12 +0100 Subject: [PATCH 001/117] Track `startDate` and `endDate` in LogPositionState --- .../logs/log_position/log_position_state.ts | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts index 1a8274024bd265..79e7fe56814614 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts @@ -10,6 +10,11 @@ import { TimeKey } from '../../../../common/time'; type TimeKeyOrNull = TimeKey | null; +interface DateRange { + startDate: string; + endDate: string; +} + interface VisiblePositions { startKey: TimeKeyOrNull; middleKey: TimeKeyOrNull; @@ -27,6 +32,8 @@ export interface LogPositionStateParams { visibleMidpoint: TimeKeyOrNull; visibleMidpointTime: number | null; visibleTimeInterval: { start: number; end: number } | null; + startDate: string; + endDate: string; } export interface LogPositionCallbacks { @@ -35,8 +42,11 @@ export interface LogPositionCallbacks { reportVisiblePositions: (visPos: VisiblePositions) => void; startLiveStreaming: () => void; stopLiveStreaming: () => void; + updateDateRange: (newDateRage: Partial) => void; } +const DEFAULT_DATE_RANGE: DateRange = { startDate: 'now-1d', endDate: 'now' }; + const useVisibleMidpoint = (middleKey: TimeKeyOrNull, targetPosition: TimeKeyOrNull) => { // Of the two dependencies `middleKey` and `targetPosition`, return // whichever one was the most recently updated. This allows the UI controls @@ -70,6 +80,10 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall pagesAfterEnd: Infinity, }); + // We group the `startDate` and `endDate` values in the same object to be able + // to set both at the same time, saving a re-render + const [dateRange, setDateRange] = useState(DEFAULT_DATE_RANGE); + const { startKey, middleKey, endKey, pagesBeforeStart, pagesAfterEnd } = visiblePositions; const visibleMidpoint = useVisibleMidpoint(middleKey, targetPosition); @@ -79,6 +93,30 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall [startKey, endKey] ); + // Allow setting `startDate` and `endDate` separately, or together + const updateDateRange = useCallback( + (newDateRange: Partial) => { + // Prevent unnecessary re-renders + if (!('startDate' in newDateRange) && !('endDate' in newDateRange)) { + return; + } + if ( + newDateRange.startDate === dateRange.startDate && + newDateRange.endDate === dateRange.endDate + ) { + return; + } + + setDateRange(previousDateRange => { + return { + startDate: newDateRange.startDate || previousDateRange.startDate, + endDate: newDateRange.endDate || previousDateRange.endDate, + }; + }); + }, + [dateRange] + ); + const state = { targetPosition, isAutoReloading, @@ -88,6 +126,7 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall visibleMidpoint, visibleMidpointTime: visibleMidpoint ? visibleMidpoint.time : null, visibleTimeInterval, + ...dateRange, }; const callbacks = { @@ -99,6 +138,7 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall reportVisiblePositions, startLiveStreaming: useCallback(() => setIsAutoReloading(true), [setIsAutoReloading]), stopLiveStreaming: useCallback(() => setIsAutoReloading(false), [setIsAutoReloading]), + updateDateRange, }; return { ...state, ...callbacks }; From a282a1e8589115380fb3dd6c34587a225c0db0de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 8 Jan 2020 11:24:20 +0100 Subject: [PATCH 002/117] Add the SuperDatePicker and wire it up to the date state --- .../public/pages/logs/stream/page_toolbar.tsx | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx index 000dfd1065f12f..56a0d3ec101c7a 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSuperDatePicker } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { useContext } from 'react'; +import React, { useContext, useCallback } from 'react'; import { AutocompleteField } from '../../../components/autocomplete_field'; import { Toolbar } from '../../../components/eui'; @@ -60,7 +60,20 @@ export const LogsToolbar = () => { jumpToTargetPositionTime, startLiveStreaming, stopLiveStreaming, + startDate, + endDate, + updateDateRange, } = useContext(LogPositionState.Context); + + const handleTimeChange = useCallback( + ({ start, end, isInvalid }) => { + if (!isInvalid) { + updateDateRange({ startDate: start, endDate: end }); + } + }, + [updateDateRange] + ); + return ( @@ -121,6 +134,7 @@ export const LogsToolbar = () => { /> + Date: Wed, 8 Jan 2020 12:09:21 +0100 Subject: [PATCH 003/117] Use date range for the log summary buckets --- .../logs/log_summary/log_summary.tsx | 29 ++++++++++--------- .../use_log_summary_buffer_interval.ts | 1 + .../logs/log_summary/with_summary.ts | 26 +++++++++++++---- 3 files changed, 36 insertions(+), 20 deletions(-) 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 c39b7075af325f..518366d5313b8d 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,10 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { useState } from 'react'; +import { useState, useMemo } from 'react'; import { useCancellableEffect } from '../../../utils/cancellable_effect'; -import { useLogSummaryBufferInterval } from './use_log_summary_buffer_interval'; import { fetchLogSummary } from './api/fetch_log_summary'; import { LogEntriesSummaryResponse } from '../../../../common/http_api'; @@ -15,26 +14,28 @@ export type LogSummaryBuckets = LogEntriesSummaryResponse['data']['buckets']; export const useLogSummary = ( sourceId: string, - midpointTime: number | null, - intervalSize: number, + startTimestamp: number | null, + endTimestamp: number | null, filterQuery: string | null ) => { const [logSummaryBuckets, setLogSummaryBuckets] = useState([]); - const { start: bufferStart, end: bufferEnd, bucketSize } = useLogSummaryBufferInterval( - midpointTime, - intervalSize - ); + const bucketSize = useMemo(() => { + if (!startTimestamp || !endTimestamp) { + return null; + } + return (endTimestamp - startTimestamp) / 100; + }, [startTimestamp, endTimestamp]); useCancellableEffect( getIsCancelled => { - if (bufferStart === null || bufferEnd === null) { + if (startTimestamp === null || endTimestamp === null || bucketSize === null) { return; } fetchLogSummary({ sourceId, - startDate: bufferStart, - endDate: bufferEnd, + startDate: startTimestamp, + endDate: endTimestamp, bucketSize, query: filterQuery, }).then(response => { @@ -43,12 +44,12 @@ export const useLogSummary = ( } }); }, - [sourceId, filterQuery, bufferStart, bufferEnd, bucketSize] + [sourceId, filterQuery, startTimestamp, endTimestamp, bucketSize] ); return { buckets: logSummaryBuckets, - start: bufferStart, - end: bufferEnd, + start: startTimestamp, + end: endTimestamp, }; }; 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 index 27af76b70f47a1..78c348cdd7714e 100644 --- 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 @@ -13,6 +13,7 @@ const UNKNOWN_BUFFER_INTERVAL = { bucketSize: 0, }; +/* TODO: cleanup */ export const useLogSummaryBufferInterval = (midpointTime: number | null, intervalSize: number) => { return useMemo(() => { if (midpointTime === null || intervalSize <= 0) { 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 4db0d2e645448c..aa8eddb099bd82 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 @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { useContext } from 'react'; +import { useContext, useMemo } from 'react'; +import datemath from '@elastic/datemath'; import { RendererFunction } from '../../../utils/typed_react'; import { Source } from '../../source'; -import { LogViewConfiguration } from '../log_view_configuration'; import { LogSummaryBuckets, useLogSummary } from './log_summary'; import { LogFilterState } from '../log_filter'; import { LogPositionState } from '../log_position'; @@ -22,15 +22,29 @@ export const WithSummary = ({ end: number | null; }>; }) => { - const { intervalSize } = useContext(LogViewConfiguration.Context); const { sourceId } = useContext(Source.Context); const { filterQuery } = useContext(LogFilterState.Context); - const { visibleMidpointTime } = useContext(LogPositionState.Context); + const { startDate, endDate } = useContext(LogPositionState.Context); + + const startTimestamp: number | null = useMemo(() => { + const date = datemath.parse(startDate); + if (!date || !date.isValid()) { + return null; + } + return date.unix() * 1000; + }, [startDate]); + const endTimestamp: number | null = useMemo(() => { + const date = datemath.parse(endDate); + if (!date || !date.isValid()) { + return null; + } + return date.unix() * 1000; + }, [endDate]); const { buckets, start, end } = useLogSummary( sourceId, - visibleMidpointTime, - intervalSize, + startTimestamp, + endTimestamp, filterQuery ); From b42df03477ca5a9372e42267b6c6f2394c955ad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 8 Jan 2020 17:54:37 +0100 Subject: [PATCH 004/117] Track start and end dates in URL --- .../with_log_position_url_state.tsx | 53 +++++++++++++++---- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx index 221dac95ef5f07..037509a2e7cfc4 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx @@ -5,6 +5,7 @@ */ import React, { useContext, useMemo } from 'react'; +import datemath from '@elastic/datemath'; import { pickTimeKey } from '../../../../common/time'; import { replaceStateKeyInQueryString, UrlStateContainer } from '../../../utils/url_state'; @@ -17,6 +18,8 @@ import { LogPositionState, LogPositionStateParams } from './log_position_state'; interface LogPositionUrlState { position: LogPositionStateParams['visibleMidpoint'] | undefined; streamLive: boolean; + start?: string; + end?: string; } export const WithLogPositionUrlState = () => { @@ -27,13 +30,18 @@ export const WithLogPositionUrlState = () => { jumpToTargetPositionTime, startLiveStreaming, stopLiveStreaming, + startDate, + endDate, + updateDateRange, } = useContext(LogPositionState.Context); const urlState = useMemo( () => ({ position: visibleMidpoint ? pickTimeKey(visibleMidpoint) : null, streamLive: isAutoReloading, + start: startDate, + end: endDate, }), - [visibleMidpoint, isAutoReloading] + [visibleMidpoint, isAutoReloading, startDate, endDate] ); return ( { urlStateKey="logPosition" mapToUrlState={mapToUrlState} onChange={(newUrlState: LogPositionUrlState | undefined) => { - if (newUrlState && newUrlState.position) { + if (!newUrlState) { + return; + } + + if (newUrlState.start || newUrlState.end) { + updateDateRange({ startDate: newUrlState.start, endDate: newUrlState.end }); + } + + if (newUrlState.position) { jumpToTargetPosition(newUrlState.position); } - if (newUrlState && newUrlState.streamLive) { + + if (newUrlState.streamLive) { startLiveStreaming(); - } else if ( - newUrlState && - typeof newUrlState.streamLive !== 'undefined' && - !newUrlState.streamLive - ) { + } else if (typeof newUrlState.streamLive !== 'undefined' && !newUrlState.streamLive) { stopLiveStreaming(); } }} onInitialize={(initialUrlState: LogPositionUrlState | undefined) => { - if (initialUrlState && initialUrlState.position) { + if (!initialUrlState) { + jumpToTargetPositionTime(Date.now()); + return; + } + + if (initialUrlState.start || initialUrlState.end) { + updateDateRange({ startDate: initialUrlState.start, endDate: initialUrlState.end }); + } + + if (initialUrlState.position) { jumpToTargetPosition(initialUrlState.position); } else { jumpToTargetPositionTime(Date.now()); } - if (initialUrlState && initialUrlState.streamLive) { + + if (initialUrlState.streamLive) { startLiveStreaming(); } }} @@ -73,6 +96,8 @@ const mapToUrlState = (value: any): LogPositionUrlState | undefined => ? { position: mapToPositionUrlState(value.position), streamLive: mapToStreamLiveUrlState(value.streamLive), + start: mapToDate(value.start), + end: mapToDate(value.end), } : undefined; @@ -83,6 +108,14 @@ const mapToPositionUrlState = (value: any) => const mapToStreamLiveUrlState = (value: any) => (typeof value === 'boolean' ? value : false); +const mapToDate = (value: any) => { + const parsed = datemath.parse(value); + if (!parsed || !parsed.isValid()) { + return undefined; + } + return value; +}; + export const replaceLogPositionInQueryString = (time: number) => Number.isNaN(time) ? (value: string) => value From ca8a7d3fc590bd88376fe5190c221887ff880dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 9 Jan 2020 12:00:24 +0100 Subject: [PATCH 005/117] Adjust minimap to start/end range --- .../logging/log_minimap/density_chart.tsx | 12 ++++---- .../logging/log_minimap/log_minimap.tsx | 29 +++++++++---------- .../pages/logs/stream/page_logs_content.tsx | 4 ++- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/density_chart.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/density_chart.tsx index b31afe6abea284..79b6cd32856b55 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/density_chart.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/density_chart.tsx @@ -40,25 +40,23 @@ export const DensityChart: React.FC = ({ .domain([0, xMax]) .range([0, width * (2 / 3)]); + // FIXME: path is not closed at the bottom. const path = area() .x0(xScale(0)) .x1(bucket => xScale(bucket.entriesCount)) - .y(bucket => yScale((bucket.start + bucket.end) / 2)) + .y0(bucket => yScale(bucket.start)) + .y1(bucket => yScale(bucket.end)) .curve(curveMonotoneY); const pathData = path(buckets); - const highestPathCoord = String(pathData) - .replace(/[^.0-9,]/g, ' ') - .split(/[ ,]/) - .reduce((result, num) => (Number(num) > result ? Number(num) : result), 0); return ( - + ); 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 75d8c5a47d32dd..6f04c87159636c 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 @@ -34,6 +34,8 @@ interface LogMinimapProps { summaryBuckets: SummaryBucket[]; summaryHighlightBuckets?: SummaryHighlightBucket[]; target: number | null; + start: number | null; + end: number | null; width: number; } @@ -44,11 +46,9 @@ interface LogMinimapState { timeCursorY: number; } -function calculateYScale(target: number | null, height: number, intervalSize: number) { - const domainStart = target ? target - intervalSize / 2 : 0; - const domainEnd = target ? target + intervalSize / 2 : 0; +function calculateYScale(start: number | null, end: number | null, height: number) { return scaleLinear() - .domain([domainStart, domainEnd]) + .domain([start || 0, end || 0]) .range([0, height]); } @@ -166,6 +166,8 @@ export class LogMinimap extends React.Component - + 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 4d2b99da9b4129..c085b5b5b2c05b 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 @@ -113,10 +113,12 @@ export const LogsPageLogsContent: React.FunctionComponent = () => { return ( - {({ buckets }) => ( + {({ buckets, start, end }) => ( {({ isReloading }) => ( Date: Thu, 9 Jan 2020 13:45:59 +0100 Subject: [PATCH 006/117] Remove drag&drop from the minimap The minimap now shows a fixed range, so it doesn't make sense anymore to allow users to move it around. --- .../logging/log_minimap/log_minimap.tsx | 115 +++--------------- 1 file changed, 14 insertions(+), 101 deletions(-) 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 6f04c87159636c..3effd23d62acdf 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 @@ -20,11 +20,6 @@ interface Interval { start: number; } -interface DragRecord { - startY: number; - currentY: number | null; -} - interface LogMinimapProps { className?: string; height: number; @@ -41,7 +36,6 @@ interface LogMinimapProps { interface LogMinimapState { target: number | null; - drag: DragRecord | null; svgPosition: ClientRect; timeCursorY: number; } @@ -58,7 +52,6 @@ export class LogMinimap extends React.Component { - if (!this.dragTargetArea) return; - const svgPosition = this.dragTargetArea.getBoundingClientRect(); - const clickedYPosition = event.clientY - svgPosition.top; - const clickedTime = Math.floor(this.getYScale().invert(clickedYPosition)); - this.setState({ - drag: null, - }); - this.props.jumpToTarget({ - tiebreaker: 0, - time: clickedTime, - }); - }; - - private handleMouseDown: React.MouseEventHandler = event => { - const { clientY, target } = event; - if (target === this.dragTargetArea) { - const svgPosition = event.currentTarget.getBoundingClientRect(); - this.setState({ - drag: { - startY: clientY, - currentY: null, - }, - svgPosition, - }); - window.addEventListener('mousemove', this.handleDragMove); - } - window.addEventListener('mouseup', this.handleMouseUp); - }; + public handleClick: React.MouseEventHandler = event => { + const minimapTop = event.currentTarget.getBoundingClientRect().top; + const clickedYPosition = event.clientY - minimapTop; - private handleMouseUp = (event: MouseEvent) => { - window.removeEventListener('mousemove', this.handleDragMove); - window.removeEventListener('mouseup', this.handleMouseUp); + const clickedTime = Math.floor(this.getYScale().invert(clickedYPosition)); - const { drag, svgPosition } = this.state; - if (!drag || !drag.currentY) { - this.handleClick(event); - return; - } - const getTime = (pos: number) => Math.floor(this.getYScale().invert(pos)); - const startYPosition = drag.startY - svgPosition.top; - const endYPosition = event.clientY - svgPosition.top; - const startTime = getTime(startYPosition); - const endTime = getTime(endYPosition); - const timeDifference = endTime - startTime; - const newTime = (this.props.target || 0) - timeDifference; - this.setState({ drag: null, target: newTime }); this.props.jumpToTarget({ tiebreaker: 0, - time: newTime, - }); - }; - - private handleDragMove = (event: MouseEvent) => { - const { drag } = this.state; - if (!drag) return; - this.setState({ - drag: { - ...drag, - currentY: event.clientY, - }, + time: clickedTime, }); }; public getYScale = () => { - const { target } = this.state; - const { height, intervalSize } = this.props; - return calculateYScale(target, height, intervalSize); + const { start, end, height } = this.props; + return calculateYScale(start, end, height); }; public getPositionOfTime = (time: number) => { @@ -176,14 +111,9 @@ export class LogMinimap extends React.Component - + ) : null} - { - this.dragTargetArea = node; - }} - x={0} - y={0} - width={width / 3} - height={height} - /> ); } } -const DragTargetArea = euiStyled.rect<{ isGrabbing: boolean }>` - fill: transparent; - cursor: ${({ isGrabbing }) => (isGrabbing ? 'grabbing' : 'grab')}; -`; - const MinimapBorder = euiStyled.line` stroke: ${props => props.theme.eui.euiColorMediumShade}; stroke-width: 1px; @@ -266,9 +180,8 @@ const TimeCursor = euiStyled.line` : props.theme.eui.euiColorDarkShade}; `; -const MinimapWrapper = euiStyled.svg<{ showOverscanBoundaries: boolean }>` - background: ${props => - props.showOverscanBoundaries ? props.theme.eui.euiColorMediumShade : 'transparent'}; +const MinimapWrapper = euiStyled.svg` + cursor: pointer; & ${TimeCursor} { visibility: hidden; } From 24ee94ca7062f590ef70a0387c26e2d312b7cbaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 9 Jan 2020 14:08:54 +0100 Subject: [PATCH 007/117] Add API client to fetch entries --- .../logs/log_entries/api/fetch_log_entries.ts | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 x-pack/legacy/plugins/infra/public/containers/logs/log_entries/api/fetch_log_entries.ts diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/api/fetch_log_entries.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/api/fetch_log_entries.ts new file mode 100644 index 00000000000000..4c452b98e71139 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/api/fetch_log_entries.ts @@ -0,0 +1,29 @@ +/* + * 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 { fold } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { identity } from 'fp-ts/lib/function'; +import { kfetch } from 'ui/kfetch'; + +import { throwErrors, createPlainError } from '../../../../../common/runtime_types'; + +import { + LOG_ENTRIES_PATH, + LogEntriesRequest, + logEntriesRequestRT, + logEntriesResponseRT, +} from '../../../../../common/http_api'; + +export const fetchLogEntries = async (requestArgs: LogEntriesRequest) => { + const response = await kfetch({ + method: 'POST', + pathname: LOG_ENTRIES_PATH, + body: JSON.stringify(logEntriesRequestRT.encode(requestArgs)), + }); + + return pipe(logEntriesResponseRT.decode(response), fold(throwErrors(createPlainError), identity)); +}; From e43039710f1f78c0c824ade297aa34dde9ed337d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 9 Jan 2020 14:34:41 +0100 Subject: [PATCH 008/117] Deduplicate log summary bucket types --- .../logging/log_minimap/density_chart.tsx | 6 +++--- .../logging/log_minimap/log_minimap.tsx | 9 ++++++--- .../logging/log_minimap/search_marker.tsx | 5 ++--- .../logging/log_minimap/search_markers.tsx | 4 ++-- .../components/logging/log_minimap/types.ts | 17 ----------------- 5 files changed, 13 insertions(+), 28 deletions(-) delete mode 100644 x-pack/legacy/plugins/infra/public/components/logging/log_minimap/types.ts diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/density_chart.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/density_chart.tsx index 79b6cd32856b55..3eac729edbabae 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/density_chart.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/density_chart.tsx @@ -10,10 +10,10 @@ import max from 'lodash/fp/max'; import * as React from 'react'; import euiStyled from '../../../../../../common/eui_styled_components'; -import { SummaryBucket } from './types'; +import { LogEntriesSummaryBucket } from '../../../../common/http_api'; interface DensityChartProps { - buckets: SummaryBucket[]; + buckets: LogEntriesSummaryBucket[]; end: number; start: number; width: number; @@ -41,7 +41,7 @@ export const DensityChart: React.FC = ({ .range([0, width * (2 / 3)]); // FIXME: path is not closed at the bottom. - const path = area() + const path = area() .x0(xScale(0)) .x1(bucket => xScale(bucket.entriesCount)) .y0(bucket => yScale(bucket.start)) 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 3effd23d62acdf..b0c6028375ac2a 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 @@ -13,7 +13,10 @@ import { DensityChart } from './density_chart'; import { HighlightedInterval } from './highlighted_interval'; import { SearchMarkers } from './search_markers'; import { TimeRuler } from './time_ruler'; -import { SummaryBucket, SummaryHighlightBucket } from './types'; +import { + LogEntriesSummaryBucket, + LogEntriesSummaryHighlightsBucket, +} from '../../../../common/http_api'; interface Interval { end: number; @@ -26,8 +29,8 @@ interface LogMinimapProps { highlightedInterval: Interval | null; jumpToTarget: (params: LogEntryTime) => any; intervalSize: number; - summaryBuckets: SummaryBucket[]; - summaryHighlightBuckets?: SummaryHighlightBucket[]; + summaryBuckets: LogEntriesSummaryBucket[]; + summaryHighlightBuckets?: LogEntriesSummaryHighlightsBucket[]; target: number | null; start: number | null; end: number | null; 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 5b661562a451ef..ae7828ab8647d8 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 @@ -10,10 +10,9 @@ import * as React from 'react'; import euiStyled, { keyframes } from '../../../../../../common/eui_styled_components'; import { LogEntryTime } from '../../../../common/log_entry'; import { SearchMarkerTooltip } from './search_marker_tooltip'; -import { SummaryHighlightBucket } from './types'; - +import { LogEntriesSummaryHighlightsBucket } from '../../../../common/http_api'; interface SearchMarkerProps { - bucket: SummaryHighlightBucket; + bucket: LogEntriesSummaryHighlightsBucket; height: number; width: number; jumpToTarget: (target: LogEntryTime) => void; 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 ebdc390aef11b5..1e254d999036e5 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 @@ -10,10 +10,10 @@ import * as React from 'react'; import { LogEntryTime } from '../../../../common/log_entry'; import { SearchMarker } from './search_marker'; -import { SummaryHighlightBucket } from './types'; +import { LogEntriesSummaryHighlightsBucket } from '../../../../common/http_api'; interface SearchMarkersProps { - buckets: SummaryHighlightBucket[]; + buckets: LogEntriesSummaryHighlightsBucket[]; className?: string; end: number; start: number; 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 deleted file mode 100644 index d8197935dafa72..00000000000000 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/types.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * 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 { TimeKey } from '../../../../common/time'; - -export interface SummaryBucket { - start: number; - end: number; - entriesCount: number; -} - -export interface SummaryHighlightBucket extends SummaryBucket { - representativeKey: TimeKey; -} From eeb64d4ce39a58d71447c9cd480a520c9938a0ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 9 Jan 2020 15:11:58 +0100 Subject: [PATCH 009/117] Extract datemath helpers into a module --- .../with_log_position_url_state.tsx | 11 ++-------- .../logs/log_summary/with_summary.ts | 18 +++-------------- .../plugins/infra/public/utils/datemath.ts | 20 +++++++++++++++++++ 3 files changed, 25 insertions(+), 24 deletions(-) create mode 100644 x-pack/legacy/plugins/infra/public/utils/datemath.ts diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx index 037509a2e7cfc4..6a15304a374ae4 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx @@ -5,11 +5,11 @@ */ import React, { useContext, useMemo } from 'react'; -import datemath from '@elastic/datemath'; import { pickTimeKey } from '../../../../common/time'; import { replaceStateKeyInQueryString, UrlStateContainer } from '../../../utils/url_state'; import { LogPositionState, LogPositionStateParams } from './log_position_state'; +import { isValidDatemath } from '../../../utils/datemath'; /** * Url State @@ -108,14 +108,7 @@ const mapToPositionUrlState = (value: any) => const mapToStreamLiveUrlState = (value: any) => (typeof value === 'boolean' ? value : false); -const mapToDate = (value: any) => { - const parsed = datemath.parse(value); - if (!parsed || !parsed.isValid()) { - return undefined; - } - return value; -}; - +const mapToDate = (value: any) => (isValidDatemath(value) ? value : undefined); export const replaceLogPositionInQueryString = (time: number) => Number.isNaN(time) ? (value: string) => value 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 aa8eddb099bd82..4b3a28c436b5f0 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 @@ -5,13 +5,13 @@ */ import { useContext, useMemo } from 'react'; -import datemath from '@elastic/datemath'; import { RendererFunction } from '../../../utils/typed_react'; import { Source } from '../../source'; import { LogSummaryBuckets, useLogSummary } from './log_summary'; import { LogFilterState } from '../log_filter'; import { LogPositionState } from '../log_position'; +import { datemathToEpochMillis } from '../../../utils/datemath'; export const WithSummary = ({ children, @@ -26,20 +26,8 @@ export const WithSummary = ({ const { filterQuery } = useContext(LogFilterState.Context); const { startDate, endDate } = useContext(LogPositionState.Context); - const startTimestamp: number | null = useMemo(() => { - const date = datemath.parse(startDate); - if (!date || !date.isValid()) { - return null; - } - return date.unix() * 1000; - }, [startDate]); - const endTimestamp: number | null = useMemo(() => { - const date = datemath.parse(endDate); - if (!date || !date.isValid()) { - return null; - } - return date.unix() * 1000; - }, [endDate]); + const startTimestamp = useMemo(() => datemathToEpochMillis(startDate), [startDate]); + const endTimestamp = useMemo(() => datemathToEpochMillis(endDate), [endDate]); const { buckets, start, end } = useLogSummary( sourceId, diff --git a/x-pack/legacy/plugins/infra/public/utils/datemath.ts b/x-pack/legacy/plugins/infra/public/utils/datemath.ts new file mode 100644 index 00000000000000..36172515e31947 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/utils/datemath.ts @@ -0,0 +1,20 @@ +/* + * 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 dateMath from '@elastic/datemath'; + +export function isValidDatemath(value: string): boolean { + const parsedValue = dateMath.parse(value); + return !!(parsedValue && parsedValue.isValid()); +} + +export function datemathToEpochMillis(value: string): number | null { + const parsedValue = dateMath.parse(value); + if (!parsedValue || !parsedValue.isValid()) { + return null; + } + return parsedValue.unix() * 1000; +} From 4c3ee9dcc56f0b5eff1e815c5c501541a2d85f77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 10 Jan 2020 11:46:44 +0100 Subject: [PATCH 010/117] Allow arbitrary values in field columns --- .../legacy/plugins/infra/common/http_api/log_entries/entries.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts b/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts index 97bdad23beb240..59c49f3d4cabc7 100644 --- a/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts +++ b/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts @@ -64,7 +64,7 @@ export const logColumnRT = rt.union([ rt.type({ columnId: rt.string, field: rt.string, - value: rt.union([rt.string, rt.undefined]), + value: rt.unknown, highlights: rt.array(rt.string), }), rt.type({ From 1abb4b75625c408dc25dbcbf99ba94d57fbd470f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 10 Jan 2020 11:50:13 +0100 Subject: [PATCH 011/117] Deprecate old log response in the reducer --- .../public/containers/logs/log_entries/gql_queries.ts | 4 ++-- .../infra/public/containers/logs/log_entries/index.ts | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/gql_queries.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/gql_queries.ts index 83bae37c348d4d..124c0bd7123740 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/gql_queries.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/gql_queries.ts @@ -7,7 +7,7 @@ import { ApolloClient } from 'apollo-client'; import { TimeKey } from '../../../../common/time'; import { logEntriesQuery } from '../../../graphql/log_entries.gql_query'; import { useApolloClient } from '../../../utils/apollo_context'; -import { LogEntriesResponse } from '.'; +import { LogEntriesResponseLEGACY } from '.'; const LOAD_CHUNK_SIZE = 200; @@ -19,7 +19,7 @@ type LogEntriesGetter = ( sourceId: string; timeKey: TimeKey | null; filterQuery: string | null; -}) => Promise; +}) => Promise; const getLogEntries: LogEntriesGetter = (client, countBefore, countAfter) => async ({ sourceId, diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts index 04412f5fdd8710..75fbc7692fd6e2 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts @@ -29,7 +29,7 @@ type ReceiveActions = interface ReceiveEntriesAction { type: ReceiveActions; - payload: LogEntriesResponse; + payload: LogEntriesResponseLEGACY; } interface FetchOrErrorAction { type: Exclude; @@ -51,7 +51,8 @@ interface LogEntriesProps { type FetchEntriesParams = Omit; type FetchMoreEntriesParams = Pick; -export interface LogEntriesResponse { +/** @deprecated */ +export interface LogEntriesResponseLEGACY { entries: InfraLogEntry[]; entriesStart: TimeKey | null; entriesEnd: TimeKey | null; @@ -63,7 +64,7 @@ export interface LogEntriesResponse { export type LogEntriesStateParams = { isReloading: boolean; isLoadingMore: boolean; -} & LogEntriesResponse; +} & LogEntriesResponseLEGACY; export interface LogEntriesCallbacks { fetchNewerEntries: () => Promise; From 154dc3f6242eeb6327c2eacb2f561e3a62e6ae65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 10 Jan 2020 15:52:26 +0100 Subject: [PATCH 012/117] Load entries from HTTP API This commit is WIP and breaks a bunch of stuff. Work will be refined later. --- .../log_entry_field_column.tsx | 14 +- .../logs/log_entries/gql_queries.ts | 64 -------- .../containers/logs/log_entries/index.ts | 142 ++++++++++++------ .../pages/logs/stream/page_providers.tsx | 4 + 4 files changed, 109 insertions(+), 115 deletions(-) delete mode 100644 x-pack/legacy/plugins/infra/public/containers/logs/log_entries/gql_queries.ts diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx index 6252b3a396d1b6..e82e9cf362182e 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx @@ -35,9 +35,17 @@ export const LogEntryFieldColumn: React.FunctionComponent { - const value = useMemo(() => (isFieldColumn(columnValue) ? JSON.parse(columnValue.value) : null), [ - columnValue, - ]); + const value = useMemo(() => { + if (isFieldColumn(columnValue)) { + // FIXME + try { + return JSON.parse(columnValue.value); + } catch (e) { + return columnValue.value; + } + } + return null; + }, [columnValue]); const formattedValue = Array.isArray(value) ? (
    {value.map((entry, i) => ( diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/gql_queries.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/gql_queries.ts deleted file mode 100644 index 124c0bd7123740..00000000000000 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/gql_queries.ts +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 { ApolloClient } from 'apollo-client'; -import { TimeKey } from '../../../../common/time'; -import { logEntriesQuery } from '../../../graphql/log_entries.gql_query'; -import { useApolloClient } from '../../../utils/apollo_context'; -import { LogEntriesResponseLEGACY } from '.'; - -const LOAD_CHUNK_SIZE = 200; - -type LogEntriesGetter = ( - client: ApolloClient<{}>, - countBefore: number, - countAfter: number -) => (params: { - sourceId: string; - timeKey: TimeKey | null; - filterQuery: string | null; -}) => Promise; - -const getLogEntries: LogEntriesGetter = (client, countBefore, countAfter) => async ({ - sourceId, - timeKey, - filterQuery, -}) => { - if (!timeKey) throw new Error('TimeKey is null'); - const result = await client.query({ - query: logEntriesQuery, - variables: { - sourceId, - timeKey: { time: timeKey.time, tiebreaker: timeKey.tiebreaker }, - countBefore, - countAfter, - filterQuery, - }, - fetchPolicy: 'no-cache', - }); - // Workaround for Typescript. Since we're removing the GraphQL API in another PR or two - // 7.6 goes out I don't think it's worth the effort to actually make this - // typecheck pass - const { source } = result.data as any; - const { logEntriesAround } = source; - return { - entries: logEntriesAround.entries, - entriesStart: logEntriesAround.start, - entriesEnd: logEntriesAround.end, - hasMoreAfterEnd: logEntriesAround.hasMoreAfter, - hasMoreBeforeStart: logEntriesAround.hasMoreBefore, - lastLoadedTime: new Date(), - }; -}; - -export const useGraphQLQueries = () => { - const client = useApolloClient(); - if (!client) throw new Error('Unable to get Apollo Client from context'); - return { - getLogEntriesAround: getLogEntries(client, LOAD_CHUNK_SIZE, LOAD_CHUNK_SIZE), - getLogEntriesBefore: getLogEntries(client, LOAD_CHUNK_SIZE, 0), - getLogEntriesAfter: getLogEntries(client, 0, LOAD_CHUNK_SIZE), - }; -}; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts index 75fbc7692fd6e2..e70356c0602b17 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts @@ -3,12 +3,14 @@ * 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, useState, useReducer, useCallback } from 'react'; +import { useEffect, useState, useReducer, useCallback, useMemo } from 'react'; import createContainer from 'constate'; -import { pick, throttle, omit } from 'lodash'; -import { useGraphQLQueries } from './gql_queries'; +import { pick, throttle } from 'lodash'; import { TimeKey, timeKeyIsBetween } from '../../../../common/time'; -import { InfraLogEntry } from './types'; +import { LogEntriesResponse, LogEntry, LogEntriesRequest } from '../../../../common/http_api'; +import { fetchLogEntries } from './api/fetch_log_entries'; +import { datemathToEpochMillis } from '../../../utils/datemath'; +import { InfraLogEntry, InfraLogEntryColumn } from './types'; const DESIRED_BUFFER_PAGES = 2; @@ -29,7 +31,7 @@ type ReceiveActions = interface ReceiveEntriesAction { type: ReceiveActions; - payload: LogEntriesResponseLEGACY; + payload: LogEntriesResponse['data']; } interface FetchOrErrorAction { type: Exclude; @@ -39,6 +41,8 @@ type ActionObj = ReceiveEntriesAction | FetchOrErrorAction; type Dispatch = (action: ActionObj) => void; interface LogEntriesProps { + startDate: string | null; + endDate: string | null; filterQuery: string | null; timeKey: TimeKey | null; pagesBeforeStart: number | null; @@ -51,20 +55,14 @@ interface LogEntriesProps { type FetchEntriesParams = Omit; type FetchMoreEntriesParams = Pick; -/** @deprecated */ -export interface LogEntriesResponseLEGACY { - entries: InfraLogEntry[]; - entriesStart: TimeKey | null; - entriesEnd: TimeKey | null; - hasMoreAfterEnd: boolean; - hasMoreBeforeStart: boolean; - lastLoadedTime: Date | null; -} - -export type LogEntriesStateParams = { +export interface LogEntriesStateParams { + entries: LogEntriesResponse['data']['entries']; + topCursor: LogEntriesResponse['data']['topCursor'] | null; + bottomCursor: LogEntriesResponse['data']['bottomCursor'] | null; isReloading: boolean; isLoadingMore: boolean; -} & LogEntriesResponseLEGACY; + lastLoadedTime: Date | null; +} export interface LogEntriesCallbacks { fetchNewerEntries: () => Promise; @@ -76,10 +74,8 @@ export const logEntriesInitialCallbacks = { export const logEntriesInitialState: LogEntriesStateParams = { entries: [], - entriesStart: null, - entriesEnd: null, - hasMoreAfterEnd: false, - hasMoreBeforeStart: false, + topCursor: null, + bottomCursor: null, isReloading: true, isLoadingMore: false, lastLoadedTime: null, @@ -125,17 +121,32 @@ const useFetchEntriesEffect = ( dispatch: Dispatch, props: LogEntriesProps ) => { - const { getLogEntriesAround, getLogEntriesBefore, getLogEntriesAfter } = useGraphQLQueries(); - const [prevParams, cachePrevParams] = useState(props); const [startedStreaming, setStartedStreaming] = useState(false); + const startTimestamp = useMemo( + () => (props.startDate ? datemathToEpochMillis(props.startDate) : null), + [props.startDate] + ); + const endTimestamp = useMemo( + () => (props.endDate ? datemathToEpochMillis(props.endDate) : null), + [props.endDate] + ); + const runFetchNewEntriesRequest = async (override = {}) => { + if (!startTimestamp || !endTimestamp) { + return; + } + dispatch({ type: Action.FetchingNewEntries }); + try { - const payload = await getLogEntriesAround({ - ...omit(props, 'jumpToTargetPosition'), - ...override, + const { data: payload } = await fetchLogEntries({ + sourceId: props.sourceId, + startDate: startTimestamp, + endDate: endTimestamp, + before: 'last', // TODO distinguish between first load and position-load + query: props.filterQuery || undefined, // FIXME }); dispatch({ type: Action.ReceiveNewEntries, payload }); } catch (e) { @@ -144,26 +155,43 @@ const useFetchEntriesEffect = ( }; const runFetchMoreEntriesRequest = async (direction: ShouldFetchMoreEntries) => { + if (!startTimestamp || !endTimestamp) { + return; + } + dispatch({ type: Action.FetchingMoreEntries }); + const getEntriesBefore = direction === ShouldFetchMoreEntries.Before; - const timeKey = getEntriesBefore - ? state.entries[0].key - : state.entries[state.entries.length - 1].key; - const getMoreLogEntries = getEntriesBefore ? getLogEntriesBefore : getLogEntriesAfter; + try { - const payload = await getMoreLogEntries({ ...props, timeKey }); + const fetchArgs: LogEntriesRequest = { + sourceId: props.sourceId, + startDate: startTimestamp, + endDate: endTimestamp, + query: props.filterQuery || undefined, // FIXME + }; + + if (getEntriesBefore) { + fetchArgs.before = state.topCursor; + } else { + fetchArgs.after = state.bottomCursor; + } + + const { data: payload } = await fetchLogEntries(fetchArgs); + dispatch({ type: getEntriesBefore ? Action.ReceiveEntriesBefore : Action.ReceiveEntriesAfter, payload, }); - return payload.entriesEnd; + + return payload.bottomCursor; } catch (e) { dispatch({ type: Action.ErrorOnMoreEntries }); } }; const fetchNewEntriesEffectDependencies = Object.values( - pick(props, ['sourceId', 'filterQuery', 'timeKey']) + pick(props, ['sourceId', 'filterQuery', 'timeKey', 'startDate', 'endDate']) ); const fetchNewEntriesEffect = () => { if (props.isAutoReloading) return; @@ -250,31 +278,37 @@ export const useLogEntriesState: ( const logEntriesStateReducer = (prevState: LogEntriesStateParams, action: ActionObj) => { switch (action.type) { case Action.ReceiveNewEntries: - return { ...prevState, ...action.payload, isReloading: false }; + return { + ...prevState, + ...action.payload, + entries: newEntriesToOldEntries(action.payload.entries), + lastLoadedTime: new Date(), + isReloading: false, + }; case Action.ReceiveEntriesBefore: { - const prevEntries = cleanDuplicateItems(prevState.entries, action.payload.entries); - const newEntries = [...action.payload.entries, ...prevEntries]; - const { hasMoreBeforeStart, entriesStart, lastLoadedTime } = action.payload; + const newEntries = newEntriesToOldEntries(action.payload.entries); + const prevEntries = cleanDuplicateItems(prevState.entries, newEntries); + const update = { - entries: newEntries, + entries: [...newEntries, ...prevEntries], isLoadingMore: false, - hasMoreBeforeStart, - entriesStart, - lastLoadedTime, + topCursor: action.payload.topCursor, + lastLoadedTime: new Date(), }; + return { ...prevState, ...update }; } case Action.ReceiveEntriesAfter: { - const prevEntries = cleanDuplicateItems(prevState.entries, action.payload.entries); - const newEntries = [...prevEntries, ...action.payload.entries]; - const { hasMoreAfterEnd, entriesEnd, lastLoadedTime } = action.payload; + const newEntries = newEntriesToOldEntries(action.payload.entries); + const prevEntries = cleanDuplicateItems(prevState.entries, newEntries); + const update = { - entries: newEntries, + entries: [...prevEntries, ...newEntries], isLoadingMore: false, - hasMoreAfterEnd, - entriesEnd, - lastLoadedTime, + bottomCursor: action.payload.bottomCursor, + lastLoadedTime: new Date(), }; + return { ...prevState, ...update }; } case Action.FetchingNewEntries: @@ -291,3 +325,15 @@ const logEntriesStateReducer = (prevState: LogEntriesStateParams, action: Action }; export const LogEntriesState = createContainer(useLogEntriesState); + +// FIXME temporal helper function to make it work while we adjust the types +function newEntriesToOldEntries(entries: LogEntry[]): InfraLogEntry[] { + return entries.map(entry => { + return { + gid: entry.id, + key: entry.cursor, + columns: entry.columns as InfraLogEntryColumn[], + source: 'default', + }; + }); +} diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx index d19f27bf1286ea..06023ee5c9c13f 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx @@ -29,6 +29,8 @@ const LogFilterStateProvider: React.FC = ({ children }) => { const LogEntriesStateProvider: React.FC = ({ children }) => { const { sourceId } = useContext(Source.Context); const { + startDate, + endDate, targetPosition, pagesBeforeStart, pagesAfterEnd, @@ -38,6 +40,8 @@ const LogEntriesStateProvider: React.FC = ({ children }) => { const { filterQuery } = useContext(LogFilterState.Context); const entriesProps = { + startDate, + endDate, timeKey: targetPosition, pagesBeforeStart, pagesAfterEnd, From 50bc02c4f2b5a1f66f4f4364df19275c1b0f9304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Mon, 13 Jan 2020 12:28:50 +0100 Subject: [PATCH 013/117] Extract column types --- .../common/http_api/log_entries/entries.ts | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts b/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts index 59c49f3d4cabc7..6e05d47c1abbf8 100644 --- a/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts +++ b/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts @@ -59,19 +59,19 @@ export const logMessagePartRT = rt.union([ }), ]); -export const logColumnRT = rt.union([ - rt.type({ columnId: rt.string, timestamp: rt.number }), - rt.type({ - columnId: rt.string, - field: rt.string, - value: rt.unknown, - highlights: rt.array(rt.string), - }), - rt.type({ - columnId: rt.string, - message: rt.array(logMessagePartRT), - }), -]); +export const logTimestampColumnRT = rt.type({ columnId: rt.string, timestamp: rt.number }); +export const logFieldColumnRT = rt.type({ + columnId: rt.string, + field: rt.string, + value: rt.unknown, + highlights: rt.array(rt.string), +}); +export const logMessageColumnRT = rt.type({ + columnId: rt.string, + message: rt.array(logMessagePartRT), +}); + +export const logColumnRT = rt.union([logTimestampColumnRT, logFieldColumnRT, logMessageColumnRT]); export const logEntryRT = rt.type({ id: rt.string, @@ -80,6 +80,9 @@ export const logEntryRT = rt.type({ }); export type LogMessagepart = rt.TypeOf; +export type LogTimestampColumn = rt.TypeOf; +export type LogFieldColumn = rt.TypeOf; +export type LogMessageColumn = rt.TypeOf; export type LogColumn = rt.TypeOf; export type LogEntry = rt.TypeOf; From 9d190ac29d12ae5d1223cc6c70384d8410978aa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Mon, 13 Jan 2020 12:31:14 +0100 Subject: [PATCH 014/117] Adapt components to new API response --- .../logging/log_text_stream/item.ts | 7 +++-- .../log_entry_field_column.test.tsx | 11 +++++--- .../logging/log_text_stream/log_entry_row.tsx | 6 ++-- .../scrollable_log_text_stream_view.tsx | 8 +++--- .../containers/logs/log_entries/index.ts | 25 ++++------------- .../containers/logs/with_stream_items.ts | 5 ++-- .../infra/public/utils/log_entry/log_entry.ts | 28 +++++++++---------- 7 files changed, 41 insertions(+), 49 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/item.ts b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/item.ts index ca5ca9736b7b38..b12fa7c385f130 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/item.ts +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/item.ts @@ -7,7 +7,8 @@ import { bisector } from 'd3-array'; import { compareToTimeKey, TimeKey } from '../../../../common/time'; -import { LogEntry, LogEntryHighlight } from '../../../utils/log_entry'; +import { LogEntryHighlight } from '../../../utils/log_entry'; +import { LogEntry } from '../../../../common/http_api'; export type StreamItem = LogEntryStreamItem; @@ -20,14 +21,14 @@ export interface LogEntryStreamItem { export function getStreamItemTimeKey(item: StreamItem) { switch (item.kind) { case 'logEntry': - return item.logEntry.key; + return item.logEntry.cursor; } } export function getStreamItemId(item: StreamItem) { switch (item.kind) { case 'logEntry': - return `${item.logEntry.key.time}:${item.logEntry.key.tiebreaker}:${item.logEntry.gid}`; + return `${item.logEntry.cursor.time}:${item.logEntry.cursor.tiebreaker}:${item.logEntry.id}`; } } diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx index f947a0fb1adcd4..41a452736fc619 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx @@ -8,15 +8,16 @@ import { mount } from 'enzyme'; import React from 'react'; import { EuiThemeProvider } from '../../../../../../common/eui_styled_components'; -import { LogEntryColumn } from '../../../utils/log_entry'; import { LogEntryFieldColumn } from './log_entry_field_column'; +import { LogColumn } from '../../../../common/http_api'; describe('LogEntryFieldColumn', () => { it('should output a
      when displaying an Array of values', () => { - const column: LogEntryColumn = { + const column: LogColumn = { columnId: 'TEST_COLUMN', field: 'TEST_FIELD', value: JSON.stringify(['a', 'b', 'c']), + highlights: [], }; const component = mount( @@ -42,13 +43,14 @@ describe('LogEntryFieldColumn', () => { }); it('should output a text representation of a passed complex value', () => { - const column: LogEntryColumn = { + const column: LogColumn = { columnId: 'TEST_COLUMN', field: 'TEST_FIELD', value: JSON.stringify({ lat: 1, lon: 2, }), + highlights: [], }; const component = mount( @@ -67,10 +69,11 @@ describe('LogEntryFieldColumn', () => { }); it('should output just text when passed a non-Array', () => { - const column: LogEntryColumn = { + const column: LogColumn = { columnId: 'TEST_COLUMN', field: 'TEST_FIELD', value: JSON.stringify('foo'), + highlights: [], }; const component = mount( diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx index 0da601ae520882..21fd277f26346a 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx @@ -9,7 +9,6 @@ import React, { useState, useCallback, useMemo } from 'react'; import euiStyled from '../../../../../../common/eui_styled_components'; import { - LogEntry, LogEntryHighlight, LogEntryHighlightColumn, isTimestampColumn, @@ -27,6 +26,7 @@ import { LogEntryDetailsIconColumn } from './log_entry_icon_column'; import { LogEntryMessageColumn } from './log_entry_message_column'; import { LogEntryTimestampColumn } from './log_entry_timestamp_column'; import { monospaceTextStyle } from './text_styles'; +import { LogEntry } from '../../../../common/http_api'; interface LogEntryRowProps { boundingBoxRef?: React.Ref; @@ -63,9 +63,9 @@ export const LogEntryRow = ({ setIsHovered(false); }, []); - const openFlyout = useCallback(() => openFlyoutWithItem(logEntry.gid), [ + const openFlyout = useCallback(() => openFlyoutWithItem(logEntry.id), [ openFlyoutWithItem, - logEntry.gid, + logEntry.id, ]); const logEntryColumnsById = useMemo( diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx index 15d3c83ffebe90..4ed05fb7d57a99 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx @@ -196,11 +196,11 @@ export class ScrollableLogTextStreamView extends React.PureComponent< lastStreamingUpdate={null} /> {items.map((item, idx) => { - const currentTimestamp = item.logEntry.key.time; + const currentTimestamp = item.logEntry.cursor.time; let showDate = false; if (idx > 0) { - const prevTimestamp = items[idx - 1].logEntry.key.time; + const prevTimestamp = items[idx - 1].logEntry.cursor.time; showDate = !moment(currentTimestamp).isSame(prevTimestamp, 'day'); } @@ -221,13 +221,13 @@ export class ScrollableLogTextStreamView extends React.PureComponent< highlights={item.highlights} isActiveHighlight={ !!currentHighlightKey && - currentHighlightKey.gid === item.logEntry.gid + currentHighlightKey.gid === item.logEntry.id } scale={scale} wrap={wrap} isHighlighted={ highlightedItem - ? item.logEntry.gid === highlightedItem + ? item.logEntry.id === highlightedItem : false } /> diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts index e70356c0602b17..3ae604114e6936 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts @@ -10,7 +10,6 @@ import { TimeKey, timeKeyIsBetween } from '../../../../common/time'; import { LogEntriesResponse, LogEntry, LogEntriesRequest } from '../../../../common/http_api'; import { fetchLogEntries } from './api/fetch_log_entries'; import { datemathToEpochMillis } from '../../../utils/datemath'; -import { InfraLogEntry, InfraLogEntryColumn } from './types'; const DESIRED_BUFFER_PAGES = 2; @@ -81,9 +80,9 @@ export const logEntriesInitialState: LogEntriesStateParams = { lastLoadedTime: null, }; -const cleanDuplicateItems = (entriesA: InfraLogEntry[], entriesB: InfraLogEntry[]) => { - const gids = new Set(entriesB.map(item => item.gid)); - return entriesA.filter(item => !gids.has(item.gid)); +const cleanDuplicateItems = (entriesA: LogEntry[], entriesB: LogEntry[]) => { + const ids = new Set(entriesB.map(item => item.id)); + return entriesA.filter(item => !ids.has(item.id)); }; const shouldFetchNewEntries = ({ @@ -281,12 +280,12 @@ const logEntriesStateReducer = (prevState: LogEntriesStateParams, action: Action return { ...prevState, ...action.payload, - entries: newEntriesToOldEntries(action.payload.entries), + entries: action.payload.entries, lastLoadedTime: new Date(), isReloading: false, }; case Action.ReceiveEntriesBefore: { - const newEntries = newEntriesToOldEntries(action.payload.entries); + const newEntries = action.payload.entries; const prevEntries = cleanDuplicateItems(prevState.entries, newEntries); const update = { @@ -299,7 +298,7 @@ const logEntriesStateReducer = (prevState: LogEntriesStateParams, action: Action return { ...prevState, ...update }; } case Action.ReceiveEntriesAfter: { - const newEntries = newEntriesToOldEntries(action.payload.entries); + const newEntries = action.payload.entries; const prevEntries = cleanDuplicateItems(prevState.entries, newEntries); const update = { @@ -325,15 +324,3 @@ const logEntriesStateReducer = (prevState: LogEntriesStateParams, action: Action }; export const LogEntriesState = createContainer(useLogEntriesState); - -// FIXME temporal helper function to make it work while we adjust the types -function newEntriesToOldEntries(entries: LogEntry[]): InfraLogEntry[] { - return entries.map(entry => { - return { - gid: entry.id, - key: entry.cursor, - columns: entry.columns as InfraLogEntryColumn[], - source: 'default', - }; - }); -} 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 6da9cd7513cbab..426277cc36b81c 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 @@ -6,12 +6,13 @@ import { useContext, useMemo } from 'react'; import { StreamItem, LogEntryStreamItem } from '../../components/logging/log_text_stream/item'; -import { LogEntry, LogEntryHighlight } from '../../utils/log_entry'; +import { LogEntryHighlight } from '../../utils/log_entry'; import { RendererFunction } from '../../utils/typed_react'; // deep inporting to avoid a circular import problem import { LogHighlightsState } from './log_highlights/log_highlights'; import { LogEntriesState, LogEntriesStateParams, LogEntriesCallbacks } from './log_entries'; import { UniqueTimeKey } from '../../../common/time'; +import { LogEntry } from '../../../common/http_api'; export const WithStreamItems: React.FunctionComponent<{ children: RendererFunction< @@ -30,7 +31,7 @@ export const WithStreamItems: React.FunctionComponent<{ logEntries.isReloading ? [] : logEntries.entries.map(logEntry => - createLogEntryStreamItem(logEntry, logEntryHighlightsById[logEntry.gid] || []) + createLogEntryStreamItem(logEntry, logEntryHighlightsById[logEntry.id] || []) ), [logEntries.entries, logEntries.isReloading, logEntryHighlightsById] diff --git a/x-pack/legacy/plugins/infra/public/utils/log_entry/log_entry.ts b/x-pack/legacy/plugins/infra/public/utils/log_entry/log_entry.ts index be6b8c40753ae9..4fad524d1a42ad 100644 --- a/x-pack/legacy/plugins/infra/public/utils/log_entry/log_entry.ts +++ b/x-pack/legacy/plugins/infra/public/utils/log_entry/log_entry.ts @@ -8,23 +8,23 @@ import { bisector } from 'd3-array'; import { compareToTimeKey, getIndexAtTimeKey, TimeKey, UniqueTimeKey } from '../../../common/time'; import { InfraLogEntryFields } from '../../graphql/types'; - -export type LogEntry = InfraLogEntryFields.Fragment; - -export type LogEntryColumn = InfraLogEntryFields.Columns; -export type LogEntryMessageColumn = InfraLogEntryFields.InfraLogEntryMessageColumnInlineFragment; -export type LogEntryTimestampColumn = InfraLogEntryFields.InfraLogEntryTimestampColumnInlineFragment; -export type LogEntryFieldColumn = InfraLogEntryFields.InfraLogEntryFieldColumnInlineFragment; +import { + LogEntry, + LogColumn, + LogTimestampColumn, + LogFieldColumn, + LogMessageColumn, +} from '../../../common/http_api'; export type LogEntryMessageSegment = InfraLogEntryFields.Message; export type LogEntryConstantMessageSegment = InfraLogEntryFields.InfraLogMessageConstantSegmentInlineFragment; export type LogEntryFieldMessageSegment = InfraLogEntryFields.InfraLogMessageFieldSegmentInlineFragment; -export const getLogEntryKey = (entry: { key: TimeKey }) => entry.key; +export const getLogEntryKey = (entry: { cursor: TimeKey }) => entry.cursor; -export const getUniqueLogEntryKey = (entry: { gid: string; key: TimeKey }): UniqueTimeKey => ({ - ...entry.key, - gid: entry.gid, +export const getUniqueLogEntryKey = (entry: { id: string; cursor: TimeKey }): UniqueTimeKey => ({ + ...entry.cursor, + gid: entry.id, }); const logEntryTimeBisector = bisector(compareToTimeKey(getLogEntryKey)); @@ -39,13 +39,13 @@ export const getLogEntryAtTime = (entries: LogEntry[], time: TimeKey) => { return entryIndex !== null ? entries[entryIndex] : null; }; -export const isTimestampColumn = (column: LogEntryColumn): column is LogEntryTimestampColumn => +export const isTimestampColumn = (column: LogColumn): column is LogTimestampColumn => column != null && 'timestamp' in column; -export const isMessageColumn = (column: LogEntryColumn): column is LogEntryMessageColumn => +export const isMessageColumn = (column: LogColumn): column is LogMessageColumn => column != null && 'message' in column; -export const isFieldColumn = (column: LogEntryColumn): column is LogEntryFieldColumn => +export const isFieldColumn = (column: LogColumn): column is LogFieldColumn => column != null && 'field' in column; export const isConstantSegment = ( From e3733fe2f7c3bd6e0043d19ec335057d95538306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Mon, 13 Jan 2020 13:35:24 +0100 Subject: [PATCH 015/117] Allow jumping to specific points in time --- .../common/http_api/log_entries/entries.ts | 8 ++++-- .../common/http_api/log_entries/highlights.ts | 4 +-- .../containers/logs/log_entries/index.ts | 28 ++++++++++++++----- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts b/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts index 6e05d47c1abbf8..50511867af5d04 100644 --- a/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts +++ b/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts @@ -31,7 +31,7 @@ export const logEntriesAfterRequestRT = rt.intersection([ rt.type({ after: rt.union([logEntriesCursorRT, rt.literal('first')]) }), ]); -export const logEntriesCenteredRT = rt.intersection([ +export const logEntriesCenteredRequestRT = rt.intersection([ logEntriesBaseRequestRT, rt.type({ center: logEntriesCursorRT }), ]); @@ -40,9 +40,13 @@ export const logEntriesRequestRT = rt.union([ logEntriesBaseRequestRT, logEntriesBeforeRequestRT, logEntriesAfterRequestRT, - logEntriesCenteredRT, + logEntriesCenteredRequestRT, ]); +export type LogEntriesBaseRequest = rt.TypeOf; +export type LogEntriesBeforeRequest = rt.TypeOf; +export type LogEntriesAfterRequest = rt.TypeOf; +export type LogEntriesCenteredRequest = rt.TypeOf; export type LogEntriesRequest = rt.TypeOf; // JSON value diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_entries/highlights.ts b/x-pack/legacy/plugins/infra/common/http_api/log_entries/highlights.ts index 516cd67f2764da..f6d61a7177b494 100644 --- a/x-pack/legacy/plugins/infra/common/http_api/log_entries/highlights.ts +++ b/x-pack/legacy/plugins/infra/common/http_api/log_entries/highlights.ts @@ -9,7 +9,7 @@ import { logEntriesBaseRequestRT, logEntriesBeforeRequestRT, logEntriesAfterRequestRT, - logEntriesCenteredRT, + logEntriesCenteredRequestRT, logEntryRT, } from './entries'; import { logEntriesCursorRT } from './common'; @@ -36,7 +36,7 @@ export const logEntriesHighlightsAfterRequestRT = rt.intersection([ ]); export const logEntriesHighlightsCenteredRequestRT = rt.intersection([ - logEntriesCenteredRT, + logEntriesCenteredRequestRT, highlightsRT, ]); diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts index 3ae604114e6936..3a11b8ac1a8245 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts @@ -7,7 +7,14 @@ import { useEffect, useState, useReducer, useCallback, useMemo } from 'react'; import createContainer from 'constate'; import { pick, throttle } from 'lodash'; import { TimeKey, timeKeyIsBetween } from '../../../../common/time'; -import { LogEntriesResponse, LogEntry, LogEntriesRequest } from '../../../../common/http_api'; +import { + LogEntriesResponse, + LogEntry, + LogEntriesRequest, + LogEntriesCenteredRequest, + LogEntriesBeforeRequest, + LogEntriesAfterRequest, +} from '../../../../common/http_api'; import { fetchLogEntries } from './api/fetch_log_entries'; import { datemathToEpochMillis } from '../../../utils/datemath'; @@ -133,20 +140,27 @@ const useFetchEntriesEffect = ( ); const runFetchNewEntriesRequest = async (override = {}) => { - if (!startTimestamp || !endTimestamp) { + if (!startTimestamp || !endTimestamp || !props.timeKey) { return; } dispatch({ type: Action.FetchingNewEntries }); try { - const { data: payload } = await fetchLogEntries({ + const fetchArgs: LogEntriesRequest = { sourceId: props.sourceId, startDate: startTimestamp, endDate: endTimestamp, - before: 'last', // TODO distinguish between first load and position-load query: props.filterQuery || undefined, // FIXME - }); + }; + + if (props.timeKey) { + (fetchArgs as LogEntriesCenteredRequest).center = props.timeKey; + } else { + (fetchArgs as LogEntriesBeforeRequest).before = 'last'; + } + + const { data: payload } = await fetchLogEntries(fetchArgs); dispatch({ type: Action.ReceiveNewEntries, payload }); } catch (e) { dispatch({ type: Action.ErrorOnNewEntries }); @@ -171,9 +185,9 @@ const useFetchEntriesEffect = ( }; if (getEntriesBefore) { - fetchArgs.before = state.topCursor; + (fetchArgs as LogEntriesBeforeRequest).before = state.topCursor; } else { - fetchArgs.after = state.bottomCursor; + (fetchArgs as LogEntriesAfterRequest).after = state.bottomCursor; } const { data: payload } = await fetchLogEntries(fetchArgs); From cc62bf85edde8f5f0cd8734e5f877bdcccf7fa6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Mon, 13 Jan 2020 13:48:45 +0100 Subject: [PATCH 016/117] Adjust logic to fetch new entries --- .../infra/public/containers/logs/log_entries/index.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts index 3a11b8ac1a8245..2e47fe6247c25d 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts @@ -96,13 +96,12 @@ const shouldFetchNewEntries = ({ prevParams, timeKey, filterQuery, - entriesStart, - entriesEnd, + topCursor, + bottomCursor, }: FetchEntriesParams & LogEntriesStateParams & { prevParams: FetchEntriesParams }) => { - if (!timeKey) return false; const shouldLoadWithNewFilter = filterQuery !== prevParams.filterQuery; const shouldLoadAroundNewPosition = - !entriesStart || !entriesEnd || !timeKeyIsBetween(entriesStart, entriesEnd, timeKey); + timeKey && (!topCursor || !bottomCursor || !timeKeyIsBetween(topCursor, bottomCursor, timeKey)); return shouldLoadWithNewFilter || shouldLoadAroundNewPosition; }; From e66215a53af30c2174905d41d15317277f0f51ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Mon, 13 Jan 2020 13:49:32 +0100 Subject: [PATCH 017/117] Clean state when fetching new entries --- .../plugins/infra/public/containers/logs/log_entries/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts index 2e47fe6247c25d..0a1393a0b14d2a 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts @@ -324,7 +324,7 @@ const logEntriesStateReducer = (prevState: LogEntriesStateParams, action: Action return { ...prevState, ...update }; } case Action.FetchingNewEntries: - return { ...prevState, isReloading: true }; + return { ...prevState, isReloading: true, entries: [], topCursor: null, bottomCursor: null }; case Action.FetchingMoreEntries: return { ...prevState, isLoadingMore: true }; case Action.ErrorOnNewEntries: From db1df11ad912603f0aca47ff7d4ddcc3ba3bc347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Mon, 13 Jan 2020 14:57:10 +0100 Subject: [PATCH 018/117] Handle empty responses in entries API --- .../common/http_api/log_entries/entries.ts | 4 +-- .../server/routes/log_entries/entries.ts | 6 ++-- .../api_integration/apis/infra/log_entries.ts | 30 +++++++++++++++++-- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts b/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts index 50511867af5d04..e99ec5e8521dac 100644 --- a/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts +++ b/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts @@ -93,8 +93,8 @@ export type LogEntry = rt.TypeOf; export const logEntriesResponseRT = rt.type({ data: rt.type({ entries: rt.array(logEntryRT), - topCursor: logEntriesCursorRT, - bottomCursor: logEntriesCursorRT, + topCursor: rt.union([logEntriesCursorRT, rt.null]), + bottomCursor: rt.union([logEntriesCursorRT, rt.null]), }), }); diff --git a/x-pack/legacy/plugins/infra/server/routes/log_entries/entries.ts b/x-pack/legacy/plugins/infra/server/routes/log_entries/entries.ts index 361535886ab22d..618e496982ed3f 100644 --- a/x-pack/legacy/plugins/infra/server/routes/log_entries/entries.ts +++ b/x-pack/legacy/plugins/infra/server/routes/log_entries/entries.ts @@ -66,12 +66,14 @@ export const initLogEntriesRoute = ({ framework, logEntries }: InfraBackendLibs) }); } + const hasEntries = entries.length > 0; + return response.ok({ body: logEntriesResponseRT.encode({ data: { entries, - topCursor: entries[0].cursor, - bottomCursor: entries[entries.length - 1].cursor, + topCursor: hasEntries ? entries[0].cursor : null, + bottomCursor: hasEntries ? entries[entries.length - 1].cursor : null, }, }), }); diff --git a/x-pack/test/api_integration/apis/infra/log_entries.ts b/x-pack/test/api_integration/apis/infra/log_entries.ts index 8db1426a219d4e..8f0b0f14f7eb7d 100644 --- a/x-pack/test/api_integration/apis/infra/log_entries.ts +++ b/x-pack/test/api_integration/apis/infra/log_entries.ts @@ -182,7 +182,7 @@ export default function({ getService }: FtrProviderContext) { sourceId: 'default', startDate: EARLIEST_KEY_WITH_DATA.time, endDate: KEY_WITHIN_DATA_RANGE.time, - after: firstPage.data.bottomCursor, + after: firstPage.data.bottomCursor!, size: 10, }) ); @@ -242,7 +242,7 @@ export default function({ getService }: FtrProviderContext) { sourceId: 'default', startDate: KEY_WITHIN_DATA_RANGE.time, endDate: LATEST_KEY_WITH_DATA.time, - before: lastPage.data.topCursor, + before: lastPage.data.topCursor!, size: 10, }) ); @@ -303,6 +303,32 @@ export default function({ getService }: FtrProviderContext) { expect(firstEntry.cursor.time >= EARLIEST_KEY_WITH_DATA.time).to.be(true); expect(lastEntry.cursor.time <= LATEST_KEY_WITH_DATA.time).to.be(true); }); + + it('Handles empty responses', async () => { + const startDate = Date.now() + 1000; + const endDate = Date.now() + 5000; + + const { body } = await supertest + .post(LOG_ENTRIES_PATH) + .set(COMMON_HEADERS) + .send( + logEntriesRequestRT.encode({ + sourceId: 'default', + startDate, + endDate, + }) + ) + .expect(200); + + const logEntriesResponse = pipe( + logEntriesResponseRT.decode(body), + fold(throwErrors(createPlainError), identity) + ); + + expect(logEntriesResponse.data.entries).to.have.length(0); + expect(logEntriesResponse.data.topCursor).to.be(null); + expect(logEntriesResponse.data.bottomCursor).to.be(null); + }); }); }); From f48cbfc9727ef092481cecd90aae515d9a02b1ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Mon, 13 Jan 2020 16:03:58 +0100 Subject: [PATCH 019/117] Expose start and end timestamps from LogPositionState This ensures that both the summary and the entries have consistent timestamps. --- .../containers/logs/log_entries/index.ts | 30 +++++++------------ .../logs/log_position/log_position_state.ts | 10 +++++++ .../logs/log_summary/with_summary.ts | 8 ++--- .../pages/logs/stream/page_providers.tsx | 8 ++--- 4 files changed, 26 insertions(+), 30 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts index 0a1393a0b14d2a..0d485314e1b319 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts @@ -3,7 +3,7 @@ * 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, useState, useReducer, useCallback, useMemo } from 'react'; +import { useEffect, useState, useReducer, useCallback } from 'react'; import createContainer from 'constate'; import { pick, throttle } from 'lodash'; import { TimeKey, timeKeyIsBetween } from '../../../../common/time'; @@ -16,7 +16,6 @@ import { LogEntriesAfterRequest, } from '../../../../common/http_api'; import { fetchLogEntries } from './api/fetch_log_entries'; -import { datemathToEpochMillis } from '../../../utils/datemath'; const DESIRED_BUFFER_PAGES = 2; @@ -47,8 +46,8 @@ type ActionObj = ReceiveEntriesAction | FetchOrErrorAction; type Dispatch = (action: ActionObj) => void; interface LogEntriesProps { - startDate: string | null; - endDate: string | null; + startTimestamp: number | null; + endTimestamp: number | null; filterQuery: string | null; timeKey: TimeKey | null; pagesBeforeStart: number | null; @@ -129,17 +128,8 @@ const useFetchEntriesEffect = ( const [prevParams, cachePrevParams] = useState(props); const [startedStreaming, setStartedStreaming] = useState(false); - const startTimestamp = useMemo( - () => (props.startDate ? datemathToEpochMillis(props.startDate) : null), - [props.startDate] - ); - const endTimestamp = useMemo( - () => (props.endDate ? datemathToEpochMillis(props.endDate) : null), - [props.endDate] - ); - const runFetchNewEntriesRequest = async (override = {}) => { - if (!startTimestamp || !endTimestamp || !props.timeKey) { + if (!props.startTimestamp || !props.endTimestamp || !props.timeKey) { return; } @@ -148,8 +138,8 @@ const useFetchEntriesEffect = ( try { const fetchArgs: LogEntriesRequest = { sourceId: props.sourceId, - startDate: startTimestamp, - endDate: endTimestamp, + startDate: props.startTimestamp, + endDate: props.endTimestamp, query: props.filterQuery || undefined, // FIXME }; @@ -167,7 +157,7 @@ const useFetchEntriesEffect = ( }; const runFetchMoreEntriesRequest = async (direction: ShouldFetchMoreEntries) => { - if (!startTimestamp || !endTimestamp) { + if (!props.startTimestamp || !props.endTimestamp) { return; } @@ -178,8 +168,8 @@ const useFetchEntriesEffect = ( try { const fetchArgs: LogEntriesRequest = { sourceId: props.sourceId, - startDate: startTimestamp, - endDate: endTimestamp, + startDate: props.startTimestamp, + endDate: props.endTimestamp, query: props.filterQuery || undefined, // FIXME }; @@ -203,7 +193,7 @@ const useFetchEntriesEffect = ( }; const fetchNewEntriesEffectDependencies = Object.values( - pick(props, ['sourceId', 'filterQuery', 'timeKey', 'startDate', 'endDate']) + pick(props, ['sourceId', 'filterQuery', 'timeKey', 'startTimestamp', 'endTimestamp']) ); const fetchNewEntriesEffect = () => { if (props.isAutoReloading) return; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts index 79e7fe56814614..93296a988d3bd4 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts @@ -7,6 +7,7 @@ import { useState, useMemo, useEffect, useCallback } from 'react'; import createContainer from 'constate'; import { TimeKey } from '../../../../common/time'; +import { datemathToEpochMillis } from '../../../utils/datemath'; type TimeKeyOrNull = TimeKey | null; @@ -34,6 +35,8 @@ export interface LogPositionStateParams { visibleTimeInterval: { start: number; end: number } | null; startDate: string; endDate: string; + startTimestamp: number | null; + endTimestamp: number | null; } export interface LogPositionCallbacks { @@ -117,6 +120,11 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall [dateRange] ); + const [startTimestamp, endTimestamp] = useMemo( + () => [datemathToEpochMillis(dateRange.startDate), datemathToEpochMillis(dateRange.endDate)], + [dateRange] + ); + const state = { targetPosition, isAutoReloading, @@ -127,6 +135,8 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall visibleMidpointTime: visibleMidpoint ? visibleMidpoint.time : null, visibleTimeInterval, ...dateRange, + startTimestamp, + endTimestamp, }; const callbacks = { 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 4b3a28c436b5f0..32940928c5d8c1 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 @@ -4,14 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { useContext, useMemo } from 'react'; +import { useContext } from 'react'; import { RendererFunction } from '../../../utils/typed_react'; import { Source } from '../../source'; import { LogSummaryBuckets, useLogSummary } from './log_summary'; import { LogFilterState } from '../log_filter'; import { LogPositionState } from '../log_position'; -import { datemathToEpochMillis } from '../../../utils/datemath'; export const WithSummary = ({ children, @@ -24,10 +23,7 @@ export const WithSummary = ({ }) => { const { sourceId } = useContext(Source.Context); const { filterQuery } = useContext(LogFilterState.Context); - const { startDate, endDate } = useContext(LogPositionState.Context); - - const startTimestamp = useMemo(() => datemathToEpochMillis(startDate), [startDate]); - const endTimestamp = useMemo(() => datemathToEpochMillis(endDate), [endDate]); + const { startTimestamp, endTimestamp } = useContext(LogPositionState.Context); const { buckets, start, end } = useLogSummary( sourceId, diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx index 06023ee5c9c13f..88c19b1732b7ad 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx @@ -29,8 +29,8 @@ const LogFilterStateProvider: React.FC = ({ children }) => { const LogEntriesStateProvider: React.FC = ({ children }) => { const { sourceId } = useContext(Source.Context); const { - startDate, - endDate, + startTimestamp, + endTimestamp, targetPosition, pagesBeforeStart, pagesAfterEnd, @@ -40,8 +40,8 @@ const LogEntriesStateProvider: React.FC = ({ children }) => { const { filterQuery } = useContext(LogFilterState.Context); const entriesProps = { - startDate, - endDate, + startTimestamp, + endTimestamp, timeKey: targetPosition, pagesBeforeStart, pagesAfterEnd, From ba1fb9a3419595f5a588ba32708f15df929d9449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Mon, 13 Jan 2020 16:16:44 +0100 Subject: [PATCH 020/117] Ensure entries fetch when changing dates. --- .../infra/public/containers/logs/log_entries/index.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts index 0d485314e1b319..9e8a8e5e1566ec 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts @@ -97,11 +97,16 @@ const shouldFetchNewEntries = ({ filterQuery, topCursor, bottomCursor, + startTimestamp, + endTimestamp, }: FetchEntriesParams & LogEntriesStateParams & { prevParams: FetchEntriesParams }) => { + const shouldLoadWithNewTimestamps = + startTimestamp !== prevParams.startTimestamp || endTimestamp !== prevParams.endTimestamp; + const shouldLoadWithNewFilter = filterQuery !== prevParams.filterQuery; const shouldLoadAroundNewPosition = timeKey && (!topCursor || !bottomCursor || !timeKeyIsBetween(topCursor, bottomCursor, timeKey)); - return shouldLoadWithNewFilter || shouldLoadAroundNewPosition; + return shouldLoadWithNewTimestamps || shouldLoadWithNewFilter || shouldLoadAroundNewPosition; }; enum ShouldFetchMoreEntries { From 5ceaf05b856f6688a8a0242bffac3d49d6e905e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Tue, 14 Jan 2020 15:28:33 +0100 Subject: [PATCH 021/117] Restore scroll pagination functionality --- .../public/containers/logs/log_entries/index.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts index 9e8a8e5e1566ec..bdfd9ff8f1bf08 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts @@ -67,6 +67,8 @@ export interface LogEntriesStateParams { isReloading: boolean; isLoadingMore: boolean; lastLoadedTime: Date | null; + hasMoreBeforeStart: boolean; + hasMoreAfterEnd: boolean; } export interface LogEntriesCallbacks { @@ -84,6 +86,8 @@ export const logEntriesInitialState: LogEntriesStateParams = { isReloading: true, isLoadingMore: false, lastLoadedTime: null, + hasMoreBeforeStart: false, + hasMoreAfterEnd: false, }; const cleanDuplicateItems = (entriesA: LogEntry[], entriesB: LogEntry[]) => { @@ -291,6 +295,11 @@ const logEntriesStateReducer = (prevState: LogEntriesStateParams, action: Action entries: action.payload.entries, lastLoadedTime: new Date(), isReloading: false, + + // Be optimistic. If any of the before/after requests comes empty, set + // the corresponding flag to `false` + hasMoreBeforeStart: true, + hasMoreAfterEnd: true, }; case Action.ReceiveEntriesBefore: { const newEntries = action.payload.entries; @@ -299,7 +308,9 @@ const logEntriesStateReducer = (prevState: LogEntriesStateParams, action: Action const update = { entries: [...newEntries, ...prevEntries], isLoadingMore: false, - topCursor: action.payload.topCursor, + hasMoreBeforeStart: newEntries.length > 0, + // Keep the previous cursor if request comes empty, to easily extend the range. + topCursor: newEntries.length > 0 ? action.payload.topCursor : prevState.topCursor, lastLoadedTime: new Date(), }; @@ -312,7 +323,9 @@ const logEntriesStateReducer = (prevState: LogEntriesStateParams, action: Action const update = { entries: [...prevEntries, ...newEntries], isLoadingMore: false, - bottomCursor: action.payload.bottomCursor, + hasMoreAfterEnd: newEntries.length > 0, + // Keep the previous cursor if request comes empty, to easily extend the range. + bottomCursor: newEntries.length > 0 ? action.payload.bottomCursor : prevState.bottomCursor, lastLoadedTime: new Date(), }; From c4d2748c9fbdc3b766d6347486865a7b86664c71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Tue, 14 Jan 2020 16:30:23 +0100 Subject: [PATCH 022/117] Tweak types --- .../public/containers/logs/log_entries/index.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts index bdfd9ff8f1bf08..be8fc6aacee7a7 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts @@ -169,10 +169,16 @@ const useFetchEntriesEffect = ( if (!props.startTimestamp || !props.endTimestamp) { return; } + const getEntriesBefore = direction === ShouldFetchMoreEntries.Before; - dispatch({ type: Action.FetchingMoreEntries }); + // Control cursors are correct + if (getEntriesBefore && !state.topCursor) { + return; + } else if (!state.bottomCursor) { + return; + } - const getEntriesBefore = direction === ShouldFetchMoreEntries.Before; + dispatch({ type: Action.FetchingMoreEntries }); try { const fetchArgs: LogEntriesRequest = { @@ -183,7 +189,7 @@ const useFetchEntriesEffect = ( }; if (getEntriesBefore) { - (fetchArgs as LogEntriesBeforeRequest).before = state.topCursor; + (fetchArgs as LogEntriesBeforeRequest).before = state.topCursor!; // We check for nullity above already } else { (fetchArgs as LogEntriesAfterRequest).after = state.bottomCursor; } From e1760b0e0828765b9e245da3ed5974a4851c2775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 15 Jan 2020 10:53:35 +0100 Subject: [PATCH 023/117] Scaffold new loading indicators Show a revised version of the loading indicators, with CTAs to extend the date range or start streaming. The CTAs don't work yet. --- .../log_text_stream/loading_item_view.tsx | 218 +++++++++--------- .../log_text_stream/log_text_separator.tsx | 21 ++ .../scrollable_log_text_stream_view.tsx | 19 +- 3 files changed, 145 insertions(+), 113 deletions(-) create mode 100644 x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_text_separator.tsx diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx index 4cefbea7225ecf..d9a547b40152a3 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx @@ -6,20 +6,34 @@ /* eslint-disable max-classes-per-file */ -import { EuiButtonEmpty, EuiIcon, EuiProgress, EuiText } from '@elastic/eui'; -import { FormattedMessage, FormattedRelative } from '@kbn/i18n/react'; +import { + EuiText, + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiLoadingSpinner, + EuiButton, +} from '@elastic/eui'; +import { FormattedMessage, FormattedTime } from '@kbn/i18n/react'; import * as React from 'react'; import euiStyled from '../../../../../../common/eui_styled_components'; +import { LogTextSeparator } from './log_text_separator'; + +type Position = 'start' | 'end'; interface LogTextStreamLoadingItemViewProps { - alignment: 'top' | 'bottom'; + position: Position; + /** topCursor.time || bottomCursor.time */ + timestamp?: number; + /** startDate || endDate */ + rangeEdge?: string; className?: string; hasMore: boolean; isLoading: boolean; isStreaming: boolean; - lastStreamingUpdate: Date | null; - onLoadMore?: () => void; + onExtendRange?: () => void; + onStreamStart?: () => void; } export class LogTextStreamLoadingItemView extends React.PureComponent< @@ -28,122 +42,110 @@ export class LogTextStreamLoadingItemView extends React.PureComponent< > { public render() { const { - alignment, + position, + timestamp, + rangeEdge, className, hasMore, isLoading, isStreaming, - lastStreamingUpdate, - onLoadMore, + onExtendRange, + onStreamStart, } = this.props; - if (isStreaming) { - return ( - - - - - - - {lastStreamingUpdate ? ( - - - - - ), - }} - /> - - - ) : null} - - ); - } else if (isLoading) { - return ( - - - - - - ); - } else if (!hasMore) { - return ( - - - - - {onLoadMore ? ( - - - - ) : null} - - ); - } else { - return null; - } + const shouldShowCta = !hasMore && !isStreaming; + + const extra = ( + + {isLoading || isStreaming ? ( + + ) : shouldShowCta ? ( + + ) : null} + + ); + + return ( + + {position === 'start' ? <>{extra} : null} + + {position === 'end' ? <>{extra} : null} + + ); } } -interface ProgressEntryProps { - alignment: 'top' | 'bottom'; - className?: string; - color: 'subdued' | 'primary'; - isLoading: boolean; -} +const ProgressEntryWrapper = euiStyled.div<{ position: Position }>` + padding-left: ${props => props.theme.eui.euiSizeS}; + padding-top: ${props => + props.position === 'start' ? props.theme.eui.euiSizeL : props.theme.eui.euiSizeM}; + padding-bottom: ${props => + props.position === 'end' ? props.theme.eui.euiSizeL : props.theme.eui.euiSizeM}; +`; + +const ProgressMessage: React.FC<{ timestamp?: number }> = ({ timestamp }) => { + return ( + + + {timestamp ? ( + }} + /> + ) : ( + + )} + + + ); +}; + +const ProgressSpinner: React.FC = () => ( + <> + + + + + + + + + +); -const ProgressEntry: React.FC = props => { - const { alignment, children, className, color, isLoading } = props; +const ProgressCta: React.FC> = ({ position, rangeEdge, onExtendRange, onStreamStart }) => { + if (rangeEdge === 'now' && position === 'end') { + return ( + + + + ); + } - // NOTE: styled-components seems to make all props in EuiProgress required, so this - // style attribute hacking replaces styled-components here for now until that can be fixed - // see: https://github.com/elastic/eui/issues/1655 - const alignmentStyle = - alignment === 'top' ? { top: 0, bottom: 'initial' } : { top: 'initial', bottom: 0 }; + const iconType = position === 'start' ? 'arrowUp' : 'arrowDown'; return ( - - + - {children} - + ); }; - -const ProgressEntryWrapper = euiStyled.div` - align-items: center; - display: flex; - min-height: ${props => props.theme.eui.euiSizeXXL}; - position: relative; -`; - -const ProgressMessage = euiStyled.div` - padding: 8px 16px; -`; diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_text_separator.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_text_separator.tsx new file mode 100644 index 00000000000000..9cc91fa11e4edf --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_text_separator.tsx @@ -0,0 +1,21 @@ +/* + * 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 React from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui'; + +/** + * Create a separator with a text on the right side + */ +export const LogTextSeparator: React.FC = ({ children }) => { + return ( + + {children} + + + + + ); +}; diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx index 4ed05fb7d57a99..94feafe26270e9 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx @@ -189,11 +189,14 @@ export class ScrollableLogTextStreamView extends React.PureComponent< {registerChild => ( <> 0 ? items[0].logEntry.cursor.time : undefined + } isStreaming={false} - lastStreamingUpdate={null} + // onExtendRange={(...args) => console.log('start.extendRange', args)} /> {items.map((item, idx) => { const currentTimestamp = item.logEntry.cursor.time; @@ -237,12 +240,18 @@ export class ScrollableLogTextStreamView extends React.PureComponent< ); })} 0 + ? items[items.length - 1].logEntry.cursor.time + : undefined + } + // onExtendRange={(...args) => console.log('end.extendRange', args)} + // onStreamStart={(...args) => console.log('end.streamStart', args)} + // onLoadMore={this.handleLoadNewerItems} /> {isScrollLocked && ( Date: Wed, 15 Jan 2020 11:17:41 +0100 Subject: [PATCH 024/117] Add tests for datemath utils --- .../infra/public/utils/datemath.test.ts | 48 +++++++++++++++++++ .../plugins/infra/public/utils/datemath.ts | 2 +- 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 x-pack/legacy/plugins/infra/public/utils/datemath.test.ts diff --git a/x-pack/legacy/plugins/infra/public/utils/datemath.test.ts b/x-pack/legacy/plugins/infra/public/utils/datemath.test.ts new file mode 100644 index 00000000000000..4af436d74e0907 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/utils/datemath.test.ts @@ -0,0 +1,48 @@ +/* + * 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 { isValidDatemath, datemathToEpochMillis } from './datemath'; +import sinon from 'sinon'; + +describe('isValidDatemath()', () => { + it('Returns `false` for empty strings', () => { + expect(isValidDatemath('')).toBe(false); + }); + + it('Returns `false` for invalid strings', () => { + expect(isValidDatemath('wadus')).toBe(false); + expect(isValidDatemath('nowww-')).toBe(false); + expect(isValidDatemath('now-')).toBe(false); + expect(isValidDatemath('now-1')).toBe(false); + expect(isValidDatemath('now-1d/')).toBe(false); + }); + + it('Returns `true` for valid strings', () => { + expect(isValidDatemath('now')).toBe(true); + expect(isValidDatemath('now-1d')).toBe(true); + expect(isValidDatemath('now-1d/d')).toBe(true); + }); +}); + +describe('datemathToEpochMillis()', () => { + let clock: sinon.SinonFakeTimers; + + beforeEach(() => { + clock = sinon.useFakeTimers(Date.now()); + }); + + afterEach(() => { + clock.restore(); + }); + + it('Returns `0` for the dawn of time', () => { + expect(datemathToEpochMillis('1970-01-01T00:00:00+00:00')).toEqual(0); + }); + + it('Returns the current timestamp when `now`', () => { + expect(datemathToEpochMillis('now')).toEqual(Date.now()); + }); +}); diff --git a/x-pack/legacy/plugins/infra/public/utils/datemath.ts b/x-pack/legacy/plugins/infra/public/utils/datemath.ts index 36172515e31947..31ea83f4d57ed2 100644 --- a/x-pack/legacy/plugins/infra/public/utils/datemath.ts +++ b/x-pack/legacy/plugins/infra/public/utils/datemath.ts @@ -16,5 +16,5 @@ export function datemathToEpochMillis(value: string): number | null { if (!parsedValue || !parsedValue.isValid()) { return null; } - return parsedValue.unix() * 1000; + return parsedValue.valueOf(); } From d5eb7e54b98d955853ece059a6105cd5d7d329c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 15 Jan 2020 17:57:50 +0100 Subject: [PATCH 025/117] Implement `extendDatemath()` The function takes a dateMath expression and extends it in the specified direction. This is useful to extend the `startDate` and `endDate` when the user reaches the first or last pages respectively and wants to see more log entries. --- .../infra/public/utils/datemath.test.ts | 291 +++++++++++++++++- .../plugins/infra/public/utils/datemath.ts | 189 +++++++++++- 2 files changed, 478 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/utils/datemath.test.ts b/x-pack/legacy/plugins/infra/public/utils/datemath.test.ts index 4af436d74e0907..ed795728c57fdd 100644 --- a/x-pack/legacy/plugins/infra/public/utils/datemath.test.ts +++ b/x-pack/legacy/plugins/infra/public/utils/datemath.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { isValidDatemath, datemathToEpochMillis } from './datemath'; +import { isValidDatemath, datemathToEpochMillis, extendDatemath, convertDate } from './datemath'; import sinon from 'sinon'; describe('isValidDatemath()', () => { @@ -46,3 +46,292 @@ describe('datemathToEpochMillis()', () => { expect(datemathToEpochMillis('now')).toEqual(Date.now()); }); }); + +describe('extendDatemath()', () => { + it('Returns `undefined` for invalid values', () => { + expect(extendDatemath('')).toBeUndefined(); + }); + + it('Keeps `"now"` stable', () => { + expect(extendDatemath('now')).toEqual({ value: 'now' }); + expect(extendDatemath('now', 'before')).toEqual({ value: 'now' }); + expect(extendDatemath('now', 'after')).toEqual({ value: 'now' }); + }); + + describe('moving before', () => { + describe('with a negative operator', () => { + it('doubles miliseconds', () => { + expect(extendDatemath('now-250ms')).toEqual({ + value: 'now-500ms', + diffAmount: 250, + diffUnit: 'ms', + }); + }); + + it('doubles seconds', () => { + expect(extendDatemath('now-10s')).toEqual({ + value: 'now-20s', + diffAmount: 10, + diffUnit: 's', + }); + }); + + it('doubles minutes when amount is low', () => { + expect(extendDatemath('now-1m')).toEqual({ value: 'now-2m', diffAmount: 1, diffUnit: 'm' }); + expect(extendDatemath('now-2m')).toEqual({ value: 'now-4m', diffAmount: 2, diffUnit: 'm' }); + expect(extendDatemath('now-3m')).toEqual({ value: 'now-6m', diffAmount: 3, diffUnit: 'm' }); + }); + + it('adds half the minutes when the amount is high', () => { + expect(extendDatemath('now-20m')).toEqual({ + value: 'now-30m', + diffAmount: 10, + diffUnit: 'm', + }); + }); + + it('Adds half an hour when the amount is one hour', () => { + expect(extendDatemath('now-1h')).toEqual({ + value: 'now-90m', + diffAmount: 30, + diffUnit: 'm', + }); + }); + + it('Adds one hour when the amount more than one hour', () => { + expect(extendDatemath('now-2h')).toEqual({ + value: 'now-3h', + diffAmount: 1, + diffUnit: 'h', + }); + }); + + it('Adds one hour when the amount is one day', () => { + expect(extendDatemath('now-1d')).toEqual({ + value: 'now-25h', + diffAmount: 1, + diffUnit: 'h', + }); + }); + + it('Adds one day when the amount is more than one day', () => { + expect(extendDatemath('now-2d')).toEqual({ + value: 'now-3d', + diffAmount: 1, + diffUnit: 'd', + }); + expect(extendDatemath('now-3d')).toEqual({ + value: 'now-4d', + diffAmount: 1, + diffUnit: 'd', + }); + }); + + it('Adds one day when the amount is one week', () => { + expect(extendDatemath('now-1w')).toEqual({ + value: 'now-8d', + diffAmount: 1, + diffUnit: 'd', + }); + }); + + it('Adds one week when the amount is more than one week', () => { + expect(extendDatemath('now-2w')).toEqual({ + value: 'now-3w', + diffAmount: 1, + diffUnit: 'w', + }); + }); + + it('Adds one week when the amount is one month', () => { + expect(extendDatemath('now-1M')).toEqual({ + value: 'now-5w', + diffAmount: 1, + diffUnit: 'w', + }); + }); + + it('Adds one month when the amount is more than one month', () => { + expect(extendDatemath('now-2M')).toEqual({ + value: 'now-3M', + diffAmount: 1, + diffUnit: 'M', + }); + }); + + it('Adds one month when the amount is one year', () => { + expect(extendDatemath('now-1y')).toEqual({ + value: 'now-13M', + diffAmount: 1, + diffUnit: 'M', + }); + }); + + it('Adds one year when the amount is in years', () => { + expect(extendDatemath('now-2y')).toEqual({ + value: 'now-3y', + diffAmount: 1, + diffUnit: 'y', + }); + }); + }); + + describe('with a positive Operator', () => { + it('Halves miliseconds', () => { + expect(extendDatemath('now+250ms')).toEqual({ + value: 'now+125ms', + diffAmount: 125, + diffUnit: 'ms', + }); + }); + + it('Halves seconds', () => { + expect(extendDatemath('now+10s')).toEqual({ + value: 'now+5s', + diffAmount: 5, + diffUnit: 's', + }); + }); + + it('Halves minutes when the amount is low', () => { + expect(extendDatemath('now+2m')).toEqual({ value: 'now+1m', diffAmount: 1, diffUnit: 'm' }); + expect(extendDatemath('now+4m')).toEqual({ value: 'now+2m', diffAmount: 2, diffUnit: 'm' }); + expect(extendDatemath('now+6m')).toEqual({ value: 'now+3m', diffAmount: 3, diffUnit: 'm' }); + }); + + it('Decreases minutes in half ammounts when the amount is high', () => { + expect(extendDatemath('now+30m')).toEqual({ + value: 'now+20m', + diffAmount: 10, + diffUnit: 'm', + }); + }); + + it('Decreases half an hour when the amount is one hour', () => { + expect(extendDatemath('now+1h')).toEqual({ + value: 'now+30m', + diffAmount: 30, + diffUnit: 'm', + }); + }); + + it('Removes one hour when the amount is one day', () => { + expect(extendDatemath('now+1d')).toEqual({ + value: 'now+23h', + diffAmount: 1, + diffUnit: 'h', + }); + }); + + it('Removes one day when the amount is more than one day', () => { + expect(extendDatemath('now+2d')).toEqual({ + value: 'now+1d', + diffAmount: 1, + diffUnit: 'd', + }); + expect(extendDatemath('now+3d')).toEqual({ + value: 'now+2d', + diffAmount: 1, + diffUnit: 'd', + }); + }); + + it('Removes one day when the amount is one week', () => { + expect(extendDatemath('now+1w')).toEqual({ + value: 'now+6d', + diffAmount: 1, + diffUnit: 'd', + }); + }); + + it('Removes one week when the amount is more than one week', () => { + expect(extendDatemath('now+2w')).toEqual({ + value: 'now+1w', + diffAmount: 1, + diffUnit: 'w', + }); + }); + + it('Removes one week when the amount is one month', () => { + expect(extendDatemath('now+1M')).toEqual({ + value: 'now+3w', + diffAmount: 1, + diffUnit: 'w', + }); + }); + + it('Removes one month when the amount is more than one month', () => { + expect(extendDatemath('now+2M')).toEqual({ + value: 'now+1M', + diffAmount: 1, + diffUnit: 'M', + }); + }); + + it('Removes one month when the amount is one year', () => { + expect(extendDatemath('now+1y')).toEqual({ + value: 'now+11M', + diffAmount: 1, + diffUnit: 'M', + }); + }); + + it('Adds one year when the amount is in years', () => { + expect(extendDatemath('now+2y')).toEqual({ + value: 'now+1y', + diffAmount: 1, + diffUnit: 'y', + }); + }); + }); + }); +}); + +describe('convertDate()', () => { + it('returns same value if units are the same', () => { + expect(convertDate(1, 'h', 'h')).toEqual(1); + }); + + it('converts from big units to small units', () => { + expect(convertDate(1, 's', 'ms')).toEqual(1000); + expect(convertDate(1, 'm', 'ms')).toEqual(60000); + expect(convertDate(1, 'h', 'ms')).toEqual(3600000); + expect(convertDate(1, 'd', 'ms')).toEqual(86400000); + expect(convertDate(1, 'M', 'ms')).toEqual(2592000000); + expect(convertDate(1, 'y', 'ms')).toEqual(31536000000); + }); + + it('converts from small units to big units', () => { + expect(convertDate(1000, 'ms', 's')).toEqual(1); + expect(convertDate(60000, 'ms', 'm')).toEqual(1); + expect(convertDate(3600000, 'ms', 'h')).toEqual(1); + expect(convertDate(86400000, 'ms', 'd')).toEqual(1); + expect(convertDate(2592000000, 'ms', 'M')).toEqual(1); + expect(convertDate(31536000000, 'ms', 'y')).toEqual(1); + }); + + it('Handles days to years', () => { + expect(convertDate(1, 'y', 'd')).toEqual(365); + expect(convertDate(365, 'd', 'y')).toEqual(1); + }); + + it('Handles years to months', () => { + expect(convertDate(1, 'y', 'M')).toEqual(12); + expect(convertDate(12, 'M', 'y')).toEqual(1); + }); + + it('Handles days to months', () => { + expect(convertDate(1, 'M', 'd')).toEqual(30); + expect(convertDate(30, 'd', 'M')).toEqual(1); + }); + + it('Handles days to weeks', () => { + expect(convertDate(1, 'w', 'd')).toEqual(7); + expect(convertDate(7, 'd', 'w')).toEqual(1); + }); + + it('Handles weeks to years', () => { + expect(convertDate(1, 'y', 'w')).toEqual(52); + expect(convertDate(52, 'w', 'y')).toEqual(1); + }); +}); diff --git a/x-pack/legacy/plugins/infra/public/utils/datemath.ts b/x-pack/legacy/plugins/infra/public/utils/datemath.ts index 31ea83f4d57ed2..eec7cfc32e3ecd 100644 --- a/x-pack/legacy/plugins/infra/public/utils/datemath.ts +++ b/x-pack/legacy/plugins/infra/public/utils/datemath.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import dateMath from '@elastic/datemath'; +import dateMath, { Unit } from '@elastic/datemath'; export function isValidDatemath(value: string): boolean { const parsedValue = dateMath.parse(value); @@ -18,3 +18,190 @@ export function datemathToEpochMillis(value: string): number | null { } return parsedValue.valueOf(); } + +type DatemathExtension = + | { + value: string; + diffUnit: Unit; + diffAmount: number; + } + | { value: 'now' } + | undefined; + +const datemathNowExpression = /(\+|\-)(\d+)(ms|s|m|h|d|w|M|y)$/; + +export function extendDatemath( + value: string, + direction: 'before' | 'after' = 'before' +): DatemathExtension { + if (!isValidDatemath(value)) { + return undefined; + } + + if (value === 'now') { + return { value: 'now' }; + } + + if (value.startsWith('now')) { + const [, operator, amount, unit] = datemathNowExpression.exec(value) || []; + if (!operator || !amount || !unit) { + return undefined; + } + + const parsedAmount = parseInt(amount, 10); + let newUnit: Unit = unit as Unit; + let newAmount: number; + + switch (unit) { + case 'ms': + case 's': + newAmount = + operator === '-' && direction === 'before' + ? parsedAmount * 2 + : Math.floor(parsedAmount / 2); + break; + case 'm': + let ratio; + if (operator === '-' && direction === 'before') { + ratio = parsedAmount >= 10 ? 0.5 : 1; + newAmount = parsedAmount + parsedAmount * ratio; + } else { + newAmount = + parsedAmount >= 10 ? Math.floor(parsedAmount / 1.5) : parsedAmount - parsedAmount * 0.5; + } + break; + case 'h': + if (parsedAmount === 1) { + newAmount = operator === '-' && direction === 'before' ? 90 : 30; + newUnit = 'm'; + } else { + newAmount = + operator === '-' && direction === 'before' ? parsedAmount + 1 : parsedAmount - 1; + } + break; + case 'd': + if (parsedAmount === 1) { + newAmount = operator === '-' && direction === 'before' ? 25 : 23; + newUnit = 'h'; + } else { + newAmount = + operator === '-' && direction === 'before' ? parsedAmount + 1 : parsedAmount - 1; + } + break; + + case 'w': + if (parsedAmount === 1) { + newAmount = operator === '-' && direction === 'before' ? 8 : 6; + newUnit = 'd'; + } else { + newAmount = + operator === '-' && direction === 'before' ? parsedAmount + 1 : parsedAmount - 1; + } + break; + + case 'M': + if (parsedAmount === 1) { + newAmount = operator === '-' && direction === 'before' ? 5 : 3; + newUnit = 'w'; + } else { + newAmount = + operator === '-' && direction === 'before' ? parsedAmount + 1 : parsedAmount - 1; + } + break; + + case 'y': + if (parsedAmount === 1) { + newAmount = operator === '-' && direction === 'before' ? 13 : 11; + newUnit = 'M'; + } else { + newAmount = + operator === '-' && direction === 'before' ? parsedAmount + 1 : parsedAmount - 1; + } + break; + + default: + throw new TypeError('Unhandled datemath unit'); + } + + return { + value: `now${operator}${newAmount}${newUnit}`, + diffUnit: newUnit, + diffAmount: + newUnit !== unit + ? Math.abs(newAmount - convertDate(parsedAmount, unit, newUnit)) + : Math.abs(newAmount - parsedAmount), + }; + } + + return undefined; +} + +const CONVERSION_RATIOS: Record> = { + wy: [ + ['w', 52], // 1 year = 52 weeks + ['y', 1], + ], + w: [ + ['ms', 1000], + ['s', 60], + ['m', 60], + ['h', 24], + ['d', 7], // 1 week = 7 days + ['w', 4], // 1 month = 4 weeks = 28 days + ['M', 12], // 1 year = 12 months = 52 weeks = 364 days + ['y', 1], + ], + M: [ + ['ms', 1000], + ['s', 60], + ['m', 60], + ['h', 24], + ['d', 30], // 1 month = 30 days + ['M', 12], // 1 year = 12 months = 360 days + ['y', 1], + ], + default: [ + ['ms', 1000], + ['s', 60], + ['m', 60], + ['h', 24], + ['d', 365], // 1 year = 365 days + ['y', 1], + ], +}; + +export function convertDate(value: number, from: Unit, to: Unit): number { + if (from === to) { + return value; + } + + let ratios; + if ((from === 'y' && to === 'w') || (from === 'w' && to === 'y')) { + ratios = CONVERSION_RATIOS.wy; + } else if (from === 'w' || to === 'w') { + ratios = CONVERSION_RATIOS.w; + } else if (from === 'M' || to === 'M') { + ratios = CONVERSION_RATIOS.M; + } else { + ratios = CONVERSION_RATIOS.default; + } + + let convertedValue = value; + + const fromIdx = ratios.findIndex(ratio => ratio[0] === from); + const toIdx = ratios.findIndex(ratio => ratio[0] === to); + + if (fromIdx > toIdx) { + // `from` is the bigger unit. Multiply the value + for (let i = toIdx; i < fromIdx; i++) { + convertedValue *= ratios[i][1]; + } + } else { + // `from` is the smaller unit. Divide the value + for (let i = fromIdx; i < toIdx; i++) { + convertedValue /= ratios[i][1]; + } + } + + return convertedValue; +} From b1dece77a915d8f8924fe3415fe02f0fdd474be1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 15 Jan 2020 18:13:00 +0100 Subject: [PATCH 026/117] DRY conditions to increase/decrease date amounts --- .../plugins/infra/public/utils/datemath.ts | 33 ++++++++----------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/utils/datemath.ts b/x-pack/legacy/plugins/infra/public/utils/datemath.ts index eec7cfc32e3ecd..3c0dacee2a9235 100644 --- a/x-pack/legacy/plugins/infra/public/utils/datemath.ts +++ b/x-pack/legacy/plugins/infra/public/utils/datemath.ts @@ -48,6 +48,7 @@ export function extendDatemath( return undefined; } + const mustIncreaseAmount = operator === '-' && direction === 'before'; const parsedAmount = parseInt(amount, 10); let newUnit: Unit = unit as Unit; let newAmount: number; @@ -55,14 +56,11 @@ export function extendDatemath( switch (unit) { case 'ms': case 's': - newAmount = - operator === '-' && direction === 'before' - ? parsedAmount * 2 - : Math.floor(parsedAmount / 2); + newAmount = mustIncreaseAmount ? parsedAmount * 2 : Math.floor(parsedAmount / 2); break; case 'm': let ratio; - if (operator === '-' && direction === 'before') { + if (mustIncreaseAmount) { ratio = parsedAmount >= 10 ? 0.5 : 1; newAmount = parsedAmount + parsedAmount * ratio; } else { @@ -72,50 +70,45 @@ export function extendDatemath( break; case 'h': if (parsedAmount === 1) { - newAmount = operator === '-' && direction === 'before' ? 90 : 30; + newAmount = mustIncreaseAmount ? 90 : 30; newUnit = 'm'; } else { - newAmount = - operator === '-' && direction === 'before' ? parsedAmount + 1 : parsedAmount - 1; + newAmount = mustIncreaseAmount ? parsedAmount + 1 : parsedAmount - 1; } break; case 'd': if (parsedAmount === 1) { - newAmount = operator === '-' && direction === 'before' ? 25 : 23; + newAmount = mustIncreaseAmount ? 25 : 23; newUnit = 'h'; } else { - newAmount = - operator === '-' && direction === 'before' ? parsedAmount + 1 : parsedAmount - 1; + newAmount = mustIncreaseAmount ? parsedAmount + 1 : parsedAmount - 1; } break; case 'w': if (parsedAmount === 1) { - newAmount = operator === '-' && direction === 'before' ? 8 : 6; + newAmount = mustIncreaseAmount ? 8 : 6; newUnit = 'd'; } else { - newAmount = - operator === '-' && direction === 'before' ? parsedAmount + 1 : parsedAmount - 1; + newAmount = mustIncreaseAmount ? parsedAmount + 1 : parsedAmount - 1; } break; case 'M': if (parsedAmount === 1) { - newAmount = operator === '-' && direction === 'before' ? 5 : 3; + newAmount = mustIncreaseAmount ? 5 : 3; newUnit = 'w'; } else { - newAmount = - operator === '-' && direction === 'before' ? parsedAmount + 1 : parsedAmount - 1; + newAmount = mustIncreaseAmount ? parsedAmount + 1 : parsedAmount - 1; } break; case 'y': if (parsedAmount === 1) { - newAmount = operator === '-' && direction === 'before' ? 13 : 11; + newAmount = mustIncreaseAmount ? 13 : 11; newUnit = 'M'; } else { - newAmount = - operator === '-' && direction === 'before' ? parsedAmount + 1 : parsedAmount - 1; + newAmount = mustIncreaseAmount ? parsedAmount + 1 : parsedAmount - 1; } break; From cd7de34037d3786101d803f701a0420d088dd711 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 15 Jan 2020 18:49:14 +0100 Subject: [PATCH 027/117] DRY implementation of `extendDatemath` --- .../plugins/infra/public/utils/datemath.ts | 46 ++++++++----------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/utils/datemath.ts b/x-pack/legacy/plugins/infra/public/utils/datemath.ts index 3c0dacee2a9235..197ddc48227f91 100644 --- a/x-pack/legacy/plugins/infra/public/utils/datemath.ts +++ b/x-pack/legacy/plugins/infra/public/utils/datemath.ts @@ -54,20 +54,29 @@ export function extendDatemath( let newAmount: number; switch (unit) { + // For small units, always double or halve the amount case 'ms': case 's': newAmount = mustIncreaseAmount ? parsedAmount * 2 : Math.floor(parsedAmount / 2); break; + // For minutes, increase or decrease in doubles or halves, depending on + // the amount of minutes case 'm': let ratio; + const MINUTES_LARGE = 10; if (mustIncreaseAmount) { - ratio = parsedAmount >= 10 ? 0.5 : 1; + ratio = parsedAmount >= MINUTES_LARGE ? 0.5 : 1; newAmount = parsedAmount + parsedAmount * ratio; } else { newAmount = - parsedAmount >= 10 ? Math.floor(parsedAmount / 1.5) : parsedAmount - parsedAmount * 0.5; + parsedAmount >= MINUTES_LARGE + ? Math.floor(parsedAmount / 1.5) + : parsedAmount - parsedAmount * 0.5; } break; + + // For hours, increase or decrease half an hour for 1 hour. Otherwise + // increase full hours case 'h': if (parsedAmount === 1) { newAmount = mustIncreaseAmount ? 90 : 30; @@ -76,37 +85,18 @@ export function extendDatemath( newAmount = mustIncreaseAmount ? parsedAmount + 1 : parsedAmount - 1; } break; - case 'd': - if (parsedAmount === 1) { - newAmount = mustIncreaseAmount ? 25 : 23; - newUnit = 'h'; - } else { - newAmount = mustIncreaseAmount ? parsedAmount + 1 : parsedAmount - 1; - } - break; + // For the rest of units, increase or decrease one smaller unit for + // amounts of 1. Otherwise increase or decrease the unit + case 'd': case 'w': - if (parsedAmount === 1) { - newAmount = mustIncreaseAmount ? 8 : 6; - newUnit = 'd'; - } else { - newAmount = mustIncreaseAmount ? parsedAmount + 1 : parsedAmount - 1; - } - break; - case 'M': - if (parsedAmount === 1) { - newAmount = mustIncreaseAmount ? 5 : 3; - newUnit = 'w'; - } else { - newAmount = mustIncreaseAmount ? parsedAmount + 1 : parsedAmount - 1; - } - break; - case 'y': if (parsedAmount === 1) { - newAmount = mustIncreaseAmount ? 13 : 11; - newUnit = 'M'; + newUnit = dateMath.unitsDesc[dateMath.unitsDesc.indexOf(unit) + 1]; + newAmount = mustIncreaseAmount + ? convertDate(1, unit, newUnit) + 1 + : convertDate(1, unit, newUnit) - 1; } else { newAmount = mustIncreaseAmount ? parsedAmount + 1 : parsedAmount - 1; } From 40903d19af48792c8c81de9100bc96c29217e98d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 16 Jan 2020 11:41:01 +0100 Subject: [PATCH 028/117] Extend log range --- .../log_text_stream/loading_item_view.tsx | 25 ++++++++++++++++--- .../scrollable_log_text_stream_view.tsx | 12 +++++++-- .../pages/logs/stream/page_logs_content.tsx | 6 +++++ 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx index d9a547b40152a3..4d59cd9887bf94 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx @@ -19,6 +19,7 @@ import * as React from 'react'; import euiStyled from '../../../../../../common/eui_styled_components'; import { LogTextSeparator } from './log_text_separator'; +import { extendDatemath } from '../../../utils/datemath'; type Position = 'start' | 'end'; @@ -32,7 +33,7 @@ interface LogTextStreamLoadingItemViewProps { hasMore: boolean; isLoading: boolean; isStreaming: boolean; - onExtendRange?: () => void; + onExtendRange?: (newDate: string) => void; onStreamStart?: () => void; } @@ -139,12 +140,30 @@ const ProgressCta: React.FC + { + if (typeof onExtendRange === 'function') { + onExtendRange(extendedRange.value); + } + }} + iconType={iconType} + size="s" + > ); diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx index 94feafe26270e9..0aff92bea8f7eb 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx @@ -56,6 +56,9 @@ interface ScrollableLogTextStreamViewProps { setFlyoutVisibility: (visible: boolean) => void; highlightedItem: string | null; currentHighlightKey: UniqueTimeKey | null; + startDate: string; + endDate: string; + updateDateRange: (range: { startDate?: string; endDate?: string }) => void; } interface ScrollableLogTextStreamViewState { @@ -134,6 +137,9 @@ export class ScrollableLogTextStreamView extends React.PureComponent< lastLoadedTime, scale, wrap, + startDate, + endDate, + updateDateRange, } = this.props; const { targetId, items, isScrollLocked } = this.state; const hasItems = items.length > 0; @@ -196,7 +202,8 @@ export class ScrollableLogTextStreamView extends React.PureComponent< items.length > 0 ? items[0].logEntry.cursor.time : undefined } isStreaming={false} - // onExtendRange={(...args) => console.log('start.extendRange', args)} + rangeEdge={startDate} + onExtendRange={newDate => updateDateRange({ startDate: newDate })} /> {items.map((item, idx) => { const currentTimestamp = item.logEntry.cursor.time; @@ -249,7 +256,8 @@ export class ScrollableLogTextStreamView extends React.PureComponent< ? items[items.length - 1].logEntry.cursor.time : undefined } - // onExtendRange={(...args) => console.log('end.extendRange', args)} + rangeEdge={endDate} + onExtendRange={newDate => updateDateRange({ endDate: newDate })} // onStreamStart={(...args) => console.log('end.streamStart', args)} // onLoadMore={this.handleLoadNewerItems} /> 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 c085b5b5b2c05b..7506d69d1f7ead 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 @@ -51,6 +51,9 @@ export const LogsPageLogsContent: React.FunctionComponent = () => { reportVisiblePositions, jumpToTargetPosition, stopLiveStreaming, + startDate, + endDate, + updateDateRange, } = useContext(LogPositionState.Context); return ( <> @@ -104,6 +107,9 @@ export const LogsPageLogsContent: React.FunctionComponent = () => { setFlyoutVisibility={setFlyoutVisibility} highlightedItem={surroundingLogsId ? surroundingLogsId : null} currentHighlightKey={currentHighlightKey} + startDate={startDate} + endDate={endDate} + updateDateRange={updateDateRange} /> )} From 8e0773d767cc63cf343a4b254a8e8c5ef8a50fd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 16 Jan 2020 15:13:02 +0100 Subject: [PATCH 029/117] Perform unit conversion when extending time too much Prevent showing ridiculous numbers like `3000 seconds`, by converting the extended time in a bigger unit --- .../infra/public/utils/datemath.test.ts | 66 +++++++++++++++- .../plugins/infra/public/utils/datemath.ts | 78 ++++++++++++++----- 2 files changed, 122 insertions(+), 22 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/utils/datemath.test.ts b/x-pack/legacy/plugins/infra/public/utils/datemath.test.ts index ed795728c57fdd..0f272733c5f972 100644 --- a/x-pack/legacy/plugins/infra/public/utils/datemath.test.ts +++ b/x-pack/legacy/plugins/infra/public/utils/datemath.test.ts @@ -4,7 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { isValidDatemath, datemathToEpochMillis, extendDatemath, convertDate } from './datemath'; +import { + isValidDatemath, + datemathToEpochMillis, + extendDatemath, + convertDate, + normalizeDate, +} from './datemath'; import sinon from 'sinon'; describe('isValidDatemath()', () => { @@ -68,6 +74,14 @@ describe('extendDatemath()', () => { }); }); + it('normalizes miliseconds', () => { + expect(extendDatemath('now-500ms')).toEqual({ + value: 'now-1s', + diffAmount: 500, + diffUnit: 'ms', + }); + }); + it('doubles seconds', () => { expect(extendDatemath('now-10s')).toEqual({ value: 'now-20s', @@ -76,6 +90,14 @@ describe('extendDatemath()', () => { }); }); + it('normalizes seconds', () => { + expect(extendDatemath('now-30s')).toEqual({ + value: 'now-1m', + diffAmount: 30, + diffUnit: 's', + }); + }); + it('doubles minutes when amount is low', () => { expect(extendDatemath('now-1m')).toEqual({ value: 'now-2m', diffAmount: 1, diffUnit: 'm' }); expect(extendDatemath('now-2m')).toEqual({ value: 'now-4m', diffAmount: 2, diffUnit: 'm' }); @@ -335,3 +357,45 @@ describe('convertDate()', () => { expect(convertDate(52, 'w', 'y')).toEqual(1); }); }); + +describe('normalizeDate()', () => { + it('keeps units under the conversion ratio the same', () => { + expect(normalizeDate(999, 'ms')).toEqual({ amount: 999, unit: 'ms' }); + expect(normalizeDate(59, 's')).toEqual({ amount: 59, unit: 's' }); + expect(normalizeDate(59, 'm')).toEqual({ amount: 59, unit: 'm' }); + expect(normalizeDate(23, 'h')).toEqual({ amount: 23, unit: 'h' }); + expect(normalizeDate(6, 'd')).toEqual({ amount: 6, unit: 'd' }); + expect(normalizeDate(3, 'w')).toEqual({ amount: 3, unit: 'w' }); + expect(normalizeDate(11, 'M')).toEqual({ amount: 11, unit: 'M' }); + }); + + it('Moves to the next unit for values equal to the conversion ratio', () => { + expect(normalizeDate(1000, 'ms')).toEqual({ amount: 1, unit: 's' }); + expect(normalizeDate(60, 's')).toEqual({ amount: 1, unit: 'm' }); + expect(normalizeDate(60, 'm')).toEqual({ amount: 1, unit: 'h' }); + expect(normalizeDate(24, 'h')).toEqual({ amount: 1, unit: 'd' }); + expect(normalizeDate(7, 'd')).toEqual({ amount: 1, unit: 'w' }); + expect(normalizeDate(4, 'w')).toEqual({ amount: 1, unit: 'M' }); + expect(normalizeDate(12, 'M')).toEqual({ amount: 1, unit: 'y' }); + }); + + it('keeps units slightly over the conversion ratio the same', () => { + expect(normalizeDate(1001, 'ms')).toEqual({ amount: 1001, unit: 'ms' }); + expect(normalizeDate(61, 's')).toEqual({ amount: 61, unit: 's' }); + expect(normalizeDate(61, 'm')).toEqual({ amount: 61, unit: 'm' }); + expect(normalizeDate(25, 'h')).toEqual({ amount: 25, unit: 'h' }); + expect(normalizeDate(8, 'd')).toEqual({ amount: 8, unit: 'd' }); + expect(normalizeDate(5, 'w')).toEqual({ amount: 5, unit: 'w' }); + expect(normalizeDate(13, 'M')).toEqual({ amount: 13, unit: 'M' }); + }); + + it('moves to the next unit for any value higher than twice the conversion ratio', () => { + expect(normalizeDate(2001, 'ms')).toEqual({ amount: 2, unit: 's' }); + expect(normalizeDate(121, 's')).toEqual({ amount: 2, unit: 'm' }); + expect(normalizeDate(121, 'm')).toEqual({ amount: 2, unit: 'h' }); + expect(normalizeDate(49, 'h')).toEqual({ amount: 2, unit: 'd' }); + expect(normalizeDate(15, 'd')).toEqual({ amount: 2, unit: 'w' }); + expect(normalizeDate(9, 'w')).toEqual({ amount: 2, unit: 'M' }); + expect(normalizeDate(25, 'M')).toEqual({ amount: 2, unit: 'y' }); + }); +}); diff --git a/x-pack/legacy/plugins/infra/public/utils/datemath.ts b/x-pack/legacy/plugins/infra/public/utils/datemath.ts index 197ddc48227f91..fd7d7a731098cd 100644 --- a/x-pack/legacy/plugins/infra/public/utils/datemath.ts +++ b/x-pack/legacy/plugins/infra/public/utils/datemath.ts @@ -53,6 +53,7 @@ export function extendDatemath( let newUnit: Unit = unit as Unit; let newAmount: number; + // Extend the amount switch (unit) { // For small units, always double or halve the amount case 'ms': @@ -106,13 +107,18 @@ export function extendDatemath( throw new TypeError('Unhandled datemath unit'); } + // normalize amount and unit (i.e. 120s -> 2m) + const { unit: normalizedUnit, amount: normalizedAmount } = normalizeDate(newAmount, newUnit); + + // How much have we changed the time? + const diffAmount = Math.abs(normalizedAmount - convertDate(parsedAmount, unit, normalizedUnit)); + // if `diffAmount` is not an integer after normalization, express the difference in the original unit + const shouldKeepDiffUnit = diffAmount % 1 !== 0; + return { - value: `now${operator}${newAmount}${newUnit}`, - diffUnit: newUnit, - diffAmount: - newUnit !== unit - ? Math.abs(newAmount - convertDate(parsedAmount, unit, newUnit)) - : Math.abs(newAmount - parsedAmount), + value: `now${operator}${normalizedAmount}${normalizedUnit}`, + diffUnit: shouldKeepDiffUnit ? unit : newUnit, + diffAmount: shouldKeepDiffUnit ? Math.abs(newAmount - parsedAmount) : diffAmount, }; } @@ -153,38 +159,68 @@ const CONVERSION_RATIOS: Record> = { ], }; -export function convertDate(value: number, from: Unit, to: Unit): number { - if (from === to) { - return value; - } - - let ratios; +function getRatioScale(from: Unit, to?: Unit) { if ((from === 'y' && to === 'w') || (from === 'w' && to === 'y')) { - ratios = CONVERSION_RATIOS.wy; + return CONVERSION_RATIOS.wy; } else if (from === 'w' || to === 'w') { - ratios = CONVERSION_RATIOS.w; + return CONVERSION_RATIOS.w; } else if (from === 'M' || to === 'M') { - ratios = CONVERSION_RATIOS.M; + return CONVERSION_RATIOS.M; } else { - ratios = CONVERSION_RATIOS.default; + return CONVERSION_RATIOS.default; } +} - let convertedValue = value; +export function convertDate(value: number, from: Unit, to: Unit): number { + if (from === to) { + return value; + } - const fromIdx = ratios.findIndex(ratio => ratio[0] === from); - const toIdx = ratios.findIndex(ratio => ratio[0] === to); + const ratioScale = getRatioScale(from, to); + const fromIdx = ratioScale.findIndex(ratio => ratio[0] === from); + const toIdx = ratioScale.findIndex(ratio => ratio[0] === to); + + let convertedValue = value; if (fromIdx > toIdx) { // `from` is the bigger unit. Multiply the value for (let i = toIdx; i < fromIdx; i++) { - convertedValue *= ratios[i][1]; + convertedValue *= ratioScale[i][1]; } } else { // `from` is the smaller unit. Divide the value for (let i = fromIdx; i < toIdx; i++) { - convertedValue /= ratios[i][1]; + convertedValue /= ratioScale[i][1]; } } return convertedValue; } + +export function normalizeDate(amount: number, unit: Unit): { amount: number; unit: Unit } { + // There is nothing after years + if (unit === 'y') { + return { amount, unit }; + } + + const nextUnit = dateMath.unitsAsc[dateMath.unitsAsc.indexOf(unit) + 1]; + const ratioScale = getRatioScale(unit, nextUnit); + const ratio = ratioScale.find(r => r[0] === unit)![1]; + + const newAmount = amount / ratio; + + // Exact conversion + if (newAmount === 1) { + return { amount: newAmount, unit: nextUnit }; + } + + // Might be able to go one unit more, so try again, rounding the value + // 7200s => 120m => 2h + // 7249s ~> 120m ~> 2h + if (newAmount >= 2) { + return normalizeDate(Math.round(newAmount), nextUnit); + } + + // Cannot go one one unit above. Return as it is + return { amount, unit }; +} From c544c469bd3330c5b5109d5a29501bfe5be4e80e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 16 Jan 2020 16:18:31 +0100 Subject: [PATCH 030/117] Prettify messages to extend time --- .../log_text_stream/loading_item_view.tsx | 80 +++++++++++++++++-- .../plugins/infra/public/utils/datemath.ts | 4 +- 2 files changed, 76 insertions(+), 8 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx index 4d59cd9887bf94..c0777c5f6bbdf8 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx @@ -16,6 +16,7 @@ import { } from '@elastic/eui'; import { FormattedMessage, FormattedTime } from '@kbn/i18n/react'; import * as React from 'react'; +import { Unit } from '@elastic/datemath'; import euiStyled from '../../../../../../common/eui_styled_components'; import { LogTextSeparator } from './log_text_separator'; @@ -146,7 +147,7 @@ const ProgressCta: React.FC - + ); }; + +const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amount, unit }) => { + switch (unit) { + case 'ms': + return ( + + ); + case 's': + return ( + + ); + case 'm': + return ( + + ); + case 'h': + return ( + + ); + case 'd': + return ( + + ); + case 'w': + return ( + + ); + case 'M': + return ( + + ); + case 'y': + return ( + + ); + default: + throw new TypeError('Unhandled unit: ' + unit); + } +}; diff --git a/x-pack/legacy/plugins/infra/public/utils/datemath.ts b/x-pack/legacy/plugins/infra/public/utils/datemath.ts index fd7d7a731098cd..61d74fa8f87d32 100644 --- a/x-pack/legacy/plugins/infra/public/utils/datemath.ts +++ b/x-pack/legacy/plugins/infra/public/utils/datemath.ts @@ -67,12 +67,12 @@ export function extendDatemath( const MINUTES_LARGE = 10; if (mustIncreaseAmount) { ratio = parsedAmount >= MINUTES_LARGE ? 0.5 : 1; - newAmount = parsedAmount + parsedAmount * ratio; + newAmount = parsedAmount + Math.floor(parsedAmount * ratio); } else { newAmount = parsedAmount >= MINUTES_LARGE ? Math.floor(parsedAmount / 1.5) - : parsedAmount - parsedAmount * 0.5; + : parsedAmount - Math.floor(parsedAmount * 0.5); } break; From 5554fb74cf033261f86cbd30a33f79874a7465b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 30 Jan 2020 11:31:50 +0100 Subject: [PATCH 031/117] Adjust scale for highlight markers --- .../infra/public/components/logging/log_minimap/log_minimap.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b0c6028375ac2a..1550e0d87ce04a 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 @@ -150,7 +150,7 @@ export class LogMinimap extends React.Component From d84e9239a218c105e0ecf93fc00e8d5496d5b0d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 30 Jan 2020 15:20:01 +0100 Subject: [PATCH 032/117] Extract bucket size calculation to its own hook --- .../logs/log_summary/bucket_size.ts | 21 +++++++++++++++++++ .../logs/log_summary/log_summary.tsx | 9 +++----- 2 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 x-pack/legacy/plugins/infra/public/containers/logs/log_summary/bucket_size.ts diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/bucket_size.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/bucket_size.ts new file mode 100644 index 00000000000000..5e091e1f305b66 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/bucket_size.ts @@ -0,0 +1,21 @@ +/* + * 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'; + +export function useBucketSize( + startTimestamp: number | null, + endTimestamp: number | null +): number | null { + const bucketSize = useMemo(() => { + if (!startTimestamp || !endTimestamp) { + return null; + } + return (endTimestamp - startTimestamp) / 100; + }, [startTimestamp, endTimestamp]); + + return bucketSize; +} 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 518366d5313b8d..9719335a2016ab 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 @@ -9,6 +9,7 @@ import { useState, useMemo } from 'react'; import { useCancellableEffect } from '../../../utils/cancellable_effect'; import { fetchLogSummary } from './api/fetch_log_summary'; import { LogEntriesSummaryResponse } from '../../../../common/http_api'; +import { useBucketSize } from './bucket_size'; export type LogSummaryBuckets = LogEntriesSummaryResponse['data']['buckets']; @@ -19,12 +20,8 @@ export const useLogSummary = ( filterQuery: string | null ) => { const [logSummaryBuckets, setLogSummaryBuckets] = useState([]); - const bucketSize = useMemo(() => { - if (!startTimestamp || !endTimestamp) { - return null; - } - return (endTimestamp - startTimestamp) / 100; - }, [startTimestamp, endTimestamp]); + + const bucketSize = useBucketSize(startTimestamp, endTimestamp); useCancellableEffect( getIsCancelled => { From 1806925315c747566338a4e339c6af84ade0f0cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 30 Jan 2020 15:22:47 +0100 Subject: [PATCH 033/117] Use the date range to fetch summary highlights --- .../logs/log_highlights/log_highlights.tsx | 18 +++-------- .../log_highlights/log_summary_highlights.ts | 26 ++++++++++------ .../containers/logs/log_summary/index.ts | 1 - .../use_log_summary_buffer_interval.ts | 31 ------------------- 4 files changed, 20 insertions(+), 56 deletions(-) delete mode 100644 x-pack/legacy/plugins/infra/public/containers/logs/log_summary/use_log_summary_buffer_interval.ts 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 a4a94851ad383d..25b5a375347c98 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 @@ -9,8 +9,6 @@ import { useState, useContext } from 'react'; import { useLogEntryHighlights } from './log_entry_highlights'; import { useLogSummaryHighlights } from './log_summary_highlights'; import { useNextAndPrevious } from './next_and_previous'; -import { useLogSummaryBufferInterval } from '../log_summary'; -import { LogViewConfiguration } from '../log_view_configuration'; import { LogPositionState } from '../log_position'; import { TimeKey } from '../../../../common/time'; @@ -28,15 +26,8 @@ export const useLogHighlightsState = ({ filterQuery: string | null; }) => { const [highlightTerms, setHighlightTerms] = useState([]); - const { visibleMidpoint, jumpToTargetPosition } = useContext(LogPositionState.Context); - const { intervalSize: summaryIntervalSize } = useContext(LogViewConfiguration.Context); - const { - start: summaryStart, - end: summaryEnd, - bucketSize: summaryBucketSize, - } = useLogSummaryBufferInterval( - visibleMidpoint ? visibleMidpoint.time : null, - summaryIntervalSize + const { visibleMidpoint, jumpToTargetPosition, startTimestamp, endTimestamp } = useContext( + LogPositionState.Context ); const { @@ -55,9 +46,8 @@ export const useLogHighlightsState = ({ const { logSummaryHighlights, loadLogSummaryHighlightsRequest } = useLogSummaryHighlights( sourceId, sourceVersion, - summaryStart, - summaryEnd, - summaryBucketSize, + startTimestamp, + endTimestamp, filterQuery, highlightTerms ); 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 index 81639aba411efb..c73e814704322c 100644 --- 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 @@ -10,13 +10,13 @@ import { debounce } from 'lodash'; import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { fetchLogSummaryHighlights } from './api/fetch_log_summary_highlights'; import { LogEntriesSummaryHighlightsResponse } from '../../../../common/http_api'; +import { useBucketSize } from '../log_summary/bucket_size'; export const useLogSummaryHighlights = ( sourceId: string, sourceVersion: string | undefined, - start: number | null, - end: number | null, - bucketSize: number, + startTimestamp: number | null, + endTimestamp: number | null, filterQuery: string | null, highlightTerms: string[] ) => { @@ -24,18 +24,20 @@ export const useLogSummaryHighlights = ( LogEntriesSummaryHighlightsResponse['data'] >([]); + const bucketSize = useBucketSize(startTimestamp, endTimestamp); + const [loadLogSummaryHighlightsRequest, loadLogSummaryHighlights] = useTrackedPromise( { cancelPreviousOn: 'resolution', createPromise: async () => { - if (!start || !end || !highlightTerms.length) { + if (!startTimestamp || !endTimestamp || !bucketSize || !highlightTerms.length) { throw new Error('Skipping request: Insufficient parameters'); } return await fetchLogSummaryHighlights({ sourceId, - startDate: start, - endDate: end, + startDate: startTimestamp, + endDate: endTimestamp, bucketSize, query: filterQuery, highlightTerms, @@ -45,7 +47,7 @@ export const useLogSummaryHighlights = ( setLogSummaryHighlights(response.data); }, }, - [sourceId, start, end, bucketSize, filterQuery, highlightTerms] + [sourceId, startTimestamp, endTimestamp, bucketSize, filterQuery, highlightTerms] ); const debouncedLoadSummaryHighlights = useMemo(() => debounce(loadLogSummaryHighlights, 275), [ @@ -57,7 +59,11 @@ export const useLogSummaryHighlights = ( }, [highlightTerms]); useEffect(() => { - if (highlightTerms.filter(highlightTerm => highlightTerm.length > 0).length && start && end) { + if ( + highlightTerms.filter(highlightTerm => highlightTerm.length > 0).length && + startTimestamp && + endTimestamp + ) { debouncedLoadSummaryHighlights(); } else { setLogSummaryHighlights([]); @@ -65,11 +71,11 @@ export const useLogSummaryHighlights = ( }, [ bucketSize, debouncedLoadSummaryHighlights, - end, + endTimestamp, filterQuery, highlightTerms, sourceVersion, - start, + startTimestamp, ]); return { 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 20c4267000a250..dc0437fa75a314 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 @@ -5,5 +5,4 @@ */ 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/use_log_summary_buffer_interval.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/use_log_summary_buffer_interval.ts deleted file mode 100644 index 78c348cdd7714e..00000000000000 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/use_log_summary_buffer_interval.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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, -}; - -/* TODO: cleanup */ -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]); -}; From 96ebf1193559ba8830bcfc80274a84213af78d3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 31 Jan 2020 12:45:54 +0100 Subject: [PATCH 034/117] Wire stream buttons in superdatepicker --- .../logs/log_position/log_position_state.ts | 5 ++++ .../public/pages/logs/stream/page_toolbar.tsx | 23 ++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts index 93296a988d3bd4..dada04bc88fe1f 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts @@ -27,6 +27,7 @@ interface VisiblePositions { export interface LogPositionStateParams { targetPosition: TimeKeyOrNull; isAutoReloading: boolean; + liveStreamingInterval: number; firstVisiblePosition: TimeKeyOrNull; pagesBeforeStart: number; pagesAfterEnd: number; @@ -43,6 +44,7 @@ export interface LogPositionCallbacks { jumpToTargetPosition: (pos: TimeKeyOrNull) => void; jumpToTargetPositionTime: (time: number) => void; reportVisiblePositions: (visPos: VisiblePositions) => void; + setLiveStreamingInterval: (interval: number) => void; startLiveStreaming: () => void; stopLiveStreaming: () => void; updateDateRange: (newDateRage: Partial) => void; @@ -75,6 +77,7 @@ const useVisibleMidpoint = (middleKey: TimeKeyOrNull, targetPosition: TimeKeyOrN export const useLogPositionState: () => LogPositionStateParams & LogPositionCallbacks = () => { const [targetPosition, jumpToTargetPosition] = useState(null); const [isAutoReloading, setIsAutoReloading] = useState(false); + const [liveStreamingInterval, setLiveStreamingInterval] = useState(10000); const [visiblePositions, reportVisiblePositions] = useState({ endKey: null, middleKey: null, @@ -137,6 +140,7 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall ...dateRange, startTimestamp, endTimestamp, + liveStreamingInterval, }; const callbacks = { @@ -149,6 +153,7 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall startLiveStreaming: useCallback(() => setIsAutoReloading(true), [setIsAutoReloading]), stopLiveStreaming: useCallback(() => setIsAutoReloading(false), [setIsAutoReloading]), updateDateRange, + setLiveStreamingInterval, }; return { ...state, ...callbacks }; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx index 56a0d3ec101c7a..78bb1b12963912 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx @@ -63,6 +63,8 @@ export const LogsToolbar = () => { startDate, endDate, updateDateRange, + liveStreamingInterval, + setLiveStreamingInterval, } = useContext(LogPositionState.Context); const handleTimeChange = useCallback( @@ -134,8 +136,23 @@ export const LogsToolbar = () => { /> - - {}} + onRefreshChange={({ refreshInterval, isPaused }) => { + if (isPaused) { + stopLiveStreaming(); + } else { + startLiveStreaming(); + } + setLiveStreamingInterval(refreshInterval); + }} + /> + {/* { setSurroundingLogsId(null); }} stopLiveStreaming={stopLiveStreaming} - /> + /> */} From 69b5fdc006682aa51ea1a586ae1789ef53d87c6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 31 Jan 2020 15:00:09 +0100 Subject: [PATCH 035/117] Update timestamps for streaming --- .../scrollable_log_text_stream_view.tsx | 4 +++- .../containers/logs/log_entries/index.ts | 23 ++++++++----------- .../pages/logs/stream/page_logs_content.tsx | 2 ++ 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx index 0aff92bea8f7eb..d90f0b1c872258 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx @@ -59,6 +59,7 @@ interface ScrollableLogTextStreamViewProps { startDate: string; endDate: string; updateDateRange: (range: { startDate?: string; endDate?: string }) => void; + startLiveStreaming: () => void; } interface ScrollableLogTextStreamViewState { @@ -140,6 +141,7 @@ export class ScrollableLogTextStreamView extends React.PureComponent< startDate, endDate, updateDateRange, + startLiveStreaming, } = this.props; const { targetId, items, isScrollLocked } = this.state; const hasItems = items.length > 0; @@ -258,7 +260,7 @@ export class ScrollableLogTextStreamView extends React.PureComponent< } rangeEdge={endDate} onExtendRange={newDate => updateDateRange({ endDate: newDate })} - // onStreamStart={(...args) => console.log('end.streamStart', args)} + onStreamStart={() => startLiveStreaming()} // onLoadMore={this.handleLoadNewerItems} /> {isScrollLocked && ( diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts index be8fc6aacee7a7..6c11d4ab213687 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts @@ -137,7 +137,7 @@ const useFetchEntriesEffect = ( const [prevParams, cachePrevParams] = useState(props); const [startedStreaming, setStartedStreaming] = useState(false); - const runFetchNewEntriesRequest = async (override = {}) => { + const runFetchNewEntriesRequest = async (overrides: Partial = {}) => { if (!props.startTimestamp || !props.endTimestamp || !props.timeKey) { return; } @@ -146,10 +146,10 @@ const useFetchEntriesEffect = ( try { const fetchArgs: LogEntriesRequest = { - sourceId: props.sourceId, - startDate: props.startTimestamp, - endDate: props.endTimestamp, - query: props.filterQuery || undefined, // FIXME + sourceId: overrides.sourceId || props.sourceId, + startDate: overrides.startTimestamp || props.startTimestamp, + endDate: overrides.endTimestamp || props.endTimestamp, + query: overrides.filterQuery || props.filterQuery || undefined, // FIXME }; if (props.timeKey) { @@ -237,7 +237,7 @@ const useFetchEntriesEffect = ( const fetchNewerEntries = useCallback( throttle(() => runFetchMoreEntriesRequest(ShouldFetchMoreEntries.After), 500), - [props, state.entriesEnd] + [props, state.bottomCursor] ); const streamEntriesEffectDependencies = [ @@ -251,16 +251,11 @@ const useFetchEntriesEffect = ( if (startedStreaming) { await new Promise(res => setTimeout(res, 5000)); } else { - const nowKey = { - tiebreaker: 0, - time: Date.now(), - }; - props.jumpToTargetPosition(nowKey); + const endTimestamp = Date.now(); + props.jumpToTargetPosition({ tiebreaker: 0, time: endTimestamp }); setStartedStreaming(true); if (state.hasMoreAfterEnd) { - runFetchNewEntriesRequest({ - timeKey: nowKey, - }); + runFetchNewEntriesRequest({ endTimestamp }); return; } } 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 7506d69d1f7ead..5c7ccce00dbb2d 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 @@ -50,6 +50,7 @@ export const LogsPageLogsContent: React.FunctionComponent = () => { visibleTimeInterval, reportVisiblePositions, jumpToTargetPosition, + startLiveStreaming, stopLiveStreaming, startDate, endDate, @@ -110,6 +111,7 @@ export const LogsPageLogsContent: React.FunctionComponent = () => { startDate={startDate} endDate={endDate} updateDateRange={updateDateRange} + startLiveStreaming={startLiveStreaming} /> )} From 1a1d2a49f817dd282bf09b764b39162a8ff0bff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Tue, 4 Feb 2020 13:50:18 +0100 Subject: [PATCH 036/117] Link `endTimestamp` to realtime `now` This enables live streaming, since the bottom of the range will always be, well, now. --- .../public/containers/logs/log_entries/index.ts | 15 +++++++++------ .../logs/log_position/log_position_state.ts | 13 +++++++++---- .../public/pages/logs/stream/page_providers.tsx | 6 ++++++ 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts index 6c11d4ab213687..1c34db244753b1 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts @@ -46,8 +46,11 @@ type ActionObj = ReceiveEntriesAction | FetchOrErrorAction; type Dispatch = (action: ActionObj) => void; interface LogEntriesProps { + startDate: string | null; + endDate: string | null; startTimestamp: number | null; endTimestamp: number | null; + liveStreamingInterval: number; filterQuery: string | null; timeKey: TimeKey | null; pagesBeforeStart: number | null; @@ -101,16 +104,16 @@ const shouldFetchNewEntries = ({ filterQuery, topCursor, bottomCursor, - startTimestamp, - endTimestamp, + startDate, + endDate, }: FetchEntriesParams & LogEntriesStateParams & { prevParams: FetchEntriesParams }) => { - const shouldLoadWithNewTimestamps = - startTimestamp !== prevParams.startTimestamp || endTimestamp !== prevParams.endTimestamp; + const shouldLoadWithNewDates = + startDate !== prevParams.startDate || endDate !== prevParams.endDate; const shouldLoadWithNewFilter = filterQuery !== prevParams.filterQuery; const shouldLoadAroundNewPosition = timeKey && (!topCursor || !bottomCursor || !timeKeyIsBetween(topCursor, bottomCursor, timeKey)); - return shouldLoadWithNewTimestamps || shouldLoadWithNewFilter || shouldLoadAroundNewPosition; + return shouldLoadWithNewDates || shouldLoadWithNewFilter || shouldLoadAroundNewPosition; }; enum ShouldFetchMoreEntries { @@ -249,7 +252,7 @@ const useFetchEntriesEffect = ( (async () => { if (props.isAutoReloading && !state.isLoadingMore && !state.isReloading) { if (startedStreaming) { - await new Promise(res => setTimeout(res, 5000)); + await new Promise(res => setTimeout(res, props.liveStreamingInterval)); } else { const endTimestamp = Date.now(); props.jumpToTargetPosition({ tiebreaker: 0, time: endTimestamp }); diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts index dada04bc88fe1f..26927777e53a7a 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts @@ -113,6 +113,7 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall return; } + jumpToTargetPosition(null); setDateRange(previousDateRange => { return { startDate: newDateRange.startDate || previousDateRange.startDate, @@ -123,10 +124,14 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall [dateRange] ); - const [startTimestamp, endTimestamp] = useMemo( - () => [datemathToEpochMillis(dateRange.startDate), datemathToEpochMillis(dateRange.endDate)], - [dateRange] - ); + const startTimestamp = useMemo(() => datemathToEpochMillis(dateRange.startDate), [ + dateRange.startDate, + ]); + + // endTimestamp needs to be synced to `now` to allow auto-streaming + const endTimestampDep = dateRange.endDate === 'now' ? Date.now() : dateRange.endDate; + // eslint-disable-next-line react-hooks/exhaustive-deps + const endTimestamp = useMemo(() => datemathToEpochMillis(dateRange.endDate), [endTimestampDep]); const state = { targetPosition, diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx index 88c19b1732b7ad..86073f66e7086d 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx @@ -29,6 +29,8 @@ const LogFilterStateProvider: React.FC = ({ children }) => { const LogEntriesStateProvider: React.FC = ({ children }) => { const { sourceId } = useContext(Source.Context); const { + startDate, + endDate, startTimestamp, endTimestamp, targetPosition, @@ -36,12 +38,16 @@ const LogEntriesStateProvider: React.FC = ({ children }) => { pagesAfterEnd, isAutoReloading, jumpToTargetPosition, + liveStreamingInterval, } = useContext(LogPositionState.Context); const { filterQuery } = useContext(LogFilterState.Context); const entriesProps = { + startDate, + endDate, startTimestamp, endTimestamp, + liveStreamingInterval, timeKey: targetPosition, pagesBeforeStart, pagesAfterEnd, From 555c08a8c0d30c17e6f21b89271fc07bc832aa14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Tue, 4 Feb 2020 14:10:26 +0100 Subject: [PATCH 037/117] s/isAutoReloading/isStreaming/g Change the name of the flag to better reflect its purpose --- .../public/containers/logs/log_entries/index.ts | 14 +++++++------- .../logs/log_position/log_position_state.ts | 10 +++++----- .../log_position/with_log_position_url_state.tsx | 6 +++--- .../public/pages/logs/stream/page_logs_content.tsx | 4 ++-- .../public/pages/logs/stream/page_providers.tsx | 4 ++-- .../public/pages/logs/stream/page_toolbar.tsx | 4 ++-- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts index 1c34db244753b1..7b0ba67523a091 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts @@ -56,11 +56,11 @@ interface LogEntriesProps { pagesBeforeStart: number | null; pagesAfterEnd: number | null; sourceId: string; - isAutoReloading: boolean; + isStreaming: boolean; jumpToTargetPosition: (position: TimeKey) => void; } -type FetchEntriesParams = Omit; +type FetchEntriesParams = Omit; type FetchMoreEntriesParams = Pick; export interface LogEntriesStateParams { @@ -214,7 +214,7 @@ const useFetchEntriesEffect = ( pick(props, ['sourceId', 'filterQuery', 'timeKey', 'startTimestamp', 'endTimestamp']) ); const fetchNewEntriesEffect = () => { - if (props.isAutoReloading) return; + if (props.isStreaming) return; if (shouldFetchNewEntries({ ...props, ...state, prevParams })) { runFetchNewEntriesRequest(); } @@ -226,7 +226,7 @@ const useFetchEntriesEffect = ( Object.values(pick(state, ['hasMoreBeforeStart', 'hasMoreAfterEnd'])), ]; const fetchMoreEntriesEffect = () => { - if (state.isLoadingMore || props.isAutoReloading) return; + if (state.isLoadingMore || props.isStreaming) return; const direction = shouldFetchMoreEntries(props, state); switch (direction) { case ShouldFetchMoreEntries.Before: @@ -244,13 +244,13 @@ const useFetchEntriesEffect = ( ); const streamEntriesEffectDependencies = [ - props.isAutoReloading, + props.isStreaming, state.isLoadingMore, state.isReloading, ]; const streamEntriesEffect = () => { (async () => { - if (props.isAutoReloading && !state.isLoadingMore && !state.isReloading) { + if (props.isStreaming && !state.isLoadingMore && !state.isReloading) { if (startedStreaming) { await new Promise(res => setTimeout(res, props.liveStreamingInterval)); } else { @@ -266,7 +266,7 @@ const useFetchEntriesEffect = ( if (newEntriesEnd) { props.jumpToTargetPosition(newEntriesEnd); } - } else if (!props.isAutoReloading) { + } else if (!props.isStreaming) { setStartedStreaming(false); } })(); diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts index 26927777e53a7a..120587a36b1b3c 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts @@ -26,7 +26,7 @@ interface VisiblePositions { export interface LogPositionStateParams { targetPosition: TimeKeyOrNull; - isAutoReloading: boolean; + isStreaming: boolean; liveStreamingInterval: number; firstVisiblePosition: TimeKeyOrNull; pagesBeforeStart: number; @@ -76,7 +76,7 @@ const useVisibleMidpoint = (middleKey: TimeKeyOrNull, targetPosition: TimeKeyOrN export const useLogPositionState: () => LogPositionStateParams & LogPositionCallbacks = () => { const [targetPosition, jumpToTargetPosition] = useState(null); - const [isAutoReloading, setIsAutoReloading] = useState(false); + const [isStreaming, setIsStreaming] = useState(false); const [liveStreamingInterval, setLiveStreamingInterval] = useState(10000); const [visiblePositions, reportVisiblePositions] = useState({ endKey: null, @@ -135,7 +135,7 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall const state = { targetPosition, - isAutoReloading, + isStreaming, firstVisiblePosition: startKey, pagesBeforeStart, pagesAfterEnd, @@ -155,8 +155,8 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall [jumpToTargetPosition] ), reportVisiblePositions, - startLiveStreaming: useCallback(() => setIsAutoReloading(true), [setIsAutoReloading]), - stopLiveStreaming: useCallback(() => setIsAutoReloading(false), [setIsAutoReloading]), + startLiveStreaming: useCallback(() => setIsStreaming(true), [setIsStreaming]), + stopLiveStreaming: useCallback(() => setIsStreaming(false), [setIsStreaming]), updateDateRange, setLiveStreamingInterval, }; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx index 6a15304a374ae4..ba964374c35cf8 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx @@ -25,7 +25,7 @@ interface LogPositionUrlState { export const WithLogPositionUrlState = () => { const { visibleMidpoint, - isAutoReloading, + isStreaming, jumpToTargetPosition, jumpToTargetPositionTime, startLiveStreaming, @@ -37,11 +37,11 @@ export const WithLogPositionUrlState = () => { const urlState = useMemo( () => ({ position: visibleMidpoint ? pickTimeKey(visibleMidpoint) : null, - streamLive: isAutoReloading, + streamLive: isStreaming, start: startDate, end: endDate, }), - [visibleMidpoint, isAutoReloading, startDate, endDate] + [visibleMidpoint, isStreaming, startDate, endDate] ); return ( { const { logSummaryHighlights } = useContext(LogHighlightsState.Context); const { applyLogFilterQuery } = useContext(LogFilterState.Context); const { - isAutoReloading, + isStreaming, targetPosition, visibleMidpointTime, visibleTimeInterval, @@ -94,7 +94,7 @@ export const LogsPageLogsContent: React.FunctionComponent = () => { hasMoreBeforeStart={hasMoreBeforeStart} isLoadingMore={isLoadingMore} isReloading={isReloading} - isStreaming={isAutoReloading} + isStreaming={isStreaming} items={items} jumpToTarget={jumpToTargetPosition} lastLoadedTime={lastLoadedTime} diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx index 86073f66e7086d..20eaaa638ecef1 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx @@ -36,7 +36,7 @@ const LogEntriesStateProvider: React.FC = ({ children }) => { targetPosition, pagesBeforeStart, pagesAfterEnd, - isAutoReloading, + isStreaming, jumpToTargetPosition, liveStreamingInterval, } = useContext(LogPositionState.Context); @@ -53,7 +53,7 @@ const LogEntriesStateProvider: React.FC = ({ children }) => { pagesAfterEnd, filterQuery, sourceId, - isAutoReloading, + isStreaming, jumpToTargetPosition, }; return {children}; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx index 78bb1b12963912..4f65d48e361fc0 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx @@ -56,7 +56,7 @@ export const LogsToolbar = () => { } = useContext(LogHighlightsState.Context); const { visibleMidpointTime, - isAutoReloading, + isStreaming, jumpToTargetPositionTime, startLiveStreaming, stopLiveStreaming, @@ -140,7 +140,7 @@ export const LogsToolbar = () => { start={startDate} end={endDate} onTimeChange={handleTimeChange} - isPaused={!isAutoReloading} + isPaused={!isStreaming} refreshInterval={liveStreamingInterval} onRefresh={() => {}} onRefreshChange={({ refreshInterval, isPaused }) => { From 339d041c8bf093583153d0f2a90c47d203e809d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Tue, 4 Feb 2020 15:11:18 +0100 Subject: [PATCH 038/117] Fetch Highlights with new API --- .../api/fetch_log_entries_highlights.ts | 32 +++++++++++ .../log_highlights/log_entry_highlights.tsx | 55 ++++++------------- .../pages/logs/stream/page_providers.tsx | 6 +- .../utils/log_entry/log_entry_highlight.ts | 3 +- 4 files changed, 55 insertions(+), 41 deletions(-) create mode 100644 x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_entries_highlights.ts diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_entries_highlights.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_entries_highlights.ts new file mode 100644 index 00000000000000..c391ab3db0cdb8 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_entries_highlights.ts @@ -0,0 +1,32 @@ +/* + * 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 { fold } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { identity } from 'fp-ts/lib/function'; +import { kfetch } from 'ui/kfetch'; + +import { throwErrors, createPlainError } from '../../../../../common/runtime_types'; + +import { + LOG_ENTRIES_HIGHLIGHTS_PATH, + LogEntriesHighlightsRequest, + logEntriesHighlightsRequestRT, + logEntriesHighlightsResponseRT, +} from '../../../../../common/http_api'; + +export const fetchLogEntriesHighlights = async (requestArgs: LogEntriesHighlightsRequest) => { + const response = await kfetch({ + method: 'POST', + pathname: LOG_ENTRIES_HIGHLIGHTS_PATH, + body: JSON.stringify(logEntriesHighlightsRequestRT.encode(requestArgs)), + }); + + return pipe( + logEntriesHighlightsResponseRT.decode(response), + fold(throwErrors(createPlainError), identity) + ); +}; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx index 2b19958a9b1a11..104b20243205ea 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx @@ -7,13 +7,9 @@ import { useEffect, useMemo, useState } from 'react'; import { getNextTimeKey, getPreviousTimeKey, TimeKey } from '../../../../common/time'; -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_entry_highlights.gql_query'; - -export type LogEntryHighlights = LogEntryHighlightsQuery.Query['source']['logEntryHighlights']; +import { fetchLogEntriesHighlights } from './api/fetch_log_entries_highlights'; +import { LogEntry, LogEntriesHighlightsResponse } from '../../../../common/http_api'; export const useLogEntryHighlights = ( sourceId: string, @@ -23,45 +19,30 @@ export const useLogEntryHighlights = ( filterQuery: string | null, highlightTerms: string[] ) => { - const apolloClient = useApolloClient(); - const [logEntryHighlights, setLogEntryHighlights] = useState([]); + const [logEntryHighlights, setLogEntryHighlights] = useState< + LogEntriesHighlightsResponse['data'] + >([]); const [loadLogEntryHighlightsRequest, loadLogEntryHighlights] = useTrackedPromise( { cancelPreviousOn: 'resolution', createPromise: async () => { - if (!apolloClient) { - throw new DependencyError('Failed to load source: No apollo client available.'); - } if (!startKey || !endKey || !highlightTerms.length) { throw new Error('Skipping request: Insufficient parameters'); } - return await apolloClient.query< - LogEntryHighlightsQuery.Query, - LogEntryHighlightsQuery.Variables - >({ - fetchPolicy: 'no-cache', - query: logEntryHighlightsQuery, - variables: { - sourceId, - startKey: getPreviousTimeKey(startKey), // interval boundaries are exclusive - endKey: getNextTimeKey(endKey), // interval boundaries are exclusive - filterQuery, - highlights: [ - { - query: highlightTerms[0], - countBefore: 1, - countAfter: 1, - }, - ], - }, + return await fetchLogEntriesHighlights({ + sourceId, + startDate: getPreviousTimeKey(startKey).time, + endDate: getNextTimeKey(endKey).time, + query: filterQuery || undefined, + highlightTerms, }); }, onResolve: response => { - setLogEntryHighlights(response.data.source.logEntryHighlights); + setLogEntryHighlights(response.data); }, }, - [apolloClient, sourceId, startKey, endKey, filterQuery, highlightTerms] + [sourceId, startKey, endKey, filterQuery, highlightTerms] ); useEffect(() => { @@ -82,13 +63,13 @@ export const useLogEntryHighlights = ( const logEntryHighlightsById = useMemo( () => - logEntryHighlights.reduce( - (accumulatedLogEntryHighlightsById, { entries }) => { - return entries.reduce((singleHighlightLogEntriesById, entry) => { - const highlightsForId = singleHighlightLogEntriesById[entry.gid] || []; + logEntryHighlights.reduce>( + (accumulatedLogEntryHighlightsById, highlightData) => { + return highlightData.entries.reduce((singleHighlightLogEntriesById, entry) => { + const highlightsForId = singleHighlightLogEntriesById[entry.id] || []; return { ...singleHighlightLogEntriesById, - [entry.gid]: [...highlightsForId, entry], + [entry.id]: [...highlightsForId, entry], }; }, accumulatedLogEntryHighlightsById); }, diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx index 20eaaa638ecef1..1abc794744e42a 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx @@ -61,13 +61,13 @@ const LogEntriesStateProvider: React.FC = ({ children }) => { const LogHighlightsStateProvider: React.FC = ({ children }) => { const { sourceId, version } = useContext(Source.Context); - const [{ entriesStart, entriesEnd }] = useContext(LogEntriesState.Context); + const [{ topCursor, bottomCursor }] = useContext(LogEntriesState.Context); const { filterQuery } = useContext(LogFilterState.Context); const highlightsProps = { sourceId, sourceVersion: version, - entriesStart, - entriesEnd, + entriesStart: topCursor, + entriesEnd: bottomCursor, filterQuery, }; return {children}; diff --git a/x-pack/legacy/plugins/infra/public/utils/log_entry/log_entry_highlight.ts b/x-pack/legacy/plugins/infra/public/utils/log_entry/log_entry_highlight.ts index 3361faa23a124c..ff5dfd4665bb49 100644 --- a/x-pack/legacy/plugins/infra/public/utils/log_entry/log_entry_highlight.ts +++ b/x-pack/legacy/plugins/infra/public/utils/log_entry/log_entry_highlight.ts @@ -5,6 +5,7 @@ */ import { InfraLogEntryHighlightFields } from '../../graphql/types'; +import { LogEntry } from '../../../common/http_api'; export type LogEntryHighlight = InfraLogEntryHighlightFields.Fragment; @@ -16,7 +17,7 @@ export type LogEntryHighlightMessageSegment = InfraLogEntryHighlightFields.Messa export type LogEntryHighlightFieldMessageSegment = InfraLogEntryHighlightFields.InfraLogMessageFieldSegmentInlineFragment; export interface LogEntryHighlightsMap { - [entryId: string]: LogEntryHighlight[]; + [entryId: string]: LogEntry[]; } export const isHighlightMessageColumn = ( From da0b2d3c1d974593897fc18e9be535cd13ff42f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Tue, 4 Feb 2020 15:50:53 +0100 Subject: [PATCH 039/117] Prevent re-fetches of summary info --- .../logs/log_highlights/log_highlights.tsx | 13 ++++++++++--- .../logs/log_highlights/log_summary_highlights.ts | 7 ++++++- .../containers/logs/log_summary/log_summary.tsx | 8 ++++++-- .../containers/logs/log_summary/with_summary.ts | 4 +++- 4 files changed, 25 insertions(+), 7 deletions(-) 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 25b5a375347c98..9f20a439735044 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 @@ -26,9 +26,14 @@ export const useLogHighlightsState = ({ filterQuery: string | null; }) => { const [highlightTerms, setHighlightTerms] = useState([]); - const { visibleMidpoint, jumpToTargetPosition, startTimestamp, endTimestamp } = useContext( - LogPositionState.Context - ); + const { + visibleMidpoint, + jumpToTargetPosition, + startDate, + endDate, + startTimestamp, + endTimestamp, + } = useContext(LogPositionState.Context); const { logEntryHighlights, @@ -46,6 +51,8 @@ export const useLogHighlightsState = ({ const { logSummaryHighlights, loadLogSummaryHighlightsRequest } = useLogSummaryHighlights( sourceId, sourceVersion, + startDate, + endDate, startTimestamp, endTimestamp, filterQuery, 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 index c73e814704322c..f20ec3ff34b0b0 100644 --- 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 @@ -15,6 +15,8 @@ import { useBucketSize } from '../log_summary/bucket_size'; export const useLogSummaryHighlights = ( sourceId: string, sourceVersion: string | undefined, + startDate: string | null, + endDate: string | null, startTimestamp: number | null, endTimestamp: number | null, filterQuery: string | null, @@ -47,7 +49,10 @@ export const useLogSummaryHighlights = ( setLogSummaryHighlights(response.data); }, }, - [sourceId, startTimestamp, endTimestamp, bucketSize, filterQuery, highlightTerms] + // Use `*Timestamp` values in the fetch. + // Use `*Date` to decide if it should refetch or not. `endTimestamp` updates + // frequently when `endDate` is `"now"` and triggers a lot of re-renders. + [sourceId, startDate, endDate, filterQuery, highlightTerms] ); const debouncedLoadSummaryHighlights = useMemo(() => debounce(loadLogSummaryHighlights, 275), [ 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 9719335a2016ab..e021e83dd7c4e8 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 @@ -15,6 +15,8 @@ export type LogSummaryBuckets = LogEntriesSummaryResponse['data']['buckets']; export const useLogSummary = ( sourceId: string, + startDate: string | null, + endDate: string | null, startTimestamp: number | null, endTimestamp: number | null, filterQuery: string | null @@ -22,7 +24,6 @@ export const useLogSummary = ( const [logSummaryBuckets, setLogSummaryBuckets] = useState([]); const bucketSize = useBucketSize(startTimestamp, endTimestamp); - useCancellableEffect( getIsCancelled => { if (startTimestamp === null || endTimestamp === null || bucketSize === null) { @@ -41,7 +42,10 @@ export const useLogSummary = ( } }); }, - [sourceId, filterQuery, startTimestamp, endTimestamp, bucketSize] + // Use `*Timestamp` values in the fetch. + // Use `*Date` to decide if it should refetch or not. `endTimestamp` updates + // frequently when `endDate` is `"now"` and triggers a lot of re-renders. + [sourceId, filterQuery, startDate, endDate] ); return { 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 32940928c5d8c1..fa3a88fe9524f5 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 @@ -23,10 +23,12 @@ export const WithSummary = ({ }) => { const { sourceId } = useContext(Source.Context); const { filterQuery } = useContext(LogFilterState.Context); - const { startTimestamp, endTimestamp } = useContext(LogPositionState.Context); + const { startDate, endDate, startTimestamp, endTimestamp } = useContext(LogPositionState.Context); const { buckets, start, end } = useLogSummary( sourceId, + startDate, + endDate, startTimestamp, endTimestamp, filterQuery From 817c19bba25cc55617ef4dbdf75516d269067eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 5 Feb 2020 13:03:44 +0100 Subject: [PATCH 040/117] Tweak loading message for streaming --- .../log_text_stream/loading_item_view.tsx | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx index c0777c5f6bbdf8..6129333d439f0b 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx @@ -60,7 +60,7 @@ export class LogTextStreamLoadingItemView extends React.PureComponent< const extra = ( {isLoading || isStreaming ? ( - + ) : shouldShowCta ? ( = ({ timestamp }) => { ); }; -const ProgressSpinner: React.FC = () => ( +const ProgressSpinner: React.FC<{ kind: 'streaming' | 'loading' }> = ({ kind }) => ( <> - + {kind === 'streaming' ? ( + + ) : ( + + )} From d8d8040cea3cf2fa2b4acacc6356f6e626a0ec7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 5 Feb 2020 13:49:04 +0100 Subject: [PATCH 041/117] Tweak initialization logic for LogPositionState Some of the intial state for the LogPositionState might come from the URL. Ensure that state is in place before fetching any entries to prevent double fetching. --- .../scrollable_log_text_stream_view.tsx | 2 +- .../containers/logs/log_entries/index.ts | 23 +++++++++++----- .../logs/log_position/log_position_state.ts | 14 ++++++++++ .../with_log_position_url_state.tsx | 27 +++++++++---------- .../pages/logs/stream/page_providers.tsx | 8 ++++++ 5 files changed, 51 insertions(+), 23 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx index d90f0b1c872258..c3d22d177d626a 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx @@ -96,7 +96,7 @@ export class ScrollableLogTextStreamView extends React.PureComponent< targetId: getStreamItemId(getStreamItemBeforeTimeKey(nextProps.items, nextProps.target!)), items: nextItems, }; - } else if (!nextProps.target || !hasItems) { + } else if (!hasItems) { return { target: null, targetId: null, diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts index 7b0ba67523a091..3a5c765f94d764 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts @@ -106,13 +106,14 @@ const shouldFetchNewEntries = ({ bottomCursor, startDate, endDate, -}: FetchEntriesParams & LogEntriesStateParams & { prevParams: FetchEntriesParams }) => { - const shouldLoadWithNewDates = - startDate !== prevParams.startDate || endDate !== prevParams.endDate; - - const shouldLoadWithNewFilter = filterQuery !== prevParams.filterQuery; +}: FetchEntriesParams & LogEntriesStateParams & { prevParams: FetchEntriesParams | undefined }) => { + const shouldLoadWithNewDates = prevParams + ? startDate !== prevParams.startDate || endDate !== prevParams.endDate + : true; + const shouldLoadWithNewFilter = prevParams ? filterQuery !== prevParams.filterQuery : true; const shouldLoadAroundNewPosition = timeKey && (!topCursor || !bottomCursor || !timeKeyIsBetween(topCursor, bottomCursor, timeKey)); + return shouldLoadWithNewDates || shouldLoadWithNewFilter || shouldLoadAroundNewPosition; }; @@ -137,11 +138,11 @@ const useFetchEntriesEffect = ( dispatch: Dispatch, props: LogEntriesProps ) => { - const [prevParams, cachePrevParams] = useState(props); + const [prevParams, cachePrevParams] = useState(); const [startedStreaming, setStartedStreaming] = useState(false); const runFetchNewEntriesRequest = async (overrides: Partial = {}) => { - if (!props.startTimestamp || !props.endTimestamp || !props.timeKey) { + if (!props.startTimestamp || !props.endTimestamp) { return; } @@ -163,6 +164,14 @@ const useFetchEntriesEffect = ( const { data: payload } = await fetchLogEntries(fetchArgs); dispatch({ type: Action.ReceiveNewEntries, payload }); + + // Move position to the bottom if it's the first load. + // Do it in the next tick to allow the `dispatch` to fire + if (!props.timeKey && payload.bottomCursor) { + setTimeout(() => { + props.jumpToTargetPosition(payload.bottomCursor!); + }); + } } catch (e) { dispatch({ type: Action.ErrorOnNewEntries }); } diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts index 120587a36b1b3c..7e15cdc573284d 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts @@ -25,6 +25,7 @@ interface VisiblePositions { } export interface LogPositionStateParams { + initialized: boolean; targetPosition: TimeKeyOrNull; isStreaming: boolean; liveStreamingInterval: number; @@ -41,6 +42,7 @@ export interface LogPositionStateParams { } export interface LogPositionCallbacks { + initialize: () => void; jumpToTargetPosition: (pos: TimeKeyOrNull) => void; jumpToTargetPositionTime: (time: number) => void; reportVisiblePositions: (visPos: VisiblePositions) => void; @@ -75,6 +77,16 @@ const useVisibleMidpoint = (middleKey: TimeKeyOrNull, targetPosition: TimeKeyOrN }; export const useLogPositionState: () => LogPositionStateParams & LogPositionCallbacks = () => { + // Flag to determine if `LogPositionState` has been fully initialized. + // + // When the page loads, there might be initial state in the URL. We want to + // prevent the entries from showing until we have processed that initial + // state. That prevents double fetching. + const [initialized, setInitialized] = useState(false); + const initialize = useCallback(() => { + setInitialized(true); + }, [setInitialized]); + const [targetPosition, jumpToTargetPosition] = useState(null); const [isStreaming, setIsStreaming] = useState(false); const [liveStreamingInterval, setLiveStreamingInterval] = useState(10000); @@ -134,6 +146,7 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall const endTimestamp = useMemo(() => datemathToEpochMillis(dateRange.endDate), [endTimestampDep]); const state = { + initialized, targetPosition, isStreaming, firstVisiblePosition: startKey, @@ -149,6 +162,7 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall }; const callbacks = { + initialize, jumpToTargetPosition, jumpToTargetPositionTime: useCallback( (time: number) => jumpToTargetPosition({ tiebreaker: 0, time }), diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx index ba964374c35cf8..12576d91f537e6 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx @@ -27,12 +27,12 @@ export const WithLogPositionUrlState = () => { visibleMidpoint, isStreaming, jumpToTargetPosition, - jumpToTargetPositionTime, startLiveStreaming, stopLiveStreaming, startDate, endDate, updateDateRange, + initialize, } = useContext(LogPositionState.Context); const urlState = useMemo( () => ({ @@ -68,24 +68,21 @@ export const WithLogPositionUrlState = () => { } }} onInitialize={(initialUrlState: LogPositionUrlState | undefined) => { - if (!initialUrlState) { - jumpToTargetPositionTime(Date.now()); - return; - } + if (initialUrlState) { + if (initialUrlState.start || initialUrlState.end) { + updateDateRange({ startDate: initialUrlState.start, endDate: initialUrlState.end }); + } - if (initialUrlState.start || initialUrlState.end) { - updateDateRange({ startDate: initialUrlState.start, endDate: initialUrlState.end }); - } + if (initialUrlState.position) { + jumpToTargetPosition(initialUrlState.position); + } - if (initialUrlState.position) { - jumpToTargetPosition(initialUrlState.position); - } else { - jumpToTargetPositionTime(Date.now()); + if (initialUrlState.streamLive) { + startLiveStreaming(); + } } - if (initialUrlState.streamLive) { - startLiveStreaming(); - } + initialize(); }} /> ); diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx index 1abc794744e42a..a8ab8b5455fb2a 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx @@ -39,6 +39,7 @@ const LogEntriesStateProvider: React.FC = ({ children }) => { isStreaming, jumpToTargetPosition, liveStreamingInterval, + initialized, } = useContext(LogPositionState.Context); const { filterQuery } = useContext(LogFilterState.Context); @@ -56,6 +57,13 @@ const LogEntriesStateProvider: React.FC = ({ children }) => { isStreaming, jumpToTargetPosition, }; + + // Don't initialize the entries until the position has been fully intialized. + // See `` + if (!initialized) { + return null; + } + return {children}; }; From 2f4498493e18be652dc039e76ccb557b779e319c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 5 Feb 2020 16:14:01 +0100 Subject: [PATCH 042/117] Normalize `*Date` and `*Timestamp` names in the APIs --- .../common/http_api/log_entries/entries.ts | 4 +- .../common/http_api/log_entries/summary.ts | 4 +- .../containers/logs/log_entries/index.ts | 8 ++-- .../log_highlights/log_entry_highlights.tsx | 4 +- .../log_highlights/log_summary_highlights.ts | 4 +- .../logs/log_summary/log_summary.tsx | 6 +-- .../log_entries/kibana_log_entries_adapter.ts | 20 ++++++---- .../log_entries_domain/log_entries_domain.ts | 30 +++++++------- .../server/routes/log_entries/entries.ts | 16 +++++--- .../server/routes/log_entries/highlights.ts | 10 ++--- .../server/routes/log_entries/summary.ts | 10 ++--- .../routes/log_entries/summary_highlights.ts | 17 +++++--- .../api_integration/apis/infra/log_entries.ts | 40 +++++++++---------- .../apis/infra/log_entry_highlights.ts | 12 +++--- .../api_integration/apis/infra/log_summary.ts | 11 ++--- .../apis/infra/logs_without_millis.ts | 10 ++--- 16 files changed, 112 insertions(+), 94 deletions(-) diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts b/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts index e99ec5e8521dac..28c33db9bdb044 100644 --- a/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts +++ b/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts @@ -12,8 +12,8 @@ export const LOG_ENTRIES_PATH = '/api/log_entries/entries'; export const logEntriesBaseRequestRT = rt.intersection([ rt.type({ sourceId: rt.string, - startDate: rt.number, - endDate: rt.number, + startTimestamp: rt.number, + endTimestamp: rt.number, }), rt.partial({ query: rt.string, diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_entries/summary.ts b/x-pack/legacy/plugins/infra/common/http_api/log_entries/summary.ts index 4a2c0db0e995ea..6af4b7c592ab6a 100644 --- a/x-pack/legacy/plugins/infra/common/http_api/log_entries/summary.ts +++ b/x-pack/legacy/plugins/infra/common/http_api/log_entries/summary.ts @@ -10,8 +10,8 @@ export const LOG_ENTRIES_SUMMARY_PATH = '/api/log_entries/summary'; export const logEntriesSummaryRequestRT = rt.type({ sourceId: rt.string, - startDate: rt.number, - endDate: rt.number, + startTimestamp: rt.number, + endTimestamp: rt.number, bucketSize: rt.number, query: rt.union([rt.string, rt.undefined, rt.null]), }); diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts index 3a5c765f94d764..6dc3f04a8a0ad5 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts @@ -151,8 +151,8 @@ const useFetchEntriesEffect = ( try { const fetchArgs: LogEntriesRequest = { sourceId: overrides.sourceId || props.sourceId, - startDate: overrides.startTimestamp || props.startTimestamp, - endDate: overrides.endTimestamp || props.endTimestamp, + startTimestamp: overrides.startTimestamp || props.startTimestamp, + endTimestamp: overrides.endTimestamp || props.endTimestamp, query: overrides.filterQuery || props.filterQuery || undefined, // FIXME }; @@ -195,8 +195,8 @@ const useFetchEntriesEffect = ( try { const fetchArgs: LogEntriesRequest = { sourceId: props.sourceId, - startDate: props.startTimestamp, - endDate: props.endTimestamp, + startTimestamp: props.startTimestamp, + endTimestamp: props.endTimestamp, query: props.filterQuery || undefined, // FIXME }; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx index 104b20243205ea..456a6ba2fe83f1 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx @@ -32,8 +32,8 @@ export const useLogEntryHighlights = ( return await fetchLogEntriesHighlights({ sourceId, - startDate: getPreviousTimeKey(startKey).time, - endDate: getNextTimeKey(endKey).time, + startTimestamp: getPreviousTimeKey(startKey).time, + endTimestamp: getNextTimeKey(endKey).time, query: filterQuery || undefined, highlightTerms, }); 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 index f20ec3ff34b0b0..9856dd8fa1d3f1 100644 --- 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 @@ -38,8 +38,8 @@ export const useLogSummaryHighlights = ( return await fetchLogSummaryHighlights({ sourceId, - startDate: startTimestamp, - endDate: endTimestamp, + startTimestamp, + endTimestamp, bucketSize, query: filterQuery, highlightTerms, 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 e021e83dd7c4e8..172b4c14c90a0e 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,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { useState, useMemo } from 'react'; +import { useState } from 'react'; import { useCancellableEffect } from '../../../utils/cancellable_effect'; import { fetchLogSummary } from './api/fetch_log_summary'; @@ -32,8 +32,8 @@ export const useLogSummary = ( fetchLogSummary({ sourceId, - startDate: startTimestamp, - endDate: endTimestamp, + startTimestamp, + endTimestamp, bucketSize, query: filterQuery, }).then(response => { 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 b936d79a8edcdf..36c63d884482f7 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 @@ -91,7 +91,7 @@ export class InfraKibanaLogEntriesAdapter implements LogEntriesAdapter { fields: string[], params: LogEntriesParams ): Promise { - const { startDate, endDate, query, cursor, size, highlightTerm } = params; + const { startTimestamp, endTimestamp, query, cursor, size, highlightTerm } = params; const { sortDirection, searchAfterClause } = processCursor(cursor); @@ -137,8 +137,8 @@ export class InfraKibanaLogEntriesAdapter implements LogEntriesAdapter { { range: { [sourceConfiguration.fields.timestamp]: { - gte: startDate, - lte: endDate, + gte: startTimestamp, + lte: endTimestamp, format: TIMESTAMP_FORMAT, }, }, @@ -190,12 +190,16 @@ export class InfraKibanaLogEntriesAdapter implements LogEntriesAdapter { public async getContainedLogSummaryBuckets( requestContext: RequestHandlerContext, sourceConfiguration: InfraSourceConfiguration, - start: number, - end: number, + startTimestamp: number, + endTimestamp: number, bucketSize: number, filterQuery?: LogEntryQuery ): Promise { - const bucketIntervalStarts = timeMilliseconds(new Date(start), new Date(end), bucketSize); + const bucketIntervalStarts = timeMilliseconds( + new Date(startTimestamp), + new Date(endTimestamp), + bucketSize + ); const query = { allowNoIndices: true, @@ -233,8 +237,8 @@ export class InfraKibanaLogEntriesAdapter implements LogEntriesAdapter { { range: { [sourceConfiguration.fields.timestamp]: { - gte: start, - lte: end, + gte: startTimestamp, + lte: endTimestamp, format: TIMESTAMP_FORMAT, }, }, 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 2f71d56e1e0e31..da58e8acc8ca33 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 @@ -36,16 +36,16 @@ import { } from './message'; export interface LogEntriesParams { - startDate: number; - endDate: number; + startTimestamp: number; + endTimestamp: number; size?: number; query?: JsonObject; cursor?: { before: LogEntriesCursor | 'last' } | { after: LogEntriesCursor | 'first' }; highlightTerm?: string; } export interface LogEntriesAroundParams { - startDate: number; - endDate: number; + startTimestamp: number; + endTimestamp: number; size?: number; center: LogEntriesCursor; query?: JsonObject; @@ -67,7 +67,7 @@ export class InfraLogEntriesDomain { sourceId: string, params: LogEntriesAroundParams ) { - const { startDate, endDate, center, query, size, highlightTerm } = params; + const { startTimestamp, endTimestamp, center, query, size, highlightTerm } = params; /* * For odd sizes we will round this value down for the first half, and up @@ -80,8 +80,8 @@ export class InfraLogEntriesDomain { const halfSize = (size || LOG_ENTRIES_PAGE_SIZE) / 2; const entriesBefore = await this.getLogEntries(requestContext, sourceId, { - startDate, - endDate, + startTimestamp, + endTimestamp, query, cursor: { before: center }, size: Math.floor(halfSize), @@ -101,8 +101,8 @@ export class InfraLogEntriesDomain { : { time: center.time - 1, tiebreaker: 0 }; const entriesAfter = await this.getLogEntries(requestContext, sourceId, { - startDate, - endDate, + startTimestamp, + endTimestamp, query, cursor: { after: cursorAfter }, size: Math.ceil(halfSize), @@ -368,8 +368,8 @@ export class InfraLogEntriesDomain { public async getLogSummaryHighlightBucketsBetween( requestContext: RequestHandlerContext, sourceId: string, - start: number, - end: number, + startTimestamp: number, + endTimestamp: number, bucketSize: number, highlightQueries: string[], filterQuery?: LogEntryQuery @@ -396,8 +396,8 @@ export class InfraLogEntriesDomain { const summaryBuckets = await this.adapter.getContainedLogSummaryBuckets( requestContext, configuration, - start, - end, + startTimestamp, + endTimestamp, bucketSize, query ); @@ -476,8 +476,8 @@ export interface LogEntriesAdapter { getContainedLogSummaryBuckets( requestContext: RequestHandlerContext, sourceConfiguration: InfraSourceConfiguration, - start: number, - end: number, + startTimestamp: number, + endTimestamp: number, bucketSize: number, filterQuery?: LogEntryQuery ): Promise; diff --git a/x-pack/legacy/plugins/infra/server/routes/log_entries/entries.ts b/x-pack/legacy/plugins/infra/server/routes/log_entries/entries.ts index 618e496982ed3f..51505edd70ce5a 100644 --- a/x-pack/legacy/plugins/infra/server/routes/log_entries/entries.ts +++ b/x-pack/legacy/plugins/infra/server/routes/log_entries/entries.ts @@ -38,13 +38,19 @@ export const initLogEntriesRoute = ({ framework, logEntries }: InfraBackendLibs) fold(throwErrors(Boom.badRequest), identity) ); - const { startDate, endDate, sourceId, query, size } = payload; + const { + startTimestamp: startTimestamp, + endTimestamp: endTimestamp, + sourceId, + query, + size, + } = payload; let entries; if ('center' in payload) { entries = await logEntries.getLogEntriesAround__new(requestContext, sourceId, { - startDate, - endDate, + startTimestamp, + endTimestamp, query: parseFilterQuery(query), center: payload.center, size, @@ -58,8 +64,8 @@ export const initLogEntriesRoute = ({ framework, logEntries }: InfraBackendLibs) } entries = await logEntries.getLogEntries(requestContext, sourceId, { - startDate, - endDate, + startTimestamp, + endTimestamp, query: parseFilterQuery(query), cursor, size, diff --git a/x-pack/legacy/plugins/infra/server/routes/log_entries/highlights.ts b/x-pack/legacy/plugins/infra/server/routes/log_entries/highlights.ts index 8af81a6ee313dc..d4164550534057 100644 --- a/x-pack/legacy/plugins/infra/server/routes/log_entries/highlights.ts +++ b/x-pack/legacy/plugins/infra/server/routes/log_entries/highlights.ts @@ -38,7 +38,7 @@ export const initLogEntriesHighlightsRoute = ({ framework, logEntries }: InfraBa fold(throwErrors(Boom.badRequest), identity) ); - const { startDate, endDate, sourceId, query, size, highlightTerms } = payload; + const { startTimestamp, endTimestamp, sourceId, query, size, highlightTerms } = payload; let entriesPerHighlightTerm; @@ -46,8 +46,8 @@ export const initLogEntriesHighlightsRoute = ({ framework, logEntries }: InfraBa entriesPerHighlightTerm = await Promise.all( highlightTerms.map(highlightTerm => logEntries.getLogEntriesAround__new(requestContext, sourceId, { - startDate, - endDate, + startTimestamp, + endTimestamp, query: parseFilterQuery(query), center: payload.center, size, @@ -66,8 +66,8 @@ export const initLogEntriesHighlightsRoute = ({ framework, logEntries }: InfraBa entriesPerHighlightTerm = await Promise.all( highlightTerms.map(highlightTerm => logEntries.getLogEntries(requestContext, sourceId, { - startDate, - endDate, + startTimestamp, + endTimestamp, query: parseFilterQuery(query), cursor, size, diff --git a/x-pack/legacy/plugins/infra/server/routes/log_entries/summary.ts b/x-pack/legacy/plugins/infra/server/routes/log_entries/summary.ts index 05643adbe781fa..8783c9984b778a 100644 --- a/x-pack/legacy/plugins/infra/server/routes/log_entries/summary.ts +++ b/x-pack/legacy/plugins/infra/server/routes/log_entries/summary.ts @@ -36,13 +36,13 @@ export const initLogEntriesSummaryRoute = ({ framework, logEntries }: InfraBacke logEntriesSummaryRequestRT.decode(request.body), fold(throwErrors(Boom.badRequest), identity) ); - const { sourceId, startDate, endDate, bucketSize, query } = payload; + const { sourceId, startTimestamp, endTimestamp, bucketSize, query } = payload; const buckets = await logEntries.getLogSummaryBucketsBetween( requestContext, sourceId, - startDate, - endDate, + startTimestamp, + endTimestamp, bucketSize, parseFilterQuery(query) ); @@ -50,8 +50,8 @@ export const initLogEntriesSummaryRoute = ({ framework, logEntries }: InfraBacke return response.ok({ body: logEntriesSummaryResponseRT.encode({ data: { - start: startDate, - end: endDate, + start: startTimestamp, + end: endTimestamp, buckets, }, }), diff --git a/x-pack/legacy/plugins/infra/server/routes/log_entries/summary_highlights.ts b/x-pack/legacy/plugins/infra/server/routes/log_entries/summary_highlights.ts index ecccd931bb3718..c8ad9268b9d90b 100644 --- a/x-pack/legacy/plugins/infra/server/routes/log_entries/summary_highlights.ts +++ b/x-pack/legacy/plugins/infra/server/routes/log_entries/summary_highlights.ts @@ -39,13 +39,20 @@ export const initLogEntriesSummaryHighlightsRoute = ({ logEntriesSummaryHighlightsRequestRT.decode(request.body), fold(throwErrors(Boom.badRequest), identity) ); - const { sourceId, startDate, endDate, bucketSize, query, highlightTerms } = payload; + const { + sourceId, + startTimestamp, + endTimestamp, + bucketSize, + query, + highlightTerms, + } = payload; const bucketsPerHighlightTerm = await logEntries.getLogSummaryHighlightBucketsBetween( requestContext, sourceId, - startDate, - endDate, + startTimestamp, + endTimestamp, bucketSize, highlightTerms, parseFilterQuery(query) @@ -54,8 +61,8 @@ export const initLogEntriesSummaryHighlightsRoute = ({ return response.ok({ body: logEntriesSummaryHighlightsResponseRT.encode({ data: bucketsPerHighlightTerm.map(buckets => ({ - start: startDate, - end: endDate, + start: startTimestamp, + end: endTimestamp, buckets, })), }), diff --git a/x-pack/test/api_integration/apis/infra/log_entries.ts b/x-pack/test/api_integration/apis/infra/log_entries.ts index 8f0b0f14f7eb7d..48325c1a8d9da0 100644 --- a/x-pack/test/api_integration/apis/infra/log_entries.ts +++ b/x-pack/test/api_integration/apis/infra/log_entries.ts @@ -129,8 +129,8 @@ export default function({ getService }: FtrProviderContext) { .send( logEntriesRequestRT.encode({ sourceId: 'default', - startDate: EARLIEST_KEY_WITH_DATA.time, - endDate: KEY_WITHIN_DATA_RANGE.time, + startTimestamp: EARLIEST_KEY_WITH_DATA.time, + endTimestamp: KEY_WITHIN_DATA_RANGE.time, }) ) .expect(200); @@ -164,8 +164,8 @@ export default function({ getService }: FtrProviderContext) { .send( logEntriesRequestRT.encode({ sourceId: 'default', - startDate: EARLIEST_KEY_WITH_DATA.time, - endDate: KEY_WITHIN_DATA_RANGE.time, + startTimestamp: EARLIEST_KEY_WITH_DATA.time, + endTimestamp: KEY_WITHIN_DATA_RANGE.time, size: 10, }) ); @@ -180,8 +180,8 @@ export default function({ getService }: FtrProviderContext) { .send( logEntriesRequestRT.encode({ sourceId: 'default', - startDate: EARLIEST_KEY_WITH_DATA.time, - endDate: KEY_WITHIN_DATA_RANGE.time, + startTimestamp: EARLIEST_KEY_WITH_DATA.time, + endTimestamp: KEY_WITHIN_DATA_RANGE.time, after: firstPage.data.bottomCursor!, size: 10, }) @@ -197,8 +197,8 @@ export default function({ getService }: FtrProviderContext) { .send( logEntriesRequestRT.encode({ sourceId: 'default', - startDate: EARLIEST_KEY_WITH_DATA.time, - endDate: KEY_WITHIN_DATA_RANGE.time, + startTimestamp: EARLIEST_KEY_WITH_DATA.time, + endTimestamp: KEY_WITHIN_DATA_RANGE.time, size: 20, }) ); @@ -223,8 +223,8 @@ export default function({ getService }: FtrProviderContext) { .send( logEntriesRequestRT.encode({ sourceId: 'default', - startDate: KEY_WITHIN_DATA_RANGE.time, - endDate: LATEST_KEY_WITH_DATA.time, + startTimestamp: KEY_WITHIN_DATA_RANGE.time, + endTimestamp: LATEST_KEY_WITH_DATA.time, before: 'last', size: 10, }) @@ -240,8 +240,8 @@ export default function({ getService }: FtrProviderContext) { .send( logEntriesRequestRT.encode({ sourceId: 'default', - startDate: KEY_WITHIN_DATA_RANGE.time, - endDate: LATEST_KEY_WITH_DATA.time, + startTimestamp: KEY_WITHIN_DATA_RANGE.time, + endTimestamp: LATEST_KEY_WITH_DATA.time, before: lastPage.data.topCursor!, size: 10, }) @@ -257,8 +257,8 @@ export default function({ getService }: FtrProviderContext) { .send( logEntriesRequestRT.encode({ sourceId: 'default', - startDate: KEY_WITHIN_DATA_RANGE.time, - endDate: LATEST_KEY_WITH_DATA.time, + startTimestamp: KEY_WITHIN_DATA_RANGE.time, + endTimestamp: LATEST_KEY_WITH_DATA.time, before: 'last', size: 20, }) @@ -284,8 +284,8 @@ export default function({ getService }: FtrProviderContext) { .send( logEntriesRequestRT.encode({ sourceId: 'default', - startDate: EARLIEST_KEY_WITH_DATA.time, - endDate: LATEST_KEY_WITH_DATA.time, + startTimestamp: EARLIEST_KEY_WITH_DATA.time, + endTimestamp: LATEST_KEY_WITH_DATA.time, center: KEY_WITHIN_DATA_RANGE, }) ) @@ -305,8 +305,8 @@ export default function({ getService }: FtrProviderContext) { }); it('Handles empty responses', async () => { - const startDate = Date.now() + 1000; - const endDate = Date.now() + 5000; + const startTimestamp = Date.now() + 1000; + const endTimestamp = Date.now() + 5000; const { body } = await supertest .post(LOG_ENTRIES_PATH) @@ -314,8 +314,8 @@ export default function({ getService }: FtrProviderContext) { .send( logEntriesRequestRT.encode({ sourceId: 'default', - startDate, - endDate, + startTimestamp, + endTimestamp, }) ) .expect(200); 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 66701de2704a68..7e5e15a863865c 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 @@ -69,8 +69,8 @@ export default function({ getService }: FtrProviderContext) { .send( logEntriesHighlightsRequestRT.encode({ sourceId: 'default', - startDate: KEY_BEFORE_START.time, - endDate: KEY_AFTER_END.time, + startTimestamp: KEY_BEFORE_START.time, + endTimestamp: KEY_AFTER_END.time, highlightTerms: ['message of document 0'], }) ) @@ -119,8 +119,8 @@ export default function({ getService }: FtrProviderContext) { .send( logEntriesHighlightsRequestRT.encode({ sourceId: 'default', - startDate: KEY_BEFORE_START.time, - endDate: KEY_AFTER_END.time, + startTimestamp: KEY_BEFORE_START.time, + endTimestamp: KEY_AFTER_END.time, highlightTerms: ['generate_test_data/simple_logs'], }) ) @@ -155,8 +155,8 @@ export default function({ getService }: FtrProviderContext) { .send( logEntriesHighlightsRequestRT.encode({ sourceId: 'default', - startDate: KEY_BEFORE_START.time, - endDate: KEY_AFTER_END.time, + startTimestamp: KEY_BEFORE_START.time, + endTimestamp: KEY_AFTER_END.time, query: JSON.stringify({ multi_match: { query: 'host-a', type: 'phrase', lenient: true }, }), diff --git a/x-pack/test/api_integration/apis/infra/log_summary.ts b/x-pack/test/api_integration/apis/infra/log_summary.ts index 017b1c6f7ab458..7ac0ba30b067d7 100644 --- a/x-pack/test/api_integration/apis/infra/log_summary.ts +++ b/x-pack/test/api_integration/apis/infra/log_summary.ts @@ -41,9 +41,10 @@ export default function({ getService }: FtrProviderContext) { after(() => esArchiver.unload('infra/metrics_and_logs')); it('should return empty and non-empty consecutive buckets', async () => { - const startDate = EARLIEST_TIME_WITH_DATA; - const endDate = LATEST_TIME_WITH_DATA + (LATEST_TIME_WITH_DATA - EARLIEST_TIME_WITH_DATA); - const bucketSize = Math.ceil((endDate - startDate) / 10); + const startTimestamp = EARLIEST_TIME_WITH_DATA; + const endTimestamp = + LATEST_TIME_WITH_DATA + (LATEST_TIME_WITH_DATA - EARLIEST_TIME_WITH_DATA); + const bucketSize = Math.ceil((endTimestamp - startTimestamp) / 10); const { body } = await supertest .post(LOG_ENTRIES_SUMMARY_PATH) @@ -51,8 +52,8 @@ export default function({ getService }: FtrProviderContext) { .send( logEntriesSummaryRequestRT.encode({ sourceId: 'default', - startDate, - endDate, + startTimestamp, + endTimestamp, bucketSize, query: null, }) diff --git a/x-pack/test/api_integration/apis/infra/logs_without_millis.ts b/x-pack/test/api_integration/apis/infra/logs_without_millis.ts index ce2319259985b8..8c30a7a480715a 100644 --- a/x-pack/test/api_integration/apis/infra/logs_without_millis.ts +++ b/x-pack/test/api_integration/apis/infra/logs_without_millis.ts @@ -92,9 +92,9 @@ export default function({ getService }: FtrProviderContext) { }); it('logSummaryBetween should return non-empty buckets', async () => { - const startDate = EARLIEST_KEY_WITH_DATA.time; - const endDate = LATEST_KEY_WITH_DATA.time + 1; // the interval end is exclusive - const bucketSize = Math.ceil((endDate - startDate) / 10); + const startTimestamp = EARLIEST_KEY_WITH_DATA.time; + const endTimestamp = LATEST_KEY_WITH_DATA.time + 1; // the interval end is exclusive + const bucketSize = Math.ceil((endTimestamp - startTimestamp) / 10); const { body } = await supertest .post(LOG_ENTRIES_SUMMARY_PATH) @@ -102,8 +102,8 @@ export default function({ getService }: FtrProviderContext) { .send( logEntriesSummaryRequestRT.encode({ sourceId: 'default', - startDate, - endDate, + startTimestamp, + endTimestamp, bucketSize, query: null, }) From d7d738b3295c4fd1b8c84032469c97fe33c79afb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 5 Feb 2020 16:38:56 +0100 Subject: [PATCH 043/117] WIP: cleanup --- .../log_entry_field_column.tsx | 6 +- .../log_entry_message_column.tsx | 4 +- .../scrollable_log_text_stream_view.tsx | 10 -- .../components/logging/log_time_controls.tsx | 97 ------------------- .../public/pages/logs/stream/page_toolbar.tsx | 13 --- 5 files changed, 5 insertions(+), 125 deletions(-) delete mode 100644 x-pack/legacy/plugins/infra/public/components/logging/log_time_controls.tsx diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx index e82e9cf362182e..52ac95d8da6ee6 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx @@ -12,14 +12,14 @@ import euiStyled, { css } from '../../../../../../common/eui_styled_components'; import { isFieldColumn, isHighlightFieldColumn, - LogEntryColumn, LogEntryHighlightColumn, } from '../../../utils/log_entry'; import { ActiveHighlightMarker, highlightFieldValue, HighlightMarker } from './highlighting'; import { LogEntryColumnContent } from './log_entry_column'; +import { LogColumn } from '../../../../common/http_api'; interface LogEntryFieldColumnProps { - columnValue: LogEntryColumn; + columnValue: LogColumn; highlights: LogEntryHighlightColumn[]; isActiveHighlight: boolean; isHighlighted: boolean; @@ -39,7 +39,7 @@ export const LogEntryFieldColumn: React.FunctionComponent updateDateRange({ endDate: newDate })} onStreamStart={() => startLiveStreaming()} - // onLoadMore={this.handleLoadNewerItems} /> {isScrollLocked && ( { - const { loadNewerItems } = this.props; - - if (loadNewerItems) { - loadNewerItems(); - } - }; - // this is actually a method but not recognized as such // eslint-disable-next-line @typescript-eslint/member-ordering private handleVisibleChildrenChange = callWithoutRepeats( diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_time_controls.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_time_controls.tsx deleted file mode 100644 index 3653a6d6bbeaed..00000000000000 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_time_controls.tsx +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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 { EuiDatePicker, EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import moment, { Moment } from 'moment'; -import React from 'react'; -import { FixedDatePicker } from '../fixed_datepicker'; - -const noop = () => undefined; - -interface LogTimeControlsProps { - currentTime: number | null; - startLiveStreaming: () => any; - stopLiveStreaming: () => void; - isLiveStreaming: boolean; - jumpToTime: (time: number) => any; -} - -export class LogTimeControls extends React.PureComponent { - public render() { - const { currentTime, isLiveStreaming } = this.props; - - const currentMoment = currentTime ? moment(currentTime) : null; - if (isLiveStreaming) { - return ( - - - - - - - - - - - ); - } else { - return ( - - - - - - - - - - - ); - } - } - - private handleChangeDate = (date: Moment | null) => { - if (date !== null) { - this.props.jumpToTime(date.valueOf()); - } - }; - - private startLiveStreaming = () => { - this.props.startLiveStreaming(); - }; - - private stopLiveStreaming = () => { - this.props.stopLiveStreaming(); - }; -} diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx index 4f65d48e361fc0..7ea0281f5bbcbb 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx @@ -16,7 +16,6 @@ import { LogHighlightsState } from '../../../containers/logs/log_highlights/log_ import { LogMinimapScaleControls } from '../../../components/logging/log_minimap_scale_controls'; import { LogTextScaleControls } from '../../../components/logging/log_text_scale_controls'; import { LogTextWrapControls } from '../../../components/logging/log_text_wrap_controls'; -import { LogTimeControls } from '../../../components/logging/log_time_controls'; import { LogFlyout } from '../../../containers/logs/log_flyout'; import { LogViewConfiguration } from '../../../containers/logs/log_view_configuration'; import { LogFilterState } from '../../../containers/logs/log_filter'; @@ -55,9 +54,7 @@ export const LogsToolbar = () => { goToNextHighlight, } = useContext(LogHighlightsState.Context); const { - visibleMidpointTime, isStreaming, - jumpToTargetPositionTime, startLiveStreaming, stopLiveStreaming, startDate, @@ -152,16 +149,6 @@ export const LogsToolbar = () => { setLiveStreamingInterval(refreshInterval); }} /> - {/* { - startLiveStreaming(); - setSurroundingLogsId(null); - }} - stopLiveStreaming={stopLiveStreaming} - /> */} From c2c8b8df797977f07f4665433aa70a51e0295574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 5 Feb 2020 17:46:00 +0100 Subject: [PATCH 044/117] Fix `logSummary` tests --- .../logs/log_summary/log_summary.test.tsx | 91 +++++++++---------- 1 file changed, 42 insertions(+), 49 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx index 2bbcc22b150e46..ff37ea466d6d8a 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx @@ -9,6 +9,7 @@ import { renderHook } from '@testing-library/react-hooks'; import { useLogSummary } from './log_summary'; import { fetchLogSummary } from './api/fetch_log_summary'; +import { datemathToEpochMillis } from '../../../utils/datemath'; // Typescript doesn't know that `fetchLogSummary` is a jest mock. // We use a second variable with a type cast to help the compiler further down the line. @@ -21,20 +22,27 @@ describe('useLogSummary hook', () => { }); it('provides an empty list of buckets by default', () => { - const { result } = renderHook(() => useLogSummary('SOURCE_ID', null, 1000, null)); + const { result } = renderHook(() => useLogSummary('SOURCE_ID', null, null, null, null, null)); expect(result.current.buckets).toEqual([]); }); it('queries for new summary buckets when the source id changes', async () => { - const firstMockResponse = createMockResponse([{ start: 99000, end: 101000, entriesCount: 1 }]); - const secondMockResponse = createMockResponse([{ start: 99000, end: 101000, entriesCount: 2 }]); + const { startDate, endDate, startTimestamp, endTimestamp } = createMockDateRange(); + + const firstMockResponse = createMockResponse([ + { start: startTimestamp, end: endTimestamp, entriesCount: 1 }, + ]); + const secondMockResponse = createMockResponse([ + { start: startTimestamp, end: endTimestamp, entriesCount: 2 }, + ]); fetchLogSummaryMock .mockResolvedValueOnce(firstMockResponse) .mockResolvedValueOnce(secondMockResponse); const { result, waitForNextUpdate, rerender } = renderHook( - ({ sourceId }) => useLogSummary(sourceId, 100000, 1000, null), + ({ sourceId }) => + useLogSummary(sourceId, startDate, endDate, startTimestamp, endTimestamp, null), { initialProps: { sourceId: 'INITIAL_SOURCE_ID' }, } @@ -63,15 +71,22 @@ describe('useLogSummary hook', () => { }); it('queries for new summary buckets when the filter query changes', async () => { - const firstMockResponse = createMockResponse([{ start: 99000, end: 101000, entriesCount: 1 }]); - const secondMockResponse = createMockResponse([{ start: 99000, end: 101000, entriesCount: 2 }]); + const { startDate, endDate, startTimestamp, endTimestamp } = createMockDateRange(); + + const firstMockResponse = createMockResponse([ + { start: startTimestamp, end: endTimestamp, entriesCount: 1 }, + ]); + const secondMockResponse = createMockResponse([ + { start: startTimestamp, end: endTimestamp, entriesCount: 2 }, + ]); fetchLogSummaryMock .mockResolvedValueOnce(firstMockResponse) .mockResolvedValueOnce(secondMockResponse); const { result, waitForNextUpdate, rerender } = renderHook( - ({ filterQuery }) => useLogSummary('SOURCE_ID', 100000, 1000, filterQuery), + ({ filterQuery }) => + useLogSummary('SOURCE_ID', startDate, endDate, startTimestamp, endTimestamp, filterQuery), { initialProps: { filterQuery: 'INITIAL_FILTER_QUERY' }, } @@ -99,15 +114,17 @@ describe('useLogSummary hook', () => { expect(result.current.buckets).toEqual(secondMockResponse.data.buckets); }); - it('queries for new summary buckets when the midpoint time changes', async () => { + it('queries for new summary buckets when the start and end date changes', async () => { fetchLogSummaryMock .mockResolvedValueOnce(createMockResponse([])) .mockResolvedValueOnce(createMockResponse([])); + const firstRange = createMockDateRange(); const { waitForNextUpdate, rerender } = renderHook( - ({ midpointTime }) => useLogSummary('SOURCE_ID', midpointTime, 1000, null), + ({ startDate, endDate, startTimestamp, endTimestamp }) => + useLogSummary('SOURCE_ID', startDate, endDate, startTimestamp, endTimestamp, null), { - initialProps: { midpointTime: 100000 }, + initialProps: firstRange, } ); @@ -115,54 +132,21 @@ describe('useLogSummary hook', () => { expect(fetchLogSummaryMock).toHaveBeenCalledTimes(1); expect(fetchLogSummaryMock).toHaveBeenLastCalledWith( expect.objectContaining({ - startDate: 98500, - endDate: 101500, - }) - ); - - rerender({ midpointTime: 200000 }); - await waitForNextUpdate(); - - expect(fetchLogSummaryMock).toHaveBeenCalledTimes(2); - expect(fetchLogSummaryMock).toHaveBeenLastCalledWith( - expect.objectContaining({ - startDate: 198500, - endDate: 201500, + startTimestamp: firstRange.startTimestamp, + endTimestamp: firstRange.endTimestamp, }) ); - }); - it('queries for new summary buckets when the interval size changes', async () => { - fetchLogSummaryMock - .mockResolvedValueOnce(createMockResponse([])) - .mockResolvedValueOnce(createMockResponse([])); - - const { waitForNextUpdate, rerender } = renderHook( - ({ intervalSize }) => useLogSummary('SOURCE_ID', 100000, intervalSize, null), - { - initialProps: { intervalSize: 1000 }, - } - ); + const secondRange = createMockDateRange('now-20s', 'now'); - await waitForNextUpdate(); - expect(fetchLogSummaryMock).toHaveBeenCalledTimes(1); - expect(fetchLogSummaryMock).toHaveBeenLastCalledWith( - expect.objectContaining({ - bucketSize: 10, - startDate: 98500, - endDate: 101500, - }) - ); - - rerender({ intervalSize: 2000 }); + rerender(secondRange); await waitForNextUpdate(); expect(fetchLogSummaryMock).toHaveBeenCalledTimes(2); expect(fetchLogSummaryMock).toHaveBeenLastCalledWith( expect.objectContaining({ - bucketSize: 20, - startDate: 97000, - endDate: 103000, + startTimestamp: secondRange.startTimestamp, + endTimestamp: secondRange.endTimestamp, }) ); }); @@ -171,3 +155,12 @@ describe('useLogSummary hook', () => { const createMockResponse = ( buckets: Array<{ start: number; end: number; entriesCount: number }> ) => ({ data: { buckets, start: Number.NEGATIVE_INFINITY, end: Number.POSITIVE_INFINITY } }); + +const createMockDateRange = (startDate = 'now-10s', endDate = 'now') => { + return { + startDate, + endDate, + startTimestamp: datemathToEpochMillis(startDate)!, + endTimestamp: datemathToEpochMillis(endDate)!, + }; +}; From 57b6eaa67ead6570780e2bd534fac045a94d70ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 5 Feb 2020 20:42:52 +0100 Subject: [PATCH 045/117] Fix types --- .../common/http_api/log_entries/entries.ts | 24 +++++++------- .../logging/log_text_stream/item.ts | 3 +- .../log_entry_field_column.tsx | 17 +++------- .../log_entry_message_column.tsx | 33 ++++++++++++------- .../logging/log_text_stream/log_entry_row.tsx | 12 +++---- .../logs/log_highlights/next_and_previous.tsx | 4 +-- .../containers/logs/with_stream_items.ts | 3 +- .../infra/public/utils/log_entry/log_entry.ts | 13 ++++---- .../utils/log_entry/log_entry_highlight.ts | 25 +++++++------- 9 files changed, 67 insertions(+), 67 deletions(-) diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts b/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts index 28c33db9bdb044..fb58f367118ab7 100644 --- a/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts +++ b/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts @@ -52,16 +52,16 @@ export type LogEntriesRequest = rt.TypeOf; // JSON value const valueRT = rt.union([rt.string, rt.number, rt.boolean, rt.object, rt.null, rt.undefined]); -export const logMessagePartRT = rt.union([ - rt.type({ - constant: rt.string, - }), - rt.type({ - field: rt.string, - value: valueRT, - highlights: rt.array(rt.string), - }), -]); +export const logMessagePartConstantRT = rt.type({ + constant: rt.string, +}); +export const logMessagePartFieldRT = rt.type({ + field: rt.string, + value: valueRT, + highlights: rt.array(rt.string), +}); + +export const logMessagePartRT = rt.union([logMessagePartConstantRT, logMessagePartFieldRT]); export const logTimestampColumnRT = rt.type({ columnId: rt.string, timestamp: rt.number }); export const logFieldColumnRT = rt.type({ @@ -83,7 +83,9 @@ export const logEntryRT = rt.type({ columns: rt.array(logColumnRT), }); -export type LogMessagepart = rt.TypeOf; +export type LogMessagePartConstant = rt.TypeOf; +export type LogMessagePartField = rt.TypeOf; +export type LogMessagePart = rt.TypeOf; export type LogTimestampColumn = rt.TypeOf; export type LogFieldColumn = rt.TypeOf; export type LogMessageColumn = rt.TypeOf; diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/item.ts b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/item.ts index b12fa7c385f130..19e8108ee50e85 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/item.ts +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/item.ts @@ -7,7 +7,6 @@ import { bisector } from 'd3-array'; import { compareToTimeKey, TimeKey } from '../../../../common/time'; -import { LogEntryHighlight } from '../../../utils/log_entry'; import { LogEntry } from '../../../../common/http_api'; export type StreamItem = LogEntryStreamItem; @@ -15,7 +14,7 @@ export type StreamItem = LogEntryStreamItem; export interface LogEntryStreamItem { kind: 'logEntry'; logEntry: LogEntry; - highlights: LogEntryHighlight[]; + highlights: LogEntry[]; } export function getStreamItemTimeKey(item: StreamItem) { diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx index 52ac95d8da6ee6..7a0a1437bd26fd 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx @@ -9,18 +9,14 @@ import { darken, transparentize } from 'polished'; import React, { useMemo } from 'react'; import euiStyled, { css } from '../../../../../../common/eui_styled_components'; -import { - isFieldColumn, - isHighlightFieldColumn, - LogEntryHighlightColumn, -} from '../../../utils/log_entry'; +import { isFieldColumn, isHighlightFieldColumn } from '../../../utils/log_entry'; import { ActiveHighlightMarker, highlightFieldValue, HighlightMarker } from './highlighting'; import { LogEntryColumnContent } from './log_entry_column'; import { LogColumn } from '../../../../common/http_api'; interface LogEntryFieldColumnProps { columnValue: LogColumn; - highlights: LogEntryHighlightColumn[]; + highlights: LogColumn[]; isActiveHighlight: boolean; isHighlighted: boolean; isHovered: boolean; @@ -37,12 +33,7 @@ export const LogEntryFieldColumn: React.FunctionComponent { const value = useMemo(() => { if (isFieldColumn(columnValue)) { - // FIXME - try { - return JSON.parse(columnValue.value as string); - } catch (e) { - return columnValue.value; - } + return columnValue.value; } return null; }, [columnValue]); @@ -60,7 +51,7 @@ export const LogEntryFieldColumn: React.FunctionComponent ) : ( highlightFieldValue( - typeof value === 'object' && value != null ? stringify(value) : value, + typeof value === 'string' ? value : stringify(value), isHighlightFieldColumn(firstHighlight) ? firstHighlight.highlights : [], isActiveHighlight ? ActiveHighlightMarker : HighlightMarker ) diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx index b8b75bc7c86504..599ef652a2ae09 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx @@ -5,6 +5,7 @@ */ import React, { memo, useMemo } from 'react'; +import stringify from 'json-stable-stringify'; import euiStyled, { css } from '../../../../../../common/eui_styled_components'; import { @@ -12,17 +13,16 @@ import { isFieldSegment, isHighlightMessageColumn, isMessageColumn, - LogEntryHighlightColumn, - LogEntryMessageSegment, + isHighlightFieldSegment, } from '../../../utils/log_entry'; import { ActiveHighlightMarker, highlightFieldValue, HighlightMarker } from './highlighting'; import { LogEntryColumnContent } from './log_entry_column'; import { hoveredContentStyle } from './text_styles'; -import { LogColumn } from '../../../../common/http_api'; +import { LogColumn, LogMessagePart } from '../../../../common/http_api'; interface LogEntryMessageColumnProps { columnValue: LogColumn; - highlights: LogEntryHighlightColumn[]; + highlights: LogColumn[]; isActiveHighlight: boolean; isHighlighted: boolean; isHovered: boolean; @@ -77,28 +77,39 @@ const MessageColumnContent = euiStyled(LogEntryColumnContent) messageSegments.map((messageSegment, index) => formatMessageSegment( messageSegment, - highlights.map(highlight => - isHighlightMessageColumn(highlight) ? highlight.message[index].highlights : [] - ), + highlights.map(highlight => { + if (isHighlightMessageColumn(highlight)) { + const segment = highlight.message[index]; + if (isHighlightFieldSegment(segment)) { + return segment.highlights; + } + } + return []; + }), isActiveHighlight ) ); const formatMessageSegment = ( - messageSegment: LogEntryMessageSegment, + messageSegment: LogMessagePart, [firstHighlight = []]: string[][], // we only support one highlight for now isActiveHighlight: boolean ): React.ReactNode => { if (isFieldSegment(messageSegment)) { + const value = + typeof messageSegment.value === 'string' + ? messageSegment.value + : stringify(messageSegment.value); + return highlightFieldValue( - messageSegment.value, + value, firstHighlight, isActiveHighlight ? ActiveHighlightMarker : HighlightMarker ); diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx index 21fd277f26346a..ea622fce3c6b1c 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx @@ -8,11 +8,7 @@ import React, { useState, useCallback, useMemo } from 'react'; import euiStyled from '../../../../../../common/eui_styled_components'; -import { - LogEntryHighlight, - LogEntryHighlightColumn, - isTimestampColumn, -} from '../../../utils/log_entry'; +import { isTimestampColumn } from '../../../utils/log_entry'; import { LogColumnConfiguration, isTimestampLogColumnConfiguration, @@ -26,13 +22,13 @@ import { LogEntryDetailsIconColumn } from './log_entry_icon_column'; import { LogEntryMessageColumn } from './log_entry_message_column'; import { LogEntryTimestampColumn } from './log_entry_timestamp_column'; import { monospaceTextStyle } from './text_styles'; -import { LogEntry } from '../../../../common/http_api'; +import { LogEntry, LogColumn } from '../../../../common/http_api'; interface LogEntryRowProps { boundingBoxRef?: React.Ref; columnConfigurations: LogColumnConfiguration[]; columnWidths: LogEntryColumnWidths; - highlights: LogEntryHighlight[]; + highlights: LogEntry[]; isActiveHighlight: boolean; isHighlighted: boolean; logEntry: LogEntry; @@ -85,7 +81,7 @@ export const LogEntryRow = ({ const highlightsByColumnId = useMemo( () => highlights.reduce<{ - [columnId: string]: LogEntryHighlightColumn[]; + [columnId: string]: LogColumn[]; }>( (columnsById, highlight) => highlight.columns.reduce( 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 7557550883f113..689c30a52b5977 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 './log_entry_highlights'; +import { LogEntriesHighlightsResponse } from '../../../../common/http_api'; export const useNextAndPrevious = ({ highlightTerms, @@ -23,7 +23,7 @@ export const useNextAndPrevious = ({ }: { highlightTerms: string[]; jumpToTargetPosition: (target: TimeKey) => void; - logEntryHighlights: LogEntryHighlights | undefined; + logEntryHighlights: LogEntriesHighlightsResponse['data'] | undefined; visibleMidpoint: TimeKey | null; }) => { const [currentTimeKey, setCurrentTimeKey] = useState(null); 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 426277cc36b81c..5c0e245448ce5b 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 @@ -6,7 +6,6 @@ import { useContext, useMemo } from 'react'; import { StreamItem, LogEntryStreamItem } from '../../components/logging/log_text_stream/item'; -import { LogEntryHighlight } from '../../utils/log_entry'; import { RendererFunction } from '../../utils/typed_react'; // deep inporting to avoid a circular import problem import { LogHighlightsState } from './log_highlights/log_highlights'; @@ -47,7 +46,7 @@ export const WithStreamItems: React.FunctionComponent<{ const createLogEntryStreamItem = ( logEntry: LogEntry, - highlights: LogEntryHighlight[] + highlights: LogEntry[] ): LogEntryStreamItem => ({ kind: 'logEntry' as 'logEntry', logEntry, diff --git a/x-pack/legacy/plugins/infra/public/utils/log_entry/log_entry.ts b/x-pack/legacy/plugins/infra/public/utils/log_entry/log_entry.ts index 4fad524d1a42ad..05808daebdfc18 100644 --- a/x-pack/legacy/plugins/infra/public/utils/log_entry/log_entry.ts +++ b/x-pack/legacy/plugins/infra/public/utils/log_entry/log_entry.ts @@ -14,6 +14,9 @@ import { LogTimestampColumn, LogFieldColumn, LogMessageColumn, + LogMessagePart, + LogMessagePartField, + LogMessagePartConstant, } from '../../../common/http_api'; export type LogEntryMessageSegment = InfraLogEntryFields.Message; @@ -48,10 +51,8 @@ export const isMessageColumn = (column: LogColumn): column is LogMessageColumn = export const isFieldColumn = (column: LogColumn): column is LogFieldColumn => column != null && 'field' in column; -export const isConstantSegment = ( - segment: LogEntryMessageSegment -): segment is LogEntryConstantMessageSegment => 'constant' in segment; +export const isConstantSegment = (segment: LogMessagePart): segment is LogMessagePartConstant => + 'constant' in segment; -export const isFieldSegment = ( - segment: LogEntryMessageSegment -): segment is LogEntryFieldMessageSegment => 'field' in segment && 'value' in segment; +export const isFieldSegment = (segment: LogMessagePart): segment is LogMessagePartField => + 'field' in segment && 'value' in segment; diff --git a/x-pack/legacy/plugins/infra/public/utils/log_entry/log_entry_highlight.ts b/x-pack/legacy/plugins/infra/public/utils/log_entry/log_entry_highlight.ts index ff5dfd4665bb49..c19454887e4f11 100644 --- a/x-pack/legacy/plugins/infra/public/utils/log_entry/log_entry_highlight.ts +++ b/x-pack/legacy/plugins/infra/public/utils/log_entry/log_entry_highlight.ts @@ -5,9 +5,14 @@ */ import { InfraLogEntryHighlightFields } from '../../graphql/types'; -import { LogEntry } from '../../../common/http_api'; - -export type LogEntryHighlight = InfraLogEntryHighlightFields.Fragment; +import { + LogEntry, + LogColumn, + LogMessageColumn, + LogFieldColumn, + LogMessagePart, + LogMessagePartField, +} from '../../../common/http_api'; export type LogEntryHighlightColumn = InfraLogEntryHighlightFields.Columns; export type LogEntryHighlightMessageColumn = InfraLogEntryHighlightFields.InfraLogEntryMessageColumnInlineFragment; @@ -20,15 +25,11 @@ export interface LogEntryHighlightsMap { [entryId: string]: LogEntry[]; } -export const isHighlightMessageColumn = ( - column: LogEntryHighlightColumn -): column is LogEntryHighlightMessageColumn => column != null && 'message' in column; +export const isHighlightMessageColumn = (column: LogColumn): column is LogMessageColumn => + column != null && 'message' in column; -export const isHighlightFieldColumn = ( - column: LogEntryHighlightColumn -): column is LogEntryHighlightFieldColumn => column != null && 'field' in column; +export const isHighlightFieldColumn = (column: LogColumn): column is LogFieldColumn => + column != null && 'field' in column; -export const isHighlightFieldSegment = ( - segment: LogEntryHighlightMessageSegment -): segment is LogEntryHighlightFieldMessageSegment => +export const isHighlightFieldSegment = (segment: LogMessagePart): segment is LogMessagePartField => segment && 'field' in segment && 'highlights' in segment; From ae97e49701acaed228638f1c930fbdeba3381f25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 5 Feb 2020 20:51:07 +0100 Subject: [PATCH 046/117] Remove deprecated functions/methods from domain and adapter --- .../log_entries/kibana_log_entries_adapter.ts | 47 ---- .../log_entries_domain/log_entries_domain.ts | 206 ------------------ 2 files changed, 253 deletions(-) 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 36c63d884482f7..e04d37c67cfb6d 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 @@ -162,31 +162,6 @@ export class InfraKibanaLogEntriesAdapter implements LogEntriesAdapter { return mapHitsToLogEntryDocuments(hits, sourceConfiguration.fields.timestamp, fields); } - /** @deprecated */ - public async getContainedLogEntryDocuments( - requestContext: RequestHandlerContext, - sourceConfiguration: InfraSourceConfiguration, - fields: string[], - start: TimeKey, - end: TimeKey, - filterQuery?: LogEntryQuery, - highlightQuery?: LogEntryQuery - ): Promise { - const documents = await this.getLogEntryDocumentsBetween( - requestContext, - sourceConfiguration, - fields, - start.time, - end.time, - start, - 10000, - filterQuery, - highlightQuery - ); - - return documents.filter(document => compareTimeKeys(document.key, end) < 0); - } - public async getContainedLogSummaryBuckets( requestContext: RequestHandlerContext, sourceConfiguration: InfraSourceConfiguration, @@ -431,28 +406,6 @@ function mapHitsToLogEntryDocuments( }); } -/** @deprecated */ -const convertHitToLogEntryDocument = (fields: string[]) => ( - hit: SortedSearchHit -): LogEntryDocument => ({ - gid: hit._id, - fields: fields.reduce( - (flattenedFields, fieldName) => - has(fieldName, hit._source) - ? { - ...flattenedFields, - [fieldName]: get(fieldName, hit._source), - } - : flattenedFields, - {} as { [fieldName: string]: string | number | boolean | null } - ), - highlights: hit.highlight || {}, - key: { - time: hit.sort[0], - tiebreaker: hit.sort[1], - }, -}); - const convertDateRangeBucketToSummaryBucket = ( bucket: LogSummaryDateRangeBucket ): LogSummaryBucket => ({ 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 da58e8acc8ca33..ba8f55fb0724c8 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 @@ -112,71 +112,6 @@ export class InfraLogEntriesDomain { return [...entriesBefore, ...entriesAfter]; } - /** @deprecated */ - public async getLogEntriesAround( - requestContext: RequestHandlerContext, - sourceId: string, - key: TimeKey, - maxCountBefore: number, - maxCountAfter: number, - filterQuery?: LogEntryQuery, - highlightQuery?: LogEntryQuery - ): Promise<{ entriesBefore: InfraLogEntry[]; entriesAfter: InfraLogEntry[] }> { - if (maxCountBefore <= 0 && maxCountAfter <= 0) { - return { - entriesBefore: [], - entriesAfter: [], - }; - } - - const { configuration } = await this.libs.sources.getSourceConfiguration( - requestContext, - sourceId - ); - const messageFormattingRules = compileFormattingRules( - getBuiltinRules(configuration.fields.message) - ); - const requiredFields = getRequiredFields(configuration, messageFormattingRules); - - const documentsBefore = await this.adapter.getAdjacentLogEntryDocuments( - requestContext, - configuration, - requiredFields, - key, - 'desc', - Math.max(maxCountBefore, 1), - filterQuery, - highlightQuery - ); - const lastKeyBefore = - documentsBefore.length > 0 - ? documentsBefore[documentsBefore.length - 1].key - : { - time: key.time - 1, - tiebreaker: 0, - }; - - const documentsAfter = await this.adapter.getAdjacentLogEntryDocuments( - requestContext, - configuration, - requiredFields, - lastKeyBefore, - 'asc', - maxCountAfter, - filterQuery, - highlightQuery - ); - - return { - entriesBefore: (maxCountBefore > 0 ? documentsBefore : []).map( - convertLogDocumentToEntry(sourceId, configuration.logColumns, messageFormattingRules.format) - ), - entriesAfter: documentsAfter.map( - convertLogDocumentToEntry(sourceId, configuration.logColumns, messageFormattingRules.format) - ), - }; - } - public async getLogEntries( requestContext: RequestHandlerContext, sourceId: string, @@ -232,116 +167,6 @@ export class InfraLogEntriesDomain { return entries; } - /** @deprecated */ - public async getLogEntriesBetween( - requestContext: RequestHandlerContext, - sourceId: string, - startKey: TimeKey, - endKey: TimeKey, - filterQuery?: LogEntryQuery, - highlightQuery?: LogEntryQuery - ): Promise { - const { configuration } = await this.libs.sources.getSourceConfiguration( - requestContext, - sourceId - ); - const messageFormattingRules = compileFormattingRules( - getBuiltinRules(configuration.fields.message) - ); - const requiredFields = getRequiredFields(configuration, messageFormattingRules); - const documents = await this.adapter.getContainedLogEntryDocuments( - requestContext, - configuration, - requiredFields, - startKey, - endKey, - filterQuery, - highlightQuery - ); - const entries = documents.map( - convertLogDocumentToEntry(sourceId, configuration.logColumns, messageFormattingRules.format) - ); - return entries; - } - - /** @deprecated */ - public async getLogEntryHighlights( - requestContext: RequestHandlerContext, - sourceId: string, - startKey: TimeKey, - endKey: TimeKey, - highlights: Array<{ - query: string; - countBefore: number; - countAfter: number; - }>, - filterQuery?: LogEntryQuery - ): Promise { - const { configuration } = await this.libs.sources.getSourceConfiguration( - requestContext, - sourceId - ); - const messageFormattingRules = compileFormattingRules( - getBuiltinRules(configuration.fields.message) - ); - const requiredFields = getRequiredFields(configuration, messageFormattingRules); - - const documentSets = await Promise.all( - highlights.map(async highlight => { - const highlightQuery = createHighlightQueryDsl(highlight.query, requiredFields); - const query = filterQuery - ? { - bool: { - filter: [filterQuery, highlightQuery], - }, - } - : highlightQuery; - const [documentsBefore, documents, documentsAfter] = await Promise.all([ - this.adapter.getAdjacentLogEntryDocuments( - requestContext, - configuration, - requiredFields, - startKey, - 'desc', - highlight.countBefore, - query, - highlightQuery - ), - this.adapter.getContainedLogEntryDocuments( - requestContext, - configuration, - requiredFields, - startKey, - endKey, - query, - highlightQuery - ), - this.adapter.getAdjacentLogEntryDocuments( - requestContext, - configuration, - requiredFields, - endKey, - 'asc', - highlight.countAfter, - query, - highlightQuery - ), - ]); - const entries = [...documentsBefore, ...documents, ...documentsAfter].map( - convertLogDocumentToEntry( - sourceId, - configuration.logColumns, - messageFormattingRules.format - ) - ); - - return entries; - }) - ); - - return documentSets; - } - public async getLogSummaryBucketsBetween( requestContext: RequestHandlerContext, sourceId: string, @@ -505,37 +330,6 @@ export interface LogSummaryBucket { topEntryKeys: TimeKey[]; } -/** @deprecated */ -const convertLogDocumentToEntry = ( - sourceId: string, - logColumns: InfraSourceConfiguration['logColumns'], - formatLogMessage: (fields: Fields, highlights: Highlights) => InfraLogMessageSegment[] -) => (document: LogEntryDocument): InfraLogEntry => ({ - key: document.key, - gid: document.gid, - source: sourceId, - columns: logColumns.map(logColumn => { - if (SavedSourceConfigurationTimestampColumnRuntimeType.is(logColumn)) { - return { - columnId: logColumn.timestampColumn.id, - timestamp: document.key.time, - }; - } else if (SavedSourceConfigurationMessageColumnRuntimeType.is(logColumn)) { - return { - columnId: logColumn.messageColumn.id, - message: formatLogMessage(document.fields, document.highlights), - }; - } else { - return { - columnId: logColumn.fieldColumn.id, - field: logColumn.fieldColumn.field, - highlights: document.highlights[logColumn.fieldColumn.field] || [], - value: stringify(document.fields[logColumn.fieldColumn.field] || null), - }; - } - }), -}); - const logSummaryBucketHasEntries = (bucket: LogSummaryBucket) => bucket.entriesCount > 0 && bucket.topEntryKeys.length > 0; From f7341410ded223bcc8eea8a29ea003a0b9487133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 5 Feb 2020 21:08:06 +0100 Subject: [PATCH 047/117] Cleanup GraphQL --- .../plugins/infra/server/graphql/index.ts | 9 +- .../infra/server/graphql/log_entries/index.ts | 7 - .../server/graphql/log_entries/resolvers.ts | 175 ------------------ .../server/graphql/log_entries/schema.gql.ts | 136 -------------- .../plugins/infra/server/infra_server.ts | 2 - .../log_entries/kibana_log_entries_adapter.ts | 150 --------------- .../log_entries_domain/log_entries_domain.ts | 24 --- 7 files changed, 1 insertion(+), 502 deletions(-) delete mode 100644 x-pack/legacy/plugins/infra/server/graphql/log_entries/index.ts delete mode 100644 x-pack/legacy/plugins/infra/server/graphql/log_entries/resolvers.ts delete mode 100644 x-pack/legacy/plugins/infra/server/graphql/log_entries/schema.gql.ts diff --git a/x-pack/legacy/plugins/infra/server/graphql/index.ts b/x-pack/legacy/plugins/infra/server/graphql/index.ts index 82fef41db1a739..f5150972a3a652 100644 --- a/x-pack/legacy/plugins/infra/server/graphql/index.ts +++ b/x-pack/legacy/plugins/infra/server/graphql/index.ts @@ -6,14 +6,7 @@ import { rootSchema } from '../../common/graphql/root/schema.gql'; import { sharedSchema } from '../../common/graphql/shared/schema.gql'; -import { logEntriesSchema } from './log_entries/schema.gql'; import { sourceStatusSchema } from './source_status/schema.gql'; import { sourcesSchema } from './sources/schema.gql'; -export const schemas = [ - rootSchema, - sharedSchema, - logEntriesSchema, - sourcesSchema, - sourceStatusSchema, -]; +export const schemas = [rootSchema, sharedSchema, sourcesSchema, sourceStatusSchema]; diff --git a/x-pack/legacy/plugins/infra/server/graphql/log_entries/index.ts b/x-pack/legacy/plugins/infra/server/graphql/log_entries/index.ts deleted file mode 100644 index 21134862663ec2..00000000000000 --- a/x-pack/legacy/plugins/infra/server/graphql/log_entries/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/* - * 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. - */ - -export { createLogEntriesResolvers } from './resolvers'; 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 deleted file mode 100644 index edbb736b2c4fdc..00000000000000 --- a/x-pack/legacy/plugins/infra/server/graphql/log_entries/resolvers.ts +++ /dev/null @@ -1,175 +0,0 @@ -/* - * 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 { - InfraLogEntryColumn, - InfraLogEntryFieldColumn, - InfraLogEntryMessageColumn, - InfraLogEntryTimestampColumn, - InfraLogMessageConstantSegment, - InfraLogMessageFieldSegment, - InfraLogMessageSegment, - InfraSourceResolvers, -} from '../../graphql/types'; -import { InfraLogEntriesDomain } from '../../lib/domains/log_entries_domain'; -import { parseFilterQuery } from '../../utils/serialized_query'; -import { ChildResolverOf, InfraResolverOf } from '../../utils/typed_resolvers'; -import { QuerySourceResolver } from '../sources/resolvers'; - -export type InfraSourceLogEntriesAroundResolver = ChildResolverOf< - InfraResolverOf, - QuerySourceResolver ->; - -export type InfraSourceLogEntriesBetweenResolver = ChildResolverOf< - InfraResolverOf, - QuerySourceResolver ->; - -export type InfraSourceLogEntryHighlightsResolver = ChildResolverOf< - InfraResolverOf, - QuerySourceResolver ->; - -export const createLogEntriesResolvers = (libs: { - logEntries: InfraLogEntriesDomain; -}): { - InfraSource: { - logEntriesAround: InfraSourceLogEntriesAroundResolver; - logEntriesBetween: InfraSourceLogEntriesBetweenResolver; - logEntryHighlights: InfraSourceLogEntryHighlightsResolver; - }; - InfraLogEntryColumn: { - __resolveType( - logEntryColumn: InfraLogEntryColumn - ): - | 'InfraLogEntryTimestampColumn' - | 'InfraLogEntryMessageColumn' - | 'InfraLogEntryFieldColumn' - | null; - }; - InfraLogMessageSegment: { - __resolveType( - messageSegment: InfraLogMessageSegment - ): 'InfraLogMessageFieldSegment' | 'InfraLogMessageConstantSegment' | null; - }; -} => ({ - InfraSource: { - async logEntriesAround(source, args, { req }) { - const countBefore = args.countBefore || 0; - const countAfter = args.countAfter || 0; - - const { entriesBefore, entriesAfter } = await libs.logEntries.getLogEntriesAround( - req, - source.id, - args.key, - countBefore + 1, - countAfter + 1, - parseFilterQuery(args.filterQuery) - ); - - const hasMoreBefore = entriesBefore.length > countBefore; - const hasMoreAfter = entriesAfter.length > countAfter; - - const entries = [ - ...(hasMoreBefore ? entriesBefore.slice(1) : entriesBefore), - ...(hasMoreAfter ? entriesAfter.slice(0, -1) : entriesAfter), - ]; - - return { - start: entries.length > 0 ? entries[0].key : null, - end: entries.length > 0 ? entries[entries.length - 1].key : null, - hasMoreBefore, - hasMoreAfter, - filterQuery: args.filterQuery, - entries, - }; - }, - async logEntriesBetween(source, args, { req }) { - const entries = await libs.logEntries.getLogEntriesBetween( - req, - source.id, - args.startKey, - args.endKey, - parseFilterQuery(args.filterQuery) - ); - - return { - start: entries.length > 0 ? entries[0].key : null, - end: entries.length > 0 ? entries[entries.length - 1].key : null, - hasMoreBefore: true, - hasMoreAfter: true, - filterQuery: args.filterQuery, - entries, - }; - }, - async logEntryHighlights(source, args, { req }) { - const highlightedLogEntrySets = await libs.logEntries.getLogEntryHighlights( - req, - source.id, - args.startKey, - args.endKey, - args.highlights.filter(highlightInput => !!highlightInput.query), - parseFilterQuery(args.filterQuery) - ); - - return highlightedLogEntrySets.map(entries => ({ - start: entries.length > 0 ? entries[0].key : null, - end: entries.length > 0 ? entries[entries.length - 1].key : null, - hasMoreBefore: true, - hasMoreAfter: true, - filterQuery: args.filterQuery, - entries, - })); - }, - }, - InfraLogEntryColumn: { - __resolveType(logEntryColumn) { - if (isTimestampColumn(logEntryColumn)) { - return 'InfraLogEntryTimestampColumn'; - } - - if (isMessageColumn(logEntryColumn)) { - return 'InfraLogEntryMessageColumn'; - } - - if (isFieldColumn(logEntryColumn)) { - return 'InfraLogEntryFieldColumn'; - } - - return null; - }, - }, - InfraLogMessageSegment: { - __resolveType(messageSegment) { - if (isConstantSegment(messageSegment)) { - return 'InfraLogMessageConstantSegment'; - } - - if (isFieldSegment(messageSegment)) { - return 'InfraLogMessageFieldSegment'; - } - - return null; - }, - }, -}); - -const isTimestampColumn = (column: InfraLogEntryColumn): column is InfraLogEntryTimestampColumn => - 'timestamp' in column; - -const isMessageColumn = (column: InfraLogEntryColumn): column is InfraLogEntryMessageColumn => - 'message' in column; - -const isFieldColumn = (column: InfraLogEntryColumn): column is InfraLogEntryFieldColumn => - 'field' in column && 'value' in column; - -const isConstantSegment = ( - segment: InfraLogMessageSegment -): segment is InfraLogMessageConstantSegment => 'constant' in segment; - -const isFieldSegment = (segment: InfraLogMessageSegment): segment is InfraLogMessageFieldSegment => - 'field' in segment && 'value' in segment && 'highlights' in segment; 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 deleted file mode 100644 index 945f2f85435e5d..00000000000000 --- a/x-pack/legacy/plugins/infra/server/graphql/log_entries/schema.gql.ts +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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'; - -export const logEntriesSchema = gql` - "A segment of the log entry message that was derived from a field" - type InfraLogMessageFieldSegment { - "The field the segment was derived from" - field: String! - "The segment's message" - value: String! - "A list of highlighted substrings of the value" - highlights: [String!]! - } - - "A segment of the log entry message that was derived from a string literal" - type InfraLogMessageConstantSegment { - "The segment's message" - constant: String! - } - - "A segment of the log entry message" - union InfraLogMessageSegment = InfraLogMessageFieldSegment | InfraLogMessageConstantSegment - - "A special built-in column that contains the log entry's timestamp" - type InfraLogEntryTimestampColumn { - "The id of the corresponding column configuration" - columnId: ID! - "The timestamp" - timestamp: Float! - } - - "A special built-in column that contains the log entry's constructed message" - type InfraLogEntryMessageColumn { - "The id of the corresponding column configuration" - columnId: ID! - "A list of the formatted log entry segments" - message: [InfraLogMessageSegment!]! - } - - "A column that contains the value of a field of the log entry" - type InfraLogEntryFieldColumn { - "The id of the corresponding column configuration" - columnId: ID! - "The field name of the column" - field: String! - "The value of the field in the log entry" - value: String! - "A list of highlighted substrings of the value" - highlights: [String!]! - } - - "A column of a log entry" - union InfraLogEntryColumn = - InfraLogEntryTimestampColumn - | InfraLogEntryMessageColumn - | InfraLogEntryFieldColumn - - "A log entry" - type InfraLogEntry { - "A unique representation of the log entry's position in the event stream" - key: InfraTimeKey! - "The log entry's id" - gid: String! - "The source id" - source: String! - "The columns used for rendering the log entry" - columns: [InfraLogEntryColumn!]! - } - - "A highlighting definition" - input InfraLogEntryHighlightInput { - "The query to highlight by" - query: String! - "The number of highlighted documents to include beyond the beginning of the interval" - countBefore: Int! - "The number of highlighted documents to include beyond the end of the interval" - countAfter: Int! - } - - "A consecutive sequence of log entries" - type InfraLogEntryInterval { - "The key corresponding to the start of the interval covered by the entries" - start: InfraTimeKey - "The key corresponding to the end of the interval covered by the entries" - end: InfraTimeKey - "Whether there are more log entries available before the start" - hasMoreBefore: Boolean! - "Whether there are more log entries available after the end" - hasMoreAfter: Boolean! - "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" - entries: [InfraLogEntry!]! - } - - extend type InfraSource { - "A consecutive span of log entries surrounding a point in time" - logEntriesAround( - "The sort key that corresponds to the point in time" - key: InfraTimeKeyInput! - "The maximum number of preceding to return" - countBefore: Int = 0 - "The maximum number of following to return" - countAfter: Int = 0 - "The query to filter the log entries by" - filterQuery: String - ): InfraLogEntryInterval! - "A consecutive span of log entries within an interval" - logEntriesBetween( - "The sort key that corresponds to the start of the interval" - startKey: InfraTimeKeyInput! - "The sort key that corresponds to the end of the interval" - endKey: InfraTimeKeyInput! - "The query to filter the log entries by" - filterQuery: String - ): InfraLogEntryInterval! - "Sequences of log entries matching sets of highlighting queries within an interval" - logEntryHighlights( - "The sort key that corresponds to the start of the interval" - startKey: InfraTimeKeyInput! - "The sort key that corresponds to the end of the interval" - endKey: InfraTimeKeyInput! - "The query to filter the log entries by" - filterQuery: String - "The highlighting to apply to the log entries" - highlights: [InfraLogEntryHighlightInput!]! - ): [InfraLogEntryInterval!]! - } -`; diff --git a/x-pack/legacy/plugins/infra/server/infra_server.ts b/x-pack/legacy/plugins/infra/server/infra_server.ts index 4f290cb05f0561..d79749f33d9bda 100644 --- a/x-pack/legacy/plugins/infra/server/infra_server.ts +++ b/x-pack/legacy/plugins/infra/server/infra_server.ts @@ -7,7 +7,6 @@ import { IResolvers, makeExecutableSchema } from 'graphql-tools'; import { initIpToHostName } from './routes/ip_to_hostname'; import { schemas } from './graphql'; -import { createLogEntriesResolvers } from './graphql/log_entries'; import { createSourceStatusResolvers } from './graphql/source_status'; import { createSourcesResolvers } from './graphql/sources'; import { InfraBackendLibs } from './lib/infra_types'; @@ -33,7 +32,6 @@ import { initInventoryMetaRoute } from './routes/inventory_metadata'; export const initInfraServer = (libs: InfraBackendLibs) => { const schema = makeExecutableSchema({ resolvers: [ - createLogEntriesResolvers(libs) as IResolvers, createSourcesResolvers(libs) as IResolvers, createSourceStatusResolvers(libs) as IResolvers, ], 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 e04d37c67cfb6d..3c6e6efaa9207f 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 @@ -12,12 +12,10 @@ import { compact } from 'lodash'; import first from 'lodash/fp/first'; import get from 'lodash/fp/get'; import has from 'lodash/fp/has'; -import zip from 'lodash/fp/zip'; import { pipe } from 'fp-ts/lib/pipeable'; import { map, fold } from 'fp-ts/lib/Either'; import { identity, constant } from 'fp-ts/lib/function'; import { RequestHandlerContext } from 'src/core/server'; -import { compareTimeKeys, isTimeKey, TimeKey } from '../../../../common/time'; import { JsonObject, JsonValue } from '../../../../common/typed_json'; import { LogEntriesAdapter, @@ -31,8 +29,6 @@ import { InfraSourceConfiguration } from '../../sources'; import { SortedSearchHit } from '../framework'; import { KibanaFramework } from '../framework/kibana_framework_adapter'; -const DAY_MILLIS = 24 * 60 * 60 * 1000; -const LOOKUP_OFFSETS = [0, 1, 7, 30, 365, 10000, Infinity].map(days => days * DAY_MILLIS); const TIMESTAMP_FORMAT = 'epoch_millis'; interface LogItemHit { @@ -45,46 +41,6 @@ interface LogItemHit { export class InfraKibanaLogEntriesAdapter implements LogEntriesAdapter { constructor(private readonly framework: KibanaFramework) {} - public async getAdjacentLogEntryDocuments( - requestContext: RequestHandlerContext, - sourceConfiguration: InfraSourceConfiguration, - fields: string[], - start: TimeKey, - direction: 'asc' | 'desc', - maxCount: number, - filterQuery?: LogEntryQuery, - highlightQuery?: LogEntryQuery - ): Promise { - if (maxCount <= 0) { - return []; - } - - const intervals = getLookupIntervals(start.time, direction); - - let documents: LogEntryDocument[] = []; - for (const [intervalStart, intervalEnd] of intervals) { - if (documents.length >= maxCount) { - break; - } - - const documentsInInterval = await this.getLogEntryDocumentsBetween( - requestContext, - sourceConfiguration, - fields, - intervalStart, - intervalEnd, - documents.length > 0 ? documents[documents.length - 1].key : start, - maxCount - documents.length, - filterQuery, - highlightQuery - ); - - documents = [...documents, ...documentsInInterval]; - } - - return direction === 'asc' ? documents : documents.reverse(); - } - public async getLogEntries( requestContext: RequestHandlerContext, sourceConfiguration: InfraSourceConfiguration, @@ -271,112 +227,6 @@ export class InfraKibanaLogEntriesAdapter implements LogEntriesAdapter { } return document; } - - private async getLogEntryDocumentsBetween( - requestContext: RequestHandlerContext, - sourceConfiguration: InfraSourceConfiguration, - fields: string[], - start: number, - end: number, - after: TimeKey | null, - maxCount: number, - filterQuery?: LogEntryQuery, - highlightQuery?: LogEntryQuery - ): Promise { - if (maxCount <= 0) { - return []; - } - - const sortDirection: 'asc' | 'desc' = start <= end ? 'asc' : 'desc'; - - const startRange = { - [sortDirection === 'asc' ? 'gte' : 'lte']: start, - }; - const endRange = - end === Infinity - ? {} - : { - [sortDirection === 'asc' ? 'lte' : 'gte']: end, - }; - - const highlightClause = highlightQuery - ? { - highlight: { - boundary_scanner: 'word', - fields: fields.reduce( - (highlightFieldConfigs, fieldName) => ({ - ...highlightFieldConfigs, - [fieldName]: {}, - }), - {} - ), - fragment_size: 1, - number_of_fragments: 100, - post_tags: [''], - pre_tags: [''], - highlight_query: highlightQuery, - }, - } - : {}; - - const searchAfterClause = isTimeKey(after) - ? { - search_after: [after.time, after.tiebreaker], - } - : {}; - - const query = { - allowNoIndices: true, - index: sourceConfiguration.logAlias, - ignoreUnavailable: true, - body: { - query: { - bool: { - filter: [ - ...createQueryFilterClauses(filterQuery), - { - range: { - [sourceConfiguration.fields.timestamp]: { - ...startRange, - ...endRange, - format: TIMESTAMP_FORMAT, - }, - }, - }, - ], - }, - }, - ...highlightClause, - ...searchAfterClause, - _source: fields, - size: maxCount, - sort: [ - { [sourceConfiguration.fields.timestamp]: sortDirection }, - { [sourceConfiguration.fields.tiebreaker]: sortDirection }, - ], - track_total_hits: false, - }, - }; - - const response = await this.framework.callWithRequest( - requestContext, - 'search', - query - ); - const hits = response.hits.hits; - const documents = hits.map(convertHitToLogEntryDocument(fields)); - - return documents; - } -} - -function getLookupIntervals(start: number, direction: 'asc' | 'desc'): Array<[number, number]> { - const offsetSign = direction === 'asc' ? 1 : -1; - const translatedOffsets = LOOKUP_OFFSETS.map(offset => start + offset * offsetSign); - const intervals = zip(translatedOffsets.slice(0, -1), translatedOffsets.slice(1)) as Array< - [number, number] - >; - return intervals; } function mapHitsToLogEntryDocuments( 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 ba8f55fb0724c8..c38b63dddb88e9 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 @@ -18,13 +18,10 @@ import { LogEntriesCursor, LogColumn, } from '../../../../common/http_api'; -import { InfraLogEntry, InfraLogMessageSegment } from '../../../graphql/types'; import { InfraSourceConfiguration, InfraSources, SavedSourceConfigurationFieldColumnRuntimeType, - SavedSourceConfigurationMessageColumnRuntimeType, - SavedSourceConfigurationTimestampColumnRuntimeType, } from '../../sources'; import { getBuiltinRules } from './builtin_rules'; import { convertDocumentSourceToLogItemFields } from './convert_document_source_to_log_item_fields'; @@ -270,17 +267,6 @@ interface LogItemHit { } export interface LogEntriesAdapter { - getAdjacentLogEntryDocuments( - requestContext: RequestHandlerContext, - sourceConfiguration: InfraSourceConfiguration, - fields: string[], - start: TimeKey, - direction: 'asc' | 'desc', - maxCount: number, - filterQuery?: LogEntryQuery, - highlightQuery?: LogEntryQuery - ): Promise; - getLogEntries( requestContext: RequestHandlerContext, sourceConfiguration: InfraSourceConfiguration, @@ -288,16 +274,6 @@ export interface LogEntriesAdapter { params: LogEntriesParams ): Promise; - getContainedLogEntryDocuments( - requestContext: RequestHandlerContext, - sourceConfiguration: InfraSourceConfiguration, - fields: string[], - start: TimeKey, - end: TimeKey, - filterQuery?: LogEntryQuery, - highlightQuery?: LogEntryQuery - ): Promise; - getContainedLogSummaryBuckets( requestContext: RequestHandlerContext, sourceConfiguration: InfraSourceConfiguration, From 1e653c0a009c33fea67f585f1dbcfc4ff35fb2dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 6 Feb 2020 15:26:54 +0100 Subject: [PATCH 048/117] Deduplicate I18n keys --- .../log_text_stream/loading_item_view.tsx | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx index 6129333d439f0b..6f3e9123bd548f 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx @@ -121,12 +121,12 @@ const ProgressSpinner: React.FC<{ kind: 'streaming' | 'loading' }> = ({ kind }) {kind === 'streaming' ? ( ) : ( )} @@ -141,7 +141,7 @@ const ProgressCta: React.FC - + ); } @@ -178,7 +178,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun case 'ms': return ( @@ -186,7 +186,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun case 's': return ( @@ -194,7 +194,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun case 'm': return ( @@ -202,7 +202,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun case 'h': return ( @@ -210,7 +210,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun case 'd': return ( @@ -218,7 +218,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun case 'w': return ( @@ -226,7 +226,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun case 'M': return ( @@ -234,7 +234,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun case 'y': return ( From c4ecb92c3b4fbd492f17afd748ddf2ed968fe68c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 7 Feb 2020 11:15:10 +0100 Subject: [PATCH 049/117] Load date range with data in the tests --- .../apps/infra/logs_source_configuration.ts | 4 +++- .../functional/page_objects/infra_logs_page.ts | 18 +++++++++++++++--- .../functional/services/logs_ui/log_stream.ts | 5 +++-- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/x-pack/test/functional/apps/infra/logs_source_configuration.ts b/x-pack/test/functional/apps/infra/logs_source_configuration.ts index ecad5a40ec42e9..3fb8dc856b5b3f 100644 --- a/x-pack/test/functional/apps/infra/logs_source_configuration.ts +++ b/x-pack/test/functional/apps/infra/logs_source_configuration.ts @@ -74,7 +74,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('renders the default log columns with their headers', async () => { - await logsUi.logStreamPage.navigateTo(); + await logsUi.logStreamPage.navigateTo({ + logPosition: { start: '2018-10-17T19:42:22.000Z', end: '2018-10-17T19:57:21.000Z' }, + }); await retry.try(async () => { const columnHeaderLabels = await logsUi.logStreamPage.getColumnHeaderLabels(); diff --git a/x-pack/test/functional/page_objects/infra_logs_page.ts b/x-pack/test/functional/page_objects/infra_logs_page.ts index 1c58f8dc41eba0..e12d4744b14690 100644 --- a/x-pack/test/functional/page_objects/infra_logs_page.ts +++ b/x-pack/test/functional/page_objects/infra_logs_page.ts @@ -6,7 +6,7 @@ // import testSubjSelector from '@kbn/test-subj-selector'; // import moment from 'moment'; - +import { encode, RisonValue } from 'rison-node'; import { FtrProviderContext } from '../ftr_provider_context'; export function InfraLogsPageProvider({ getPageObjects, getService }: FtrProviderContext) { @@ -18,8 +18,20 @@ export function InfraLogsPageProvider({ getPageObjects, getService }: FtrProvide await pageObjects.common.navigateToApp('infraLogs'); }, - async navigateToTab(logsUiTab: LogsUiTab) { - await pageObjects.common.navigateToActualUrl('infraLogs', `/logs/${logsUiTab}`); + async navigateToTab(logsUiTab: LogsUiTab, params?: Record) { + let queryString = ''; + if (params) { + queryString = Object.keys(params).reduce((qs, key, idx) => { + qs += (idx > 0 ? '&' : '') + `${key}=${encode(params[key])}`; + + return qs; + }, '?'); + } + await pageObjects.common.navigateToActualUrl( + 'infraLogs', + `/logs/${logsUiTab}${decodeURI(queryString)}`, + { ensureCurrentUrl: false } // Test runner struggles with `rison-node` escaped values + ); }, async getLogStream() { diff --git a/x-pack/test/functional/services/logs_ui/log_stream.ts b/x-pack/test/functional/services/logs_ui/log_stream.ts index ce37d2d5a60daa..f496f8552702f9 100644 --- a/x-pack/test/functional/services/logs_ui/log_stream.ts +++ b/x-pack/test/functional/services/logs_ui/log_stream.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { RisonValue } from 'rison-node'; import { FtrProviderContext } from '../../ftr_provider_context'; import { WebElementWrapper } from '../../../../../test/functional/services/lib/web_element_wrapper'; @@ -13,8 +14,8 @@ export function LogStreamPageProvider({ getPageObjects, getService }: FtrProvide const testSubjects = getService('testSubjects'); return { - async navigateTo() { - pageObjects.infraLogs.navigateToTab('stream'); + async navigateTo(params?: Record) { + pageObjects.infraLogs.navigateToTab('stream', params); }, async getColumnHeaderLabels(): Promise { From 92a1c5bf40eb70f139c90d02b21eee506513604e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 7 Feb 2020 13:23:39 +0100 Subject: [PATCH 050/117] Remove unused translations --- x-pack/plugins/translations/translations/ja-JP.json | 6 ------ x-pack/plugins/translations/translations/zh-CN.json | 6 ------ 2 files changed, 12 deletions(-) diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index ee2abeff744967..4e859f9f612cc8 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -6465,9 +6465,6 @@ "xpack.infra.logs.index.settingsTabTitle": "設定", "xpack.infra.logs.index.streamTabTitle": "ストリーム", "xpack.infra.logs.jumpToTailText": "最も新しいエントリーに移動", - "xpack.infra.logs.lastStreamingUpdateText": " 最終更新 {lastUpdateTime}", - "xpack.infra.logs.loadAgainButtonLabel": "再読み込み", - "xpack.infra.logs.loadingAdditionalEntriesText": "追加エントリーを読み込み中", "xpack.infra.logs.noAdditionalEntriesFoundText": "追加エントリーが見つかりません", "xpack.infra.logs.scrollableLogTextStreamView.loadingEntriesLabel": "エントリーを読み込み中", "xpack.infra.logs.search.nextButtonLabel": "次へ", @@ -6475,9 +6472,6 @@ "xpack.infra.logs.search.searchInLogsAriaLabel": "検索", "xpack.infra.logs.search.searchInLogsPlaceholder": "検索", "xpack.infra.logs.searchResultTooltip": "{bucketCount, plural, one {# 件のハイライトされたエントリー} other {# 件のハイライトされたエントリー}}", - "xpack.infra.logs.startStreamingButtonLabel": "ライブストリーム", - "xpack.infra.logs.stopStreamingButtonLabel": "ストリーム停止", - "xpack.infra.logs.streamingDescription": "新しいエントリーをストリーム中...", "xpack.infra.logs.streamingNewEntriesText": "新しいエントリーをストリーム中", "xpack.infra.logs.streamPage.documentTitle": "{previousTitle} | ストリーム", "xpack.infra.logsPage.noLoggingIndicesDescription": "追加しましょう!", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 3210c619d3e52e..8933be5e407ea0 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -6464,9 +6464,6 @@ "xpack.infra.logs.index.settingsTabTitle": "设置", "xpack.infra.logs.index.streamTabTitle": "流式传输", "xpack.infra.logs.jumpToTailText": "跳到最近的条目", - "xpack.infra.logs.lastStreamingUpdateText": " 最后更新时间:{lastUpdateTime}", - "xpack.infra.logs.loadAgainButtonLabel": "重新加载", - "xpack.infra.logs.loadingAdditionalEntriesText": "正在加载其他条目", "xpack.infra.logs.noAdditionalEntriesFoundText": "找不到其他条目", "xpack.infra.logs.scrollableLogTextStreamView.loadingEntriesLabel": "正在加载条目", "xpack.infra.logs.search.nextButtonLabel": "下一个", @@ -6474,9 +6471,6 @@ "xpack.infra.logs.search.searchInLogsAriaLabel": "搜索", "xpack.infra.logs.search.searchInLogsPlaceholder": "搜索", "xpack.infra.logs.searchResultTooltip": "{bucketCount, plural, one {# 个高亮条目} other {# 个高亮条目}}", - "xpack.infra.logs.startStreamingButtonLabel": "实时流式传输", - "xpack.infra.logs.stopStreamingButtonLabel": "停止流式传输", - "xpack.infra.logs.streamingDescription": "正在流式传输新条目……", "xpack.infra.logs.streamingNewEntriesText": "正在流式传输新条目", "xpack.infra.logs.streamPage.documentTitle": "{previousTitle} | 流式传输", "xpack.infra.logsPage.noLoggingIndicesDescription": "让我们添加一些!", From d3e4521f65704697b27f5425b56b74a9923761c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 7 Feb 2020 13:39:03 +0100 Subject: [PATCH 051/117] Remove outdated tests --- .../log_entry_field_column.test.tsx | 8 +- .../test/api_integration/apis/infra/index.js | 1 - .../api_integration/apis/infra/log_entries.ts | 313 ------------------ .../apis/infra/log_entry_highlights.ts | 244 -------------- .../apis/infra/logs_without_millis.ts | 193 ----------- x-pack/test/functional/apps/infra/link_to.ts | 2 +- .../apps/infra/logs_source_configuration.ts | 4 +- 7 files changed, 8 insertions(+), 757 deletions(-) delete mode 100644 x-pack/test/api_integration/apis/infra/logs_without_millis.ts diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx index 41a452736fc619..ef91867e7ac60e 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx @@ -16,7 +16,7 @@ describe('LogEntryFieldColumn', () => { const column: LogColumn = { columnId: 'TEST_COLUMN', field: 'TEST_FIELD', - value: JSON.stringify(['a', 'b', 'c']), + value: ['a', 'b', 'c'], highlights: [], }; @@ -46,10 +46,10 @@ describe('LogEntryFieldColumn', () => { const column: LogColumn = { columnId: 'TEST_COLUMN', field: 'TEST_FIELD', - value: JSON.stringify({ + value: { lat: 1, lon: 2, - }), + }, highlights: [], }; @@ -72,7 +72,7 @@ describe('LogEntryFieldColumn', () => { const column: LogColumn = { columnId: 'TEST_COLUMN', field: 'TEST_FIELD', - value: JSON.stringify('foo'), + value: 'foo', highlights: [], }; diff --git a/x-pack/test/api_integration/apis/infra/index.js b/x-pack/test/api_integration/apis/infra/index.js index fad387130e044c..fc201682f79fe1 100644 --- a/x-pack/test/api_integration/apis/infra/index.js +++ b/x-pack/test/api_integration/apis/infra/index.js @@ -11,7 +11,6 @@ export default function({ loadTestFile }) { loadTestFile(require.resolve('./log_entries')); loadTestFile(require.resolve('./log_entry_highlights')); loadTestFile(require.resolve('./log_summary')); - loadTestFile(require.resolve('./logs_without_millis')); loadTestFile(require.resolve('./metrics')); loadTestFile(require.resolve('./sources')); loadTestFile(require.resolve('./waffle')); diff --git a/x-pack/test/api_integration/apis/infra/log_entries.ts b/x-pack/test/api_integration/apis/infra/log_entries.ts index 48325c1a8d9da0..08ab8cf9a497bf 100644 --- a/x-pack/test/api_integration/apis/infra/log_entries.ts +++ b/x-pack/test/api_integration/apis/infra/log_entries.ts @@ -5,9 +5,6 @@ */ import expect from '@kbn/expect'; -import { ascending, pairs } from 'd3-array'; -import gql from 'graphql-tag'; -import { v4 as uuidv4 } from 'uuid'; import { pipe } from 'fp-ts/lib/pipeable'; import { identity } from 'fp-ts/lib/function'; @@ -24,8 +21,6 @@ import { logEntriesResponseRT, } from '../../../../legacy/plugins/infra/common/http_api'; -import { sharedFragments } from '../../../../legacy/plugins/infra/common/graphql/shared'; -import { InfraTimeKey } from '../../../../legacy/plugins/infra/public/graphql/types'; import { FtrProviderContext } from '../../ftr_provider_context'; const KEY_WITHIN_DATA_RANGE = { @@ -41,77 +36,13 @@ const LATEST_KEY_WITH_DATA = { tiebreaker: 5603910, }; -const logEntriesAroundQuery = gql` - query LogEntriesAroundQuery( - $timeKey: InfraTimeKeyInput! - $countBefore: Int = 0 - $countAfter: Int = 0 - $filterQuery: String - ) { - source(id: "default") { - id - logEntriesAround( - key: $timeKey - countBefore: $countBefore - countAfter: $countAfter - filterQuery: $filterQuery - ) { - start { - ...InfraTimeKeyFields - } - end { - ...InfraTimeKeyFields - } - hasMoreBefore - hasMoreAfter - entries { - ...InfraLogEntryFields - } - } - } - } - - ${sharedFragments.InfraTimeKey} - ${sharedFragments.InfraLogEntryFields} -`; - -const logEntriesBetweenQuery = gql` - query LogEntriesBetweenQuery( - $startKey: InfraTimeKeyInput! - $endKey: InfraTimeKeyInput! - $filterQuery: String - ) { - source(id: "default") { - id - logEntriesBetween(startKey: $startKey, endKey: $endKey, filterQuery: $filterQuery) { - start { - ...InfraTimeKeyFields - } - end { - ...InfraTimeKeyFields - } - hasMoreBefore - hasMoreAfter - entries { - ...InfraLogEntryFields - } - } - } - } - - ${sharedFragments.InfraTimeKey} - ${sharedFragments.InfraLogEntryFields} -`; - const COMMON_HEADERS = { 'kbn-xsrf': 'some-xsrf-token', }; export default function({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); - const client = getService('infraOpsGraphQLClient'); const supertest = getService('supertest'); - const sourceConfigurationService = getService('infraOpsSourceConfiguration'); describe('log entry apis', () => { before(() => esArchiver.load('infra/metrics_and_logs')); @@ -331,249 +262,5 @@ export default function({ getService }: FtrProviderContext) { }); }); }); - - describe('logEntriesAround', () => { - describe('with the default source', () => { - before(() => esArchiver.load('empty_kibana')); - after(() => esArchiver.unload('empty_kibana')); - - it('should return newer and older log entries when present', async () => { - const { - data: { - source: { logEntriesAround }, - }, - } = await client.query({ - query: logEntriesAroundQuery, - variables: { - timeKey: KEY_WITHIN_DATA_RANGE, - countBefore: 100, - countAfter: 100, - }, - }); - - expect(logEntriesAround).to.have.property('entries'); - expect(logEntriesAround.entries).to.have.length(200); - expect(isSorted(ascendingTimeKey)(logEntriesAround.entries)).to.equal(true); - - expect(logEntriesAround.hasMoreBefore).to.equal(true); - expect(logEntriesAround.hasMoreAfter).to.equal(true); - }); - - it('should indicate if no older entries are present', async () => { - const { - data: { - source: { logEntriesAround }, - }, - } = await client.query({ - query: logEntriesAroundQuery, - variables: { - timeKey: EARLIEST_KEY_WITH_DATA, - countBefore: 100, - countAfter: 100, - }, - }); - - expect(logEntriesAround.hasMoreBefore).to.equal(false); - expect(logEntriesAround.hasMoreAfter).to.equal(true); - }); - - it('should indicate if no newer entries are present', async () => { - const { - data: { - source: { logEntriesAround }, - }, - } = await client.query({ - query: logEntriesAroundQuery, - variables: { - timeKey: LATEST_KEY_WITH_DATA, - countBefore: 100, - countAfter: 100, - }, - }); - - expect(logEntriesAround.hasMoreBefore).to.equal(true); - expect(logEntriesAround.hasMoreAfter).to.equal(false); - }); - - it('should return the default columns', async () => { - const { - data: { - source: { - logEntriesAround: { - entries: [entry], - }, - }, - }, - } = await client.query({ - query: logEntriesAroundQuery, - variables: { - timeKey: KEY_WITHIN_DATA_RANGE, - countAfter: 1, - }, - }); - - expect(entry.columns).to.have.length(3); - expect(entry.columns[0]).to.have.property('timestamp'); - expect(entry.columns[0].timestamp).to.be.a('number'); - expect(entry.columns[1]).to.have.property('field'); - expect(entry.columns[1].field).to.be('event.dataset'); - expect(entry.columns[1]).to.have.property('value'); - expect(JSON.parse) - .withArgs(entry.columns[1].value) - .to.not.throwException(); - expect(entry.columns[2]).to.have.property('message'); - expect(entry.columns[2].message).to.be.an('array'); - expect(entry.columns[2].message.length).to.be.greaterThan(0); - }); - }); - - describe('with a configured source', () => { - before(async () => { - await esArchiver.load('empty_kibana'); - await sourceConfigurationService.createConfiguration('default', { - name: 'Test Source', - logColumns: [ - { - timestampColumn: { - id: uuidv4(), - }, - }, - { - fieldColumn: { - id: uuidv4(), - field: 'host.name', - }, - }, - { - fieldColumn: { - id: uuidv4(), - field: 'event.dataset', - }, - }, - { - messageColumn: { - id: uuidv4(), - }, - }, - ], - }); - }); - after(() => esArchiver.unload('empty_kibana')); - - it('should return the configured columns', async () => { - const { - data: { - source: { - logEntriesAround: { - entries: [entry], - }, - }, - }, - } = await client.query({ - query: logEntriesAroundQuery, - variables: { - timeKey: KEY_WITHIN_DATA_RANGE, - countAfter: 1, - }, - }); - - expect(entry.columns).to.have.length(4); - expect(entry.columns[0]).to.have.property('timestamp'); - expect(entry.columns[0].timestamp).to.be.a('number'); - expect(entry.columns[1]).to.have.property('field'); - expect(entry.columns[1].field).to.be('host.name'); - expect(entry.columns[1]).to.have.property('value'); - expect(JSON.parse) - .withArgs(entry.columns[1].value) - .to.not.throwException(); - expect(entry.columns[2]).to.have.property('field'); - expect(entry.columns[2].field).to.be('event.dataset'); - expect(entry.columns[2]).to.have.property('value'); - expect(JSON.parse) - .withArgs(entry.columns[2].value) - .to.not.throwException(); - expect(entry.columns[3]).to.have.property('message'); - expect(entry.columns[3].message).to.be.an('array'); - expect(entry.columns[3].message.length).to.be.greaterThan(0); - }); - }); - }); - - describe('logEntriesBetween', () => { - describe('with the default source', () => { - before(() => esArchiver.load('empty_kibana')); - after(() => esArchiver.unload('empty_kibana')); - - it('should return log entries between the start and end keys', async () => { - const { - data: { - source: { logEntriesBetween }, - }, - } = await client.query({ - query: logEntriesBetweenQuery, - variables: { - startKey: EARLIEST_KEY_WITH_DATA, - endKey: KEY_WITHIN_DATA_RANGE, - }, - }); - - expect(logEntriesBetween).to.have.property('entries'); - expect(logEntriesBetween.entries).to.not.be.empty(); - expect(isSorted(ascendingTimeKey)(logEntriesBetween.entries)).to.equal(true); - - expect( - ascendingTimeKey(logEntriesBetween.entries[0], { key: EARLIEST_KEY_WITH_DATA }) - ).to.be.above(-1); - expect( - ascendingTimeKey(logEntriesBetween.entries[logEntriesBetween.entries.length - 1], { - key: KEY_WITHIN_DATA_RANGE, - }) - ).to.be.below(1); - }); - - it('should return results consistent with logEntriesAround', async () => { - const { - data: { - source: { logEntriesAround }, - }, - } = await client.query({ - query: logEntriesAroundQuery, - variables: { - timeKey: KEY_WITHIN_DATA_RANGE, - countBefore: 100, - countAfter: 100, - }, - }); - - const { - data: { - source: { logEntriesBetween }, - }, - } = await client.query({ - query: logEntriesBetweenQuery, - variables: { - startKey: { - time: logEntriesAround.start.time, - tiebreaker: logEntriesAround.start.tiebreaker - 1, - }, - endKey: { - time: logEntriesAround.end.time, - tiebreaker: logEntriesAround.end.tiebreaker + 1, - }, - }, - }); - - expect(logEntriesBetween).to.eql(logEntriesAround); - }); - }); - }); }); } - -const isSorted = (comparator: (first: Value, second: Value) => number) => ( - values: Value[] -) => pairs(values, comparator).every(order => order <= 0); - -const ascendingTimeKey = (first: { key: InfraTimeKey }, second: { key: InfraTimeKey }) => - ascending(first.key.time, second.key.time) || - ascending(first.key.tiebreaker, second.key.tiebreaker); 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 7e5e15a863865c..b725d4d5cb1914 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 @@ -5,8 +5,6 @@ */ import expect from '@kbn/expect'; -import { ascending, pairs } from 'd3-array'; -import gql from 'graphql-tag'; import { pipe } from 'fp-ts/lib/pipeable'; import { identity } from 'fp-ts/lib/function'; @@ -24,21 +22,11 @@ import { } from '../../../../legacy/plugins/infra/common/http_api'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { sharedFragments } from '../../../../legacy/plugins/infra/common/graphql/shared'; -import { InfraTimeKey } from '../../../../legacy/plugins/infra/public/graphql/types'; const KEY_BEFORE_START = { time: new Date('2000-01-01T00:00:00.000Z').valueOf(), tiebreaker: -1, }; -const KEY_AFTER_START = { - time: new Date('2000-01-01T00:00:04.000Z').valueOf(), - tiebreaker: -1, -}; -const KEY_BEFORE_END = { - time: new Date('2000-01-01T00:00:06.001Z').valueOf(), - tiebreaker: 0, -}; const KEY_AFTER_END = { time: new Date('2000-01-01T00:00:09.001Z').valueOf(), tiebreaker: 0, @@ -51,7 +39,6 @@ const COMMON_HEADERS = { export default function({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); - const client = getService('infraOpsGraphQLClient'); describe('log highlight apis', () => { before(() => esArchiver.load('infra/simple_logs')); @@ -188,236 +175,5 @@ export default function({ getService }: FtrProviderContext) { }); }); }); - - describe('logEntryHighlights', () => { - describe('with the default source', () => { - before(() => esArchiver.load('empty_kibana')); - after(() => esArchiver.unload('empty_kibana')); - - it('should return log highlights in the built-in message column', async () => { - const { - data: { - source: { logEntryHighlights }, - }, - } = await client.query({ - query: logEntryHighlightsQuery, - variables: { - sourceId: 'default', - startKey: KEY_BEFORE_START, - endKey: KEY_AFTER_END, - highlights: [ - { - query: 'message of document 0', - countBefore: 0, - countAfter: 0, - }, - ], - }, - }); - - expect(logEntryHighlights).to.have.length(1); - - const [logEntryHighlightSet] = logEntryHighlights; - expect(logEntryHighlightSet).to.have.property('entries'); - // ten bundles with one highlight each - expect(logEntryHighlightSet.entries).to.have.length(10); - expect(isSorted(ascendingTimeKey)(logEntryHighlightSet.entries)).to.equal(true); - - for (const logEntryHighlight of logEntryHighlightSet.entries) { - expect(logEntryHighlight.columns).to.have.length(3); - expect(logEntryHighlight.columns[1]).to.have.property('field'); - expect(logEntryHighlight.columns[1]).to.have.property('highlights'); - expect(logEntryHighlight.columns[1].highlights).to.eql([]); - expect(logEntryHighlight.columns[2]).to.have.property('message'); - expect(logEntryHighlight.columns[2].message).to.be.an('array'); - expect(logEntryHighlight.columns[2].message.length).to.be(1); - expect(logEntryHighlight.columns[2].message[0].highlights).to.eql([ - 'message', - 'of', - 'document', - '0', - ]); - } - }); - - // https://github.com/elastic/kibana/issues/49959 - it.skip('should return log highlights in a field column', async () => { - const { - data: { - source: { logEntryHighlights }, - }, - } = await client.query({ - query: logEntryHighlightsQuery, - variables: { - sourceId: 'default', - startKey: KEY_BEFORE_START, - endKey: KEY_AFTER_END, - highlights: [ - { - query: 'generate_test_data/simple_logs', - countBefore: 0, - countAfter: 0, - }, - ], - }, - }); - - expect(logEntryHighlights).to.have.length(1); - - const [logEntryHighlightSet] = logEntryHighlights; - expect(logEntryHighlightSet).to.have.property('entries'); - // ten bundles with five highlights each - expect(logEntryHighlightSet.entries).to.have.length(50); - expect(isSorted(ascendingTimeKey)(logEntryHighlightSet.entries)).to.equal(true); - - for (const logEntryHighlight of logEntryHighlightSet.entries) { - expect(logEntryHighlight.columns).to.have.length(3); - expect(logEntryHighlight.columns[1]).to.have.property('field'); - expect(logEntryHighlight.columns[1]).to.have.property('highlights'); - expect(logEntryHighlight.columns[1].highlights).to.eql([ - 'generate_test_data/simple_logs', - ]); - expect(logEntryHighlight.columns[2]).to.have.property('message'); - expect(logEntryHighlight.columns[2].message).to.be.an('array'); - expect(logEntryHighlight.columns[2].message.length).to.be(1); - expect(logEntryHighlight.columns[2].message[0].highlights).to.eql([]); - } - }); - - it('should apply the filter query in addition to the highlight query', async () => { - const { - data: { - source: { logEntryHighlights }, - }, - } = await client.query({ - query: logEntryHighlightsQuery, - variables: { - sourceId: 'default', - startKey: KEY_BEFORE_START, - endKey: KEY_AFTER_END, - filterQuery: JSON.stringify({ - multi_match: { query: 'host-a', type: 'phrase', lenient: true }, - }), - highlights: [ - { - query: 'message', - countBefore: 0, - countAfter: 0, - }, - ], - }, - }); - - expect(logEntryHighlights).to.have.length(1); - - const [logEntryHighlightSet] = logEntryHighlights; - expect(logEntryHighlightSet).to.have.property('entries'); - // half of the documenst - expect(logEntryHighlightSet.entries).to.have.length(25); - expect(isSorted(ascendingTimeKey)(logEntryHighlightSet.entries)).to.equal(true); - - for (const logEntryHighlight of logEntryHighlightSet.entries) { - expect(logEntryHighlight.columns).to.have.length(3); - expect(logEntryHighlight.columns[1]).to.have.property('field'); - expect(logEntryHighlight.columns[1]).to.have.property('highlights'); - expect(logEntryHighlight.columns[1].highlights).to.eql([]); - expect(logEntryHighlight.columns[2]).to.have.property('message'); - expect(logEntryHighlight.columns[2].message).to.be.an('array'); - expect(logEntryHighlight.columns[2].message.length).to.be(1); - expect(logEntryHighlight.columns[2].message[0].highlights).to.eql([ - 'message', - 'message', - ]); - } - }); - - it('should return highlights outside of the interval when requested', async () => { - const { - data: { - source: { logEntryHighlights }, - }, - } = await client.query({ - query: logEntryHighlightsQuery, - variables: { - sourceId: 'default', - startKey: KEY_AFTER_START, - endKey: KEY_BEFORE_END, - highlights: [ - { - query: 'message of document 0', - countBefore: 2, - countAfter: 2, - }, - ], - }, - }); - - expect(logEntryHighlights).to.have.length(1); - - const [logEntryHighlightSet] = logEntryHighlights; - expect(logEntryHighlightSet).to.have.property('entries'); - // three bundles with one highlight each plus two beyond each interval boundary - expect(logEntryHighlightSet.entries).to.have.length(3 + 4); - expect(isSorted(ascendingTimeKey)(logEntryHighlightSet.entries)).to.equal(true); - - for (const logEntryHighlight of logEntryHighlightSet.entries) { - expect(logEntryHighlight.columns).to.have.length(3); - expect(logEntryHighlight.columns[1]).to.have.property('field'); - expect(logEntryHighlight.columns[1]).to.have.property('highlights'); - expect(logEntryHighlight.columns[1].highlights).to.eql([]); - expect(logEntryHighlight.columns[2]).to.have.property('message'); - expect(logEntryHighlight.columns[2].message).to.be.an('array'); - expect(logEntryHighlight.columns[2].message.length).to.be(1); - expect(logEntryHighlight.columns[2].message[0].highlights).to.eql([ - 'message', - 'of', - 'document', - '0', - ]); - } - }); - }); - }); }); } - -const logEntryHighlightsQuery = gql` - query LogEntryHighlightsQuery( - $sourceId: ID = "default" - $startKey: InfraTimeKeyInput! - $endKey: InfraTimeKeyInput! - $filterQuery: String - $highlights: [InfraLogEntryHighlightInput!]! - ) { - source(id: $sourceId) { - id - logEntryHighlights( - startKey: $startKey - endKey: $endKey - filterQuery: $filterQuery - highlights: $highlights - ) { - start { - ...InfraTimeKeyFields - } - end { - ...InfraTimeKeyFields - } - entries { - ...InfraLogEntryHighlightFields - } - } - } - } - - ${sharedFragments.InfraTimeKey} - ${sharedFragments.InfraLogEntryHighlightFields} -`; - -const isSorted = (comparator: (first: Value, second: Value) => number) => ( - values: Value[] -) => pairs(values, comparator).every(order => order <= 0); - -const ascendingTimeKey = (first: { key: InfraTimeKey }, second: { key: InfraTimeKey }) => - ascending(first.key.time, second.key.time) || - ascending(first.key.tiebreaker, second.key.tiebreaker); diff --git a/x-pack/test/api_integration/apis/infra/logs_without_millis.ts b/x-pack/test/api_integration/apis/infra/logs_without_millis.ts deleted file mode 100644 index 8c30a7a480715a..00000000000000 --- a/x-pack/test/api_integration/apis/infra/logs_without_millis.ts +++ /dev/null @@ -1,193 +0,0 @@ -/* - * 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 expect from '@kbn/expect'; -import { ascending, pairs } from 'd3-array'; -import gql from 'graphql-tag'; - -import { pipe } from 'fp-ts/lib/pipeable'; -import { identity } from 'fp-ts/lib/function'; -import { fold } from 'fp-ts/lib/Either'; - -import { - createPlainError, - throwErrors, -} from '../../../../legacy/plugins/infra/common/runtime_types'; - -import { FtrProviderContext } from '../../ftr_provider_context'; -import { sharedFragments } from '../../../../legacy/plugins/infra/common/graphql/shared'; -import { InfraTimeKey } from '../../../../legacy/plugins/infra/public/graphql/types'; -import { - LOG_ENTRIES_SUMMARY_PATH, - logEntriesSummaryRequestRT, - logEntriesSummaryResponseRT, -} from '../../../../legacy/plugins/infra/common/http_api/log_entries'; - -const COMMON_HEADERS = { - 'kbn-xsrf': 'some-xsrf-token', -}; -const KEY_WITHIN_DATA_RANGE = { - time: new Date('2019-01-06T00:00:00.000Z').valueOf(), - tiebreaker: 0, -}; -const EARLIEST_KEY_WITH_DATA = { - time: new Date('2019-01-05T23:59:23.000Z').valueOf(), - tiebreaker: -1, -}; -const LATEST_KEY_WITH_DATA = { - time: new Date('2019-01-06T23:59:23.000Z').valueOf(), - tiebreaker: 2, -}; - -export default function({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const client = getService('infraOpsGraphQLClient'); - const supertest = getService('supertest'); - - describe('logs without epoch_millis format', () => { - before(() => esArchiver.load('infra/logs_without_epoch_millis')); - after(() => esArchiver.unload('infra/logs_without_epoch_millis')); - - it('logEntriesAround should return log entries', async () => { - const { - data: { - source: { logEntriesAround }, - }, - } = await client.query({ - query: logEntriesAroundQuery, - variables: { - timeKey: KEY_WITHIN_DATA_RANGE, - countBefore: 1, - countAfter: 1, - }, - }); - - expect(logEntriesAround).to.have.property('entries'); - expect(logEntriesAround.entries).to.have.length(2); - expect(isSorted(ascendingTimeKey)(logEntriesAround.entries)).to.equal(true); - - expect(logEntriesAround.hasMoreBefore).to.equal(false); - expect(logEntriesAround.hasMoreAfter).to.equal(false); - }); - - it('logEntriesBetween should return log entries', async () => { - const { - data: { - source: { logEntriesBetween }, - }, - } = await client.query({ - query: logEntriesBetweenQuery, - variables: { - startKey: EARLIEST_KEY_WITH_DATA, - endKey: LATEST_KEY_WITH_DATA, - }, - }); - - expect(logEntriesBetween).to.have.property('entries'); - expect(logEntriesBetween.entries).to.have.length(2); - expect(isSorted(ascendingTimeKey)(logEntriesBetween.entries)).to.equal(true); - }); - - it('logSummaryBetween should return non-empty buckets', async () => { - const startTimestamp = EARLIEST_KEY_WITH_DATA.time; - const endTimestamp = LATEST_KEY_WITH_DATA.time + 1; // the interval end is exclusive - const bucketSize = Math.ceil((endTimestamp - startTimestamp) / 10); - - const { body } = await supertest - .post(LOG_ENTRIES_SUMMARY_PATH) - .set(COMMON_HEADERS) - .send( - logEntriesSummaryRequestRT.encode({ - sourceId: 'default', - startTimestamp, - endTimestamp, - bucketSize, - query: null, - }) - ) - .expect(200); - - const logSummaryResponse = pipe( - logEntriesSummaryResponseRT.decode(body), - fold(throwErrors(createPlainError), identity) - ); - - expect( - logSummaryResponse.data.buckets.filter((bucket: any) => bucket.entriesCount > 0) - ).to.have.length(2); - }); - }); -} - -const logEntriesAroundQuery = gql` - query LogEntriesAroundQuery( - $timeKey: InfraTimeKeyInput! - $countBefore: Int = 0 - $countAfter: Int = 0 - $filterQuery: String - ) { - source(id: "default") { - id - logEntriesAround( - key: $timeKey - countBefore: $countBefore - countAfter: $countAfter - filterQuery: $filterQuery - ) { - start { - ...InfraTimeKeyFields - } - end { - ...InfraTimeKeyFields - } - hasMoreBefore - hasMoreAfter - entries { - ...InfraLogEntryFields - } - } - } - } - - ${sharedFragments.InfraTimeKey} - ${sharedFragments.InfraLogEntryFields} -`; - -const logEntriesBetweenQuery = gql` - query LogEntriesBetweenQuery( - $startKey: InfraTimeKeyInput! - $endKey: InfraTimeKeyInput! - $filterQuery: String - ) { - source(id: "default") { - id - logEntriesBetween(startKey: $startKey, endKey: $endKey, filterQuery: $filterQuery) { - start { - ...InfraTimeKeyFields - } - end { - ...InfraTimeKeyFields - } - hasMoreBefore - hasMoreAfter - entries { - ...InfraLogEntryFields - } - } - } - } - - ${sharedFragments.InfraTimeKey} - ${sharedFragments.InfraLogEntryFields} -`; - -const isSorted = (comparator: (first: Value, second: Value) => number) => ( - values: Value[] -) => pairs(values, comparator).every(order => order <= 0); - -const ascendingTimeKey = (first: { key: InfraTimeKey }, second: { key: InfraTimeKey }) => - ascending(first.key.time, second.key.time) || - ascending(first.key.tiebreaker, second.key.tiebreaker); diff --git a/x-pack/test/functional/apps/infra/link_to.ts b/x-pack/test/functional/apps/infra/link_to.ts index 738dc7efd8fd91..690bcacac59bce 100644 --- a/x-pack/test/functional/apps/infra/link_to.ts +++ b/x-pack/test/functional/apps/infra/link_to.ts @@ -22,7 +22,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { state: undefined, }; const expectedSearchString = - "sourceId=default&logPosition=(position:(tiebreaker:0,time:1565707203194),streamLive:!f)&logFilter=(expression:'trace.id:433b4651687e18be2c6c8e3b11f53d09',kind:kuery)"; + "sourceId=default&logPosition=(end:now,position:(tiebreaker:0,time:1565707203194),start:now-1d,streamLive:!f)&logFilter=(expression:'trace.id:433b4651687e18be2c6c8e3b11f53d09',kind:kuery)"; const expectedRedirectPath = '/logs/stream?'; await pageObjects.common.navigateToActualUrl( diff --git a/x-pack/test/functional/apps/infra/logs_source_configuration.ts b/x-pack/test/functional/apps/infra/logs_source_configuration.ts index 3fb8dc856b5b3f..6e02751f29650c 100644 --- a/x-pack/test/functional/apps/infra/logs_source_configuration.ts +++ b/x-pack/test/functional/apps/infra/logs_source_configuration.ts @@ -110,7 +110,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('renders the changed log columns with their headers', async () => { - await logsUi.logStreamPage.navigateTo(); + await logsUi.logStreamPage.navigateTo({ + logPosition: { start: '2018-10-17T19:42:22.000Z', end: '2018-10-17T19:57:21.000Z' }, + }); await retry.try(async () => { const columnHeaderLabels = await logsUi.logStreamPage.getColumnHeaderLabels(); From cb67abc7f3997de04f025f1c6e84ffb5c6dcb6e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 12 Feb 2020 14:01:49 +0100 Subject: [PATCH 052/117] Refine minimap dimensions Make the time ruler a bit bigger to allow for longer labels. Refactor as well how the width of certain elemens is calculated. --- .../logging/log_minimap/density_chart.tsx | 17 +++++------------ .../log_minimap/highlighted_interval.tsx | 6 ++++-- .../logging/log_minimap/log_minimap.tsx | 17 ++++++++++++----- .../logging/log_minimap/time_ruler.tsx | 11 +++-------- 4 files changed, 24 insertions(+), 27 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/density_chart.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/density_chart.tsx index 3eac729edbabae..92090ca5712ef2 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/density_chart.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/density_chart.tsx @@ -18,6 +18,7 @@ interface DensityChartProps { start: number; width: number; height: number; + x: number; } export const DensityChart: React.FC = ({ @@ -26,6 +27,7 @@ export const DensityChart: React.FC = ({ end, width, height, + x, }) => { if (start >= end || height <= 0 || width <= 0 || buckets.length <= 0) { return null; @@ -38,7 +40,7 @@ export const DensityChart: React.FC = ({ const xMax = max(buckets.map(bucket => bucket.entriesCount)) || 0; const xScale = scaleLinear() .domain([0, xMax]) - .range([0, width * (2 / 3)]); + .range([0, width]); // FIXME: path is not closed at the bottom. const path = area() @@ -50,22 +52,13 @@ export const DensityChart: React.FC = ({ const pathData = path(buckets); return ( - - - + + ); }; -const DensityChartNegativeBackground = euiStyled.rect` - fill: ${props => props.theme.eui.euiColorEmptyShade}; -`; - const DensityChartPositiveBackground = euiStyled.rect` fill: ${props => props.theme.darkMode diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/highlighted_interval.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/highlighted_interval.tsx index 4711a7ac6ffde1..e01e9748a004d5 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/highlighted_interval.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/highlighted_interval.tsx @@ -13,6 +13,7 @@ interface HighlightedIntervalProps { getPositionOfTime: (time: number) => number; start: number; end: number; + targetWidth: number; width: number; target: number | null; } @@ -22,6 +23,7 @@ export const HighlightedInterval: React.FC = ({ end, getPositionOfTime, start, + targetWidth, width, target, }) => { @@ -35,14 +37,14 @@ export const HighlightedInterval: React.FC = ({ )} ); 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 1550e0d87ce04a..4e1ca80ad12ef4 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 @@ -43,6 +43,9 @@ interface LogMinimapState { timeCursorY: number; } +// Wide enough to fit "September" +const TIMERULER_WIDTH = 50; + function calculateYScale(start: number | null, end: number | null, height: number) { return scaleLinear() .domain([start || 0, end || 0]) @@ -116,7 +119,8 @@ export class LogMinimap extends React.Component - + @@ -159,11 +164,12 @@ export class LogMinimap extends React.Component ) : null} - + ); } @@ -185,6 +191,7 @@ const TimeCursor = euiStyled.line` const MinimapWrapper = euiStyled.svg` cursor: pointer; + fill: ${props => props.theme.eui.euiColorEmptyShade}; & ${TimeCursor} { visibility: hidden; } diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx index c72403539563d5..8d3ef3bc69c39c 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx @@ -39,7 +39,8 @@ export const TimeRuler: React.FC = ({ end, height, start, tickCo {ticks.map((tick, tickIndex) => { const y = yScale(tick); const isLabeledTick = tickIndex % 12 === dateModLabel; - const tickStartX = isLabeledTick ? 0 : width / 3 - 4; + const tickStartX = isLabeledTick ? 0 : width - 4; + return ( {isLabeledTick && ( @@ -47,13 +48,7 @@ export const TimeRuler: React.FC = ({ end, height, start, tickCo {formatTick(tick)} )} - + ); })} From 0d232bb975d0f23fc427a2c803db16edeb971e47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 12 Feb 2020 16:15:14 +0100 Subject: [PATCH 053/117] Label all ticks in the time ruler Previously some of the ticks in the time ruler were not labeled and where shown as small ticks. When using large date ranges, the labels in the time ruler sometimes didn't make any sense. The solution is to render all labels as intended by D3. --- .../logging/log_minimap/log_minimap.tsx | 2 +- .../logging/log_minimap/time_ruler.tsx | 33 +++++-------------- 2 files changed, 9 insertions(+), 26 deletions(-) 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 4e1ca80ad12ef4..c49a75478c9609 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 @@ -119,7 +119,7 @@ export class LogMinimap extends React.Component = ({ end, height, start, tickCo const ticks = yScale.ticks(tickCount); const formatTick = yScale.tickFormat(); - const dateModLabel = (() => { - for (let i = 0; i < ticks.length; i++) { - const tickLabel = formatTick(ticks[i]); - if (!tickLabel[0].match(/[0-9]/)) { - return i % 12; - } - } - })(); - return ( {ticks.map((tick, tickIndex) => { const y = yScale(tick); - const isLabeledTick = tickIndex % 12 === dateModLabel; - const tickStartX = isLabeledTick ? 0 : width - 4; return ( - {isLabeledTick && ( - - {formatTick(tick)} - - )} - + + {formatTick(tick)} + + ); })} @@ -66,15 +53,11 @@ const TimeRulerTickLabel = euiStyled.text` pointer-events: none; `; -const TimeRulerGridLine = euiStyled.line<{ isDark: boolean }>` +const TimeRulerGridLine = euiStyled.line` stroke: ${props => - props.isDark - ? props.theme.darkMode - ? props.theme.eui.euiColorDarkestShade - : props.theme.eui.euiColorDarkShade - : props.theme.darkMode - ? props.theme.eui.euiColorDarkShade - : props.theme.eui.euiColorMediumShade}; + props.theme.darkMode + ? props.theme.eui.euiColorDarkestShade + : props.theme.eui.euiColorDarkShade}; stroke-opacity: 0.5; stroke-width: 1px; `; From 46ac4ae0bc8d6331170876f372df2fd52a24594b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 12 Feb 2020 16:49:38 +0100 Subject: [PATCH 054/117] Tweak time ruler format for small ranges --- .../log_minimap/time_label_formatter.tsx | 23 +++++++++++++++++++ .../logging/log_minimap/time_ruler.tsx | 3 ++- 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 x-pack/legacy/plugins/infra/public/components/logging/log_minimap/time_label_formatter.tsx diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/time_label_formatter.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/time_label_formatter.tsx new file mode 100644 index 00000000000000..267ffd005801de --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/time_label_formatter.tsx @@ -0,0 +1,23 @@ +/* + * 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. + */ + +// The default d3-time-format is a bit strange for small ranges, so we will specify our own +export function timeLabelFormat(start: number, end: number): string | undefined { + const diff = Math.abs(end - start); + + // 15 seconds + if (diff < 15 * 1000) { + return ':%S.%L'; + } + + // 16 minutes + if (diff < 16 * 60 * 1000) { + return '%I:%M:%S'; + } + + // Use D3's default + return; +} diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx index 57ee31ff69ee8d..bb4e32b4d6e0be 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx @@ -8,6 +8,7 @@ import { scaleTime } from 'd3-scale'; import * as React from 'react'; import euiStyled from '../../../../../../common/eui_styled_components'; +import { timeLabelFormat } from './time_label_formatter'; interface TimeRulerProps { end: number; @@ -23,7 +24,7 @@ export const TimeRuler: React.FC = ({ end, height, start, tickCo .range([0, height]); const ticks = yScale.ticks(tickCount); - const formatTick = yScale.tickFormat(); + const formatTick = yScale.tickFormat(tickCount, timeLabelFormat(start, end)); return ( From f699073790776b947eae30a1feca1da2484067c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 13 Feb 2020 14:51:45 +0100 Subject: [PATCH 055/117] Adjust initial start-end range for initial position Sometimes the initial position in the URL falls outside the initial start-end range. This is mostly due the nature of relative values for `startDate` and `endDate`. When the initial URL has both position and a range and the position falls outside the range we will extend the range. We do this to respect the position, which is probably what the user wanted to share anyway. --- .../with_log_position_url_state.tsx | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx index 12576d91f537e6..840ff5bb8ecdac 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx @@ -9,12 +9,11 @@ import React, { useContext, useMemo } from 'react'; import { pickTimeKey } from '../../../../common/time'; import { replaceStateKeyInQueryString, UrlStateContainer } from '../../../utils/url_state'; import { LogPositionState, LogPositionStateParams } from './log_position_state'; -import { isValidDatemath } from '../../../utils/datemath'; +import { isValidDatemath, datemathToEpochMillis } from '../../../utils/datemath'; /** * Url State */ - interface LogPositionUrlState { position: LogPositionStateParams['visibleMidpoint'] | undefined; streamLive: boolean; @@ -22,6 +21,8 @@ interface LogPositionUrlState { end?: string; } +const ONE_HOUR = 86400000; + export const WithLogPositionUrlState = () => { const { visibleMidpoint, @@ -69,12 +70,31 @@ export const WithLogPositionUrlState = () => { }} onInitialize={(initialUrlState: LogPositionUrlState | undefined) => { if (initialUrlState) { - if (initialUrlState.start || initialUrlState.end) { - updateDateRange({ startDate: initialUrlState.start, endDate: initialUrlState.end }); - } + const initialPosition = initialUrlState.position; + let initialStartDate = initialUrlState.start || 'now-1d'; + let initialEndDate = initialUrlState.end || 'now'; + + if (initialPosition) { + const initialStartTimestamp = initialStartDate + ? datemathToEpochMillis(initialStartDate) + : undefined; + const initialEndTimestamp = initialEndDate + ? datemathToEpochMillis(initialEndDate) + : undefined; + + // Adjust the start-end range if the target position falls outside + if (initialStartTimestamp && initialStartTimestamp > initialPosition.time) { + initialStartDate = new Date(initialPosition.time - ONE_HOUR).toISOString(); + } + if (initialEndTimestamp && initialEndTimestamp < initialPosition.time) { + initialEndDate = new Date(initialPosition.time + ONE_HOUR).toISOString(); + } + + if (initialStartDate || initialEndDate) { + updateDateRange({ startDate: initialStartDate, endDate: initialEndDate }); + } - if (initialUrlState.position) { - jumpToTargetPosition(initialUrlState.position); + jumpToTargetPosition(initialPosition); } if (initialUrlState.streamLive) { From 1c227f3c5ec0e7edb8a6482bb2807c19e7052d3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 13 Feb 2020 17:13:02 +0100 Subject: [PATCH 056/117] Fix rendering of visible range in the minimap --- .../public/components/logging/log_minimap/log_minimap.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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 c49a75478c9609..ceb1947d7d4ea7 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 @@ -91,11 +91,7 @@ export class LogMinimap extends React.Component { - const { height, intervalSize } = this.props; - - const [minTime] = this.getYScale().domain(); - - return ((time - minTime) * height) / intervalSize; // + return this.getYScale()(time); }; private updateTimeCursor: React.MouseEventHandler = event => { From 93cd714cb055932d82e8bfe906979d3159db8e65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 13 Feb 2020 18:29:38 +0100 Subject: [PATCH 057/117] Keep position when expanding the date range --- .../containers/logs/log_entries/index.ts | 64 +++++++++++++++++-- .../logs/log_position/log_position_state.ts | 24 ++++++- 2 files changed, 81 insertions(+), 7 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts index 6dc3f04a8a0ad5..3efbd00274dba1 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts @@ -27,6 +27,7 @@ enum Action { ReceiveEntriesAfter, ErrorOnNewEntries, ErrorOnMoreEntries, + ExpandRange, } type ReceiveActions = @@ -38,10 +39,14 @@ interface ReceiveEntriesAction { type: ReceiveActions; payload: LogEntriesResponse['data']; } +interface ExpandRangeAction { + type: Action.ExpandRange; + payload: { before: boolean; after: boolean }; +} interface FetchOrErrorAction { - type: Exclude; + type: Exclude; } -type ActionObj = ReceiveEntriesAction | FetchOrErrorAction; +type ActionObj = ReceiveEntriesAction | FetchOrErrorAction | ExpandRangeAction; type Dispatch = (action: ActionObj) => void; @@ -106,9 +111,12 @@ const shouldFetchNewEntries = ({ bottomCursor, startDate, endDate, + startTimestamp, + endTimestamp, }: FetchEntriesParams & LogEntriesStateParams & { prevParams: FetchEntriesParams | undefined }) => { const shouldLoadWithNewDates = prevParams - ? startDate !== prevParams.startDate || endDate !== prevParams.endDate + ? (startDate !== prevParams.startDate && startTimestamp > prevParams.startTimestamp) || + (endDate !== prevParams.endDate && endTimestamp < prevParams.endTimestamp) : true; const shouldLoadWithNewFilter = prevParams ? filterQuery !== prevParams.filterQuery : true; const shouldLoadAroundNewPosition = @@ -281,9 +289,38 @@ const useFetchEntriesEffect = ( })(); }; + const expandRangeEffect = () => { + const shouldExpand = { + before: false, + after: false, + }; + + if ( + !props.startTimestamp || + !props.endTimestamp || + !prevParams || + !prevParams.startTimestamp || + !prevParams.endTimestamp + ) { + return; + } + + if (props.startTimestamp < prevParams.startTimestamp) { + shouldExpand.before = true; + } + if (props.endTimestamp > prevParams.endTimestamp) { + shouldExpand.after = true; + } + + dispatch({ type: Action.ExpandRange, payload: shouldExpand }); + }; + + const expandRangeEffectDependencies = [props.startDate, props.endDate]; + useEffect(fetchNewEntriesEffect, fetchNewEntriesEffectDependencies); useEffect(fetchMoreEntriesEffect, fetchMoreEntriesEffectDependencies); useEffect(streamEntriesEffect, streamEntriesEffectDependencies); + useEffect(expandRangeEffect, expandRangeEffectDependencies); return { fetchNewerEntries, checkForNewEntries: runFetchNewEntriesRequest }; }; @@ -345,13 +382,32 @@ const logEntriesStateReducer = (prevState: LogEntriesStateParams, action: Action return { ...prevState, ...update }; } case Action.FetchingNewEntries: - return { ...prevState, isReloading: true, entries: [], topCursor: null, bottomCursor: null }; + return { + ...prevState, + isReloading: true, + entries: [], + topCursor: null, + bottomCursor: null, + hasMoreBeforeStart: true, + hasMoreAfterEnd: true, + }; case Action.FetchingMoreEntries: return { ...prevState, isLoadingMore: true }; case Action.ErrorOnNewEntries: return { ...prevState, isReloading: false }; case Action.ErrorOnMoreEntries: return { ...prevState, isLoadingMore: false }; + + case Action.ExpandRange: { + const hasMoreBeforeStart = action.payload.before ? true : prevState.hasMoreBeforeStart; + const hasMoreAfterEnd = action.payload.after ? true : prevState.hasMoreAfterEnd; + + return { + ...prevState, + hasMoreBeforeStart, + hasMoreAfterEnd, + }; + } default: throw new Error(); } diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts index 7e15cdc573284d..25222199abeef9 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts @@ -7,7 +7,7 @@ import { useState, useMemo, useEffect, useCallback } from 'react'; import createContainer from 'constate'; import { TimeKey } from '../../../../common/time'; -import { datemathToEpochMillis } from '../../../utils/datemath'; +import { datemathToEpochMillis, isValidDatemath } from '../../../utils/datemath'; type TimeKeyOrNull = TimeKey | null; @@ -125,7 +125,25 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall return; } - jumpToTargetPosition(null); + const nextStartDate = newDateRange.startDate || dateRange.startDate; + const nextEndDate = newDateRange.endDate || dateRange.endDate; + + if (!isValidDatemath(nextStartDate) || !isValidDatemath(nextEndDate)) { + return; + } + + // Dates are valid, so the function cannot return `null` + const nextStartTimestamp = datemathToEpochMillis(nextStartDate)!; + const nextEndTimestamp = datemathToEpochMillis(nextEndDate)!; + + // Reset the target position if it doesn't fall within the new range. + if ( + targetPosition && + (nextStartTimestamp > targetPosition.time || nextEndTimestamp < targetPosition.time) + ) { + jumpToTargetPosition(null); + } + setDateRange(previousDateRange => { return { startDate: newDateRange.startDate || previousDateRange.startDate, @@ -133,7 +151,7 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall }; }); }, - [dateRange] + [dateRange, targetPosition] ); const startTimestamp = useMemo(() => datemathToEpochMillis(dateRange.startDate), [ From f57ef37387e5c10bf9b35ea6106d5c9f93136e66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 13 Feb 2020 18:34:38 +0100 Subject: [PATCH 058/117] Be stricter with log entries container --- .../infra/public/containers/logs/log_entries/index.ts | 8 ++++---- .../infra/public/pages/logs/stream/page_providers.tsx | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts index 3efbd00274dba1..65c55e91fc1c00 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts @@ -51,10 +51,10 @@ type ActionObj = ReceiveEntriesAction | FetchOrErrorAction | ExpandRangeAction; type Dispatch = (action: ActionObj) => void; interface LogEntriesProps { - startDate: string | null; - endDate: string | null; - startTimestamp: number | null; - endTimestamp: number | null; + startDate: string; + endDate: string; + startTimestamp: number; + endTimestamp: number; liveStreamingInterval: number; filterQuery: string | null; timeKey: TimeKey | null; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx index a8ab8b5455fb2a..e5df0f922c7083 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx @@ -43,6 +43,11 @@ const LogEntriesStateProvider: React.FC = ({ children }) => { } = useContext(LogPositionState.Context); const { filterQuery } = useContext(LogFilterState.Context); + // Don't render anything if the date range is incorrect. + if (!startTimestamp || !endTimestamp) { + return null; + } + const entriesProps = { startDate, endDate, From b9c60132d08161685371cf8e6f3cf9612fa07801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 14 Feb 2020 11:37:07 +0100 Subject: [PATCH 059/117] Round `endDate` up This makes ranges like `today`, `this week`, etc. work as intended --- .../containers/logs/log_position/log_position_state.ts | 6 ++++-- .../logs/log_position/with_log_position_url_state.tsx | 2 +- .../public/containers/logs/log_summary/log_summary.test.tsx | 2 +- x-pack/legacy/plugins/infra/public/utils/datemath.ts | 4 ++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts index 25222199abeef9..60e8498c064336 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts @@ -134,7 +134,7 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall // Dates are valid, so the function cannot return `null` const nextStartTimestamp = datemathToEpochMillis(nextStartDate)!; - const nextEndTimestamp = datemathToEpochMillis(nextEndDate)!; + const nextEndTimestamp = datemathToEpochMillis(nextEndDate, 'up')!; // Reset the target position if it doesn't fall within the new range. if ( @@ -161,7 +161,9 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall // endTimestamp needs to be synced to `now` to allow auto-streaming const endTimestampDep = dateRange.endDate === 'now' ? Date.now() : dateRange.endDate; // eslint-disable-next-line react-hooks/exhaustive-deps - const endTimestamp = useMemo(() => datemathToEpochMillis(dateRange.endDate), [endTimestampDep]); + const endTimestamp = useMemo(() => datemathToEpochMillis(dateRange.endDate, 'up'), [ + endTimestampDep, + ]); const state = { initialized, diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx index 840ff5bb8ecdac..68a11774f1d443 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx @@ -79,7 +79,7 @@ export const WithLogPositionUrlState = () => { ? datemathToEpochMillis(initialStartDate) : undefined; const initialEndTimestamp = initialEndDate - ? datemathToEpochMillis(initialEndDate) + ? datemathToEpochMillis(initialEndDate, 'up') : undefined; // Adjust the start-end range if the target position falls outside diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx index ff37ea466d6d8a..02515b4aff62ef 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx @@ -161,6 +161,6 @@ const createMockDateRange = (startDate = 'now-10s', endDate = 'now') => { startDate, endDate, startTimestamp: datemathToEpochMillis(startDate)!, - endTimestamp: datemathToEpochMillis(endDate)!, + endTimestamp: datemathToEpochMillis(endDate, 'up')!, }; }; diff --git a/x-pack/legacy/plugins/infra/public/utils/datemath.ts b/x-pack/legacy/plugins/infra/public/utils/datemath.ts index 61d74fa8f87d32..e3af85d6aad798 100644 --- a/x-pack/legacy/plugins/infra/public/utils/datemath.ts +++ b/x-pack/legacy/plugins/infra/public/utils/datemath.ts @@ -11,8 +11,8 @@ export function isValidDatemath(value: string): boolean { return !!(parsedValue && parsedValue.isValid()); } -export function datemathToEpochMillis(value: string): number | null { - const parsedValue = dateMath.parse(value); +export function datemathToEpochMillis(value: string, round: 'down' | 'up' = 'down'): number | null { + const parsedValue = dateMath.parse(value, { roundUp: round === 'up' }); if (!parsedValue || !parsedValue.isValid()) { return null; } From d6acab1128e89bf1811b90afa3a4f981ce8c3506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 14 Feb 2020 12:00:50 +0100 Subject: [PATCH 060/117] Ensure timeRange falls within range Sometimes the user might ask for log entries in a point where there are none. The API will then return the log entires in the closest range. That would then trigger an infinite rerender, because the timeKey was always outside the top and bottom cursors. If the time key is not within the top and bottom cursors, we will force it to be the top cursor --- .../infra/public/containers/logs/log_entries/index.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts index 65c55e91fc1c00..a52b842ceb9774 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts @@ -179,6 +179,13 @@ const useFetchEntriesEffect = ( setTimeout(() => { props.jumpToTargetPosition(payload.bottomCursor!); }); + } else if ( + props.timeKey && + payload.topCursor && + payload.bottomCursor && + !timeKeyIsBetween(payload.topCursor, payload.bottomCursor, props.timeKey) + ) { + props.jumpToTargetPosition(payload.topCursor); } } catch (e) { dispatch({ type: Action.ErrorOnNewEntries }); From 220e8d6796b60bcd5234df1da3493952fd5a40b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 14 Feb 2020 14:45:49 +0100 Subject: [PATCH 061/117] Correctly set the initial start and end date if there is no initial position --- .../logs/log_position/with_log_position_url_state.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx index 68a11774f1d443..824eb978488631 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx @@ -90,13 +90,13 @@ export const WithLogPositionUrlState = () => { initialEndDate = new Date(initialPosition.time + ONE_HOUR).toISOString(); } - if (initialStartDate || initialEndDate) { - updateDateRange({ startDate: initialStartDate, endDate: initialEndDate }); - } - jumpToTargetPosition(initialPosition); } + if (initialStartDate || initialEndDate) { + updateDateRange({ startDate: initialStartDate, endDate: initialEndDate }); + } + if (initialUrlState.streamLive) { startLiveStreaming(); } From 393c7f1a3fb0be7006a8e83d9a223dbf516a6c22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 14 Feb 2020 17:17:29 +0100 Subject: [PATCH 062/117] Fix link-to test --- x-pack/test/functional/apps/infra/link_to.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/x-pack/test/functional/apps/infra/link_to.ts b/x-pack/test/functional/apps/infra/link_to.ts index 690bcacac59bce..f9e8d0499c1722 100644 --- a/x-pack/test/functional/apps/infra/link_to.ts +++ b/x-pack/test/functional/apps/infra/link_to.ts @@ -12,17 +12,20 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const retry = getService('retry'); const browser = getService('browser'); + // Ensure the start/end range is kept + const timestamp = Date.now() - 60000; + const traceId = '433b4651687e18be2c6c8e3b11f53d09'; + describe('Infra link-to', function() { this.tags('smoke'); it('redirects to the logs app and parses URL search params correctly', async () => { const location = { hash: '', pathname: '/link-to/logs', - search: '?time=1565707203194&filter=trace.id:433b4651687e18be2c6c8e3b11f53d09', + search: `?time=${timestamp}&filter=trace.id:${traceId}`, state: undefined, }; - const expectedSearchString = - "sourceId=default&logPosition=(end:now,position:(tiebreaker:0,time:1565707203194),start:now-1d,streamLive:!f)&logFilter=(expression:'trace.id:433b4651687e18be2c6c8e3b11f53d09',kind:kuery)"; + const expectedSearchString = `sourceId=default&logPosition=(end:now,position:(tiebreaker:0,time:${timestamp}),start:now-1d,streamLive:!f)&logFilter=(expression:'trace.id:${traceId}',kind:kuery)`; const expectedRedirectPath = '/logs/stream?'; await pageObjects.common.navigateToActualUrl( From 3f53f55ab15ba2308312f783b70d81df5c6a0c8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Mon, 17 Feb 2020 16:41:22 +0100 Subject: [PATCH 063/117] Remove minimap scale configuration The scale of the minimap is no longer configurable, since it's determined by the date range. --- .../logging/log_minimap/log_minimap.tsx | 1 - .../logging/log_minimap_scale_controls.tsx | 67 ------------------- .../logs/log_view_configuration.test.tsx | 25 ------- .../logs/log_view_configuration.tsx | 46 ------------- .../containers/logs/with_log_minimap.tsx | 52 -------------- .../pages/logs/stream/page_logs_content.tsx | 5 +- .../public/pages/logs/stream/page_toolbar.tsx | 19 +----- .../translations/translations/ja-JP.json | 7 -- .../translations/translations/zh-CN.json | 7 -- 9 files changed, 4 insertions(+), 225 deletions(-) delete mode 100644 x-pack/legacy/plugins/infra/public/components/logging/log_minimap_scale_controls.tsx delete mode 100644 x-pack/legacy/plugins/infra/public/containers/logs/with_log_minimap.tsx 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 ceb1947d7d4ea7..5c2305526c03af 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 @@ -28,7 +28,6 @@ interface LogMinimapProps { height: number; highlightedInterval: Interval | null; jumpToTarget: (params: LogEntryTime) => any; - intervalSize: number; summaryBuckets: LogEntriesSummaryBucket[]; summaryHighlightBuckets?: LogEntriesSummaryHighlightsBucket[]; target: number | null; diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap_scale_controls.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_minimap_scale_controls.tsx deleted file mode 100644 index 41c6e554e603a6..00000000000000 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap_scale_controls.tsx +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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 { EuiFormRow, EuiRadioGroup } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import * as React from 'react'; - -interface IntervalSizeDescriptor { - label: string; - intervalSize: number; -} - -interface LogMinimapScaleControlsProps { - availableIntervalSizes: IntervalSizeDescriptor[]; - intervalSize: number; - setIntervalSize: (intervalSize: number) => any; -} - -export class LogMinimapScaleControls extends React.PureComponent { - public handleScaleChange = (intervalSizeDescriptorKey: string) => { - const { availableIntervalSizes, setIntervalSize } = this.props; - const [sizeDescriptor] = availableIntervalSizes.filter( - intervalKeyEquals(intervalSizeDescriptorKey) - ); - - if (sizeDescriptor) { - setIntervalSize(sizeDescriptor.intervalSize); - } - }; - - public render() { - const { availableIntervalSizes, intervalSize } = this.props; - const [currentSizeDescriptor] = availableIntervalSizes.filter(intervalSizeEquals(intervalSize)); - - return ( - - } - > - ({ - id: getIntervalSizeDescriptorKey(sizeDescriptor), - label: sizeDescriptor.label, - }))} - onChange={this.handleScaleChange} - idSelected={getIntervalSizeDescriptorKey(currentSizeDescriptor)} - /> - - ); - } -} - -const getIntervalSizeDescriptorKey = (sizeDescriptor: IntervalSizeDescriptor) => - `${sizeDescriptor.intervalSize}`; - -const intervalKeyEquals = (key: string) => (sizeDescriptor: IntervalSizeDescriptor) => - getIntervalSizeDescriptorKey(sizeDescriptor) === key; - -const intervalSizeEquals = (size: number) => (sizeDescriptor: IntervalSizeDescriptor) => - sizeDescriptor.intervalSize === size; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_view_configuration.test.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_view_configuration.test.tsx index b6de1230d9a59c..5954cb834a11d1 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_view_configuration.test.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_view_configuration.test.tsx @@ -45,35 +45,10 @@ describe('useLogViewConfiguration hook', () => { }); }); - describe('intervalSize state', () => { - it('has a default value', () => { - const { getLastHookValue } = mountHook(() => useLogViewConfiguration().intervalSize); - - expect(getLastHookValue()).toEqual(86400000); - }); - - it('can be updated', () => { - const { act, getLastHookValue } = mountHook(() => useLogViewConfiguration()); - - act(({ setIntervalSize }) => { - setIntervalSize(90000000); - }); - - expect(getLastHookValue().intervalSize).toEqual(90000000); - }); - }); - it('provides the available text scales', () => { const { getLastHookValue } = mountHook(() => useLogViewConfiguration().availableTextScales); expect(getLastHookValue()).toEqual(expect.any(Array)); expect(getLastHookValue().length).toBeGreaterThan(0); }); - - it('provides the available interval sizes', () => { - const { getLastHookValue } = mountHook(() => useLogViewConfiguration().availableIntervalSizes); - - expect(getLastHookValue()).toEqual(expect.any(Array)); - expect(getLastHookValue().length).toBeGreaterThan(0); - }); }); diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_view_configuration.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_view_configuration.tsx index 8837078aa4a0df..e1351ad0b17ad9 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_view_configuration.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_view_configuration.tsx @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { i18n } from '@kbn/i18n'; import createContainer from 'constate'; import { useState } from 'react'; @@ -17,18 +16,12 @@ export const useLogViewConfiguration = () => { // text wrap const [textWrap, setTextWrap] = useState(true); - // minimap interval - const [intervalSize, setIntervalSize] = useState(1000 * 60 * 60 * 24); - return { - availableIntervalSizes, availableTextScales, setTextScale, setTextWrap, textScale, textWrap, - intervalSize, - setIntervalSize, }; }; @@ -39,42 +32,3 @@ export const LogViewConfiguration = createContainer(useLogViewConfiguration); */ export const availableTextScales: TextScale[] = ['large', 'medium', 'small']; - -export const availableIntervalSizes = [ - { - label: i18n.translate('xpack.infra.mapLogs.oneYearLabel', { - defaultMessage: '1 Year', - }), - intervalSize: 1000 * 60 * 60 * 24 * 365, - }, - { - label: i18n.translate('xpack.infra.mapLogs.oneMonthLabel', { - defaultMessage: '1 Month', - }), - intervalSize: 1000 * 60 * 60 * 24 * 30, - }, - { - label: i18n.translate('xpack.infra.mapLogs.oneWeekLabel', { - defaultMessage: '1 Week', - }), - intervalSize: 1000 * 60 * 60 * 24 * 7, - }, - { - label: i18n.translate('xpack.infra.mapLogs.oneDayLabel', { - defaultMessage: '1 Day', - }), - intervalSize: 1000 * 60 * 60 * 24, - }, - { - label: i18n.translate('xpack.infra.mapLogs.oneHourLabel', { - defaultMessage: '1 Hour', - }), - intervalSize: 1000 * 60 * 60, - }, - { - label: i18n.translate('xpack.infra.mapLogs.oneMinuteLabel', { - defaultMessage: '1 Minute', - }), - intervalSize: 1000 * 60, - }, -]; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/with_log_minimap.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/with_log_minimap.tsx deleted file mode 100644 index 3f2b4d7cc16f91..00000000000000 --- a/x-pack/legacy/plugins/infra/public/containers/logs/with_log_minimap.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 React, { useContext, useMemo } from 'react'; - -import { UrlStateContainer } from '../../utils/url_state'; -import { LogViewConfiguration } from './log_view_configuration'; - -/** - * Url State - */ - -interface LogMinimapUrlState { - intervalSize?: number; -} - -export const WithLogMinimapUrlState = () => { - const { intervalSize, setIntervalSize } = useContext(LogViewConfiguration.Context); - - const urlState = useMemo(() => ({ intervalSize }), [intervalSize]); - - return ( - { - if (newUrlState && newUrlState.intervalSize) { - setIntervalSize(newUrlState.intervalSize); - } - }} - onInitialize={newUrlState => { - if (newUrlState && newUrlState.intervalSize) { - setIntervalSize(newUrlState.intervalSize); - } - }} - /> - ); -}; - -const mapToUrlState = (value: any): LogMinimapUrlState | undefined => - value - ? { - intervalSize: mapToIntervalSizeUrlState(value.intervalSize), - } - : undefined; - -const mapToIntervalSizeUrlState = (value: any) => - value && typeof value === 'number' ? value : undefined; 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 4ec4fcba68c0a6..ba8a0685184678 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 @@ -20,7 +20,6 @@ import { LogFlyout as LogFlyoutState, WithFlyoutOptionsUrlState, } from '../../../containers/logs/log_flyout'; -import { WithLogMinimapUrlState } from '../../../containers/logs/with_log_minimap'; import { LogPositionState } from '../../../containers/logs/log_position'; import { WithLogTextviewUrlState } from '../../../containers/logs/with_log_textview'; import { WithStreamItems } from '../../../containers/logs/with_stream_items'; @@ -31,7 +30,7 @@ import { LogHighlightsState } from '../../../containers/logs/log_highlights'; export const LogsPageLogsContent: React.FunctionComponent = () => { const { source, sourceId, version } = useContext(Source.Context); - const { intervalSize, textScale, textWrap } = useContext(LogViewConfiguration.Context); + const { textScale, textWrap } = useContext(LogViewConfiguration.Context); const { setFlyoutVisibility, flyoutVisible, @@ -58,7 +57,6 @@ export const LogsPageLogsContent: React.FunctionComponent = () => { } = useContext(LogPositionState.Context); return ( <> - @@ -130,7 +128,6 @@ export const LogsPageLogsContent: React.FunctionComponent = () => { height={height} width={width} highlightedInterval={isReloading ? null : visibleTimeInterval} - intervalSize={intervalSize} jumpToTarget={jumpToTargetPosition} summaryBuckets={buckets} summaryHighlightBuckets={ diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx index 7ea0281f5bbcbb..a8a67e6606c383 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx @@ -13,7 +13,6 @@ import { Toolbar } from '../../../components/eui'; import { LogCustomizationMenu } from '../../../components/logging/log_customization_menu'; import { LogHighlightsMenu } from '../../../components/logging/log_highlights_menu'; import { LogHighlightsState } from '../../../containers/logs/log_highlights/log_highlights'; -import { LogMinimapScaleControls } from '../../../components/logging/log_minimap_scale_controls'; import { LogTextScaleControls } from '../../../components/logging/log_text_scale_controls'; import { LogTextWrapControls } from '../../../components/logging/log_text_wrap_controls'; import { LogFlyout } from '../../../containers/logs/log_flyout'; @@ -26,16 +25,9 @@ import { WithKueryAutocompletion } from '../../../containers/with_kuery_autocomp export const LogsToolbar = () => { const { createDerivedIndexPattern } = useContext(Source.Context); const derivedIndexPattern = createDerivedIndexPattern('logs'); - const { - availableIntervalSizes, - availableTextScales, - intervalSize, - setIntervalSize, - setTextScale, - setTextWrap, - textScale, - textWrap, - } = useContext(LogViewConfiguration.Context); + const { availableTextScales, setTextScale, setTextWrap, textScale, textWrap } = useContext( + LogViewConfiguration.Context + ); const { filterQueryDraft, isFilterQueryDraftValid, @@ -106,11 +98,6 @@ export const LogsToolbar = () => { - Date: Fri, 21 Feb 2020 12:49:29 +0100 Subject: [PATCH 064/117] Properly pluralise the timeframe extension messages --- .../log_text_stream/loading_item_view.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx index b5fd25dc7f1606..2859d66b57df4a 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx @@ -179,7 +179,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun return ( ); @@ -187,7 +187,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun return ( ); @@ -195,7 +195,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun return ( ); @@ -203,7 +203,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun return ( ); @@ -211,7 +211,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun return ( ); @@ -219,7 +219,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun return ( ); @@ -227,7 +227,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun return ( ); @@ -235,7 +235,7 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun return ( ); From 10a4397d0b52177d78bf72624aa04c2fb4e410e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 26 Feb 2020 14:19:50 +0100 Subject: [PATCH 065/117] Fix density chart edges --- .../logging/log_minimap/density_chart.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/infra/public/components/logging/log_minimap/density_chart.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/density_chart.tsx index d43d3fdd71538e..9071735910ce44 100644 --- a/x-pack/plugins/infra/public/components/logging/log_minimap/density_chart.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_minimap/density_chart.tsx @@ -42,14 +42,25 @@ export const DensityChart: React.FC = ({ .domain([0, xMax]) .range([0, width]); - // FIXME: path is not closed at the bottom. const path = area() .x0(xScale(0)) .x1(bucket => xScale(bucket.entriesCount)) .y0(bucket => yScale(bucket.start)) .y1(bucket => yScale(bucket.end)) .curve(curveMonotoneY); - const pathData = path(buckets); + + const firstBucket = buckets[0]; + const lastBucket = buckets[buckets.length - 1]; + const pathBuckets = [ + // Make sure the graph starts at the count of the first point + { start, end: start, entriesCount: firstBucket.entriesCount }, + ...buckets, + // Make sure the line ends at the height of the last point + { start: lastBucket.end, end: lastBucket.end, entriesCount: lastBucket.entriesCount }, + // If the last point is not at the end of the minimap, make sure it doesn't extend indefinitely and goes to 0 + { start: end, end, entriesCount: 0 }, + ]; + const pathData = path(pathBuckets); return ( From a96579c5de6971d20a696992d0a7d699b08bd2a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 26 Feb 2020 22:08:54 +0100 Subject: [PATCH 066/117] Tweak message part types --- .../infra/common/http_api/log_entries/entries.ts | 10 +++++----- .../plugins/infra/public/utils/log_entry/log_entry.ts | 8 ++++---- .../public/utils/log_entry/log_entry_highlight.ts | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/infra/common/http_api/log_entries/entries.ts b/x-pack/plugins/infra/common/http_api/log_entries/entries.ts index fb58f367118ab7..8844667d686ebe 100644 --- a/x-pack/plugins/infra/common/http_api/log_entries/entries.ts +++ b/x-pack/plugins/infra/common/http_api/log_entries/entries.ts @@ -52,16 +52,16 @@ export type LogEntriesRequest = rt.TypeOf; // JSON value const valueRT = rt.union([rt.string, rt.number, rt.boolean, rt.object, rt.null, rt.undefined]); -export const logMessagePartConstantRT = rt.type({ +export const logMessageConstantPartRT = rt.type({ constant: rt.string, }); -export const logMessagePartFieldRT = rt.type({ +export const logMessageFieldPartRT = rt.type({ field: rt.string, value: valueRT, highlights: rt.array(rt.string), }); -export const logMessagePartRT = rt.union([logMessagePartConstantRT, logMessagePartFieldRT]); +export const logMessagePartRT = rt.union([logMessageConstantPartRT, logMessageFieldPartRT]); export const logTimestampColumnRT = rt.type({ columnId: rt.string, timestamp: rt.number }); export const logFieldColumnRT = rt.type({ @@ -83,8 +83,8 @@ export const logEntryRT = rt.type({ columns: rt.array(logColumnRT), }); -export type LogMessagePartConstant = rt.TypeOf; -export type LogMessagePartField = rt.TypeOf; +export type LogMessageConstantPart = rt.TypeOf; +export type LogMessageFieldPart = rt.TypeOf; export type LogMessagePart = rt.TypeOf; export type LogTimestampColumn = rt.TypeOf; export type LogFieldColumn = rt.TypeOf; diff --git a/x-pack/plugins/infra/public/utils/log_entry/log_entry.ts b/x-pack/plugins/infra/public/utils/log_entry/log_entry.ts index 05808daebdfc18..bb528ee5b18c5c 100644 --- a/x-pack/plugins/infra/public/utils/log_entry/log_entry.ts +++ b/x-pack/plugins/infra/public/utils/log_entry/log_entry.ts @@ -15,8 +15,8 @@ import { LogFieldColumn, LogMessageColumn, LogMessagePart, - LogMessagePartField, - LogMessagePartConstant, + LogMessageFieldPart, + LogMessageConstantPart, } from '../../../common/http_api'; export type LogEntryMessageSegment = InfraLogEntryFields.Message; @@ -51,8 +51,8 @@ export const isMessageColumn = (column: LogColumn): column is LogMessageColumn = export const isFieldColumn = (column: LogColumn): column is LogFieldColumn => column != null && 'field' in column; -export const isConstantSegment = (segment: LogMessagePart): segment is LogMessagePartConstant => +export const isConstantSegment = (segment: LogMessagePart): segment is LogMessageConstantPart => 'constant' in segment; -export const isFieldSegment = (segment: LogMessagePart): segment is LogMessagePartField => +export const isFieldSegment = (segment: LogMessagePart): segment is LogMessageFieldPart => 'field' in segment && 'value' in segment; diff --git a/x-pack/plugins/infra/public/utils/log_entry/log_entry_highlight.ts b/x-pack/plugins/infra/public/utils/log_entry/log_entry_highlight.ts index c19454887e4f11..abb004911214b2 100644 --- a/x-pack/plugins/infra/public/utils/log_entry/log_entry_highlight.ts +++ b/x-pack/plugins/infra/public/utils/log_entry/log_entry_highlight.ts @@ -11,7 +11,7 @@ import { LogMessageColumn, LogFieldColumn, LogMessagePart, - LogMessagePartField, + LogMessageFieldPart, } from '../../../common/http_api'; export type LogEntryHighlightColumn = InfraLogEntryHighlightFields.Columns; @@ -31,5 +31,5 @@ export const isHighlightMessageColumn = (column: LogColumn): column is LogMessag export const isHighlightFieldColumn = (column: LogColumn): column is LogFieldColumn => column != null && 'field' in column; -export const isHighlightFieldSegment = (segment: LogMessagePart): segment is LogMessagePartField => +export const isHighlightFieldSegment = (segment: LogMessagePart): segment is LogMessageFieldPart => segment && 'field' in segment && 'highlights' in segment; From 660eb5b027dca9b38966b018fb9ec1bb39613f0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 26 Feb 2020 22:13:26 +0100 Subject: [PATCH 067/117] Align function name with Kibana standards --- .../components/logging/log_minimap/time_label_formatter.tsx | 2 +- .../public/components/logging/log_minimap/time_ruler.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/infra/public/components/logging/log_minimap/time_label_formatter.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/time_label_formatter.tsx index 267ffd005801de..af981105d17189 100644 --- a/x-pack/plugins/infra/public/components/logging/log_minimap/time_label_formatter.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_minimap/time_label_formatter.tsx @@ -5,7 +5,7 @@ */ // The default d3-time-format is a bit strange for small ranges, so we will specify our own -export function timeLabelFormat(start: number, end: number): string | undefined { +export function getTimeLabelFormat(start: number, end: number): string | undefined { const diff = Math.abs(end - start); // 15 seconds diff --git a/x-pack/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx index 9fdb23b987ef93..454935c32fe1ed 100644 --- a/x-pack/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx @@ -8,7 +8,7 @@ import { scaleTime } from 'd3-scale'; import * as React from 'react'; import { euiStyled } from '../../../../../observability/public'; -import { timeLabelFormat } from './time_label_formatter'; +import { getTimeLabelFormat } from './time_label_formatter'; interface TimeRulerProps { end: number; @@ -24,7 +24,7 @@ export const TimeRuler: React.FC = ({ end, height, start, tickCo .range([0, height]); const ticks = yScale.ticks(tickCount); - const formatTick = yScale.tickFormat(tickCount, timeLabelFormat(start, end)); + const formatTick = yScale.tickFormat(tickCount, getTimeLabelFormat(start, end)); return ( From 4dc15ae53e7a4b5135477a7aec4031ef30def6e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 26 Feb 2020 22:23:09 +0100 Subject: [PATCH 068/117] Simplify positioning in minimap components --- .../logging/log_minimap/density_chart.tsx | 4 +--- .../logging/log_minimap/log_minimap.tsx | 23 +++++++++---------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/infra/public/components/logging/log_minimap/density_chart.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/density_chart.tsx index 9071735910ce44..2bdb1f91a6dde0 100644 --- a/x-pack/plugins/infra/public/components/logging/log_minimap/density_chart.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_minimap/density_chart.tsx @@ -18,7 +18,6 @@ interface DensityChartProps { start: number; width: number; height: number; - x: number; } export const DensityChart: React.FC = ({ @@ -27,7 +26,6 @@ export const DensityChart: React.FC = ({ end, width, height, - x, }) => { if (start >= end || height <= 0 || width <= 0 || buckets.length <= 0) { return null; @@ -63,7 +61,7 @@ export const DensityChart: React.FC = ({ const pathData = path(pathBuckets); return ( - + diff --git a/x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx index 6f51d8dc18f84e..1f8ad93ef2d28c 100644 --- a/x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx @@ -126,34 +126,33 @@ export class LogMinimap extends React.Component - + + + - - - + {highlightedInterval ? ( Date: Wed, 26 Feb 2020 22:30:24 +0100 Subject: [PATCH 069/117] Tweak "extend time frame" button labels --- .../log_text_stream/loading_item_view.tsx | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx index 2859d66b57df4a..4d0dc7920c30a0 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx @@ -178,64 +178,64 @@ const ProgressExtendMessage: React.FC<{ amount: number; unit: Unit }> = ({ amoun case 'ms': return ( ); case 's': return ( ); case 'm': return ( ); case 'h': return ( ); case 'd': return ( ); case 'w': return ( ); case 'M': return ( ); case 'y': return ( ); From f0e1563532f05a3cdc3b7b0c003a4230cd37c172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 28 Feb 2020 16:58:56 +0100 Subject: [PATCH 070/117] Remove outdated state field --- .../components/logging/log_minimap/log_minimap.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx index 1f8ad93ef2d28c..767f47e5b580bd 100644 --- a/x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx @@ -38,7 +38,6 @@ interface LogMinimapProps { interface LogMinimapState { target: number | null; - svgPosition: ClientRect; timeCursorY: number; } @@ -57,14 +56,6 @@ export class LogMinimap extends React.Component Date: Mon, 2 Mar 2020 12:28:37 +0100 Subject: [PATCH 071/117] Update the timestamps when user clicks "refresh" --- .../logs/log_position/log_position_state.ts | 14 +++++++++++++- .../public/pages/logs/stream/page_toolbar.tsx | 3 ++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts index 60e8498c064336..e82b1ce88cea44 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts @@ -50,6 +50,7 @@ export interface LogPositionCallbacks { startLiveStreaming: () => void; stopLiveStreaming: () => void; updateDateRange: (newDateRage: Partial) => void; + updateTimestamps: () => void; } const DEFAULT_DATE_RANGE: DateRange = { startDate: 'now-1d', endDate: 'now' }; @@ -87,6 +88,11 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall setInitialized(true); }, [setInitialized]); + const [lastUpdate, setLastUpdate] = useState(Date.now()); + const updateTimestamps = useCallback(() => { + setLastUpdate(Date.now()); + }, [setLastUpdate]); + const [targetPosition, jumpToTargetPosition] = useState(null); const [isStreaming, setIsStreaming] = useState(false); const [liveStreamingInterval, setLiveStreamingInterval] = useState(10000); @@ -154,16 +160,21 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall [dateRange, targetPosition] ); + // `lastUpdate` needs to be a dependency for the timestamps. + // ESLint complains it's unnecessary, but we know better. + /* eslint-disable react-hooks/exhaustive-deps */ const startTimestamp = useMemo(() => datemathToEpochMillis(dateRange.startDate), [ dateRange.startDate, + lastUpdate, ]); // endTimestamp needs to be synced to `now` to allow auto-streaming const endTimestampDep = dateRange.endDate === 'now' ? Date.now() : dateRange.endDate; - // eslint-disable-next-line react-hooks/exhaustive-deps const endTimestamp = useMemo(() => datemathToEpochMillis(dateRange.endDate, 'up'), [ endTimestampDep, + lastUpdate, ]); + /* eslint-enable react-hooks/exhaustive-deps */ const state = { initialized, @@ -193,6 +204,7 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall stopLiveStreaming: useCallback(() => setIsStreaming(false), [setIsStreaming]), updateDateRange, setLiveStreamingInterval, + updateTimestamps, }; return { ...state, ...callbacks }; diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx index a8a67e6606c383..962519a92da035 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx @@ -52,6 +52,7 @@ export const LogsToolbar = () => { startDate, endDate, updateDateRange, + updateTimestamps, liveStreamingInterval, setLiveStreamingInterval, } = useContext(LogPositionState.Context); @@ -126,7 +127,7 @@ export const LogsToolbar = () => { onTimeChange={handleTimeChange} isPaused={!isStreaming} refreshInterval={liveStreamingInterval} - onRefresh={() => {}} + onRefresh={updateTimestamps} onRefreshChange={({ refreshInterval, isPaused }) => { if (isPaused) { stopLiveStreaming(); From 5a6dddc506c4be0c69e4fbfa541f3320c7da335a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Mon, 2 Mar 2020 13:35:54 +0100 Subject: [PATCH 072/117] Remove `*Date` range from useLogEntries hook --- .../infra/public/containers/logs/log_entries/index.ts | 11 ++++------- .../infra/public/pages/logs/stream/page_providers.tsx | 4 ---- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts index a52b842ceb9774..007924d152bae7 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts @@ -51,8 +51,6 @@ type ActionObj = ReceiveEntriesAction | FetchOrErrorAction | ExpandRangeAction; type Dispatch = (action: ActionObj) => void; interface LogEntriesProps { - startDate: string; - endDate: string; startTimestamp: number; endTimestamp: number; liveStreamingInterval: number; @@ -109,14 +107,13 @@ const shouldFetchNewEntries = ({ filterQuery, topCursor, bottomCursor, - startDate, - endDate, startTimestamp, endTimestamp, }: FetchEntriesParams & LogEntriesStateParams & { prevParams: FetchEntriesParams | undefined }) => { const shouldLoadWithNewDates = prevParams - ? (startDate !== prevParams.startDate && startTimestamp > prevParams.startTimestamp) || - (endDate !== prevParams.endDate && endTimestamp < prevParams.endTimestamp) + ? (startTimestamp !== prevParams.startTimestamp && + startTimestamp > prevParams.startTimestamp) || + (endTimestamp !== prevParams.endTimestamp && endTimestamp < prevParams.endTimestamp) : true; const shouldLoadWithNewFilter = prevParams ? filterQuery !== prevParams.filterQuery : true; const shouldLoadAroundNewPosition = @@ -322,7 +319,7 @@ const useFetchEntriesEffect = ( dispatch({ type: Action.ExpandRange, payload: shouldExpand }); }; - const expandRangeEffectDependencies = [props.startDate, props.endDate]; + const expandRangeEffectDependencies = [props.startTimestamp, props.endTimestamp]; useEffect(fetchNewEntriesEffect, fetchNewEntriesEffectDependencies); useEffect(fetchMoreEntriesEffect, fetchMoreEntriesEffectDependencies); diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx index e5df0f922c7083..a59ad67f0257df 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx @@ -29,8 +29,6 @@ const LogFilterStateProvider: React.FC = ({ children }) => { const LogEntriesStateProvider: React.FC = ({ children }) => { const { sourceId } = useContext(Source.Context); const { - startDate, - endDate, startTimestamp, endTimestamp, targetPosition, @@ -49,8 +47,6 @@ const LogEntriesStateProvider: React.FC = ({ children }) => { } const entriesProps = { - startDate, - endDate, startTimestamp, endTimestamp, liveStreamingInterval, From 1d4b94eb3797a344bafd02293424b4825476e2fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Mon, 2 Mar 2020 14:09:23 +0100 Subject: [PATCH 073/117] Remove `*Date` range from useLogSummary hook --- .../logs/log_summary/log_summary.test.tsx | 16 +++++++--------- .../containers/logs/log_summary/log_summary.tsx | 9 ++------- .../containers/logs/log_summary/with_summary.ts | 11 +++++++---- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx b/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx index 02515b4aff62ef..73d0e5efdf06b4 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx @@ -22,12 +22,12 @@ describe('useLogSummary hook', () => { }); it('provides an empty list of buckets by default', () => { - const { result } = renderHook(() => useLogSummary('SOURCE_ID', null, null, null, null, null)); + const { result } = renderHook(() => useLogSummary('SOURCE_ID', null, null, null)); expect(result.current.buckets).toEqual([]); }); it('queries for new summary buckets when the source id changes', async () => { - const { startDate, endDate, startTimestamp, endTimestamp } = createMockDateRange(); + const { startTimestamp, endTimestamp } = createMockDateRange(); const firstMockResponse = createMockResponse([ { start: startTimestamp, end: endTimestamp, entriesCount: 1 }, @@ -41,8 +41,7 @@ describe('useLogSummary hook', () => { .mockResolvedValueOnce(secondMockResponse); const { result, waitForNextUpdate, rerender } = renderHook( - ({ sourceId }) => - useLogSummary(sourceId, startDate, endDate, startTimestamp, endTimestamp, null), + ({ sourceId }) => useLogSummary(sourceId, startTimestamp, endTimestamp, null), { initialProps: { sourceId: 'INITIAL_SOURCE_ID' }, } @@ -71,7 +70,7 @@ describe('useLogSummary hook', () => { }); it('queries for new summary buckets when the filter query changes', async () => { - const { startDate, endDate, startTimestamp, endTimestamp } = createMockDateRange(); + const { startTimestamp, endTimestamp } = createMockDateRange(); const firstMockResponse = createMockResponse([ { start: startTimestamp, end: endTimestamp, entriesCount: 1 }, @@ -85,8 +84,7 @@ describe('useLogSummary hook', () => { .mockResolvedValueOnce(secondMockResponse); const { result, waitForNextUpdate, rerender } = renderHook( - ({ filterQuery }) => - useLogSummary('SOURCE_ID', startDate, endDate, startTimestamp, endTimestamp, filterQuery), + ({ filterQuery }) => useLogSummary('SOURCE_ID', startTimestamp, endTimestamp, filterQuery), { initialProps: { filterQuery: 'INITIAL_FILTER_QUERY' }, } @@ -121,8 +119,8 @@ describe('useLogSummary hook', () => { const firstRange = createMockDateRange(); const { waitForNextUpdate, rerender } = renderHook( - ({ startDate, endDate, startTimestamp, endTimestamp }) => - useLogSummary('SOURCE_ID', startDate, endDate, startTimestamp, endTimestamp, null), + ({ startTimestamp, endTimestamp }) => + useLogSummary('SOURCE_ID', startTimestamp, endTimestamp, null), { initialProps: firstRange, } diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx b/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx index 172b4c14c90a0e..0df1292ad079d4 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx @@ -15,15 +15,13 @@ export type LogSummaryBuckets = LogEntriesSummaryResponse['data']['buckets']; export const useLogSummary = ( sourceId: string, - startDate: string | null, - endDate: string | null, startTimestamp: number | null, endTimestamp: number | null, filterQuery: string | null ) => { const [logSummaryBuckets, setLogSummaryBuckets] = useState([]); - const bucketSize = useBucketSize(startTimestamp, endTimestamp); + useCancellableEffect( getIsCancelled => { if (startTimestamp === null || endTimestamp === null || bucketSize === null) { @@ -42,10 +40,7 @@ export const useLogSummary = ( } }); }, - // Use `*Timestamp` values in the fetch. - // Use `*Date` to decide if it should refetch or not. `endTimestamp` updates - // frequently when `endDate` is `"now"` and triggers a lot of re-renders. - [sourceId, filterQuery, startDate, endDate] + [sourceId, filterQuery, startTimestamp, endTimestamp] ); return { diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts b/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts index fa3a88fe9524f5..2c00761314423e 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts @@ -5,6 +5,7 @@ */ import { useContext } from 'react'; +import { useThrottle } from 'react-use'; import { RendererFunction } from '../../../utils/typed_react'; import { Source } from '../../source'; @@ -25,12 +26,14 @@ export const WithSummary = ({ const { filterQuery } = useContext(LogFilterState.Context); const { startDate, endDate, startTimestamp, endTimestamp } = useContext(LogPositionState.Context); + // Keep it reasonably updated for the `now` case, but don't reload all the time when the user scrolls + const throttledStartTimestamp = useThrottle(startTimestamp, 3000); + const throttledEndTimestamp = useThrottle(endTimestamp, 3000); + const { buckets, start, end } = useLogSummary( sourceId, - startDate, - endDate, - startTimestamp, - endTimestamp, + throttledStartTimestamp, + throttledEndTimestamp, filterQuery ); From fe3a70485ac7f091adee3a9e521fba61056e637b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Tue, 3 Mar 2020 10:36:49 +0100 Subject: [PATCH 074/117] Remove `*Date` range from logSummaryHighlights --- .../logs/log_highlights/log_highlights.tsx | 21 ++++++++----------- .../log_highlights/log_summary_highlights.ts | 9 ++------ 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx index 9f20a439735044..5d37aa6d0f5667 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx @@ -6,6 +6,7 @@ import createContainer from 'constate'; import { useState, useContext } from 'react'; +import { useThrottle } from 'react-use'; import { useLogEntryHighlights } from './log_entry_highlights'; import { useLogSummaryHighlights } from './log_summary_highlights'; import { useNextAndPrevious } from './next_and_previous'; @@ -26,14 +27,12 @@ export const useLogHighlightsState = ({ filterQuery: string | null; }) => { const [highlightTerms, setHighlightTerms] = useState([]); - const { - visibleMidpoint, - jumpToTargetPosition, - startDate, - endDate, - startTimestamp, - endTimestamp, - } = useContext(LogPositionState.Context); + const { visibleMidpoint, jumpToTargetPosition, startTimestamp, endTimestamp } = useContext( + LogPositionState.Context + ); + + const throttledStartTimestamp = useThrottle(startTimestamp, 3000); + const throttledEndTimestamp = useThrottle(endTimestamp, 3000); const { logEntryHighlights, @@ -51,10 +50,8 @@ export const useLogHighlightsState = ({ const { logSummaryHighlights, loadLogSummaryHighlightsRequest } = useLogSummaryHighlights( sourceId, sourceVersion, - startDate, - endDate, - startTimestamp, - endTimestamp, + throttledStartTimestamp, + throttledEndTimestamp, filterQuery, highlightTerms ); diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts index 9856dd8fa1d3f1..ad239c6926f660 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts @@ -15,8 +15,6 @@ import { useBucketSize } from '../log_summary/bucket_size'; export const useLogSummaryHighlights = ( sourceId: string, sourceVersion: string | undefined, - startDate: string | null, - endDate: string | null, startTimestamp: number | null, endTimestamp: number | null, filterQuery: string | null, @@ -49,10 +47,7 @@ export const useLogSummaryHighlights = ( setLogSummaryHighlights(response.data); }, }, - // Use `*Timestamp` values in the fetch. - // Use `*Date` to decide if it should refetch or not. `endTimestamp` updates - // frequently when `endDate` is `"now"` and triggers a lot of re-renders. - [sourceId, startDate, endDate, filterQuery, highlightTerms] + [sourceId, startTimestamp, endTimestamp, filterQuery, highlightTerms] ); const debouncedLoadSummaryHighlights = useMemo(() => debounce(loadLogSummaryHighlights, 275), [ @@ -76,11 +71,11 @@ export const useLogSummaryHighlights = ( }, [ bucketSize, debouncedLoadSummaryHighlights, - endTimestamp, filterQuery, highlightTerms, sourceVersion, startTimestamp, + endTimestamp, ]); return { From e6e00cecf401f2d2de84f2f72b83aa8935effda9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Tue, 3 Mar 2020 10:39:23 +0100 Subject: [PATCH 075/117] Remove outdated function --- .../public/components/logging/log_minimap/log_minimap.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx index 767f47e5b580bd..c67674d198a3f9 100644 --- a/x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx @@ -59,10 +59,6 @@ export class LogMinimap extends React.Component = event => { const minimapTop = event.currentTarget.getBoundingClientRect().top; const clickedYPosition = event.clientY - minimapTop; From 1e42ffa222c475f94c199f935bf2303167259ffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Tue, 3 Mar 2020 11:07:40 +0100 Subject: [PATCH 076/117] Rename `*Date` to `*DateExpression` --- .../log_text_stream/loading_item_view.tsx | 2 +- .../scrollable_log_text_stream_view.tsx | 22 ++++++---- .../logs/log_position/log_position_state.ts | 41 ++++++++++--------- .../with_log_position_url_state.tsx | 38 +++++++++-------- .../pages/logs/stream/page_logs_content.tsx | 8 ++-- .../public/pages/logs/stream/page_toolbar.tsx | 10 ++--- 6 files changed, 67 insertions(+), 54 deletions(-) diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx index 4d0dc7920c30a0..fd60c4f2fd491f 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx @@ -28,7 +28,7 @@ interface LogTextStreamLoadingItemViewProps { position: Position; /** topCursor.time || bottomCursor.time */ timestamp?: number; - /** startDate || endDate */ + /** startDateExpression || endDateExpression */ rangeEdge?: string; className?: string; hasMore: boolean; diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx index 6e51ed90eb16f4..e00da5252a3f17 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx @@ -56,9 +56,9 @@ interface ScrollableLogTextStreamViewProps { setFlyoutVisibility: (visible: boolean) => void; highlightedItem: string | null; currentHighlightKey: UniqueTimeKey | null; - startDate: string; - endDate: string; - updateDateRange: (range: { startDate?: string; endDate?: string }) => void; + startDateExpression: string; + endDateExpression: string; + updateDateRange: (range: { startDateExpression?: string; endDateExpression?: string }) => void; startLiveStreaming: () => void; } @@ -137,8 +137,8 @@ export class ScrollableLogTextStreamView extends React.PureComponent< isStreaming, scale, wrap, - startDate, - endDate, + startDateExpression, + endDateExpression, updateDateRange, startLiveStreaming, } = this.props; @@ -203,8 +203,10 @@ export class ScrollableLogTextStreamView extends React.PureComponent< items.length > 0 ? items[0].logEntry.cursor.time : undefined } isStreaming={false} - rangeEdge={startDate} - onExtendRange={newDate => updateDateRange({ startDate: newDate })} + rangeEdge={startDateExpression} + onExtendRange={newDateExpression => + updateDateRange({ startDateExpression: newDateExpression }) + } /> {items.map((item, idx) => { const currentTimestamp = item.logEntry.cursor.time; @@ -257,8 +259,10 @@ export class ScrollableLogTextStreamView extends React.PureComponent< ? items[items.length - 1].logEntry.cursor.time : undefined } - rangeEdge={endDate} - onExtendRange={newDate => updateDateRange({ endDate: newDate })} + rangeEdge={endDateExpression} + onExtendRange={newDateExpression => + updateDateRange({ endDateExpression: newDateExpression }) + } onStreamStart={() => startLiveStreaming()} /> {isScrollLocked && ( diff --git a/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts index e82b1ce88cea44..ba2896b22fb93c 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts @@ -12,8 +12,8 @@ import { datemathToEpochMillis, isValidDatemath } from '../../../utils/datemath' type TimeKeyOrNull = TimeKey | null; interface DateRange { - startDate: string; - endDate: string; + startDateExpression: string; + endDateExpression: string; } interface VisiblePositions { @@ -35,8 +35,8 @@ export interface LogPositionStateParams { visibleMidpoint: TimeKeyOrNull; visibleMidpointTime: number | null; visibleTimeInterval: { start: number; end: number } | null; - startDate: string; - endDate: string; + startDateExpression: string; + endDateExpression: string; startTimestamp: number | null; endTimestamp: number | null; } @@ -53,7 +53,7 @@ export interface LogPositionCallbacks { updateTimestamps: () => void; } -const DEFAULT_DATE_RANGE: DateRange = { startDate: 'now-1d', endDate: 'now' }; +const DEFAULT_DATE_RANGE: DateRange = { startDateExpression: 'now-1d', endDateExpression: 'now' }; const useVisibleMidpoint = (middleKey: TimeKeyOrNull, targetPosition: TimeKeyOrNull) => { // Of the two dependencies `middleKey` and `targetPosition`, return @@ -121,26 +121,27 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall const updateDateRange = useCallback( (newDateRange: Partial) => { // Prevent unnecessary re-renders - if (!('startDate' in newDateRange) && !('endDate' in newDateRange)) { + if (!('startDateExpression' in newDateRange) && !('endDateExpression' in newDateRange)) { return; } if ( - newDateRange.startDate === dateRange.startDate && - newDateRange.endDate === dateRange.endDate + newDateRange.startDateExpression === dateRange.startDateExpression && + newDateRange.endDateExpression === dateRange.endDateExpression ) { return; } - const nextStartDate = newDateRange.startDate || dateRange.startDate; - const nextEndDate = newDateRange.endDate || dateRange.endDate; + const nextStartDateExpression = + newDateRange.startDateExpression || dateRange.startDateExpression; + const nextEndDateExpression = newDateRange.endDateExpression || dateRange.endDateExpression; - if (!isValidDatemath(nextStartDate) || !isValidDatemath(nextEndDate)) { + if (!isValidDatemath(nextStartDateExpression) || !isValidDatemath(nextEndDateExpression)) { return; } // Dates are valid, so the function cannot return `null` - const nextStartTimestamp = datemathToEpochMillis(nextStartDate)!; - const nextEndTimestamp = datemathToEpochMillis(nextEndDate, 'up')!; + const nextStartTimestamp = datemathToEpochMillis(nextStartDateExpression)!; + const nextEndTimestamp = datemathToEpochMillis(nextEndDateExpression, 'up')!; // Reset the target position if it doesn't fall within the new range. if ( @@ -152,8 +153,9 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall setDateRange(previousDateRange => { return { - startDate: newDateRange.startDate || previousDateRange.startDate, - endDate: newDateRange.endDate || previousDateRange.endDate, + startDateExpression: + newDateRange.startDateExpression || previousDateRange.startDateExpression, + endDateExpression: newDateRange.endDateExpression || previousDateRange.endDateExpression, }; }); }, @@ -163,14 +165,15 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall // `lastUpdate` needs to be a dependency for the timestamps. // ESLint complains it's unnecessary, but we know better. /* eslint-disable react-hooks/exhaustive-deps */ - const startTimestamp = useMemo(() => datemathToEpochMillis(dateRange.startDate), [ - dateRange.startDate, + const startTimestamp = useMemo(() => datemathToEpochMillis(dateRange.startDateExpression), [ + dateRange.startDateExpression, lastUpdate, ]); // endTimestamp needs to be synced to `now` to allow auto-streaming - const endTimestampDep = dateRange.endDate === 'now' ? Date.now() : dateRange.endDate; - const endTimestamp = useMemo(() => datemathToEpochMillis(dateRange.endDate, 'up'), [ + const endTimestampDep = + dateRange.endDateExpression === 'now' ? Date.now() : dateRange.endDateExpression; + const endTimestamp = useMemo(() => datemathToEpochMillis(dateRange.endDateExpression, 'up'), [ endTimestampDep, lastUpdate, ]); diff --git a/x-pack/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx b/x-pack/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx index 824eb978488631..0580f51b89e299 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx @@ -30,8 +30,8 @@ export const WithLogPositionUrlState = () => { jumpToTargetPosition, startLiveStreaming, stopLiveStreaming, - startDate, - endDate, + startDateExpression, + endDateExpression, updateDateRange, initialize, } = useContext(LogPositionState.Context); @@ -39,10 +39,10 @@ export const WithLogPositionUrlState = () => { () => ({ position: visibleMidpoint ? pickTimeKey(visibleMidpoint) : null, streamLive: isStreaming, - start: startDate, - end: endDate, + start: startDateExpression, + end: endDateExpression, }), - [visibleMidpoint, isStreaming, startDate, endDate] + [visibleMidpoint, isStreaming, startDateExpression, endDateExpression] ); return ( { } if (newUrlState.start || newUrlState.end) { - updateDateRange({ startDate: newUrlState.start, endDate: newUrlState.end }); + updateDateRange({ + startDateExpression: newUrlState.start, + endDateExpression: newUrlState.end, + }); } if (newUrlState.position) { @@ -71,30 +74,33 @@ export const WithLogPositionUrlState = () => { onInitialize={(initialUrlState: LogPositionUrlState | undefined) => { if (initialUrlState) { const initialPosition = initialUrlState.position; - let initialStartDate = initialUrlState.start || 'now-1d'; - let initialEndDate = initialUrlState.end || 'now'; + let initialStartDateExpression = initialUrlState.start || 'now-1d'; + let initialEndDateExpression = initialUrlState.end || 'now'; if (initialPosition) { - const initialStartTimestamp = initialStartDate - ? datemathToEpochMillis(initialStartDate) + const initialStartTimestamp = initialStartDateExpression + ? datemathToEpochMillis(initialStartDateExpression) : undefined; - const initialEndTimestamp = initialEndDate - ? datemathToEpochMillis(initialEndDate, 'up') + const initialEndTimestamp = initialEndDateExpression + ? datemathToEpochMillis(initialEndDateExpression, 'up') : undefined; // Adjust the start-end range if the target position falls outside if (initialStartTimestamp && initialStartTimestamp > initialPosition.time) { - initialStartDate = new Date(initialPosition.time - ONE_HOUR).toISOString(); + initialStartDateExpression = new Date(initialPosition.time - ONE_HOUR).toISOString(); } if (initialEndTimestamp && initialEndTimestamp < initialPosition.time) { - initialEndDate = new Date(initialPosition.time + ONE_HOUR).toISOString(); + initialEndDateExpression = new Date(initialPosition.time + ONE_HOUR).toISOString(); } jumpToTargetPosition(initialPosition); } - if (initialStartDate || initialEndDate) { - updateDateRange({ startDate: initialStartDate, endDate: initialEndDate }); + if (initialStartDateExpression || initialEndDateExpression) { + updateDateRange({ + startDateExpression: initialStartDateExpression, + endDateExpression: initialEndDateExpression, + }); } if (initialUrlState.streamLive) { diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx index cb4b9391e82ce2..b6061203a1c727 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx @@ -51,8 +51,8 @@ export const LogsPageLogsContent: React.FunctionComponent = () => { jumpToTargetPosition, startLiveStreaming, stopLiveStreaming, - startDate, - endDate, + startDateExpression, + endDateExpression, updateDateRange, } = useContext(LogPositionState.Context); return ( @@ -106,8 +106,8 @@ export const LogsPageLogsContent: React.FunctionComponent = () => { setFlyoutVisibility={setFlyoutVisibility} highlightedItem={surroundingLogsId ? surroundingLogsId : null} currentHighlightKey={currentHighlightKey} - startDate={startDate} - endDate={endDate} + startDateExpression={startDateExpression} + endDateExpression={endDateExpression} updateDateRange={updateDateRange} startLiveStreaming={startLiveStreaming} /> diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx index 962519a92da035..b6483cd2dbacd5 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx @@ -49,8 +49,8 @@ export const LogsToolbar = () => { isStreaming, startLiveStreaming, stopLiveStreaming, - startDate, - endDate, + startDateExpression, + endDateExpression, updateDateRange, updateTimestamps, liveStreamingInterval, @@ -60,7 +60,7 @@ export const LogsToolbar = () => { const handleTimeChange = useCallback( ({ start, end, isInvalid }) => { if (!isInvalid) { - updateDateRange({ startDate: start, endDate: end }); + updateDateRange({ startDateExpression: start, endDateExpression: end }); } }, [updateDateRange] @@ -122,8 +122,8 @@ export const LogsToolbar = () => { Date: Tue, 3 Mar 2020 11:08:12 +0100 Subject: [PATCH 077/117] Cleanup unused variables --- .../infra/public/containers/logs/log_summary/with_summary.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts b/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts index 2c00761314423e..0550f9a15bfcd8 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts @@ -24,7 +24,7 @@ export const WithSummary = ({ }) => { const { sourceId } = useContext(Source.Context); const { filterQuery } = useContext(LogFilterState.Context); - const { startDate, endDate, startTimestamp, endTimestamp } = useContext(LogPositionState.Context); + const { startTimestamp, endTimestamp } = useContext(LogPositionState.Context); // Keep it reasonably updated for the `now` case, but don't reload all the time when the user scrolls const throttledStartTimestamp = useThrottle(startTimestamp, 3000); From bf842fb535dab80062201a0902b3cdd3b099c295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 4 Mar 2020 14:45:49 +0100 Subject: [PATCH 078/117] Redefine "Live streaming" The Super Date Picker comes with a "Reload" button, and a "Reload every X time" functionality attached to it. We tried to use this functionality for the live stream, but that brought us more pain than benefit. Auto reload is only "live stream" when the end of the range is "now", but the user can also set auto reload for any other range: for example, for fixed points in time, or for relative time before now. This commit removes the "reload" button and "reload ever X" functionality and adds "Start/stop live stream" buttons. We defined "live stream" as a set of conditions: - The range is always `now-1d` to `now`. - It reloads entries every 5 seconds. - The user cannot change these values. --- .../containers/logs/log_entries/index.ts | 4 +- .../logs/log_position/log_position_state.ts | 22 +++----- .../pages/logs/stream/page_providers.tsx | 2 - .../public/pages/logs/stream/page_toolbar.tsx | 56 ++++++++++++------- 4 files changed, 45 insertions(+), 39 deletions(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts index 007924d152bae7..05e1d3511537ec 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts @@ -18,6 +18,7 @@ import { import { fetchLogEntries } from './api/fetch_log_entries'; const DESIRED_BUFFER_PAGES = 2; +const LIVE_STREAM_INTERVAL = 5000; enum Action { FetchingNewEntries, @@ -53,7 +54,6 @@ type Dispatch = (action: ActionObj) => void; interface LogEntriesProps { startTimestamp: number; endTimestamp: number; - liveStreamingInterval: number; filterQuery: string | null; timeKey: TimeKey | null; pagesBeforeStart: number | null; @@ -273,7 +273,7 @@ const useFetchEntriesEffect = ( (async () => { if (props.isStreaming && !state.isLoadingMore && !state.isReloading) { if (startedStreaming) { - await new Promise(res => setTimeout(res, props.liveStreamingInterval)); + await new Promise(res => setTimeout(res, LIVE_STREAM_INTERVAL)); } else { const endTimestamp = Date.now(); props.jumpToTargetPosition({ tiebreaker: 0, time: endTimestamp }); diff --git a/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts index ba2896b22fb93c..5d5163a1c3b654 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts @@ -28,7 +28,6 @@ export interface LogPositionStateParams { initialized: boolean; targetPosition: TimeKeyOrNull; isStreaming: boolean; - liveStreamingInterval: number; firstVisiblePosition: TimeKeyOrNull; pagesBeforeStart: number; pagesAfterEnd: number; @@ -46,11 +45,9 @@ export interface LogPositionCallbacks { jumpToTargetPosition: (pos: TimeKeyOrNull) => void; jumpToTargetPositionTime: (time: number) => void; reportVisiblePositions: (visPos: VisiblePositions) => void; - setLiveStreamingInterval: (interval: number) => void; startLiveStreaming: () => void; stopLiveStreaming: () => void; updateDateRange: (newDateRage: Partial) => void; - updateTimestamps: () => void; } const DEFAULT_DATE_RANGE: DateRange = { startDateExpression: 'now-1d', endDateExpression: 'now' }; @@ -95,7 +92,6 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall const [targetPosition, jumpToTargetPosition] = useState(null); const [isStreaming, setIsStreaming] = useState(false); - const [liveStreamingInterval, setLiveStreamingInterval] = useState(10000); const [visiblePositions, reportVisiblePositions] = useState({ endKey: null, middleKey: null, @@ -124,12 +120,6 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall if (!('startDateExpression' in newDateRange) && !('endDateExpression' in newDateRange)) { return; } - if ( - newDateRange.startDateExpression === dateRange.startDateExpression && - newDateRange.endDateExpression === dateRange.endDateExpression - ) { - return; - } const nextStartDateExpression = newDateRange.startDateExpression || dateRange.startDateExpression; @@ -158,8 +148,9 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall endDateExpression: newDateRange.endDateExpression || previousDateRange.endDateExpression, }; }); + updateTimestamps(); }, - [dateRange, targetPosition] + [dateRange, targetPosition, updateTimestamps] ); // `lastUpdate` needs to be a dependency for the timestamps. @@ -192,7 +183,6 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall ...dateRange, startTimestamp, endTimestamp, - liveStreamingInterval, }; const callbacks = { @@ -203,11 +193,13 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall [jumpToTargetPosition] ), reportVisiblePositions, - startLiveStreaming: useCallback(() => setIsStreaming(true), [setIsStreaming]), + startLiveStreaming: useCallback(() => { + setIsStreaming(true); + jumpToTargetPosition(null); + updateDateRange({ startDateExpression: 'now-1d', endDateExpression: 'now' }); + }, [setIsStreaming, updateDateRange]), stopLiveStreaming: useCallback(() => setIsStreaming(false), [setIsStreaming]), updateDateRange, - setLiveStreamingInterval, - updateTimestamps, }; return { ...state, ...callbacks }; diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx index a59ad67f0257df..247c2f0e048357 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx @@ -36,7 +36,6 @@ const LogEntriesStateProvider: React.FC = ({ children }) => { pagesAfterEnd, isStreaming, jumpToTargetPosition, - liveStreamingInterval, initialized, } = useContext(LogPositionState.Context); const { filterQuery } = useContext(LogFilterState.Context); @@ -49,7 +48,6 @@ const LogEntriesStateProvider: React.FC = ({ children }) => { const entriesProps = { startTimestamp, endTimestamp, - liveStreamingInterval, timeKey: targetPosition, pagesBeforeStart, pagesAfterEnd, diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx index b6483cd2dbacd5..09a38e88ee626d 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx @@ -4,9 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexGroup, EuiFlexItem, EuiSuperDatePicker } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSuperDatePicker, EuiButtonEmpty } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useContext, useCallback } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; import { AutocompleteField } from '../../../components/autocomplete_field'; import { Toolbar } from '../../../components/eui'; @@ -52,9 +53,6 @@ export const LogsToolbar = () => { startDateExpression, endDateExpression, updateDateRange, - updateTimestamps, - liveStreamingInterval, - setLiveStreamingInterval, } = useContext(LogPositionState.Context); const handleTimeChange = useCallback( @@ -121,22 +119,40 @@ export const LogsToolbar = () => { /> - { - if (isPaused) { - stopLiveStreaming(); - } else { - startLiveStreaming(); - } - setLiveStreamingInterval(refreshInterval); - }} - /> + + + + + + {isStreaming ? ( + + + + ) : ( + + + + )} + + From 691ea8bf924093892e8f945ad758cbbf955d904d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 4 Mar 2020 16:03:06 +0100 Subject: [PATCH 079/117] Extract datepicker into its own component --- .../public/pages/logs/stream/page_toolbar.tsx | 52 ++++--------------- 1 file changed, 9 insertions(+), 43 deletions(-) diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx index 09a38e88ee626d..a11a5cc4c1e53e 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx @@ -22,6 +22,7 @@ import { LogFilterState } from '../../../containers/logs/log_filter'; import { LogPositionState } from '../../../containers/logs/log_position'; import { Source } from '../../../containers/source'; import { WithKueryAutocompletion } from '../../../containers/with_kuery_autocompletion'; +import { LogDatepicker } from '../../../components/logging/log_datepicker'; export const LogsToolbar = () => { const { createDerivedIndexPattern } = useContext(Source.Context); @@ -55,15 +56,6 @@ export const LogsToolbar = () => { updateDateRange, } = useContext(LogPositionState.Context); - const handleTimeChange = useCallback( - ({ start, end, isInvalid }) => { - if (!isInvalid) { - updateDateRange({ startDateExpression: start, endDateExpression: end }); - } - }, - [updateDateRange] - ); - return ( @@ -119,40 +111,14 @@ export const LogsToolbar = () => { /> - - - - - - {isStreaming ? ( - - - - ) : ( - - - - )} - - + From 2538f2d5426fd39539b947666e40708e3cbc7807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 4 Mar 2020 16:04:27 +0100 Subject: [PATCH 080/117] Cleanup unused code --- .../plugins/infra/public/pages/logs/stream/page_toolbar.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx index a11a5cc4c1e53e..2f9a76fd47490b 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx @@ -4,10 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexGroup, EuiFlexItem, EuiSuperDatePicker, EuiButtonEmpty } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { useContext, useCallback } from 'react'; -import { FormattedMessage } from '@kbn/i18n/react'; +import React, { useContext } from 'react'; import { AutocompleteField } from '../../../components/autocomplete_field'; import { Toolbar } from '../../../components/eui'; From 0d8e0da5f065a5f46347a626fca01475887f5b9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 4 Mar 2020 17:23:55 +0100 Subject: [PATCH 081/117] Tweak time markers on the edges --- .../logging/log_text_stream/loading_item_view.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx index fd60c4f2fd491f..896ac89da5466c 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx @@ -38,6 +38,15 @@ interface LogTextStreamLoadingItemViewProps { onStreamStart?: () => void; } +const TIMESTAMP_FORMAT = { + hour12: false, + month: 'short', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + second: 'numeric', +}; + export class LogTextStreamLoadingItemView extends React.PureComponent< LogTextStreamLoadingItemViewProps, {} @@ -98,7 +107,7 @@ const ProgressMessage: React.FC<{ timestamp?: number }> = ({ timestamp }) => { }} + values={{ timestamp: }} /> ) : ( Date: Wed, 4 Mar 2020 17:41:44 +0100 Subject: [PATCH 082/117] Use relative time when streaming --- .../log_text_stream/loading_item_view.tsx | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx index 896ac89da5466c..f3cb90209ac57c 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx @@ -14,7 +14,7 @@ import { EuiLoadingSpinner, EuiButton, } from '@elastic/eui'; -import { FormattedMessage, FormattedTime } from '@kbn/i18n/react'; +import { FormattedMessage, FormattedTime, FormattedRelative } from '@kbn/i18n/react'; import * as React from 'react'; import { Unit } from '@elastic/datemath'; @@ -84,7 +84,7 @@ export class LogTextStreamLoadingItemView extends React.PureComponent< return ( {position === 'start' ? <>{extra} : null} - + {position === 'end' ? <>{extra} : null} ); @@ -99,23 +99,37 @@ const ProgressEntryWrapper = euiStyled.div<{ position: Position }>` props.position === 'end' ? props.theme.eui.euiSizeL : props.theme.eui.euiSizeM}; `; -const ProgressMessage: React.FC<{ timestamp?: number }> = ({ timestamp }) => { +const ProgressMessage: React.FC<{ timestamp?: number; relative?: boolean }> = ({ + timestamp, + relative = false, +}) => { + let message; + + if (timestamp) { + const formattedTimestamp = relative ? ( + + ) : ( + + ); + message = ( + + ); + } else { + message = ( + + ); + } + return ( - - {timestamp ? ( - }} - /> - ) : ( - - )} - + {message} ); }; From adf5d1417d0f298167bf2a26a9c03e03b59f2cc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 4 Mar 2020 17:42:06 +0100 Subject: [PATCH 083/117] Add missing component --- .../components/logging/log_datepicker.tsx | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 x-pack/plugins/infra/public/components/logging/log_datepicker.tsx diff --git a/x-pack/plugins/infra/public/components/logging/log_datepicker.tsx b/x-pack/plugins/infra/public/components/logging/log_datepicker.tsx new file mode 100644 index 00000000000000..e80f738eac6ba3 --- /dev/null +++ b/x-pack/plugins/infra/public/components/logging/log_datepicker.tsx @@ -0,0 +1,73 @@ +/* + * 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 React, { useCallback } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiSuperDatePicker, EuiButtonEmpty } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +interface LogDatepickerProps { + startDateExpression: string; + endDateExpression: string; + isStreaming: boolean; + onUpdateDateRange?: (range: { startDateExpression: string; endDateExpression: string }) => void; + onStartStreaming?: () => void; + onStopStreaming?: () => void; +} + +export const LogDatepicker: React.FC = ({ + startDateExpression, + endDateExpression, + isStreaming, + onUpdateDateRange, + onStartStreaming, + onStopStreaming, +}) => { + const handleTimeChange = useCallback( + ({ start, end, isInvalid }) => { + if (onUpdateDateRange && !isInvalid) { + onUpdateDateRange({ startDateExpression: start, endDateExpression: end }); + } + }, + [onUpdateDateRange] + ); + + return ( + + + + + + {isStreaming ? ( + + + + ) : ( + + + + )} + + + ); +}; From 20a055814d4cf00e4ca958b76d411865ac1d4a87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 5 Mar 2020 11:14:35 +0100 Subject: [PATCH 084/117] Extract type --- .../logging/log_text_stream/loading_item_view.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx index f3cb90209ac57c..aed893ff00097a 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx @@ -157,10 +157,16 @@ const ProgressSpinner: React.FC<{ kind: 'streaming' | 'loading' }> = ({ kind }) ); -const ProgressCta: React.FC> = ({ position, rangeEdge, onExtendRange, onStreamStart }) => { +>; +const ProgressCta: React.FC = ({ + position, + rangeEdge, + onExtendRange, + onStreamStart, +}) => { if (rangeEdge === 'now' && position === 'end') { return ( From 6c8abc849d9cb68b19ad7e3cab29759a899995e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 5 Mar 2020 11:18:40 +0100 Subject: [PATCH 085/117] Tweak dependency lists for summary hooks --- .../containers/logs/log_highlights/log_summary_highlights.ts | 2 +- .../infra/public/containers/logs/log_summary/log_summary.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts index ad239c6926f660..41ee63bf0e23d4 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts @@ -47,7 +47,7 @@ export const useLogSummaryHighlights = ( setLogSummaryHighlights(response.data); }, }, - [sourceId, startTimestamp, endTimestamp, filterQuery, highlightTerms] + [sourceId, startTimestamp, endTimestamp, bucketSize, filterQuery, highlightTerms] ); const debouncedLoadSummaryHighlights = useMemo(() => debounce(loadLogSummaryHighlights, 275), [ diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx b/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx index 0df1292ad079d4..94723125cc0ec8 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx @@ -40,7 +40,7 @@ export const useLogSummary = ( } }); }, - [sourceId, filterQuery, startTimestamp, endTimestamp] + [sourceId, filterQuery, startTimestamp, endTimestamp, bucketSize] ); return { From a17e0b49096a7e2fa45f56338c026611ab694a3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 5 Mar 2020 15:30:10 +0100 Subject: [PATCH 086/117] Extend absolute datemath expressions --- .../log_text_stream/loading_item_view.tsx | 27 +-- .../scrollable_log_text_stream_view.tsx | 6 +- x-pack/plugins/infra/public/utils/datemath.ts | 196 +++++++++++------- 3 files changed, 137 insertions(+), 92 deletions(-) diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx index aed893ff00097a..1f0fc3e0b96e61 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx @@ -29,7 +29,8 @@ interface LogTextStreamLoadingItemViewProps { /** topCursor.time || bottomCursor.time */ timestamp?: number; /** startDateExpression || endDateExpression */ - rangeEdge?: string; + startDateExpression: string; + endDateExpression: string; className?: string; hasMore: boolean; isLoading: boolean; @@ -55,7 +56,8 @@ export class LogTextStreamLoadingItemView extends React.PureComponent< const { position, timestamp, - rangeEdge, + startDateExpression, + endDateExpression, className, hasMore, isLoading, @@ -75,7 +77,8 @@ export class LogTextStreamLoadingItemView extends React.PureComponent< position={position} onStreamStart={onStreamStart} onExtendRange={onExtendRange} - rangeEdge={rangeEdge} + startDateExpression={startDateExpression} + endDateExpression={endDateExpression} /> ) : null} @@ -159,14 +162,17 @@ const ProgressSpinner: React.FC<{ kind: 'streaming' | 'loading' }> = ({ kind }) type ProgressCtaProps = Pick< LogTextStreamLoadingItemViewProps, - 'position' | 'rangeEdge' | 'onExtendRange' | 'onStreamStart' + 'position' | 'startDateExpression' | 'endDateExpression' | 'onExtendRange' | 'onStreamStart' >; const ProgressCta: React.FC = ({ position, - rangeEdge, + startDateExpression, + endDateExpression, onExtendRange, onStreamStart, }) => { + const rangeEdge = position === 'start' ? startDateExpression : endDateExpression; + if (rangeEdge === 'now' && position === 'end') { return ( @@ -176,13 +182,10 @@ const ProgressCta: React.FC = ({ } const iconType = position === 'start' ? 'arrowUp' : 'arrowDown'; - - if (!rangeEdge) { - return null; - } - - const extendedRange = extendDatemath(rangeEdge, position === 'start' ? 'before' : 'after'); - + const extendedRange = + position === 'start' + ? extendDatemath(startDateExpression, 'before', endDateExpression) + : extendDatemath(endDateExpression, 'after', startDateExpression); if (!extendedRange || !('diffUnit' in extendedRange)) { return null; } diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx index e00da5252a3f17..356d524d1216e7 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx @@ -203,7 +203,8 @@ export class ScrollableLogTextStreamView extends React.PureComponent< items.length > 0 ? items[0].logEntry.cursor.time : undefined } isStreaming={false} - rangeEdge={startDateExpression} + startDateExpression={startDateExpression} + endDateExpression={endDateExpression} onExtendRange={newDateExpression => updateDateRange({ startDateExpression: newDateExpression }) } @@ -259,7 +260,8 @@ export class ScrollableLogTextStreamView extends React.PureComponent< ? items[items.length - 1].logEntry.cursor.time : undefined } - rangeEdge={endDateExpression} + startDateExpression={startDateExpression} + endDateExpression={endDateExpression} onExtendRange={newDateExpression => updateDateRange({ endDateExpression: newDateExpression }) } diff --git a/x-pack/plugins/infra/public/utils/datemath.ts b/x-pack/plugins/infra/public/utils/datemath.ts index e3af85d6aad798..50a9b6e4f69458 100644 --- a/x-pack/plugins/infra/public/utils/datemath.ts +++ b/x-pack/plugins/infra/public/utils/datemath.ts @@ -25,104 +25,144 @@ type DatemathExtension = diffUnit: Unit; diffAmount: number; } - | { value: 'now' } - | undefined; + | { value: 'now' }; const datemathNowExpression = /(\+|\-)(\d+)(ms|s|m|h|d|w|M|y)$/; +/** + * Extend a datemath value + * @param value The value to extend + * @param {'before' | 'after'} direction Should the value move before or after in time + * @param oppositeEdge For absolute values, the value of the other edge of the range + */ export function extendDatemath( value: string, - direction: 'before' | 'after' = 'before' -): DatemathExtension { + direction: 'before' | 'after' = 'before', + oppositeEdge?: string +): DatemathExtension | undefined { if (!isValidDatemath(value)) { return undefined; } + // `now` cannot be extended if (value === 'now') { return { value: 'now' }; } + // The unit is relative if (value.startsWith('now')) { - const [, operator, amount, unit] = datemathNowExpression.exec(value) || []; - if (!operator || !amount || !unit) { - return undefined; - } + return extendRelativeDatemath(value, direction); + } else if (oppositeEdge && isValidDatemath(oppositeEdge)) { + return extendAbsoluteDatemath(value, direction, oppositeEdge); + } - const mustIncreaseAmount = operator === '-' && direction === 'before'; - const parsedAmount = parseInt(amount, 10); - let newUnit: Unit = unit as Unit; - let newAmount: number; - - // Extend the amount - switch (unit) { - // For small units, always double or halve the amount - case 'ms': - case 's': - newAmount = mustIncreaseAmount ? parsedAmount * 2 : Math.floor(parsedAmount / 2); - break; - // For minutes, increase or decrease in doubles or halves, depending on - // the amount of minutes - case 'm': - let ratio; - const MINUTES_LARGE = 10; - if (mustIncreaseAmount) { - ratio = parsedAmount >= MINUTES_LARGE ? 0.5 : 1; - newAmount = parsedAmount + Math.floor(parsedAmount * ratio); - } else { - newAmount = - parsedAmount >= MINUTES_LARGE - ? Math.floor(parsedAmount / 1.5) - : parsedAmount - Math.floor(parsedAmount * 0.5); - } - break; - - // For hours, increase or decrease half an hour for 1 hour. Otherwise - // increase full hours - case 'h': - if (parsedAmount === 1) { - newAmount = mustIncreaseAmount ? 90 : 30; - newUnit = 'm'; - } else { - newAmount = mustIncreaseAmount ? parsedAmount + 1 : parsedAmount - 1; - } - break; - - // For the rest of units, increase or decrease one smaller unit for - // amounts of 1. Otherwise increase or decrease the unit - case 'd': - case 'w': - case 'M': - case 'y': - if (parsedAmount === 1) { - newUnit = dateMath.unitsDesc[dateMath.unitsDesc.indexOf(unit) + 1]; - newAmount = mustIncreaseAmount - ? convertDate(1, unit, newUnit) + 1 - : convertDate(1, unit, newUnit) - 1; - } else { - newAmount = mustIncreaseAmount ? parsedAmount + 1 : parsedAmount - 1; - } - break; - - default: - throw new TypeError('Unhandled datemath unit'); - } + return undefined; +} + +function extendRelativeDatemath( + value: string, + direction: 'before' | 'after' +): DatemathExtension | undefined { + const [, operator, amount, unit] = datemathNowExpression.exec(value) || []; + if (!operator || !amount || !unit) { + return undefined; + } - // normalize amount and unit (i.e. 120s -> 2m) - const { unit: normalizedUnit, amount: normalizedAmount } = normalizeDate(newAmount, newUnit); + const mustIncreaseAmount = operator === '-' && direction === 'before'; + const parsedAmount = parseInt(amount, 10); + let newUnit: Unit = unit as Unit; + let newAmount: number; - // How much have we changed the time? - const diffAmount = Math.abs(normalizedAmount - convertDate(parsedAmount, unit, normalizedUnit)); - // if `diffAmount` is not an integer after normalization, express the difference in the original unit - const shouldKeepDiffUnit = diffAmount % 1 !== 0; + // Extend the amount + switch (unit) { + // For small units, always double or halve the amount + case 'ms': + case 's': + newAmount = mustIncreaseAmount ? parsedAmount * 2 : Math.floor(parsedAmount / 2); + break; + // For minutes, increase or decrease in doubles or halves, depending on + // the amount of minutes + case 'm': + let ratio; + const MINUTES_LARGE = 10; + if (mustIncreaseAmount) { + ratio = parsedAmount >= MINUTES_LARGE ? 0.5 : 1; + newAmount = parsedAmount + Math.floor(parsedAmount * ratio); + } else { + newAmount = + parsedAmount >= MINUTES_LARGE + ? Math.floor(parsedAmount / 1.5) + : parsedAmount - Math.floor(parsedAmount * 0.5); + } + break; - return { - value: `now${operator}${normalizedAmount}${normalizedUnit}`, - diffUnit: shouldKeepDiffUnit ? unit : newUnit, - diffAmount: shouldKeepDiffUnit ? Math.abs(newAmount - parsedAmount) : diffAmount, - }; + // For hours, increase or decrease half an hour for 1 hour. Otherwise + // increase full hours + case 'h': + if (parsedAmount === 1) { + newAmount = mustIncreaseAmount ? 90 : 30; + newUnit = 'm'; + } else { + newAmount = mustIncreaseAmount ? parsedAmount + 1 : parsedAmount - 1; + } + break; + + // For the rest of units, increase or decrease one smaller unit for + // amounts of 1. Otherwise increase or decrease the unit + case 'd': + case 'w': + case 'M': + case 'y': + if (parsedAmount === 1) { + newUnit = dateMath.unitsDesc[dateMath.unitsDesc.indexOf(unit) + 1]; + newAmount = mustIncreaseAmount + ? convertDate(1, unit, newUnit) + 1 + : convertDate(1, unit, newUnit) - 1; + } else { + newAmount = mustIncreaseAmount ? parsedAmount + 1 : parsedAmount - 1; + } + break; + + default: + throw new TypeError('Unhandled datemath unit'); } - return undefined; + // normalize amount and unit (i.e. 120s -> 2m) + const { unit: normalizedUnit, amount: normalizedAmount } = normalizeDate(newAmount, newUnit); + + // How much have we changed the time? + const diffAmount = Math.abs(normalizedAmount - convertDate(parsedAmount, unit, normalizedUnit)); + // if `diffAmount` is not an integer after normalization, express the difference in the original unit + const shouldKeepDiffUnit = diffAmount % 1 !== 0; + + return { + value: `now${operator}${normalizedAmount}${normalizedUnit}`, + diffUnit: shouldKeepDiffUnit ? unit : newUnit, + diffAmount: shouldKeepDiffUnit ? Math.abs(newAmount - parsedAmount) : diffAmount, + }; +} + +function extendAbsoluteDatemath( + value: string, + direction: 'before' | 'after', + oppositeEdge: string +): DatemathExtension { + const valueTimestamp = datemathToEpochMillis(value)!; + const oppositeEdgeTimestamp = datemathToEpochMillis(oppositeEdge)!; + const actualTimestampDiff = Math.abs(valueTimestamp - oppositeEdgeTimestamp); + const normalizedDiff = normalizeDate(actualTimestampDiff, 'ms'); + const normalizedTimestampDiff = convertDate(normalizedDiff.amount, normalizedDiff.unit, 'ms'); + + const newValue = + direction === 'before' + ? valueTimestamp - normalizedTimestampDiff + : valueTimestamp + normalizedTimestampDiff; + + return { + value: new Date(newValue).toISOString(), + diffUnit: normalizedDiff.unit, + diffAmount: normalizedDiff.amount, + }; } const CONVERSION_RATIOS: Record> = { From 6c6f10fce81272de1947e34c2ab7e49e4e6e30d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 5 Mar 2020 17:03:23 +0100 Subject: [PATCH 087/117] Fix test runner URL navigation --- x-pack/test/functional/page_objects/infra_logs_page.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/test/functional/page_objects/infra_logs_page.ts b/x-pack/test/functional/page_objects/infra_logs_page.ts index d7185957024899..90a85e68297321 100644 --- a/x-pack/test/functional/page_objects/infra_logs_page.ts +++ b/x-pack/test/functional/page_objects/infra_logs_page.ts @@ -29,8 +29,8 @@ export function InfraLogsPageProvider({ getPageObjects, getService }: FtrProvide } await pageObjects.common.navigateToUrlWithBrowserHistory( 'infraLogs', - `/${logsUiTab}${decodeURI(queryString)}`, - decodeURI(queryString), + `/${logsUiTab}`, + queryString, { ensureCurrentUrl: false } // Test runner struggles with `rison-node` escaped values ); }, From 2f25ef2d77a62adf8f51308ebd4c1c5212a34905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 6 Mar 2020 11:06:59 +0100 Subject: [PATCH 088/117] Extract magic number to a constant --- .../containers/logs/log_highlights/log_highlights.tsx | 6 ++++-- .../public/containers/logs/log_summary/with_summary.ts | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx index 5d37aa6d0f5667..7e868a927f2a4a 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx @@ -13,6 +13,8 @@ import { useNextAndPrevious } from './next_and_previous'; import { LogPositionState } from '../log_position'; import { TimeKey } from '../../../../common/time'; +const FETCH_THROTTLE_INTERVAL = 3000; + export const useLogHighlightsState = ({ sourceId, sourceVersion, @@ -31,8 +33,8 @@ export const useLogHighlightsState = ({ LogPositionState.Context ); - const throttledStartTimestamp = useThrottle(startTimestamp, 3000); - const throttledEndTimestamp = useThrottle(endTimestamp, 3000); + const throttledStartTimestamp = useThrottle(startTimestamp, FETCH_THROTTLE_INTERVAL); + const throttledEndTimestamp = useThrottle(endTimestamp, FETCH_THROTTLE_INTERVAL); const { logEntryHighlights, diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts b/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts index 0550f9a15bfcd8..14da2b47bcfa2d 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts @@ -13,6 +13,8 @@ import { LogSummaryBuckets, useLogSummary } from './log_summary'; import { LogFilterState } from '../log_filter'; import { LogPositionState } from '../log_position'; +const FETCH_THROTTLE_INTERVAL = 3000; + export const WithSummary = ({ children, }: { @@ -27,8 +29,8 @@ export const WithSummary = ({ const { startTimestamp, endTimestamp } = useContext(LogPositionState.Context); // Keep it reasonably updated for the `now` case, but don't reload all the time when the user scrolls - const throttledStartTimestamp = useThrottle(startTimestamp, 3000); - const throttledEndTimestamp = useThrottle(endTimestamp, 3000); + const throttledStartTimestamp = useThrottle(startTimestamp, FETCH_THROTTLE_INTERVAL); + const throttledEndTimestamp = useThrottle(endTimestamp, FETCH_THROTTLE_INTERVAL); const { buckets, start, end } = useLogSummary( sourceId, From 74ccb6235ae4bbcbd5816e5c886f971255416db6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 6 Mar 2020 11:23:11 +0100 Subject: [PATCH 089/117] Remove outdated comment --- .../components/logging/log_text_stream/loading_item_view.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx index 1f0fc3e0b96e61..b12e2d6b7017f3 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx @@ -28,7 +28,6 @@ interface LogTextStreamLoadingItemViewProps { position: Position; /** topCursor.time || bottomCursor.time */ timestamp?: number; - /** startDateExpression || endDateExpression */ startDateExpression: string; endDateExpression: string; className?: string; From a906bd715504c7f636764f62d8bfabd153b45657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 6 Mar 2020 11:47:42 +0100 Subject: [PATCH 090/117] Make `timestamp` mandatory in LogTextStreamLoadingItemView The previous implementation was unnecessarily defensive. --- .../log_text_stream/loading_item_view.tsx | 39 ++--- .../scrollable_log_text_stream_view.tsx | 154 +++++++++--------- 2 files changed, 89 insertions(+), 104 deletions(-) diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx index b12e2d6b7017f3..d66f7c4a162ab7 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx @@ -27,7 +27,7 @@ type Position = 'start' | 'end'; interface LogTextStreamLoadingItemViewProps { position: Position; /** topCursor.time || bottomCursor.time */ - timestamp?: number; + timestamp: number; startDateExpression: string; endDateExpression: string; className?: string; @@ -101,33 +101,22 @@ const ProgressEntryWrapper = euiStyled.div<{ position: Position }>` props.position === 'end' ? props.theme.eui.euiSizeL : props.theme.eui.euiSizeM}; `; -const ProgressMessage: React.FC<{ timestamp?: number; relative?: boolean }> = ({ +const ProgressMessage: React.FC<{ timestamp: number; relative?: boolean }> = ({ timestamp, relative = false, }) => { - let message; - - if (timestamp) { - const formattedTimestamp = relative ? ( - - ) : ( - - ); - message = ( - - ); - } else { - message = ( - - ); - } + const formattedTimestamp = relative ? ( + + ) : ( + + ); + const message = ( + + ); return ( diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx index 356d524d1216e7..195a71338a419f 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx @@ -193,88 +193,84 @@ export class ScrollableLogTextStreamView extends React.PureComponent< isLocked={isScrollLocked} entriesCount={items.length} > - {registerChild => ( - <> - 0 ? items[0].logEntry.cursor.time : undefined - } - isStreaming={false} - startDateExpression={startDateExpression} - endDateExpression={endDateExpression} - onExtendRange={newDateExpression => - updateDateRange({ startDateExpression: newDateExpression }) - } - /> - {items.map((item, idx) => { - const currentTimestamp = item.logEntry.cursor.time; - let showDate = false; + {registerChild => + items.length > 0 ? ( + <> + + updateDateRange({ startDateExpression: newDateExpression }) + } + /> + {items.map((item, idx) => { + const currentTimestamp = item.logEntry.cursor.time; + let showDate = false; - if (idx > 0) { - const prevTimestamp = items[idx - 1].logEntry.cursor.time; - showDate = !moment(currentTimestamp).isSame(prevTimestamp, 'day'); - } + if (idx > 0) { + const prevTimestamp = items[idx - 1].logEntry.cursor.time; + showDate = !moment(currentTimestamp).isSame(prevTimestamp, 'day'); + } - return ( - - {showDate && } - - {itemMeasureRef => ( - - )} - - - ); - })} - 0 - ? items[items.length - 1].logEntry.cursor.time - : undefined - } - startDateExpression={startDateExpression} - endDateExpression={endDateExpression} - onExtendRange={newDateExpression => - updateDateRange({ endDateExpression: newDateExpression }) - } - onStreamStart={() => startLiveStreaming()} - /> - {isScrollLocked && ( - + {showDate && } + + {itemMeasureRef => ( + + )} + + + ); + })} + + updateDateRange({ endDateExpression: newDateExpression }) + } + onStreamStart={() => startLiveStreaming()} /> - )} - - )} + {isScrollLocked && ( + + )} + + ) : null + } )} From d0de07a6013dd59ac7b6887e79eeaa3ba916f057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 6 Mar 2020 11:58:13 +0100 Subject: [PATCH 091/117] Update doc comment --- .../components/logging/log_text_stream/loading_item_view.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx index d66f7c4a162ab7..a18cbde103c52b 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx @@ -26,8 +26,7 @@ type Position = 'start' | 'end'; interface LogTextStreamLoadingItemViewProps { position: Position; - /** topCursor.time || bottomCursor.time */ - timestamp: number; + timestamp: number; // Either the top of the bottom's cursor timestamp startDateExpression: string; endDateExpression: string; className?: string; From fe6f1a68ed8cae22f256803eef4ced577e87693e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 6 Mar 2020 14:37:03 +0100 Subject: [PATCH 092/117] Prevent infinite reloads at bottom edge Only update the `hasMoreBeforeStart` and `hasMoreAfterEnd` flags if the timestamps have been manually updated. --- .../containers/logs/log_entries/index.ts | 19 +++++++++++-------- .../logs/log_position/log_position_state.ts | 12 +++++++----- .../pages/logs/stream/page_providers.tsx | 2 ++ 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts index 05e1d3511537ec..b82f764cb650a7 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts @@ -54,6 +54,7 @@ type Dispatch = (action: ActionObj) => void; interface LogEntriesProps { startTimestamp: number; endTimestamp: number; + timestampsLastUpdate: number; filterQuery: string | null; timeKey: TimeKey | null; pagesBeforeStart: number | null; @@ -299,13 +300,11 @@ const useFetchEntriesEffect = ( after: false, }; - if ( - !props.startTimestamp || - !props.endTimestamp || - !prevParams || - !prevParams.startTimestamp || - !prevParams.endTimestamp - ) { + if (!prevParams || !prevParams.startTimestamp || !prevParams.endTimestamp) { + return; + } + + if (props.timestampsLastUpdate === prevParams.timestampsLastUpdate) { return; } @@ -319,7 +318,11 @@ const useFetchEntriesEffect = ( dispatch({ type: Action.ExpandRange, payload: shouldExpand }); }; - const expandRangeEffectDependencies = [props.startTimestamp, props.endTimestamp]; + const expandRangeEffectDependencies = [ + props.startTimestamp, + props.endTimestamp, + props.timestampsLastUpdate, + ]; useEffect(fetchNewEntriesEffect, fetchNewEntriesEffectDependencies); useEffect(fetchMoreEntriesEffect, fetchMoreEntriesEffectDependencies); diff --git a/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts index 5d5163a1c3b654..a422deec352d11 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts @@ -38,6 +38,7 @@ export interface LogPositionStateParams { endDateExpression: string; startTimestamp: number | null; endTimestamp: number | null; + timestampsLastUpdate: number; } export interface LogPositionCallbacks { @@ -85,10 +86,10 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall setInitialized(true); }, [setInitialized]); - const [lastUpdate, setLastUpdate] = useState(Date.now()); + const [timestampsLastUpdate, setTimestampsLastUpdate] = useState(Date.now()); const updateTimestamps = useCallback(() => { - setLastUpdate(Date.now()); - }, [setLastUpdate]); + setTimestampsLastUpdate(Date.now()); + }, [setTimestampsLastUpdate]); const [targetPosition, jumpToTargetPosition] = useState(null); const [isStreaming, setIsStreaming] = useState(false); @@ -158,7 +159,7 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall /* eslint-disable react-hooks/exhaustive-deps */ const startTimestamp = useMemo(() => datemathToEpochMillis(dateRange.startDateExpression), [ dateRange.startDateExpression, - lastUpdate, + timestampsLastUpdate, ]); // endTimestamp needs to be synced to `now` to allow auto-streaming @@ -166,7 +167,7 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall dateRange.endDateExpression === 'now' ? Date.now() : dateRange.endDateExpression; const endTimestamp = useMemo(() => datemathToEpochMillis(dateRange.endDateExpression, 'up'), [ endTimestampDep, - lastUpdate, + timestampsLastUpdate, ]); /* eslint-enable react-hooks/exhaustive-deps */ @@ -183,6 +184,7 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall ...dateRange, startTimestamp, endTimestamp, + timestampsLastUpdate, }; const callbacks = { diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx index 247c2f0e048357..3d296b9257eda6 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx @@ -31,6 +31,7 @@ const LogEntriesStateProvider: React.FC = ({ children }) => { const { startTimestamp, endTimestamp, + timestampsLastUpdate, targetPosition, pagesBeforeStart, pagesAfterEnd, @@ -48,6 +49,7 @@ const LogEntriesStateProvider: React.FC = ({ children }) => { const entriesProps = { startTimestamp, endTimestamp, + timestampsLastUpdate, timeKey: targetPosition, pagesBeforeStart, pagesAfterEnd, From 67909c1470628937c1e4b97aa683399f121645b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 6 Mar 2020 14:55:26 +0100 Subject: [PATCH 093/117] Tweak time markers for the range edges --- .../log_text_stream/loading_item_view.tsx | 50 ++++++++++++------- .../scrollable_log_text_stream_view.tsx | 7 ++- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx index a18cbde103c52b..f6d375cc31aa58 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx @@ -85,7 +85,7 @@ export class LogTextStreamLoadingItemView extends React.PureComponent< return ( {position === 'start' ? <>{extra} : null} - + {position === 'end' ? <>{extra} : null} ); @@ -100,22 +100,38 @@ const ProgressEntryWrapper = euiStyled.div<{ position: Position }>` props.position === 'end' ? props.theme.eui.euiSizeL : props.theme.eui.euiSizeM}; `; -const ProgressMessage: React.FC<{ timestamp: number; relative?: boolean }> = ({ - timestamp, - relative = false, -}) => { - const formattedTimestamp = relative ? ( - - ) : ( - - ); - const message = ( - - ); +type ProgressMessageProps = Pick< + LogTextStreamLoadingItemViewProps, + 'timestamp' | 'position' | 'isStreaming' +>; +const ProgressMessage: React.FC = ({ timestamp, position, isStreaming }) => { + const formattedTimestamp = + isStreaming && position === 'end' ? ( + + ) : ( + + ); + + const message = + position === 'start' ? ( + + ) : isStreaming ? ( + + ) : ( + + ); return ( diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx index 195a71338a419f..d90c507c095ef6 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx @@ -139,6 +139,7 @@ export class ScrollableLogTextStreamView extends React.PureComponent< wrap, startDateExpression, endDateExpression, + lastLoadedTime, updateDateRange, startLiveStreaming, } = this.props; @@ -254,7 +255,11 @@ export class ScrollableLogTextStreamView extends React.PureComponent< isLoading={isStreaming || isLoadingMore} hasMore={hasMoreAfterEnd} isStreaming={isStreaming} - timestamp={items[items.length - 1].logEntry.cursor.time} + timestamp={ + isStreaming && lastLoadedTime + ? lastLoadedTime.valueOf() + : items[items.length - 1].logEntry.cursor.time + } startDateExpression={startDateExpression} endDateExpression={endDateExpression} onExtendRange={newDateExpression => From 0c738ea2af44fa6982a6815dd37ed7d977b7917c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 6 Mar 2020 15:18:56 +0100 Subject: [PATCH 094/117] Ensure `LoadingItemView` always have the same height This prevents the main view from jumping around when the CTA and the spinner show/hide. --- .../logging/log_text_stream/loading_item_view.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx index f6d375cc31aa58..5598528c0e0f5c 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx @@ -67,7 +67,7 @@ export class LogTextStreamLoadingItemView extends React.PureComponent< const shouldShowCta = !hasMore && !isStreaming; const extra = ( - + {isLoading || isStreaming ? ( ) : shouldShowCta ? ( @@ -79,19 +79,23 @@ export class LogTextStreamLoadingItemView extends React.PureComponent< endDateExpression={endDateExpression} /> ) : null} - + ); return ( - {position === 'start' ? <>{extra} : null} + {position === 'start' ? extra : null} - {position === 'end' ? <>{extra} : null} + {position === 'end' ? extra : null} ); } } +const LoadingItemViewExtra = euiStyled(EuiFlexGroup)` + height: 40px; +`; + const ProgressEntryWrapper = euiStyled.div<{ position: Position }>` padding-left: ${props => props.theme.eui.euiSizeS}; padding-top: ${props => From 899b4239ae197170b6f64ffb7720cf0c26772932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 6 Mar 2020 15:21:07 +0100 Subject: [PATCH 095/117] Fix entry loading if streaming is on If the streaming is one through a URL param, the page wasn't loading any entries. This commit fixes this. --- .../plugins/infra/public/containers/logs/log_entries/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts index b82f764cb650a7..186ae71b1a04f3 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts @@ -236,7 +236,7 @@ const useFetchEntriesEffect = ( pick(props, ['sourceId', 'filterQuery', 'timeKey', 'startTimestamp', 'endTimestamp']) ); const fetchNewEntriesEffect = () => { - if (props.isStreaming) return; + if (props.isStreaming && prevParams) return; if (shouldFetchNewEntries({ ...props, ...state, prevParams })) { runFetchNewEntriesRequest(); } From b8ae1e615ee96910f92c5dff4ec56707f50a1aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 6 Mar 2020 15:48:58 +0100 Subject: [PATCH 096/117] Prefix boolean flag --- .../containers/logs/log_position/log_position_state.ts | 6 +++--- .../infra/public/pages/logs/stream/page_providers.tsx | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts index a422deec352d11..ccc9a2e5044aa0 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts @@ -25,7 +25,7 @@ interface VisiblePositions { } export interface LogPositionStateParams { - initialized: boolean; + isInitialized: boolean; targetPosition: TimeKeyOrNull; isStreaming: boolean; firstVisiblePosition: TimeKeyOrNull; @@ -81,7 +81,7 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall // When the page loads, there might be initial state in the URL. We want to // prevent the entries from showing until we have processed that initial // state. That prevents double fetching. - const [initialized, setInitialized] = useState(false); + const [isInitialized, setInitialized] = useState(false); const initialize = useCallback(() => { setInitialized(true); }, [setInitialized]); @@ -172,7 +172,7 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall /* eslint-enable react-hooks/exhaustive-deps */ const state = { - initialized, + isInitialized, targetPosition, isStreaming, firstVisiblePosition: startKey, diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx index 3d296b9257eda6..63fbe017fdd67c 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx @@ -37,7 +37,7 @@ const LogEntriesStateProvider: React.FC = ({ children }) => { pagesAfterEnd, isStreaming, jumpToTargetPosition, - initialized, + isInitialized, } = useContext(LogPositionState.Context); const { filterQuery } = useContext(LogFilterState.Context); @@ -61,7 +61,7 @@ const LogEntriesStateProvider: React.FC = ({ children }) => { // Don't initialize the entries until the position has been fully intialized. // See `` - if (!initialized) { + if (!isInitialized) { return null; } From 527ecf60ad2ebb187d43f0c51491d1b44df25356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 6 Mar 2020 17:52:16 +0100 Subject: [PATCH 097/117] Extract test constants to shared location --- x-pack/test/functional/apps/infra/constants.ts | 4 ++++ .../apps/infra/logs_source_configuration.ts | 11 +++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/x-pack/test/functional/apps/infra/constants.ts b/x-pack/test/functional/apps/infra/constants.ts index 947131a22d39bc..cd91867faf9dfa 100644 --- a/x-pack/test/functional/apps/infra/constants.ts +++ b/x-pack/test/functional/apps/infra/constants.ts @@ -22,5 +22,9 @@ export const DATES = { withData: '10/17/2018 7:58:03 PM', withoutData: '10/09/2018 10:00:00 PM', }, + stream: { + startWithData: '2018-10-17T19:42:22.000Z', + endWithData: '2018-10-17T19:57:21.000Z', + }, }, }; diff --git a/x-pack/test/functional/apps/infra/logs_source_configuration.ts b/x-pack/test/functional/apps/infra/logs_source_configuration.ts index 6e02751f29650c..f40c908f23c809 100644 --- a/x-pack/test/functional/apps/infra/logs_source_configuration.ts +++ b/x-pack/test/functional/apps/infra/logs_source_configuration.ts @@ -5,6 +5,7 @@ */ import expect from '@kbn/expect'; +import { DATES } from './constants'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -75,7 +76,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('renders the default log columns with their headers', async () => { await logsUi.logStreamPage.navigateTo({ - logPosition: { start: '2018-10-17T19:42:22.000Z', end: '2018-10-17T19:57:21.000Z' }, + logPosition: { + start: DATES.metricsAndLogs.stream.startWithData, + end: DATES.metricsAndLogs.stream.endWithData, + }, }); await retry.try(async () => { @@ -111,7 +115,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('renders the changed log columns with their headers', async () => { await logsUi.logStreamPage.navigateTo({ - logPosition: { start: '2018-10-17T19:42:22.000Z', end: '2018-10-17T19:57:21.000Z' }, + logPosition: { + start: DATES.metricsAndLogs.stream.startWithData, + end: DATES.metricsAndLogs.stream.endWithData, + }, }); await retry.try(async () => { From e1f404d8600369fc3b073293d6f8281c2d0757fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Fri, 6 Mar 2020 18:20:36 +0100 Subject: [PATCH 098/117] Remove unused translations --- x-pack/plugins/translations/translations/ja-JP.json | 1 - x-pack/plugins/translations/translations/zh-CN.json | 1 - 2 files changed, 2 deletions(-) diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index e831dcad5fd2c9..5daafd3bb0b73c 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -6432,7 +6432,6 @@ "xpack.infra.logs.index.settingsTabTitle": "設定", "xpack.infra.logs.index.streamTabTitle": "ストリーム", "xpack.infra.logs.jumpToTailText": "最も新しいエントリーに移動", - "xpack.infra.logs.noAdditionalEntriesFoundText": "追加エントリーが見つかりません", "xpack.infra.logs.scrollableLogTextStreamView.loadingEntriesLabel": "エントリーを読み込み中", "xpack.infra.logs.search.nextButtonLabel": "次へ", "xpack.infra.logs.search.previousButtonLabel": "前へ", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 3d770b898d7356..b89507f9d99cb3 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -6432,7 +6432,6 @@ "xpack.infra.logs.index.settingsTabTitle": "设置", "xpack.infra.logs.index.streamTabTitle": "流式传输", "xpack.infra.logs.jumpToTailText": "跳到最近的条目", - "xpack.infra.logs.noAdditionalEntriesFoundText": "找不到其他条目", "xpack.infra.logs.scrollableLogTextStreamView.loadingEntriesLabel": "正在加载条目", "xpack.infra.logs.search.nextButtonLabel": "下一个", "xpack.infra.logs.search.previousButtonLabel": "上一页", From 5a1103bf1d9ba37244bfba5bcd492bb102d45e7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Mon, 9 Mar 2020 11:58:24 +0100 Subject: [PATCH 099/117] Change default range behaviour if a log position is pre-set Set the default range to postion-1hour to position+1hour if a position is set in the URL and no range is specified. --- .../with_log_position_url_state.tsx | 22 +++++++++++-------- x-pack/test/functional/apps/infra/link_to.ts | 10 ++++++--- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx b/x-pack/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx index 0580f51b89e299..0d3586f9376f38 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx @@ -14,14 +14,14 @@ import { isValidDatemath, datemathToEpochMillis } from '../../../utils/datemath' /** * Url State */ -interface LogPositionUrlState { - position: LogPositionStateParams['visibleMidpoint'] | undefined; +export interface LogPositionUrlState { + position?: LogPositionStateParams['visibleMidpoint']; streamLive: boolean; start?: string; end?: string; } -const ONE_HOUR = 86400000; +const ONE_HOUR = 3600000; export const WithLogPositionUrlState = () => { const { @@ -74,10 +74,13 @@ export const WithLogPositionUrlState = () => { onInitialize={(initialUrlState: LogPositionUrlState | undefined) => { if (initialUrlState) { const initialPosition = initialUrlState.position; - let initialStartDateExpression = initialUrlState.start || 'now-1d'; - let initialEndDateExpression = initialUrlState.end || 'now'; + let initialStartDateExpression = initialUrlState.start; + let initialEndDateExpression = initialUrlState.end; - if (initialPosition) { + if (!initialPosition) { + initialStartDateExpression = initialStartDateExpression || 'now-1d'; + initialEndDateExpression = initialEndDateExpression || 'now'; + } else { const initialStartTimestamp = initialStartDateExpression ? datemathToEpochMillis(initialStartDateExpression) : undefined; @@ -85,11 +88,12 @@ export const WithLogPositionUrlState = () => { ? datemathToEpochMillis(initialEndDateExpression, 'up') : undefined; - // Adjust the start-end range if the target position falls outside - if (initialStartTimestamp && initialStartTimestamp > initialPosition.time) { + // Adjust the start-end range if the target position falls outside or if it's not set. + if (!initialStartTimestamp || initialStartTimestamp > initialPosition.time) { initialStartDateExpression = new Date(initialPosition.time - ONE_HOUR).toISOString(); } - if (initialEndTimestamp && initialEndTimestamp < initialPosition.time) { + + if (!initialEndTimestamp || initialEndTimestamp < initialPosition.time) { initialEndDateExpression = new Date(initialPosition.time + ONE_HOUR).toISOString(); } diff --git a/x-pack/test/functional/apps/infra/link_to.ts b/x-pack/test/functional/apps/infra/link_to.ts index 01c3d4979db085..834f2659a95f77 100644 --- a/x-pack/test/functional/apps/infra/link_to.ts +++ b/x-pack/test/functional/apps/infra/link_to.ts @@ -7,13 +7,17 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; +const ONE_HOUR = 60 * 60 * 1000; + export default ({ getPageObjects, getService }: FtrProviderContext) => { const pageObjects = getPageObjects(['common']); const retry = getService('retry'); const browser = getService('browser'); - // Ensure the start/end range is kept - const timestamp = Date.now() - 60000; + const timestamp = Date.now(); + const startDate = new Date(timestamp - ONE_HOUR).toISOString(); + const endDate = new Date(timestamp + ONE_HOUR).toISOString(); + const traceId = '433b4651687e18be2c6c8e3b11f53d09'; describe('Infra link-to', function() { @@ -25,7 +29,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { search: `time=${timestamp}&filter=trace.id:${traceId}`, state: undefined, }; - const expectedSearchString = `sourceId=default&logPosition=(end:now,position:(tiebreaker:0,time:${timestamp}),start:now-1d,streamLive:!f)&logFilter=(expression:'trace.id:${traceId}',kind:kuery)`; + const expectedSearchString = `sourceId=default&logPosition=(end:'${endDate}',position:(tiebreaker:0,time:${timestamp}),start:'${startDate}',streamLive:!f)&logFilter=(expression:'trace.id:${traceId}',kind:kuery)`; const expectedRedirectPath = '/logs/stream?'; await pageObjects.common.navigateToUrlWithBrowserHistory( From 7e1c2283f0d992015f3595a44997ef72fd10d772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Mon, 9 Mar 2020 14:19:56 +0100 Subject: [PATCH 100/117] Type `navigateTo` methods --- .../public/containers/logs/log_flyout.tsx | 2 +- .../page_objects/infra_logs_page.ts | 27 +++++++++++++++---- .../functional/services/logs_ui/log_stream.ts | 4 +-- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_flyout.tsx b/x-pack/plugins/infra/public/containers/logs/log_flyout.tsx index 5c1667a4b76804..267abe631c1423 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_flyout.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_flyout.tsx @@ -19,7 +19,7 @@ export enum FlyoutVisibility { visible = 'visible', } -interface FlyoutOptionsUrlState { +export interface FlyoutOptionsUrlState { flyoutId?: string | null; flyoutVisibility?: string | null; surroundingLogsId?: string | null; diff --git a/x-pack/test/functional/page_objects/infra_logs_page.ts b/x-pack/test/functional/page_objects/infra_logs_page.ts index 90a85e68297321..c4af137c6e838a 100644 --- a/x-pack/test/functional/page_objects/infra_logs_page.ts +++ b/x-pack/test/functional/page_objects/infra_logs_page.ts @@ -8,6 +8,18 @@ // import moment from 'moment'; import { encode, RisonValue } from 'rison-node'; import { FtrProviderContext } from '../ftr_provider_context'; +import { LogPositionUrlState } from '../../../../x-pack/plugins/infra/public/containers/logs/log_position/with_log_position_url_state'; +import { FlyoutOptionsUrlState } from '../../../../x-pack/plugins/infra/public/containers/logs/log_flyout'; + +export interface TabsParams { + stream: { + logPosition?: Partial; + flyoutOptions?: Partial; + }; + settings: never; + 'log-categories': any; + 'log-rate': any; +} export function InfraLogsPageProvider({ getPageObjects, getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); @@ -18,15 +30,20 @@ export function InfraLogsPageProvider({ getPageObjects, getService }: FtrProvide await pageObjects.common.navigateToApp('infraLogs'); }, - async navigateToTab(logsUiTab: LogsUiTab, params?: Record) { + async navigateToTab(logsUiTab: T, params?: TabsParams[T]) { let queryString = ''; if (params) { - queryString = Object.keys(params).reduce((qs, key, idx) => { - qs += (idx > 0 ? '&' : '') + `${key}=${encode(params[key])}`; + const qsPairs = []; - return qs; - }, '?'); + for (const key in params) { + if (params.hasOwnProperty(key)) { + const value = (params[key] as unknown) as RisonValue; + qsPairs.push(`${key}=${encode(value)}`); + } + } + queryString = '?' + qsPairs.join('&'); } + await pageObjects.common.navigateToUrlWithBrowserHistory( 'infraLogs', `/${logsUiTab}`, diff --git a/x-pack/test/functional/services/logs_ui/log_stream.ts b/x-pack/test/functional/services/logs_ui/log_stream.ts index f496f8552702f9..75486534cf5ccc 100644 --- a/x-pack/test/functional/services/logs_ui/log_stream.ts +++ b/x-pack/test/functional/services/logs_ui/log_stream.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RisonValue } from 'rison-node'; import { FtrProviderContext } from '../../ftr_provider_context'; import { WebElementWrapper } from '../../../../../test/functional/services/lib/web_element_wrapper'; +import { TabsParams } from '../../page_objects/infra_logs_page'; export function LogStreamPageProvider({ getPageObjects, getService }: FtrProviderContext) { const pageObjects = getPageObjects(['infraLogs']); @@ -14,7 +14,7 @@ export function LogStreamPageProvider({ getPageObjects, getService }: FtrProvide const testSubjects = getService('testSubjects'); return { - async navigateTo(params?: Record) { + async navigateTo(params?: TabsParams['stream']) { pageObjects.infraLogs.navigateToTab('stream', params); }, From adaca959d47abc5fb24cee9894f9307766699d15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Tue, 10 Mar 2020 17:22:46 +0100 Subject: [PATCH 101/117] Clean type casts --- .../containers/logs/log_entries/index.ts | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts index 186ae71b1a04f3..23d54746360ca7 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts @@ -11,9 +11,7 @@ import { LogEntriesResponse, LogEntry, LogEntriesRequest, - LogEntriesCenteredRequest, - LogEntriesBeforeRequest, - LogEntriesAfterRequest, + LogEntriesBaseRequest, } from '../../../../common/http_api'; import { fetchLogEntries } from './api/fetch_log_entries'; @@ -155,18 +153,22 @@ const useFetchEntriesEffect = ( dispatch({ type: Action.FetchingNewEntries }); try { - const fetchArgs: LogEntriesRequest = { + const commonFetchArgs: LogEntriesBaseRequest = { sourceId: overrides.sourceId || props.sourceId, startTimestamp: overrides.startTimestamp || props.startTimestamp, endTimestamp: overrides.endTimestamp || props.endTimestamp, query: overrides.filterQuery || props.filterQuery || undefined, // FIXME }; - if (props.timeKey) { - (fetchArgs as LogEntriesCenteredRequest).center = props.timeKey; - } else { - (fetchArgs as LogEntriesBeforeRequest).before = 'last'; - } + const fetchArgs: LogEntriesRequest = props.timeKey + ? { + ...commonFetchArgs, + center: props.timeKey, + } + : { + ...commonFetchArgs, + before: 'last', + }; const { data: payload } = await fetchLogEntries(fetchArgs); dispatch({ type: Action.ReceiveNewEntries, payload }); @@ -206,18 +208,22 @@ const useFetchEntriesEffect = ( dispatch({ type: Action.FetchingMoreEntries }); try { - const fetchArgs: LogEntriesRequest = { + const commonFetchArgs: LogEntriesBaseRequest = { sourceId: props.sourceId, startTimestamp: props.startTimestamp, endTimestamp: props.endTimestamp, query: props.filterQuery || undefined, // FIXME }; - if (getEntriesBefore) { - (fetchArgs as LogEntriesBeforeRequest).before = state.topCursor!; // We check for nullity above already - } else { - (fetchArgs as LogEntriesAfterRequest).after = state.bottomCursor; - } + const fetchArgs: LogEntriesRequest = getEntriesBefore + ? { + ...commonFetchArgs, + before: state.topCursor!, // We already check for nullity above + } + : { + ...commonFetchArgs, + after: state.bottomCursor, + }; const { data: payload } = await fetchLogEntries(fetchArgs); From b98afb2a644a6f1a809ac3091897982e3e1f25f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Tue, 10 Mar 2020 19:41:50 +0100 Subject: [PATCH 102/117] Add miscellaneous tweaks - Inline some conditionals together. - Tweak dependecy lists --- .../containers/logs/log_entries/index.ts | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts index 23d54746360ca7..10d6a05e55ee9a 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts @@ -198,10 +198,8 @@ const useFetchEntriesEffect = ( } const getEntriesBefore = direction === ShouldFetchMoreEntries.Before; - // Control cursors are correct - if (getEntriesBefore && !state.topCursor) { - return; - } else if (!state.bottomCursor) { + // Control that cursors are correct + if ((getEntriesBefore && !state.topCursor) || !state.bottomCursor) { return; } @@ -301,11 +299,6 @@ const useFetchEntriesEffect = ( }; const expandRangeEffect = () => { - const shouldExpand = { - before: false, - after: false, - }; - if (!prevParams || !prevParams.startTimestamp || !prevParams.endTimestamp) { return; } @@ -314,17 +307,17 @@ const useFetchEntriesEffect = ( return; } - if (props.startTimestamp < prevParams.startTimestamp) { - shouldExpand.before = true; - } - if (props.endTimestamp > prevParams.endTimestamp) { - shouldExpand.after = true; - } + const shouldExpand = { + before: props.startTimestamp < prevParams.startTimestamp, + after: props.endTimestamp > prevParams.endTimestamp, + }; dispatch({ type: Action.ExpandRange, payload: shouldExpand }); }; const expandRangeEffectDependencies = [ + prevParams?.startTimestamp, + prevParams?.endTimestamp, props.startTimestamp, props.endTimestamp, props.timestampsLastUpdate, From 45c8d3f1c33c77aac5c19775cdda8dfef844fb3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 11 Mar 2020 10:18:27 +0100 Subject: [PATCH 103/117] Add miscelalneous tweaks - Use more precise `eslint-disable` comments. - Restore old comments - Extract magic number into a constant --- .../containers/logs/log_highlights/log_entry_highlights.tsx | 4 ++-- .../public/containers/logs/log_position/log_position_state.ts | 4 ++-- .../infra/public/containers/logs/log_summary/bucket_size.ts | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx index 456a6ba2fe83f1..61d732866e5fc9 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx @@ -32,8 +32,8 @@ export const useLogEntryHighlights = ( return await fetchLogEntriesHighlights({ sourceId, - startTimestamp: getPreviousTimeKey(startKey).time, - endTimestamp: getNextTimeKey(endKey).time, + startTimestamp: getPreviousTimeKey(startKey).time, // interval boundaries are exclusive + endTimestamp: getNextTimeKey(endKey).time, // interval boundaries are exclusive query: filterQuery || undefined, highlightTerms, }); diff --git a/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts index ccc9a2e5044aa0..f5f2dc353a9451 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts @@ -156,7 +156,7 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall // `lastUpdate` needs to be a dependency for the timestamps. // ESLint complains it's unnecessary, but we know better. - /* eslint-disable react-hooks/exhaustive-deps */ + // eslint-disable-next-line react-hooks/exhaustive-deps const startTimestamp = useMemo(() => datemathToEpochMillis(dateRange.startDateExpression), [ dateRange.startDateExpression, timestampsLastUpdate, @@ -165,11 +165,11 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall // endTimestamp needs to be synced to `now` to allow auto-streaming const endTimestampDep = dateRange.endDateExpression === 'now' ? Date.now() : dateRange.endDateExpression; + // eslint-disable-next-line react-hooks/exhaustive-deps const endTimestamp = useMemo(() => datemathToEpochMillis(dateRange.endDateExpression, 'up'), [ endTimestampDep, timestampsLastUpdate, ]); - /* eslint-enable react-hooks/exhaustive-deps */ const state = { isInitialized, diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/bucket_size.ts b/x-pack/plugins/infra/public/containers/logs/log_summary/bucket_size.ts index 5e091e1f305b66..e46b304156f830 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_summary/bucket_size.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_summary/bucket_size.ts @@ -6,6 +6,8 @@ import { useMemo } from 'react'; +const SUMMARY_BUCKET_COUNT = 100; + export function useBucketSize( startTimestamp: number | null, endTimestamp: number | null @@ -14,7 +16,7 @@ export function useBucketSize( if (!startTimestamp || !endTimestamp) { return null; } - return (endTimestamp - startTimestamp) / 100; + return (endTimestamp - startTimestamp) / SUMMARY_BUCKET_COUNT; }, [startTimestamp, endTimestamp]); return bucketSize; From 72c6818f80102119a5c63b0a6d5917cab7e15724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 11 Mar 2020 15:13:28 +0100 Subject: [PATCH 104/117] Use standard `querystring` to set the tests url --- .../test/functional/page_objects/infra_logs_page.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/x-pack/test/functional/page_objects/infra_logs_page.ts b/x-pack/test/functional/page_objects/infra_logs_page.ts index c4af137c6e838a..10d86140fd1215 100644 --- a/x-pack/test/functional/page_objects/infra_logs_page.ts +++ b/x-pack/test/functional/page_objects/infra_logs_page.ts @@ -6,6 +6,7 @@ // import testSubjSelector from '@kbn/test-subj-selector'; // import moment from 'moment'; +import querystring from 'querystring'; import { encode, RisonValue } from 'rison-node'; import { FtrProviderContext } from '../ftr_provider_context'; import { LogPositionUrlState } from '../../../../x-pack/plugins/infra/public/containers/logs/log_position/with_log_position_url_state'; @@ -31,23 +32,23 @@ export function InfraLogsPageProvider({ getPageObjects, getService }: FtrProvide }, async navigateToTab(logsUiTab: T, params?: TabsParams[T]) { - let queryString = ''; + let qs = ''; if (params) { - const qsPairs = []; + const parsedParams: Record = {}; for (const key in params) { if (params.hasOwnProperty(key)) { const value = (params[key] as unknown) as RisonValue; - qsPairs.push(`${key}=${encode(value)}`); + parsedParams[key] = encode(value); } } - queryString = '?' + qsPairs.join('&'); + qs = '?' + querystring.stringify(parsedParams); } await pageObjects.common.navigateToUrlWithBrowserHistory( 'infraLogs', `/${logsUiTab}`, - queryString, + qs, { ensureCurrentUrl: false } // Test runner struggles with `rison-node` escaped values ); }, From de97635c3e5c4651d649e45c5e217b6e71e86eff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 11 Mar 2020 15:28:22 +0100 Subject: [PATCH 105/117] Allow `null` in some parts of the API The UI uses `null` extensively. It's easier to allow `null` as a value in the API than to refactor the UI to use `undefined` instead of `null`, so let's go with that. --- x-pack/plugins/infra/common/http_api/log_entries/entries.ts | 2 +- .../plugins/infra/public/containers/logs/log_entries/index.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/infra/common/http_api/log_entries/entries.ts b/x-pack/plugins/infra/common/http_api/log_entries/entries.ts index 8844667d686ebe..1a0ca0a8d4cfc0 100644 --- a/x-pack/plugins/infra/common/http_api/log_entries/entries.ts +++ b/x-pack/plugins/infra/common/http_api/log_entries/entries.ts @@ -16,7 +16,7 @@ export const logEntriesBaseRequestRT = rt.intersection([ endTimestamp: rt.number, }), rt.partial({ - query: rt.string, + query: rt.union([rt.string, rt.null]), size: rt.number, }), ]); diff --git a/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts index 10d6a05e55ee9a..2ec1206e0dbabe 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts @@ -157,7 +157,7 @@ const useFetchEntriesEffect = ( sourceId: overrides.sourceId || props.sourceId, startTimestamp: overrides.startTimestamp || props.startTimestamp, endTimestamp: overrides.endTimestamp || props.endTimestamp, - query: overrides.filterQuery || props.filterQuery || undefined, // FIXME + query: overrides.filterQuery || props.filterQuery, }; const fetchArgs: LogEntriesRequest = props.timeKey @@ -210,7 +210,7 @@ const useFetchEntriesEffect = ( sourceId: props.sourceId, startTimestamp: props.startTimestamp, endTimestamp: props.endTimestamp, - query: props.filterQuery || undefined, // FIXME + query: props.filterQuery, }; const fetchArgs: LogEntriesRequest = getEntriesBefore From db61f0508eec7760ea3af984d09876c5abfdd11b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 11 Mar 2020 15:57:47 +0100 Subject: [PATCH 106/117] Tweak `updateDateRange` callback --- .../logs/log_position/log_position_state.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts index f5f2dc353a9451..9c15012f613b93 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts @@ -6,6 +6,7 @@ import { useState, useMemo, useEffect, useCallback } from 'react'; import createContainer from 'constate'; +import { useSetState } from 'react-use'; import { TimeKey } from '../../../../common/time'; import { datemathToEpochMillis, isValidDatemath } from '../../../utils/datemath'; @@ -103,7 +104,7 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall // We group the `startDate` and `endDate` values in the same object to be able // to set both at the same time, saving a re-render - const [dateRange, setDateRange] = useState(DEFAULT_DATE_RANGE); + const [dateRange, setDateRange] = useSetState(DEFAULT_DATE_RANGE); const { startKey, middleKey, endKey, pagesBeforeStart, pagesAfterEnd } = visiblePositions; @@ -142,16 +143,10 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall jumpToTargetPosition(null); } - setDateRange(previousDateRange => { - return { - startDateExpression: - newDateRange.startDateExpression || previousDateRange.startDateExpression, - endDateExpression: newDateRange.endDateExpression || previousDateRange.endDateExpression, - }; - }); + setDateRange(newDateRange); updateTimestamps(); }, - [dateRange, targetPosition, updateTimestamps] + [setDateRange, dateRange, targetPosition, updateTimestamps] ); // `lastUpdate` needs to be a dependency for the timestamps. From 81cf057a9026e9436276f39d94442231b99f2d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 12 Mar 2020 10:21:57 +0100 Subject: [PATCH 107/117] Restore test for default columns The new API uses io-ts so we have removed the assertions that involve type checking. If these would be false the test will still fail when decoding the response. --- .../api_integration/apis/infra/log_entries.ts | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/x-pack/test/api_integration/apis/infra/log_entries.ts b/x-pack/test/api_integration/apis/infra/log_entries.ts index 1528cb842729d7..4f447d518a751d 100644 --- a/x-pack/test/api_integration/apis/infra/log_entries.ts +++ b/x-pack/test/api_integration/apis/infra/log_entries.ts @@ -5,6 +5,7 @@ */ import expect from '@kbn/expect'; +import { v4 as uuidv4 } from 'uuid'; import { pipe } from 'fp-ts/lib/pipeable'; import { identity } from 'fp-ts/lib/function'; @@ -16,6 +17,9 @@ import { LOG_ENTRIES_PATH, logEntriesRequestRT, logEntriesResponseRT, + LogTimestampColumn, + LogFieldColumn, + LogMessageColumn, } from '../../../../plugins/infra/common/http_api'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -40,6 +44,7 @@ const COMMON_HEADERS = { export default function({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const sourceConfigurationService = getService('infraOpsSourceConfiguration'); describe('log entry apis', () => { before(() => esArchiver.load('infra/metrics_and_logs')); @@ -85,6 +90,42 @@ export default function({ getService }: FtrProviderContext) { expect(lastEntry.cursor.time <= KEY_WITHIN_DATA_RANGE.time).to.be(true); }); + it('Returns the default columns', async () => { + const { body } = await supertest + .post(LOG_ENTRIES_PATH) + .set(COMMON_HEADERS) + .send( + logEntriesRequestRT.encode({ + sourceId: 'default', + startTimestamp: EARLIEST_KEY_WITH_DATA.time, + endTimestamp: LATEST_KEY_WITH_DATA.time, + center: KEY_WITHIN_DATA_RANGE, + }) + ) + .expect(200); + + const logEntriesResponse = pipe( + logEntriesResponseRT.decode(body), + fold(throwErrors(createPlainError), identity) + ); + + const entries = logEntriesResponse.data.entries; + const entry = entries[0]; + expect(entry.columns).to.have.length(3); + + const timestampColumn = entry.columns[0] as LogTimestampColumn; + expect(timestampColumn).to.have.property('timestamp'); + + const eventDatasetColumn = entry.columns[1] as LogFieldColumn; + expect(eventDatasetColumn).to.have.property('field'); + expect(eventDatasetColumn.field).to.be('event.dataset'); + expect(eventDatasetColumn).to.have.property('value'); + + const messageColumn = entry.columns[2] as LogMessageColumn; + expect(messageColumn).to.have.property('message'); + expect(messageColumn.message.length).to.be.greaterThan(0); + }); + it('Paginates correctly with `after`', async () => { const { body: firstPageBody } = await supertest .post(LOG_ENTRIES_PATH) @@ -258,6 +299,82 @@ export default function({ getService }: FtrProviderContext) { expect(logEntriesResponse.data.bottomCursor).to.be(null); }); }); + + describe('with a configured source', () => { + before(async () => { + await esArchiver.load('empty_kibana'); + await sourceConfigurationService.createConfiguration('default', { + name: 'Test Source', + logColumns: [ + { + timestampColumn: { + id: uuidv4(), + }, + }, + { + fieldColumn: { + id: uuidv4(), + field: 'host.name', + }, + }, + { + fieldColumn: { + id: uuidv4(), + field: 'event.dataset', + }, + }, + { + messageColumn: { + id: uuidv4(), + }, + }, + ], + }); + }); + after(() => esArchiver.unload('empty_kibana')); + + it('returns the configured columns', async () => { + const { body } = await supertest + .post(LOG_ENTRIES_PATH) + .set(COMMON_HEADERS) + .send( + logEntriesRequestRT.encode({ + sourceId: 'default', + startTimestamp: EARLIEST_KEY_WITH_DATA.time, + endTimestamp: LATEST_KEY_WITH_DATA.time, + center: KEY_WITHIN_DATA_RANGE, + }) + ) + .expect(200); + + const logEntriesResponse = pipe( + logEntriesResponseRT.decode(body), + fold(throwErrors(createPlainError), identity) + ); + + const entries = logEntriesResponse.data.entries; + const entry = entries[0]; + + expect(entry.columns).to.have.length(4); + + const timestampColumn = entry.columns[0] as LogTimestampColumn; + expect(timestampColumn).to.have.property('timestamp'); + + const hostNameColumn = entry.columns[1] as LogFieldColumn; + expect(hostNameColumn).to.have.property('field'); + expect(hostNameColumn.field).to.be('host.name'); + expect(hostNameColumn).to.have.property('value'); + + const eventDatasetColumn = entry.columns[2] as LogFieldColumn; + expect(eventDatasetColumn).to.have.property('field'); + expect(eventDatasetColumn.field).to.be('event.dataset'); + expect(eventDatasetColumn).to.have.property('value'); + + const messageColumn = entry.columns[3] as LogMessageColumn; + expect(messageColumn).to.have.property('message'); + expect(messageColumn.message.length).to.be.greaterThan(0); + }); + }); }); }); } From 4586cdbcfe99adc88d5472ea8e81dd087664812f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 12 Mar 2020 11:25:25 +0100 Subject: [PATCH 108/117] Restore logs_without_millis test --- .../test/api_integration/apis/infra/index.js | 1 + .../apis/infra/logs_without_millis.ts | 112 +++++++++++++----- 2 files changed, 86 insertions(+), 27 deletions(-) diff --git a/x-pack/test/api_integration/apis/infra/index.js b/x-pack/test/api_integration/apis/infra/index.js index fc201682f79fe1..f5bdf280c46d21 100644 --- a/x-pack/test/api_integration/apis/infra/index.js +++ b/x-pack/test/api_integration/apis/infra/index.js @@ -10,6 +10,7 @@ export default function({ loadTestFile }) { loadTestFile(require.resolve('./log_analysis')); loadTestFile(require.resolve('./log_entries')); loadTestFile(require.resolve('./log_entry_highlights')); + loadTestFile(require.resolve('./logs_without_millis')); loadTestFile(require.resolve('./log_summary')); loadTestFile(require.resolve('./metrics')); loadTestFile(require.resolve('./sources')); diff --git a/x-pack/test/api_integration/apis/infra/logs_without_millis.ts b/x-pack/test/api_integration/apis/infra/logs_without_millis.ts index b7cbcfc44663af..642f4fb42d3240 100644 --- a/x-pack/test/api_integration/apis/infra/logs_without_millis.ts +++ b/x-pack/test/api_integration/apis/infra/logs_without_millis.ts @@ -17,6 +17,9 @@ import { LOG_ENTRIES_SUMMARY_PATH, logEntriesSummaryRequestRT, logEntriesSummaryResponseRT, + LOG_ENTRIES_PATH, + logEntriesRequestRT, + logEntriesResponseRT, } from '../../../../plugins/infra/common/http_api/log_entries'; const COMMON_HEADERS = { @@ -30,6 +33,10 @@ const LATEST_KEY_WITH_DATA = { time: new Date('2019-01-06T23:59:23.000Z').valueOf(), tiebreaker: 2, }; +const KEY_WITHIN_DATA_RANGE = { + time: new Date('2019-01-06T00:00:00.000Z').valueOf(), + tiebreaker: 0, +}; export default function({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); @@ -39,33 +46,84 @@ export default function({ getService }: FtrProviderContext) { before(() => esArchiver.load('infra/logs_without_epoch_millis')); after(() => esArchiver.unload('infra/logs_without_epoch_millis')); - it('logSummaryBetween should return non-empty buckets', async () => { - const startTimestamp = EARLIEST_KEY_WITH_DATA.time; - const endTimestamp = LATEST_KEY_WITH_DATA.time + 1; // the interval end is exclusive - const bucketSize = Math.ceil((endTimestamp - startTimestamp) / 10); - - const { body } = await supertest - .post(LOG_ENTRIES_SUMMARY_PATH) - .set(COMMON_HEADERS) - .send( - logEntriesSummaryRequestRT.encode({ - sourceId: 'default', - startTimestamp, - endTimestamp, - bucketSize, - query: null, - }) - ) - .expect(200); - - const logSummaryResponse = pipe( - logEntriesSummaryResponseRT.decode(body), - fold(throwErrors(createPlainError), identity) - ); - - expect( - logSummaryResponse.data.buckets.filter((bucket: any) => bucket.entriesCount > 0) - ).to.have.length(2); + describe('/log_entries/summary', () => { + it('returns non-empty buckets', async () => { + const startTimestamp = EARLIEST_KEY_WITH_DATA.time; + const endTimestamp = LATEST_KEY_WITH_DATA.time + 1; // the interval end is exclusive + const bucketSize = Math.ceil((endTimestamp - startTimestamp) / 10); + + const { body } = await supertest + .post(LOG_ENTRIES_SUMMARY_PATH) + .set(COMMON_HEADERS) + .send( + logEntriesSummaryRequestRT.encode({ + sourceId: 'default', + startTimestamp, + endTimestamp, + bucketSize, + query: null, + }) + ) + .expect(200); + + const logSummaryResponse = pipe( + logEntriesSummaryResponseRT.decode(body), + fold(throwErrors(createPlainError), identity) + ); + + expect( + logSummaryResponse.data.buckets.filter((bucket: any) => bucket.entriesCount > 0) + ).to.have.length(2); + }); + }); + + describe('/log_entries/entries', () => { + it('returns log entries', async () => { + const startTimestamp = EARLIEST_KEY_WITH_DATA.time; + const endTimestamp = LATEST_KEY_WITH_DATA.time + 1; // the interval end is exclusive + + const { body } = await supertest + .post(LOG_ENTRIES_PATH) + .set(COMMON_HEADERS) + .send( + logEntriesRequestRT.encode({ + sourceId: 'default', + startTimestamp, + endTimestamp, + }) + ) + .expect(200); + + const logEntriesResponse = pipe( + logEntriesResponseRT.decode(body), + fold(throwErrors(createPlainError), identity) + ); + expect(logEntriesResponse.data.entries).to.have.length(2); + }); + + it('returns log entries when centering around a point', async () => { + const startTimestamp = EARLIEST_KEY_WITH_DATA.time; + const endTimestamp = LATEST_KEY_WITH_DATA.time + 1; // the interval end is exclusive + + const { body } = await supertest + .post(LOG_ENTRIES_PATH) + .set(COMMON_HEADERS) + .send( + logEntriesRequestRT.encode({ + sourceId: 'default', + startTimestamp, + endTimestamp, + center: KEY_WITHIN_DATA_RANGE, + }) + ) + .expect(200); + + const logEntriesResponse = pipe( + logEntriesResponseRT.decode(body), + fold(throwErrors(createPlainError), identity) + ); + expect(logEntriesResponse.data.entries).to.have.length(2); + }); }); }); } From 01aa5716c4cb1c7327ed92890d631f9d2e18d4c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 12 Mar 2020 14:51:28 +0100 Subject: [PATCH 109/117] Prevent overserializing --- .../server/lib/domains/log_entries_domain/log_entries_domain.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts index c38b63dddb88e9..045dd0f29ca6d5 100644 --- a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts +++ b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts @@ -152,7 +152,7 @@ export class InfraLogEntriesDomain { return { columnId: column.fieldColumn.id, field: column.fieldColumn.field, - value: stringify(doc.fields[column.fieldColumn.field]), + value: doc.fields[column.fieldColumn.field], highlights: doc.highlights[column.fieldColumn.field] || [], }; } From bb75f8fe1e28f3c75868f50b04f8d55ccda69ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 12 Mar 2020 17:55:22 +0100 Subject: [PATCH 110/117] Fix commit 01aa5716 --- x-pack/plugins/infra/common/http_api/log_entries/entries.ts | 5 +---- .../lib/domains/log_entries_domain/log_entries_domain.ts | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/x-pack/plugins/infra/common/http_api/log_entries/entries.ts b/x-pack/plugins/infra/common/http_api/log_entries/entries.ts index 1a0ca0a8d4cfc0..419ee021a91896 100644 --- a/x-pack/plugins/infra/common/http_api/log_entries/entries.ts +++ b/x-pack/plugins/infra/common/http_api/log_entries/entries.ts @@ -49,15 +49,12 @@ export type LogEntriesAfterRequest = rt.TypeOf; export type LogEntriesCenteredRequest = rt.TypeOf; export type LogEntriesRequest = rt.TypeOf; -// JSON value -const valueRT = rt.union([rt.string, rt.number, rt.boolean, rt.object, rt.null, rt.undefined]); - export const logMessageConstantPartRT = rt.type({ constant: rt.string, }); export const logMessageFieldPartRT = rt.type({ field: rt.string, - value: valueRT, + value: rt.unknown, highlights: rt.array(rt.string), }); diff --git a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts index 045dd0f29ca6d5..9a2631e3c2f768 100644 --- a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts +++ b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import stringify from 'json-stable-stringify'; import { sortBy } from 'lodash'; import { RequestHandlerContext } from 'src/core/server'; From 2a85f3d30aaa7d19d2644fe232236a81b7b82885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 12 Mar 2020 17:56:09 +0100 Subject: [PATCH 111/117] Fix type errors introduced when merging upstream --- .../top_categories/category_example_message.tsx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_example_message.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_example_message.tsx index 54609bcf8e2c23..023082154565cf 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_example_message.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_example_message.tsx @@ -44,11 +44,8 @@ export const CategoryExampleMessage: React.FunctionComponent<{ Date: Mon, 16 Mar 2020 16:10:12 +0100 Subject: [PATCH 112/117] Tweak entry highlight fetching --- .../log_highlights/log_entry_highlights.tsx | 29 +++++++++++++------ .../logs/log_highlights/log_highlights.tsx | 26 ++++++++++------- .../pages/logs/stream/page_providers.tsx | 7 ++++- 3 files changed, 41 insertions(+), 21 deletions(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx index 61d732866e5fc9..8528658ae709d4 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx @@ -14,8 +14,10 @@ import { LogEntry, LogEntriesHighlightsResponse } from '../../../../common/http_ export const useLogEntryHighlights = ( sourceId: string, sourceVersion: string | undefined, - startKey: TimeKey | null, - endKey: TimeKey | null, + startTimestamp: number | null, + endTimestamp: number | null, + centerPoint: TimeKey | null, + size: number, filterQuery: string | null, highlightTerms: string[] ) => { @@ -26,14 +28,16 @@ export const useLogEntryHighlights = ( { cancelPreviousOn: 'resolution', createPromise: async () => { - if (!startKey || !endKey || !highlightTerms.length) { + if (!startTimestamp || !endTimestamp || !centerPoint || !highlightTerms.length) { throw new Error('Skipping request: Insufficient parameters'); } return await fetchLogEntriesHighlights({ sourceId, - startTimestamp: getPreviousTimeKey(startKey).time, // interval boundaries are exclusive - endTimestamp: getNextTimeKey(endKey).time, // interval boundaries are exclusive + startTimestamp, + endTimestamp, + center: centerPoint, + size, query: filterQuery || undefined, highlightTerms, }); @@ -42,7 +46,7 @@ export const useLogEntryHighlights = ( setLogEntryHighlights(response.data); }, }, - [sourceId, startKey, endKey, filterQuery, highlightTerms] + [sourceId, startTimestamp, endTimestamp, centerPoint, size, filterQuery, highlightTerms] ); useEffect(() => { @@ -52,14 +56,21 @@ export const useLogEntryHighlights = ( useEffect(() => { if ( highlightTerms.filter(highlightTerm => highlightTerm.length > 0).length && - startKey && - endKey + startTimestamp && + endTimestamp ) { loadLogEntryHighlights(); } else { setLogEntryHighlights([]); } - }, [endKey, filterQuery, highlightTerms, loadLogEntryHighlights, sourceVersion, startKey]); + }, [ + endTimestamp, + filterQuery, + highlightTerms, + loadLogEntryHighlights, + sourceVersion, + startTimestamp, + ]); const logEntryHighlightsById = useMemo( () => diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx index 7e868a927f2a4a..f5538d90f087bf 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx @@ -15,19 +15,21 @@ import { TimeKey } from '../../../../common/time'; const FETCH_THROTTLE_INTERVAL = 3000; +interface UseLogHighlightsStateProps { + sourceId: string; + sourceVersion: string | undefined; + centerPoint: TimeKey | null; + size: number; + filterQuery: string | null; +} + export const useLogHighlightsState = ({ sourceId, sourceVersion, - entriesStart, - entriesEnd, + centerPoint, + size, filterQuery, -}: { - sourceId: string; - sourceVersion: string | undefined; - entriesStart: TimeKey | null; - entriesEnd: TimeKey | null; - filterQuery: string | null; -}) => { +}: UseLogHighlightsStateProps) => { const [highlightTerms, setHighlightTerms] = useState([]); const { visibleMidpoint, jumpToTargetPosition, startTimestamp, endTimestamp } = useContext( LogPositionState.Context @@ -43,8 +45,10 @@ export const useLogHighlightsState = ({ } = useLogEntryHighlights( sourceId, sourceVersion, - entriesStart, - entriesEnd, + throttledStartTimestamp, + throttledEndTimestamp, + centerPoint, + size, filterQuery, highlightTerms ); diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx index 63fbe017fdd67c..882b230ffdba84 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx @@ -70,13 +70,18 @@ const LogEntriesStateProvider: React.FC = ({ children }) => { const LogHighlightsStateProvider: React.FC = ({ children }) => { const { sourceId, version } = useContext(Source.Context); - const [{ topCursor, bottomCursor }] = useContext(LogEntriesState.Context); + const [{ topCursor, bottomCursor, entries }] = useContext(LogEntriesState.Context); const { filterQuery } = useContext(LogFilterState.Context); + + const centerPoint = entries.length > 0 ? entries[Math.floor(entries.length / 2)].cursor : null; + const highlightsProps = { sourceId, sourceVersion: version, entriesStart: topCursor, entriesEnd: bottomCursor, + centerPoint, + size: entries.length, filterQuery, }; return {children}; From 18a9324b50674b076e4b0ce03f7445899ca72126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Mon, 16 Mar 2020 19:47:59 +0100 Subject: [PATCH 113/117] Remove unused imports --- .../containers/logs/log_highlights/log_entry_highlights.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx index 8528658ae709d4..77018504437682 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx @@ -6,7 +6,7 @@ import { useEffect, useMemo, useState } from 'react'; -import { getNextTimeKey, getPreviousTimeKey, TimeKey } from '../../../../common/time'; +import { TimeKey } from '../../../../common/time'; import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { fetchLogEntriesHighlights } from './api/fetch_log_entries_highlights'; import { LogEntry, LogEntriesHighlightsResponse } from '../../../../common/http_api'; From a1466927cbc97611a59904a804609241e4212a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Tue, 17 Mar 2020 15:47:56 +0100 Subject: [PATCH 114/117] Refactor how `timestamps` are calculated Instead of using `useMemo` to update the timestamps we will explicitly set them up when the user selects a new date range or, if the `endTimestamp` is `now`, when the user scrolls down. This still enables infinite scroll and live streaming, but avoids a lot of unnecessary fetching. --- .../logs/log_position/log_position_state.ts | 62 ++++++++++--------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts index 9c15012f613b93..29c2bfd1c4a6be 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts @@ -15,6 +15,9 @@ type TimeKeyOrNull = TimeKey | null; interface DateRange { startDateExpression: string; endDateExpression: string; + startTimestamp: number; + endTimestamp: number; + timestampsLastUpdated: number; } interface VisiblePositions { @@ -52,7 +55,8 @@ export interface LogPositionCallbacks { updateDateRange: (newDateRage: Partial) => void; } -const DEFAULT_DATE_RANGE: DateRange = { startDateExpression: 'now-1d', endDateExpression: 'now' }; +const DEFAULT_DATE_RANGE = { startDateExpression: 'now-1d', endDateExpression: 'now' }; +const DESIRED_BUFFER_PAGES = 2; const useVisibleMidpoint = (middleKey: TimeKeyOrNull, targetPosition: TimeKeyOrNull) => { // Of the two dependencies `middleKey` and `targetPosition`, return @@ -87,11 +91,6 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall setInitialized(true); }, [setInitialized]); - const [timestampsLastUpdate, setTimestampsLastUpdate] = useState(Date.now()); - const updateTimestamps = useCallback(() => { - setTimestampsLastUpdate(Date.now()); - }, [setTimestampsLastUpdate]); - const [targetPosition, jumpToTargetPosition] = useState(null); const [isStreaming, setIsStreaming] = useState(false); const [visiblePositions, reportVisiblePositions] = useState({ @@ -104,7 +103,12 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall // We group the `startDate` and `endDate` values in the same object to be able // to set both at the same time, saving a re-render - const [dateRange, setDateRange] = useSetState(DEFAULT_DATE_RANGE); + const [dateRange, setDateRange] = useSetState({ + ...DEFAULT_DATE_RANGE, + startTimestamp: datemathToEpochMillis(DEFAULT_DATE_RANGE.startDateExpression)!, + endTimestamp: datemathToEpochMillis(DEFAULT_DATE_RANGE.endDateExpression, 'up')!, + timestampsLastUpdated: Date.now(), + }); const { startKey, middleKey, endKey, pagesBeforeStart, pagesAfterEnd } = visiblePositions; @@ -143,28 +147,31 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall jumpToTargetPosition(null); } - setDateRange(newDateRange); - updateTimestamps(); + setDateRange({ + ...newDateRange, + startTimestamp: nextStartTimestamp, + endTimestamp: nextEndTimestamp, + timestampsLastUpdated: Date.now(), + }); }, - [setDateRange, dateRange, targetPosition, updateTimestamps] + [setDateRange, dateRange, targetPosition] ); - // `lastUpdate` needs to be a dependency for the timestamps. - // ESLint complains it's unnecessary, but we know better. - // eslint-disable-next-line react-hooks/exhaustive-deps - const startTimestamp = useMemo(() => datemathToEpochMillis(dateRange.startDateExpression), [ - dateRange.startDateExpression, - timestampsLastUpdate, - ]); - - // endTimestamp needs to be synced to `now` to allow auto-streaming - const endTimestampDep = - dateRange.endDateExpression === 'now' ? Date.now() : dateRange.endDateExpression; - // eslint-disable-next-line react-hooks/exhaustive-deps - const endTimestamp = useMemo(() => datemathToEpochMillis(dateRange.endDateExpression, 'up'), [ - endTimestampDep, - timestampsLastUpdate, - ]); + // `endTimestamp` update conditions + useEffect(() => { + if (dateRange.endDateExpression !== 'now') { + return; + } + + // User is close to the bottom edge of the scroll. The value of + // `pagesAfterEnd` changes on live stream as well. + if (pagesAfterEnd <= DESIRED_BUFFER_PAGES) { + setDateRange({ + endTimestamp: datemathToEpochMillis(dateRange.endDateExpression, 'up')!, + timestampsLastUpdated: Date.now(), + }); + } + }, [dateRange.endDateExpression, pagesAfterEnd, setDateRange]); const state = { isInitialized, @@ -177,9 +184,6 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall visibleMidpointTime: visibleMidpoint ? visibleMidpoint.time : null, visibleTimeInterval, ...dateRange, - startTimestamp, - endTimestamp, - timestampsLastUpdate, }; const callbacks = { From af41535292a87e3acbf394c161bb89fe2a958b2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Tue, 17 Mar 2020 17:05:00 +0100 Subject: [PATCH 115/117] Fix typo --- .../containers/logs/log_position/log_position_state.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts index 29c2bfd1c4a6be..08eaa66a7e123b 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts @@ -17,7 +17,7 @@ interface DateRange { endDateExpression: string; startTimestamp: number; endTimestamp: number; - timestampsLastUpdated: number; + timestampsLastUpdate: number; } interface VisiblePositions { @@ -107,7 +107,7 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall ...DEFAULT_DATE_RANGE, startTimestamp: datemathToEpochMillis(DEFAULT_DATE_RANGE.startDateExpression)!, endTimestamp: datemathToEpochMillis(DEFAULT_DATE_RANGE.endDateExpression, 'up')!, - timestampsLastUpdated: Date.now(), + timestampsLastUpdate: Date.now(), }); const { startKey, middleKey, endKey, pagesBeforeStart, pagesAfterEnd } = visiblePositions; @@ -151,7 +151,7 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall ...newDateRange, startTimestamp: nextStartTimestamp, endTimestamp: nextEndTimestamp, - timestampsLastUpdated: Date.now(), + timestampsLastUpdate: Date.now(), }); }, [setDateRange, dateRange, targetPosition] @@ -168,7 +168,7 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall if (pagesAfterEnd <= DESIRED_BUFFER_PAGES) { setDateRange({ endTimestamp: datemathToEpochMillis(dateRange.endDateExpression, 'up')!, - timestampsLastUpdated: Date.now(), + timestampsLastUpdate: Date.now(), }); } }, [dateRange.endDateExpression, pagesAfterEnd, setDateRange]); From a806e3a1128a33cc9e5287297058a1fb531b78d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 18 Mar 2020 13:07:52 +0100 Subject: [PATCH 116/117] Tweak refresh of `endTimestamp` --- .../containers/logs/log_position/log_position_state.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts index 08eaa66a7e123b..5ac34e5df70ece 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts @@ -163,15 +163,14 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall return; } - // User is close to the bottom edge of the scroll. The value of - // `pagesAfterEnd` changes on live stream as well. - if (pagesAfterEnd <= DESIRED_BUFFER_PAGES) { + // User is close to the bottom edge of the scroll. + if (visiblePositions.pagesAfterEnd <= DESIRED_BUFFER_PAGES) { setDateRange({ endTimestamp: datemathToEpochMillis(dateRange.endDateExpression, 'up')!, timestampsLastUpdate: Date.now(), }); } - }, [dateRange.endDateExpression, pagesAfterEnd, setDateRange]); + }, [dateRange.endDateExpression, visiblePositions, setDateRange]); const state = { isInitialized, From 5269c24f2b3bc64cf36f414a6c14c688394991c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 18 Mar 2020 14:14:56 +0100 Subject: [PATCH 117/117] Expose `centerCursor` from `logEntriesState` --- .../public/containers/logs/log_entries/index.ts | 17 ++++++++++++++--- .../logs/log_highlights/log_highlights.tsx | 6 +++--- .../public/pages/logs/stream/page_providers.tsx | 6 ++---- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts index 2ec1206e0dbabe..b9a5c4068e1669 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts @@ -69,6 +69,7 @@ export interface LogEntriesStateParams { entries: LogEntriesResponse['data']['entries']; topCursor: LogEntriesResponse['data']['topCursor'] | null; bottomCursor: LogEntriesResponse['data']['bottomCursor'] | null; + centerCursor: TimeKey | null; isReloading: boolean; isLoadingMore: boolean; lastLoadedTime: Date | null; @@ -88,6 +89,7 @@ export const logEntriesInitialState: LogEntriesStateParams = { entries: [], topCursor: null, bottomCursor: null, + centerCursor: null, isReloading: true, isLoadingMore: false, lastLoadedTime: null, @@ -348,7 +350,7 @@ const logEntriesStateReducer = (prevState: LogEntriesStateParams, action: Action return { ...prevState, ...action.payload, - entries: action.payload.entries, + centerCursor: getCenterCursor(action.payload.entries), lastLoadedTime: new Date(), isReloading: false, @@ -360,13 +362,15 @@ const logEntriesStateReducer = (prevState: LogEntriesStateParams, action: Action case Action.ReceiveEntriesBefore: { const newEntries = action.payload.entries; const prevEntries = cleanDuplicateItems(prevState.entries, newEntries); + const entries = [...newEntries, ...prevEntries]; const update = { - entries: [...newEntries, ...prevEntries], + entries, isLoadingMore: false, hasMoreBeforeStart: newEntries.length > 0, // Keep the previous cursor if request comes empty, to easily extend the range. topCursor: newEntries.length > 0 ? action.payload.topCursor : prevState.topCursor, + centerCursor: getCenterCursor(entries), lastLoadedTime: new Date(), }; @@ -375,13 +379,15 @@ const logEntriesStateReducer = (prevState: LogEntriesStateParams, action: Action case Action.ReceiveEntriesAfter: { const newEntries = action.payload.entries; const prevEntries = cleanDuplicateItems(prevState.entries, newEntries); + const entries = [...prevEntries, ...newEntries]; const update = { - entries: [...prevEntries, ...newEntries], + entries, isLoadingMore: false, hasMoreAfterEnd: newEntries.length > 0, // Keep the previous cursor if request comes empty, to easily extend the range. bottomCursor: newEntries.length > 0 ? action.payload.bottomCursor : prevState.bottomCursor, + centerCursor: getCenterCursor(entries), lastLoadedTime: new Date(), }; @@ -394,6 +400,7 @@ const logEntriesStateReducer = (prevState: LogEntriesStateParams, action: Action entries: [], topCursor: null, bottomCursor: null, + centerCursor: null, hasMoreBeforeStart: true, hasMoreAfterEnd: true, }; @@ -419,4 +426,8 @@ const logEntriesStateReducer = (prevState: LogEntriesStateParams, action: Action } }; +function getCenterCursor(entries: LogEntry[]): TimeKey | null { + return entries.length > 0 ? entries[Math.floor(entries.length / 2)].cursor : null; +} + export const LogEntriesState = createContainer(useLogEntriesState); diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx index f5538d90f087bf..941e89848131ba 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx @@ -18,7 +18,7 @@ const FETCH_THROTTLE_INTERVAL = 3000; interface UseLogHighlightsStateProps { sourceId: string; sourceVersion: string | undefined; - centerPoint: TimeKey | null; + centerCursor: TimeKey | null; size: number; filterQuery: string | null; } @@ -26,7 +26,7 @@ interface UseLogHighlightsStateProps { export const useLogHighlightsState = ({ sourceId, sourceVersion, - centerPoint, + centerCursor, size, filterQuery, }: UseLogHighlightsStateProps) => { @@ -47,7 +47,7 @@ export const useLogHighlightsState = ({ sourceVersion, throttledStartTimestamp, throttledEndTimestamp, - centerPoint, + centerCursor, size, filterQuery, highlightTerms diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx index 882b230ffdba84..e4ccdaf7c57487 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx @@ -70,17 +70,15 @@ const LogEntriesStateProvider: React.FC = ({ children }) => { const LogHighlightsStateProvider: React.FC = ({ children }) => { const { sourceId, version } = useContext(Source.Context); - const [{ topCursor, bottomCursor, entries }] = useContext(LogEntriesState.Context); + const [{ topCursor, bottomCursor, centerCursor, entries }] = useContext(LogEntriesState.Context); const { filterQuery } = useContext(LogFilterState.Context); - const centerPoint = entries.length > 0 ? entries[Math.floor(entries.length / 2)].cursor : null; - const highlightsProps = { sourceId, sourceVersion: version, entriesStart: topCursor, entriesEnd: bottomCursor, - centerPoint, + centerCursor, size: entries.length, filterQuery, };