"View Surrounding Logs" functionality (#35727)
* Add "View Surrounding Logs" functionality Move Logs Flyout state from Redux to Hooks * Cleanup unused imports etc * Amend for Prettier * Stop live streaming when a filter is applied * Change tooltip wording * Compose functions for props higher in the component hierarchy * Don't make a request at all without a flyoutId * Handle no response * Use TimeKey on document * Appease Prettier * Clean up unused props * Use shared InfraTimeKey fragment * Add semicolon
This commit is contained in:
parent
53ad55cf6f
commit
dd6c2e2a2e
|
@ -186,6 +186,8 @@ export interface InfraLogItem {
|
|||
id: string;
|
||||
/** The index where the document was found */
|
||||
index: string;
|
||||
/** Time key for the document - derived from the source configuration timestamp and tiebreaker settings */
|
||||
key: InfraTimeKey;
|
||||
/** An array of flattened fields and values */
|
||||
fields: InfraLogItemField[];
|
||||
}
|
||||
|
@ -551,9 +553,19 @@ export namespace FlyoutItemQuery {
|
|||
|
||||
index: string;
|
||||
|
||||
key: Key;
|
||||
|
||||
fields: Fields[];
|
||||
};
|
||||
|
||||
export type Key = {
|
||||
__typename?: 'InfraTimeKey';
|
||||
|
||||
time: number;
|
||||
|
||||
tiebreaker: number;
|
||||
};
|
||||
|
||||
export type Fields = {
|
||||
__typename?: 'InfraLogItemField';
|
||||
|
||||
|
|
|
@ -14,24 +14,40 @@ import {
|
|||
EuiToolTip,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import moment from 'moment';
|
||||
import React from 'react';
|
||||
|
||||
import euiStyled from '../../../../../common/eui_styled_components';
|
||||
import { TimeKey } from '../../../common/time';
|
||||
import { InfraLogItem, InfraLogItemField } from '../../graphql/types';
|
||||
import { InfraLoadingPanel } from '../loading';
|
||||
|
||||
interface Props {
|
||||
flyoutItem: InfraLogItem | null;
|
||||
hideFlyout: () => void;
|
||||
setFlyoutVisibility: (visible: boolean) => void;
|
||||
setFilter: (filter: string) => void;
|
||||
setTarget: (timeKey: TimeKey, flyoutItemId: string) => void;
|
||||
intl: InjectedIntl;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
export const LogFlyout = injectI18n(
|
||||
({ flyoutItem, loading, hideFlyout, setFilter, intl }: Props) => {
|
||||
({ flyoutItem, loading, setFlyoutVisibility, setFilter, setTarget, intl }: Props) => {
|
||||
const handleFilter = (field: InfraLogItemField) => () => {
|
||||
const filter = `${field.field}:"${field.value}"`;
|
||||
setFilter(filter);
|
||||
|
||||
if (flyoutItem && flyoutItem.key) {
|
||||
const timestampMoment = moment(flyoutItem.key.time);
|
||||
if (timestampMoment.isValid()) {
|
||||
setTarget(
|
||||
{
|
||||
time: timestampMoment.valueOf(),
|
||||
tiebreaker: flyoutItem.key.tiebreaker,
|
||||
},
|
||||
flyoutItem.id
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const columns = [
|
||||
|
@ -55,7 +71,7 @@ export const LogFlyout = injectI18n(
|
|||
<EuiToolTip
|
||||
content={intl.formatMessage({
|
||||
id: 'xpack.infra.logFlyout.setFilterTooltip',
|
||||
defaultMessage: 'Set Filter',
|
||||
defaultMessage: 'View event with filter',
|
||||
})}
|
||||
>
|
||||
<EuiButtonIcon
|
||||
|
@ -74,7 +90,7 @@ export const LogFlyout = injectI18n(
|
|||
},
|
||||
];
|
||||
return (
|
||||
<EuiFlyout onClose={() => hideFlyout()} size="m">
|
||||
<EuiFlyout onClose={() => setFlyoutVisibility(false)} size="m">
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<EuiTitle size="s">
|
||||
<h3 id="flyoutTitle">
|
||||
|
|
|
@ -17,6 +17,7 @@ interface LogTextStreamItemDateFieldProps {
|
|||
hasHighlights: boolean;
|
||||
isHovered: boolean;
|
||||
scale: TextScale;
|
||||
isHighlighted: boolean;
|
||||
}
|
||||
|
||||
export class LogTextStreamItemDateField extends React.PureComponent<
|
||||
|
@ -24,12 +25,13 @@ export class LogTextStreamItemDateField extends React.PureComponent<
|
|||
{}
|
||||
> {
|
||||
public render() {
|
||||
const { children, hasHighlights, isHovered, scale } = this.props;
|
||||
const { children, hasHighlights, isHovered, isHighlighted, scale } = this.props;
|
||||
|
||||
return (
|
||||
<LogTextStreamItemDateFieldWrapper
|
||||
hasHighlights={hasHighlights}
|
||||
isHovered={isHovered}
|
||||
isHighlighted={isHighlighted}
|
||||
scale={scale}
|
||||
>
|
||||
{children}
|
||||
|
@ -59,6 +61,7 @@ const hoveredFieldStyle = css`
|
|||
const LogTextStreamItemDateFieldWrapper = LogTextStreamItemField.extend.attrs<{
|
||||
hasHighlights: boolean;
|
||||
isHovered: boolean;
|
||||
isHighlighted: boolean;
|
||||
}>({})`
|
||||
background-color: ${props => props.theme.eui.euiColorLightestShade};
|
||||
border-right: solid 2px ${props => props.theme.eui.euiColorLightShade};
|
||||
|
@ -67,5 +70,5 @@ const LogTextStreamItemDateFieldWrapper = LogTextStreamItemField.extend.attrs<{
|
|||
padding: 0 ${props => props.theme.eui.paddingSizes.l};
|
||||
|
||||
${props => (props.hasHighlights ? highlightedFieldStyle : '')};
|
||||
${props => (props.isHovered ? hoveredFieldStyle : '')};
|
||||
${props => (props.isHovered || props.isHighlighted ? hoveredFieldStyle : '')};
|
||||
`;
|
||||
|
|
|
@ -18,6 +18,7 @@ interface LogTextStreamItemMessageFieldProps {
|
|||
isHovered: boolean;
|
||||
isWrapped: boolean;
|
||||
scale: TextScale;
|
||||
isHighlighted: boolean;
|
||||
}
|
||||
|
||||
export class LogTextStreamItemMessageField extends React.PureComponent<
|
||||
|
@ -25,7 +26,7 @@ export class LogTextStreamItemMessageField extends React.PureComponent<
|
|||
{}
|
||||
> {
|
||||
public render() {
|
||||
const { children, highlights, isHovered, isWrapped, scale } = this.props;
|
||||
const { children, highlights, isHovered, isHighlighted, isWrapped, scale } = this.props;
|
||||
|
||||
const hasHighlights = highlights.length > 0;
|
||||
const content = hasHighlights ? renderHighlightFragments(children, highlights) : children;
|
||||
|
@ -33,6 +34,7 @@ export class LogTextStreamItemMessageField extends React.PureComponent<
|
|||
<LogTextStreamItemMessageFieldWrapper
|
||||
hasHighlights={hasHighlights}
|
||||
isHovered={isHovered}
|
||||
isHighlighted={isHighlighted}
|
||||
isWrapped={isWrapped}
|
||||
scale={scale}
|
||||
>
|
||||
|
@ -97,6 +99,7 @@ const unwrappedFieldStyle = css`
|
|||
const LogTextStreamItemMessageFieldWrapper = LogTextStreamItemField.extend.attrs<{
|
||||
hasHighlights: boolean;
|
||||
isHovered: boolean;
|
||||
isHighlighted: boolean;
|
||||
isWrapped?: boolean;
|
||||
}>({})`
|
||||
flex-grow: 1;
|
||||
|
@ -104,7 +107,7 @@ const LogTextStreamItemMessageFieldWrapper = LogTextStreamItemField.extend.attrs
|
|||
background-color: ${props => props.theme.eui.euiColorEmptyShade};
|
||||
|
||||
${props => (props.hasHighlights ? highlightedFieldStyle : '')};
|
||||
${props => (props.isHovered ? hoveredFieldStyle : '')};
|
||||
${props => (props.isHovered || props.isHighlighted ? hoveredFieldStyle : '')};
|
||||
${props => (props.isWrapped ? wrappedFieldStyle : unwrappedFieldStyle)};
|
||||
`;
|
||||
|
||||
|
|
|
@ -15,10 +15,11 @@ interface StreamItemProps {
|
|||
scale: TextScale;
|
||||
wrap: boolean;
|
||||
openFlyoutWithItem: (id: string) => void;
|
||||
isHighlighted: boolean;
|
||||
}
|
||||
|
||||
export const LogTextStreamItemView = React.forwardRef<Element, StreamItemProps>(
|
||||
({ item, scale, wrap, openFlyoutWithItem }, ref) => {
|
||||
({ item, scale, wrap, openFlyoutWithItem, isHighlighted }, ref) => {
|
||||
switch (item.kind) {
|
||||
case 'logEntry':
|
||||
return (
|
||||
|
@ -29,6 +30,7 @@ export const LogTextStreamItemView = React.forwardRef<Element, StreamItemProps>(
|
|||
scale={scale}
|
||||
wrap={wrap}
|
||||
openFlyoutWithItem={openFlyoutWithItem}
|
||||
isHighlighted={isHighlighted}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ interface LogTextStreamLogEntryItemViewProps {
|
|||
wrap: boolean;
|
||||
openFlyoutWithItem: (id: string) => void;
|
||||
intl: InjectedIntl;
|
||||
isHighlighted: boolean;
|
||||
}
|
||||
|
||||
interface LogTextStreamLogEntryItemViewState {
|
||||
|
@ -57,7 +58,15 @@ export const LogTextStreamLogEntryItemView = injectI18n(
|
|||
};
|
||||
|
||||
public render() {
|
||||
const { intl, boundingBoxRef, logEntry, scale, searchResult, wrap } = this.props;
|
||||
const {
|
||||
intl,
|
||||
boundingBoxRef,
|
||||
logEntry,
|
||||
scale,
|
||||
searchResult,
|
||||
wrap,
|
||||
isHighlighted,
|
||||
} = this.props;
|
||||
const { isHovered } = this.state;
|
||||
const viewDetailsLabel = intl.formatMessage({
|
||||
id: 'xpack.infra.logEntryItemView.viewDetailsToolTip',
|
||||
|
@ -76,11 +85,12 @@ export const LogTextStreamLogEntryItemView = injectI18n(
|
|||
<LogTextStreamItemDateField
|
||||
hasHighlights={!!searchResult}
|
||||
isHovered={isHovered}
|
||||
isHighlighted={isHighlighted}
|
||||
scale={scale}
|
||||
>
|
||||
<FormattedTime time={logEntry.fields.time} />
|
||||
</LogTextStreamItemDateField>
|
||||
<LogTextStreamIconDiv isHovered={isHovered}>
|
||||
<LogTextStreamIconDiv isHovered={isHovered} isHighlighted={isHighlighted}>
|
||||
{isHovered ? (
|
||||
<EuiToolTip content={viewDetailsLabel}>
|
||||
<EuiButtonIcon
|
||||
|
@ -96,6 +106,7 @@ export const LogTextStreamLogEntryItemView = injectI18n(
|
|||
<LogTextStreamItemMessageField
|
||||
highlights={searchResult ? searchResult.matches.message || [] : []}
|
||||
isHovered={isHovered}
|
||||
isHighlighted={isHighlighted}
|
||||
isWrapped={wrap}
|
||||
scale={scale}
|
||||
>
|
||||
|
@ -109,6 +120,7 @@ export const LogTextStreamLogEntryItemView = injectI18n(
|
|||
|
||||
interface IconProps {
|
||||
isHovered: boolean;
|
||||
isHighlighted: boolean;
|
||||
}
|
||||
|
||||
const EmptyIcon = euiStyled.div`
|
||||
|
@ -118,7 +130,7 @@ const EmptyIcon = euiStyled.div`
|
|||
const LogTextStreamIconDiv = euiStyled<IconProps, 'div'>('div')`
|
||||
flex-grow: 0;
|
||||
background-color: ${props =>
|
||||
props.isHovered
|
||||
props.isHovered || props.isHighlighted
|
||||
? props.theme.darkMode
|
||||
? transparentize(0.9, darken(0.05, props.theme.eui.euiColorHighlight))
|
||||
: darken(0.05, props.theme.eui.euiColorHighlight)
|
||||
|
|
|
@ -43,8 +43,9 @@ interface ScrollableLogTextStreamViewProps {
|
|||
) => any;
|
||||
loadNewerItems: () => void;
|
||||
setFlyoutItem: (id: string) => void;
|
||||
showFlyout: () => void;
|
||||
setFlyoutVisibility: (visible: boolean) => void;
|
||||
intl: InjectedIntl;
|
||||
highlightedItem: string | null;
|
||||
}
|
||||
|
||||
interface ScrollableLogTextStreamViewState {
|
||||
|
@ -102,6 +103,7 @@ class ScrollableLogTextStreamViewClass extends React.PureComponent<
|
|||
isStreaming,
|
||||
lastLoadedTime,
|
||||
intl,
|
||||
highlightedItem,
|
||||
} = this.props;
|
||||
const { targetId } = this.state;
|
||||
const hasItems = items.length > 0;
|
||||
|
@ -169,6 +171,9 @@ class ScrollableLogTextStreamViewClass extends React.PureComponent<
|
|||
item={item}
|
||||
scale={scale}
|
||||
wrap={wrap}
|
||||
isHighlighted={
|
||||
highlightedItem ? item.logEntry.gid === highlightedItem : false
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</MeasurableItemView>
|
||||
|
@ -190,7 +195,7 @@ class ScrollableLogTextStreamViewClass extends React.PureComponent<
|
|||
|
||||
private handleOpenFlyout = (id: string) => {
|
||||
this.props.setFlyoutItem(id);
|
||||
this.props.showFlyout();
|
||||
this.props.setFlyoutVisibility(true);
|
||||
};
|
||||
|
||||
private handleReload = () => {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import gql from 'graphql-tag';
|
||||
import { sharedFragments } from '../../../common/graphql/shared';
|
||||
|
||||
export const flyoutItemQuery = gql`
|
||||
query FlyoutItemQuery($sourceId: ID!, $itemId: ID!) {
|
||||
|
@ -13,6 +14,9 @@ export const flyoutItemQuery = gql`
|
|||
logItem(id: $itemId) {
|
||||
id
|
||||
index
|
||||
key {
|
||||
...InfraTimeKeyFields
|
||||
}
|
||||
fields {
|
||||
field
|
||||
value
|
||||
|
@ -20,4 +24,6 @@ export const flyoutItemQuery = gql`
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
${sharedFragments.InfraTimeKey}
|
||||
`;
|
||||
|
|
171
x-pack/plugins/infra/public/containers/logs/log_flyout.tsx
Normal file
171
x-pack/plugins/infra/public/containers/logs/log_flyout.tsx
Normal file
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* 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 createContainer from 'constate-latest';
|
||||
import { isString } from 'lodash';
|
||||
import React, { useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { FlyoutItemQuery, InfraLogItem } from '../../graphql/types';
|
||||
import { useApolloClient } from '../../utils/apollo_context';
|
||||
import { UrlStateContainer } from '../../utils/url_state';
|
||||
import { useTrackedPromise } from '../../utils/use_tracked_promise';
|
||||
import { flyoutItemQuery } from './flyout_item.gql_query';
|
||||
|
||||
export enum FlyoutVisibility {
|
||||
hidden = 'hidden',
|
||||
visible = 'visible',
|
||||
}
|
||||
|
||||
interface FlyoutOptionsUrlState {
|
||||
flyoutId?: string | null;
|
||||
flyoutVisibility?: string | null;
|
||||
surroundingLogsId?: string | null;
|
||||
}
|
||||
|
||||
export const useLogFlyout = ({ sourceId }: { sourceId: string }) => {
|
||||
const [flyoutVisible, setFlyoutVisibility] = useState<boolean>(false);
|
||||
const [flyoutId, setFlyoutId] = useState<string | null>(null);
|
||||
const [flyoutItem, setFlyoutItem] = useState<InfraLogItem | null>(null);
|
||||
const [surroundingLogsId, setSurroundingLogsId] = useState<string | null>(null);
|
||||
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const [loadFlyoutItemRequest, loadFlyoutItem] = useTrackedPromise(
|
||||
{
|
||||
cancelPreviousOn: 'creation',
|
||||
createPromise: async () => {
|
||||
if (!apolloClient) {
|
||||
throw new Error('Failed to load flyout item: No apollo client available.');
|
||||
}
|
||||
|
||||
if (!flyoutId) {
|
||||
return;
|
||||
}
|
||||
|
||||
return await apolloClient.query<FlyoutItemQuery.Query, FlyoutItemQuery.Variables>({
|
||||
fetchPolicy: 'no-cache',
|
||||
query: flyoutItemQuery,
|
||||
variables: {
|
||||
itemId: flyoutId,
|
||||
sourceId,
|
||||
},
|
||||
});
|
||||
},
|
||||
onResolve: response => {
|
||||
if (response) {
|
||||
const { data } = response;
|
||||
setFlyoutItem((data && data.source && data.source.logItem) || null);
|
||||
}
|
||||
},
|
||||
},
|
||||
[apolloClient, sourceId, flyoutId]
|
||||
);
|
||||
|
||||
const isLoading = useMemo(
|
||||
() => {
|
||||
return loadFlyoutItemRequest.state === 'pending';
|
||||
},
|
||||
[loadFlyoutItemRequest.state]
|
||||
);
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
if (flyoutId) {
|
||||
loadFlyoutItem();
|
||||
}
|
||||
},
|
||||
[loadFlyoutItem, flyoutId]
|
||||
);
|
||||
|
||||
return {
|
||||
flyoutVisible,
|
||||
setFlyoutVisibility,
|
||||
flyoutId,
|
||||
setFlyoutId,
|
||||
surroundingLogsId,
|
||||
setSurroundingLogsId,
|
||||
isLoading,
|
||||
flyoutItem,
|
||||
};
|
||||
};
|
||||
|
||||
export const LogFlyout = createContainer(useLogFlyout);
|
||||
|
||||
export const WithFlyoutOptionsUrlState = () => {
|
||||
const {
|
||||
flyoutVisible,
|
||||
setFlyoutVisibility,
|
||||
flyoutId,
|
||||
setFlyoutId,
|
||||
surroundingLogsId,
|
||||
setSurroundingLogsId,
|
||||
} = useContext(LogFlyout.Context);
|
||||
|
||||
return (
|
||||
<UrlStateContainer
|
||||
urlState={{
|
||||
flyoutVisibility: flyoutVisible ? FlyoutVisibility.visible : FlyoutVisibility.hidden,
|
||||
flyoutId,
|
||||
surroundingLogsId,
|
||||
}}
|
||||
urlStateKey="flyoutOptions"
|
||||
mapToUrlState={mapToUrlState}
|
||||
onChange={newUrlState => {
|
||||
if (newUrlState && newUrlState.flyoutId) {
|
||||
setFlyoutId(newUrlState.flyoutId);
|
||||
}
|
||||
if (newUrlState && newUrlState.surroundingLogsId) {
|
||||
setSurroundingLogsId(newUrlState.surroundingLogsId);
|
||||
}
|
||||
if (newUrlState && newUrlState.flyoutVisibility === FlyoutVisibility.visible) {
|
||||
setFlyoutVisibility(true);
|
||||
}
|
||||
if (newUrlState && newUrlState.flyoutVisibility === FlyoutVisibility.hidden) {
|
||||
setFlyoutVisibility(false);
|
||||
}
|
||||
}}
|
||||
onInitialize={initialUrlState => {
|
||||
if (initialUrlState && initialUrlState.flyoutId) {
|
||||
setFlyoutId(initialUrlState.flyoutId);
|
||||
}
|
||||
if (initialUrlState && initialUrlState.surroundingLogsId) {
|
||||
setSurroundingLogsId(initialUrlState.surroundingLogsId);
|
||||
}
|
||||
if (initialUrlState && initialUrlState.flyoutVisibility === FlyoutVisibility.visible) {
|
||||
setFlyoutVisibility(true);
|
||||
}
|
||||
if (initialUrlState && initialUrlState.flyoutVisibility === FlyoutVisibility.hidden) {
|
||||
setFlyoutVisibility(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const mapToUrlState = (value: any): FlyoutOptionsUrlState | undefined =>
|
||||
value
|
||||
? {
|
||||
flyoutId: mapToFlyoutIdState(value.flyoutId),
|
||||
flyoutVisibility: mapToFlyoutVisibilityState(value.flyoutVisibility),
|
||||
surroundingLogsId: mapToSurroundingLogsIdState(value.surroundingLogsId),
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const mapToFlyoutIdState = (subject: any) => {
|
||||
return subject && isString(subject) ? subject : undefined;
|
||||
};
|
||||
const mapToSurroundingLogsIdState = (subject: any) => {
|
||||
return subject && isString(subject) ? subject : undefined;
|
||||
};
|
||||
const mapToFlyoutVisibilityState = (subject: any) => {
|
||||
if (subject) {
|
||||
if (subject === 'visible') {
|
||||
return FlyoutVisibility.visible;
|
||||
}
|
||||
if (subject === 'hidden') {
|
||||
return FlyoutVisibility.hidden;
|
||||
}
|
||||
}
|
||||
};
|
|
@ -1,56 +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 from 'react';
|
||||
import { Query } from 'react-apollo';
|
||||
import { FlyoutItemQuery, InfraLogItem } from '../../graphql/types';
|
||||
import { FlyoutVisibility } from '../../store/local/log_flyout';
|
||||
import { flyoutItemQuery } from './flyout_item.gql_query';
|
||||
import { WithFlyoutOptions } from './with_log_flyout_options';
|
||||
|
||||
interface WithFlyoutArgs {
|
||||
flyoutItem: InfraLogItem | null;
|
||||
setFlyoutItem: (id: string) => void;
|
||||
showFlyout: () => void;
|
||||
hideFlyout: () => void;
|
||||
error?: string | undefined;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
interface WithFlyoutProps {
|
||||
children: (args: WithFlyoutArgs) => React.ReactNode;
|
||||
sourceId: string;
|
||||
}
|
||||
|
||||
export const WithLogFlyout = ({ children, sourceId }: WithFlyoutProps) => {
|
||||
return (
|
||||
<WithFlyoutOptions>
|
||||
{({ showFlyout, hideFlyout, setFlyoutItem, flyoutId, flyoutVisibility }) =>
|
||||
flyoutVisibility === FlyoutVisibility.visible ? (
|
||||
<Query<FlyoutItemQuery.Query, FlyoutItemQuery.Variables>
|
||||
query={flyoutItemQuery}
|
||||
fetchPolicy="no-cache"
|
||||
variables={{
|
||||
itemId: (flyoutId != null && flyoutId) || '',
|
||||
sourceId,
|
||||
}}
|
||||
>
|
||||
{({ data, error, loading }) => {
|
||||
return children({
|
||||
showFlyout,
|
||||
hideFlyout,
|
||||
setFlyoutItem,
|
||||
flyoutItem: (data && data.source && data.source.logItem) || null,
|
||||
error: error && error.message,
|
||||
loading,
|
||||
});
|
||||
}}
|
||||
</Query>
|
||||
) : null
|
||||
}
|
||||
</WithFlyoutOptions>
|
||||
);
|
||||
};
|
|
@ -1,105 +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 from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import { isString } from 'lodash';
|
||||
import { flyoutOptionsActions, flyoutOptionsSelectors, State } from '../../store';
|
||||
import { FlyoutVisibility } from '../../store/local/log_flyout';
|
||||
import { asChildFunctionRenderer } from '../../utils/typed_react';
|
||||
import { bindPlainActionCreators } from '../../utils/typed_redux';
|
||||
import { UrlStateContainer } from '../../utils/url_state';
|
||||
|
||||
const selectOptionsUrlState = createSelector(
|
||||
flyoutOptionsSelectors.selectFlyoutId,
|
||||
flyoutOptionsSelectors.selectFlyoutVisibility,
|
||||
(flyoutId, flyoutVisibility) => ({
|
||||
flyoutVisibility,
|
||||
flyoutId,
|
||||
})
|
||||
);
|
||||
|
||||
export const withFlyoutOptions = connect(
|
||||
(state: State) => ({
|
||||
flyoutVisibility: flyoutOptionsSelectors.selectFlyoutVisibility(state),
|
||||
flyoutId: flyoutOptionsSelectors.selectFlyoutId(state),
|
||||
urlState: selectOptionsUrlState(state),
|
||||
}),
|
||||
bindPlainActionCreators({
|
||||
setFlyoutItem: flyoutOptionsActions.setFlyoutItem,
|
||||
showFlyout: flyoutOptionsActions.showFlyout,
|
||||
hideFlyout: flyoutOptionsActions.hideFlyout,
|
||||
})
|
||||
);
|
||||
|
||||
export const WithFlyoutOptions = asChildFunctionRenderer(withFlyoutOptions);
|
||||
|
||||
/**
|
||||
* Url State
|
||||
*/
|
||||
|
||||
interface FlyoutOptionsUrlState {
|
||||
flyoutId?: ReturnType<typeof flyoutOptionsSelectors.selectFlyoutId>;
|
||||
flyoutVisibility?: ReturnType<typeof flyoutOptionsSelectors.selectFlyoutVisibility>;
|
||||
}
|
||||
|
||||
export const WithFlyoutOptionsUrlState = () => (
|
||||
<WithFlyoutOptions>
|
||||
{({ setFlyoutItem, showFlyout, hideFlyout, urlState }) => (
|
||||
<UrlStateContainer
|
||||
urlState={urlState}
|
||||
urlStateKey="flyoutOptions"
|
||||
mapToUrlState={mapToUrlState}
|
||||
onChange={newUrlState => {
|
||||
if (newUrlState && newUrlState.flyoutId) {
|
||||
setFlyoutItem(newUrlState.flyoutId);
|
||||
}
|
||||
if (newUrlState && newUrlState.flyoutVisibility === FlyoutVisibility.visible) {
|
||||
showFlyout();
|
||||
}
|
||||
if (newUrlState && newUrlState.flyoutVisibility === FlyoutVisibility.hidden) {
|
||||
hideFlyout();
|
||||
}
|
||||
}}
|
||||
onInitialize={initialUrlState => {
|
||||
if (initialUrlState && initialUrlState.flyoutId) {
|
||||
setFlyoutItem(initialUrlState.flyoutId);
|
||||
}
|
||||
if (initialUrlState && initialUrlState.flyoutVisibility === FlyoutVisibility.visible) {
|
||||
showFlyout();
|
||||
}
|
||||
if (initialUrlState && initialUrlState.flyoutVisibility === FlyoutVisibility.hidden) {
|
||||
hideFlyout();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</WithFlyoutOptions>
|
||||
);
|
||||
|
||||
const mapToUrlState = (value: any): FlyoutOptionsUrlState | undefined =>
|
||||
value
|
||||
? {
|
||||
flyoutId: mapToFlyoutIdState(value.flyoutId),
|
||||
flyoutVisibility: mapToFlyoutVisibilityState(value.flyoutVisibility),
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const mapToFlyoutIdState = (subject: any) => {
|
||||
return subject && isString(subject) ? subject : undefined;
|
||||
};
|
||||
const mapToFlyoutVisibilityState = (subject: any) => {
|
||||
if (subject) {
|
||||
if (subject === 'visible') {
|
||||
return FlyoutVisibility.visible;
|
||||
}
|
||||
if (subject === 'hidden') {
|
||||
return FlyoutVisibility.hidden;
|
||||
}
|
||||
}
|
||||
};
|
|
@ -1382,6 +1382,18 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "key",
|
||||
"description": "Time key for the document - derived from the source configuration timestamp and tiebreaker settings",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": { "kind": "OBJECT", "name": "InfraTimeKey", "ofType": null }
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "fields",
|
||||
"description": "An array of flattened fields and values",
|
||||
|
|
|
@ -186,6 +186,8 @@ export interface InfraLogItem {
|
|||
id: string;
|
||||
/** The index where the document was found */
|
||||
index: string;
|
||||
/** Time key for the document - derived from the source configuration timestamp and tiebreaker settings */
|
||||
key: InfraTimeKey;
|
||||
/** An array of flattened fields and values */
|
||||
fields: InfraLogItemField[];
|
||||
}
|
||||
|
@ -551,9 +553,19 @@ export namespace FlyoutItemQuery {
|
|||
|
||||
index: string;
|
||||
|
||||
key: Key;
|
||||
|
||||
fields: Fields[];
|
||||
};
|
||||
|
||||
export type Key = {
|
||||
__typename?: 'InfraTimeKey';
|
||||
|
||||
time: number;
|
||||
|
||||
tiebreaker: number;
|
||||
};
|
||||
|
||||
export type Fields = {
|
||||
__typename?: 'InfraLogItemField';
|
||||
|
||||
|
|
|
@ -16,9 +16,10 @@ import { PageContent } from '../../components/page';
|
|||
import { WithSummary } from '../../containers/logs/log_summary';
|
||||
import { LogViewConfiguration } from '../../containers/logs/log_view_configuration';
|
||||
import { WithLogFilter, WithLogFilterUrlState } from '../../containers/logs/with_log_filter';
|
||||
import { WithLogFlyout } from '../../containers/logs/with_log_flyout';
|
||||
import { WithFlyoutOptionsUrlState } from '../../containers/logs/with_log_flyout_options';
|
||||
import { WithFlyoutOptions } from '../../containers/logs/with_log_flyout_options';
|
||||
import {
|
||||
LogFlyout as LogFlyoutState,
|
||||
WithFlyoutOptionsUrlState,
|
||||
} from '../../containers/logs/log_flyout';
|
||||
import { WithLogMinimapUrlState } from '../../containers/logs/with_log_minimap';
|
||||
import { WithLogPositionUrlState } from '../../containers/logs/with_log_position';
|
||||
import { WithLogPosition } from '../../containers/logs/with_log_position';
|
||||
|
@ -29,8 +30,17 @@ import { Source } from '../../containers/source';
|
|||
import { LogsToolbar } from './page_toolbar';
|
||||
|
||||
export const LogsPageLogsContent: React.FunctionComponent = () => {
|
||||
const { derivedIndexPattern, sourceId } = useContext(Source.Context);
|
||||
const { derivedIndexPattern } = useContext(Source.Context);
|
||||
const { intervalSize, textScale, textWrap } = useContext(LogViewConfiguration.Context);
|
||||
const {
|
||||
setFlyoutVisibility,
|
||||
flyoutVisible,
|
||||
setFlyoutId,
|
||||
surroundingLogsId,
|
||||
setSurroundingLogsId,
|
||||
flyoutItem,
|
||||
isLoading,
|
||||
} = useContext(LogFlyoutState.Context);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -40,22 +50,29 @@ export const LogsPageLogsContent: React.FunctionComponent = () => {
|
|||
<WithLogTextviewUrlState />
|
||||
<WithFlyoutOptionsUrlState />
|
||||
<LogsToolbar />
|
||||
<WithLogFilter indexPattern={derivedIndexPattern}>
|
||||
{({ applyFilterQueryFromKueryExpression }) => (
|
||||
<WithLogFlyout sourceId={sourceId}>
|
||||
{({ flyoutItem, hideFlyout, loading }) => (
|
||||
<LogFlyout
|
||||
setFilter={applyFilterQueryFromKueryExpression}
|
||||
flyoutItem={flyoutItem}
|
||||
hideFlyout={hideFlyout}
|
||||
loading={loading}
|
||||
/>
|
||||
)}
|
||||
</WithLogFlyout>
|
||||
<WithLogPosition>
|
||||
{({ jumpToTargetPosition, stopLiveStreaming }) => (
|
||||
<WithLogFilter indexPattern={derivedIndexPattern}>
|
||||
{({ applyFilterQueryFromKueryExpression }) =>
|
||||
flyoutVisible ? (
|
||||
<LogFlyout
|
||||
setFilter={applyFilterQueryFromKueryExpression}
|
||||
setTarget={(timeKey, flyoutItemId) => {
|
||||
jumpToTargetPosition(timeKey);
|
||||
setSurroundingLogsId(flyoutItemId);
|
||||
stopLiveStreaming();
|
||||
}}
|
||||
setFlyoutVisibility={setFlyoutVisibility}
|
||||
flyoutItem={flyoutItem}
|
||||
loading={isLoading}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
</WithLogFilter>
|
||||
)}
|
||||
</WithLogFilter>
|
||||
<WithFlyoutOptions>
|
||||
{({ showFlyout, setFlyoutItem }) => (
|
||||
</WithLogPosition>
|
||||
<WithLogFilter indexPattern={derivedIndexPattern}>
|
||||
{({ filterQuery }) => (
|
||||
<PageContent>
|
||||
<AutoSizer content>
|
||||
{({ measureRef, content: { width = 0, height = 0 } }) => (
|
||||
|
@ -93,8 +110,9 @@ export const LogsPageLogsContent: React.FunctionComponent = () => {
|
|||
target={targetPosition}
|
||||
width={width}
|
||||
wrap={textWrap}
|
||||
setFlyoutItem={setFlyoutItem}
|
||||
showFlyout={showFlyout}
|
||||
setFlyoutItem={setFlyoutId}
|
||||
setFlyoutVisibility={setFlyoutVisibility}
|
||||
highlightedItem={surroundingLogsId ? surroundingLogsId : null}
|
||||
/>
|
||||
)}
|
||||
</WithStreamItems>
|
||||
|
@ -130,7 +148,7 @@ export const LogsPageLogsContent: React.FunctionComponent = () => {
|
|||
</AutoSizer>
|
||||
</PageContent>
|
||||
)}
|
||||
</WithFlyoutOptions>
|
||||
</WithLogFilter>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -7,13 +7,16 @@
|
|||
import React from 'react';
|
||||
|
||||
import { SourceConfigurationFlyoutState } from '../../components/source_configuration';
|
||||
import { LogFlyout } from '../../containers/logs/log_flyout';
|
||||
import { LogViewConfiguration } from '../../containers/logs/log_view_configuration';
|
||||
import { Source } from '../../containers/source';
|
||||
|
||||
export const LogsPageProviders: React.FunctionComponent = ({ children }) => (
|
||||
<Source.Provider sourceId="default">
|
||||
<SourceConfigurationFlyoutState.Provider>
|
||||
<LogViewConfiguration.Provider>{children}</LogViewConfiguration.Provider>
|
||||
<LogViewConfiguration.Provider>
|
||||
<LogFlyout.Provider sourceId="default">{children}</LogFlyout.Provider>
|
||||
</LogViewConfiguration.Provider>
|
||||
</SourceConfigurationFlyoutState.Provider>
|
||||
</Source.Provider>
|
||||
);
|
||||
|
|
|
@ -16,6 +16,7 @@ import { LogTextScaleControls } from '../../components/logging/log_text_scale_co
|
|||
import { LogTextWrapControls } from '../../components/logging/log_text_wrap_controls';
|
||||
import { LogTimeControls } from '../../components/logging/log_time_controls';
|
||||
import { SourceConfigurationButton } from '../../components/source_configuration';
|
||||
import { LogFlyout } from '../../containers/logs/log_flyout';
|
||||
import { LogViewConfiguration } from '../../containers/logs/log_view_configuration';
|
||||
import { WithLogFilter } from '../../containers/logs/with_log_filter';
|
||||
import { WithLogPosition } from '../../containers/logs/with_log_position';
|
||||
|
@ -35,6 +36,7 @@ export const LogsToolbar = injectI18n(({ intl }) => {
|
|||
textWrap,
|
||||
} = useContext(LogViewConfiguration.Context);
|
||||
|
||||
const { setSurroundingLogsId } = useContext(LogFlyout.Context);
|
||||
return (
|
||||
<Toolbar>
|
||||
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween" gutterSize="s">
|
||||
|
@ -52,8 +54,14 @@ export const LogsToolbar = injectI18n(({ intl }) => {
|
|||
isLoadingSuggestions={isLoadingSuggestions}
|
||||
isValid={isFilterQueryDraftValid}
|
||||
loadSuggestions={loadSuggestions}
|
||||
onChange={setFilterQueryDraftFromKueryExpression}
|
||||
onSubmit={applyFilterQueryFromKueryExpression}
|
||||
onChange={(expression: string) => {
|
||||
setSurroundingLogsId(null);
|
||||
setFilterQueryDraftFromKueryExpression(expression);
|
||||
}}
|
||||
onSubmit={(expression: string) => {
|
||||
setSurroundingLogsId(null);
|
||||
applyFilterQueryFromKueryExpression(expression);
|
||||
}}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'xpack.infra.logsPage.toolbar.kqlSearchFieldPlaceholder',
|
||||
defaultMessage: 'Search for log entries… (e.g. host.name:host-1)',
|
||||
|
@ -97,7 +105,10 @@ export const LogsToolbar = injectI18n(({ intl }) => {
|
|||
currentTime={visibleMidpointTime}
|
||||
isLiveStreaming={isAutoReloading}
|
||||
jumpToTime={jumpToTargetPositionTime}
|
||||
startLiveStreaming={startLiveStreaming}
|
||||
startLiveStreaming={interval => {
|
||||
startLiveStreaming(interval);
|
||||
setSurroundingLogsId(null);
|
||||
}}
|
||||
stopLiveStreaming={stopLiveStreaming}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -10,6 +10,5 @@ export {
|
|||
waffleFilterActions,
|
||||
waffleTimeActions,
|
||||
waffleOptionsActions,
|
||||
flyoutOptionsActions,
|
||||
} from './local';
|
||||
export { logEntriesActions } from './remote';
|
||||
|
|
|
@ -9,4 +9,3 @@ export { logPositionActions } from './log_position';
|
|||
export { waffleFilterActions } from './waffle_filter';
|
||||
export { waffleTimeActions } from './waffle_time';
|
||||
export { waffleOptionsActions } from './waffle_options';
|
||||
export { flyoutOptionsActions } from './log_flyout';
|
||||
|
|
|
@ -1,11 +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 actionCreatorFactory from 'typescript-fsa';
|
||||
const actionCreator = actionCreatorFactory('x-pack/infra/local/log_flyout');
|
||||
export const setFlyoutItem = actionCreator<string>('SET_FLYOUT_ITEM');
|
||||
export const showFlyout = actionCreator('SHOW_FLYOUT');
|
||||
export const hideFlyout = actionCreator('HIDE_FLYOUT');
|
|
@ -1,11 +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 * as flyoutOptionsActions from './actions';
|
||||
import * as flyoutOptionsSelectors from './selector';
|
||||
|
||||
export { flyoutOptionsActions, flyoutOptionsSelectors };
|
||||
export * from './reducer';
|
|
@ -1,39 +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 { combineReducers } from 'redux';
|
||||
import { reducerWithInitialState } from 'typescript-fsa-reducers';
|
||||
|
||||
import { hideFlyout, setFlyoutItem, showFlyout } from './actions';
|
||||
|
||||
export enum FlyoutVisibility {
|
||||
hidden = 'hidden',
|
||||
visible = 'visible',
|
||||
}
|
||||
|
||||
export interface FlyoutOptionsState {
|
||||
visibility: FlyoutVisibility;
|
||||
itemId: string;
|
||||
}
|
||||
|
||||
export const initialFlyoutOptionsState: FlyoutOptionsState = {
|
||||
visibility: FlyoutVisibility.hidden,
|
||||
itemId: '',
|
||||
};
|
||||
|
||||
const currentFlyoutReducer = reducerWithInitialState(initialFlyoutOptionsState.itemId).case(
|
||||
setFlyoutItem,
|
||||
(current, target) => target
|
||||
);
|
||||
|
||||
const currentFlyoutVisibilityReducer = reducerWithInitialState(initialFlyoutOptionsState.visibility)
|
||||
.case(hideFlyout, () => FlyoutVisibility.hidden)
|
||||
.case(showFlyout, () => FlyoutVisibility.visible);
|
||||
|
||||
export const flyoutOptionsReducer = combineReducers<FlyoutOptionsState>({
|
||||
itemId: currentFlyoutReducer,
|
||||
visibility: currentFlyoutVisibilityReducer,
|
||||
});
|
|
@ -1,10 +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 { FlyoutOptionsState } from './reducer';
|
||||
|
||||
export const selectFlyoutId = (state: FlyoutOptionsState) => state.itemId;
|
||||
export const selectFlyoutVisibility = (state: FlyoutOptionsState) => state.visibility;
|
|
@ -7,7 +7,6 @@
|
|||
import { combineReducers } from 'redux';
|
||||
|
||||
import { initialLogFilterState, logFilterReducer, LogFilterState } from './log_filter';
|
||||
import { flyoutOptionsReducer, FlyoutOptionsState, initialFlyoutOptionsState } from './log_flyout';
|
||||
import { initialLogPositionState, logPositionReducer, LogPositionState } from './log_position';
|
||||
import { initialWaffleFilterState, waffleFilterReducer, WaffleFilterState } from './waffle_filter';
|
||||
import {
|
||||
|
@ -23,7 +22,6 @@ export interface LocalState {
|
|||
waffleFilter: WaffleFilterState;
|
||||
waffleTime: WaffleTimeState;
|
||||
waffleMetrics: WaffleOptionsState;
|
||||
logFlyout: FlyoutOptionsState;
|
||||
}
|
||||
|
||||
export const initialLocalState: LocalState = {
|
||||
|
@ -32,7 +30,6 @@ export const initialLocalState: LocalState = {
|
|||
waffleFilter: initialWaffleFilterState,
|
||||
waffleTime: initialWaffleTimeState,
|
||||
waffleMetrics: initialWaffleOptionsState,
|
||||
logFlyout: initialFlyoutOptionsState,
|
||||
};
|
||||
|
||||
export const localReducer = combineReducers<LocalState>({
|
||||
|
@ -41,5 +38,4 @@ export const localReducer = combineReducers<LocalState>({
|
|||
waffleFilter: waffleFilterReducer,
|
||||
waffleTime: waffleTimeReducer,
|
||||
waffleMetrics: waffleOptionsReducer,
|
||||
logFlyout: flyoutOptionsReducer,
|
||||
});
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
import { globalizeSelectors } from '../../utils/typed_redux';
|
||||
import { logFilterSelectors as innerLogFilterSelectors } from './log_filter';
|
||||
import { flyoutOptionsSelectors as innerFlyoutOptionsSelectors } from './log_flyout';
|
||||
import { logPositionSelectors as innerLogPositionSelectors } from './log_position';
|
||||
import { LocalState } from './reducer';
|
||||
import { waffleFilterSelectors as innerWaffleFilterSelectors } from './waffle_filter';
|
||||
|
@ -37,8 +36,3 @@ export const waffleOptionsSelectors = globalizeSelectors(
|
|||
(state: LocalState) => state.waffleMetrics,
|
||||
innerWaffleOptionsSelectors
|
||||
);
|
||||
|
||||
export const flyoutOptionsSelectors = globalizeSelectors(
|
||||
(state: LocalState) => state.logFlyout,
|
||||
innerFlyoutOptionsSelectors
|
||||
);
|
||||
|
|
|
@ -9,7 +9,6 @@ import { createSelector } from 'reselect';
|
|||
import { getLogEntryAtTime } from '../utils/log_entry';
|
||||
import { globalizeSelectors } from '../utils/typed_redux';
|
||||
import {
|
||||
flyoutOptionsSelectors as localFlyoutOptionsSelectors,
|
||||
logFilterSelectors as localLogFilterSelectors,
|
||||
logPositionSelectors as localLogPositionSelectors,
|
||||
waffleFilterSelectors as localWaffleFilterSelectors,
|
||||
|
@ -30,7 +29,6 @@ export const logPositionSelectors = globalizeSelectors(selectLocal, localLogPosi
|
|||
export const waffleFilterSelectors = globalizeSelectors(selectLocal, localWaffleFilterSelectors);
|
||||
export const waffleTimeSelectors = globalizeSelectors(selectLocal, localWaffleTimeSelectors);
|
||||
export const waffleOptionsSelectors = globalizeSelectors(selectLocal, localWaffleOptionsSelectors);
|
||||
export const flyoutOptionsSelectors = globalizeSelectors(selectLocal, localFlyoutOptionsSelectors);
|
||||
|
||||
/**
|
||||
* remote selectors
|
||||
|
|
|
@ -90,6 +90,8 @@ export const logEntriesSchema = gql`
|
|||
id: ID!
|
||||
"The index where the document was found"
|
||||
index: String!
|
||||
"Time key for the document - derived from the source configuration timestamp and tiebreaker settings"
|
||||
key: InfraTimeKey!
|
||||
"An array of flattened fields and values"
|
||||
fields: [InfraLogItemField!]!
|
||||
}
|
||||
|
|
|
@ -214,6 +214,8 @@ export interface InfraLogItem {
|
|||
id: string;
|
||||
/** The index where the document was found */
|
||||
index: string;
|
||||
/** Time key for the document - derived from the source configuration timestamp and tiebreaker settings */
|
||||
key: InfraTimeKey;
|
||||
/** An array of flattened fields and values */
|
||||
fields: InfraLogItemField[];
|
||||
}
|
||||
|
@ -1170,6 +1172,8 @@ export namespace InfraLogItemResolvers {
|
|||
id?: IdResolver<string, TypeParent, Context>;
|
||||
/** The index where the document was found */
|
||||
index?: IndexResolver<string, TypeParent, Context>;
|
||||
/** Time key for the document - derived from the source configuration timestamp and tiebreaker settings */
|
||||
key?: KeyResolver<InfraTimeKey, TypeParent, Context>;
|
||||
/** An array of flattened fields and values */
|
||||
fields?: FieldsResolver<InfraLogItemField[], TypeParent, Context>;
|
||||
}
|
||||
|
@ -1184,6 +1188,11 @@ export namespace InfraLogItemResolvers {
|
|||
Parent,
|
||||
Context
|
||||
>;
|
||||
export type KeyResolver<
|
||||
R = InfraTimeKey,
|
||||
Parent = InfraLogItem,
|
||||
Context = InfraContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type FieldsResolver<
|
||||
R = InfraLogItemField[],
|
||||
Parent = InfraLogItem,
|
||||
|
|
|
@ -34,6 +34,7 @@ interface LogItemHit {
|
|||
_index: string;
|
||||
_id: string;
|
||||
_source: JsonObject;
|
||||
sort: [number, number];
|
||||
}
|
||||
|
||||
export class InfraKibanaLogEntriesAdapter implements LogEntriesAdapter {
|
||||
|
@ -173,6 +174,10 @@ export class InfraKibanaLogEntriesAdapter implements LogEntriesAdapter {
|
|||
terminate_after: 1,
|
||||
body: {
|
||||
size: 1,
|
||||
sort: [
|
||||
{ [sourceConfiguration.fields.timestamp]: 'desc' },
|
||||
{ [sourceConfiguration.fields.tiebreaker]: 'desc' },
|
||||
],
|
||||
query: {
|
||||
ids: {
|
||||
values: [id],
|
||||
|
|
|
@ -135,9 +135,14 @@ export class InfraLogEntriesDomain {
|
|||
{ field: '_index', value: document._index },
|
||||
{ field: '_id', value: document._id },
|
||||
];
|
||||
|
||||
return {
|
||||
id: document._id,
|
||||
index: document._index,
|
||||
key: {
|
||||
time: document.sort[0],
|
||||
tiebreaker: document.sort[1],
|
||||
},
|
||||
fields: sortBy(
|
||||
[...defaultFields, ...convertDocumentSourceToLogItemFields(document._source)],
|
||||
'field'
|
||||
|
@ -150,6 +155,7 @@ interface LogItemHit {
|
|||
_index: string;
|
||||
_id: string;
|
||||
_source: JsonObject;
|
||||
sort: [number, number];
|
||||
}
|
||||
|
||||
export interface LogEntriesAdapter {
|
||||
|
|
Loading…
Reference in a new issue