Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Logs UI] Fetch single log entries via a search strategy #81710

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
1982fd4
add empty log entry search strategy
weltenwort Oct 27, 2020
896656c
WIP: send query to ES and parse response
weltenwort Oct 27, 2020
51c8458
Merge branch 'master' into logs-ui-add-log-entry-search-strategy
weltenwort Oct 29, 2020
ea1eb75
WIP: use the source config to extract fields
weltenwort Oct 29, 2020
9e65553
Pass sources dependency
weltenwort Oct 30, 2020
79e555a
Fix LogEntry imports
weltenwort Oct 30, 2020
a6ceba1
Merge branch 'master' into logs-ui-add-log-entry-search-strategy
weltenwort Oct 30, 2020
b48e5de
Fix source config fetching and state encoding
weltenwort Oct 30, 2020
edd4047
Add basic unit test
weltenwort Oct 30, 2020
6a07bec
Merge branch 'master' into logs-ui-add-log-entry-search-strategy
weltenwort Nov 10, 2020
7d9cc2e
Adapt to changed search strategy signature
weltenwort Nov 10, 2020
78a6bef
Add tests and fix bugs
weltenwort Nov 11, 2020
8bb0d80
Merge branch 'master' into logs-ui-add-log-entry-search-strategy
weltenwort Nov 12, 2020
2327000
Wire up client-side search strategy usage
weltenwort Nov 12, 2020
d1c7b2d
Make validation error messages more readable
weltenwort Nov 12, 2020
0a43fb5
Merge branch 'master' into logs-ui-add-log-entry-search-strategy
weltenwort Nov 16, 2020
05f8212
Merge branch 'master' into logs-ui-add-log-entry-search-strategy
weltenwort Nov 17, 2020
7dac882
Fix types and props
weltenwort Nov 17, 2020
b402407
Factor out async request runtime types
weltenwort Nov 17, 2020
99a8d64
Remove superfluous `take`
weltenwort Nov 17, 2020
737ec78
Factor out search strategy error utilities
weltenwort Nov 17, 2020
856ae62
Satisfy linter
weltenwort Nov 17, 2020
ca7681a
Reintroduce `take(1)` and ensure branches are exclusive
weltenwort Nov 17, 2020
36a8a1c
Remove old route and unused code
weltenwort Nov 17, 2020
3776445
Merge branch 'master' into logs-ui-add-log-entry-search-strategy
weltenwort Nov 18, 2020
35f8142
Merge branch 'master' into logs-ui-add-log-entry-search-strategy
weltenwort Nov 24, 2020
9cfe578
Merge branch 'master' into logs-ui-add-log-entry-search-strategy
weltenwort Nov 24, 2020
b24c3a6
Merge branch 'master' into logs-ui-add-log-entry-search-strategy
weltenwort Nov 26, 2020
c4e6e32
Replace EuiBasicTable with EuiInMemoryTable for sorting
weltenwort Nov 26, 2020
3fa56e0
Work around type error due to licensing ambient type
weltenwort Nov 26, 2020
8076833
Remove commented-out code
weltenwort Nov 26, 2020
f87b248
Rename get/submit to recovered/initial
weltenwort Nov 26, 2020
c80de2f
Merge branch 'master' into logs-ui-add-log-entry-search-strategy
weltenwort Nov 30, 2020
fb3b2b6
Merge branch 'master' into logs-ui-add-log-entry-search-strategy
weltenwort Dec 2, 2020
997fee0
Add log entry id and index to flyout header
weltenwort Dec 2, 2020
ff57b2e
Update translations
weltenwort Dec 2, 2020
2fa8a93
Merge branch 'master' into logs-ui-add-log-entry-search-strategy
weltenwort Dec 3, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions x-pack/plugins/infra/common/http_api/log_entries/entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
*/

import * as rt from 'io-ts';
import { logEntryCursorRT } from '../../log_entry';
import { jsonArrayRT } from '../../typed_json';
import { logEntriesCursorRT } from './common';
import { logSourceColumnConfigurationRT } from '../log_sources';

export const LOG_ENTRIES_PATH = '/api/log_entries/entries';
Expand All @@ -26,17 +26,17 @@ export const logEntriesBaseRequestRT = rt.intersection([

export const logEntriesBeforeRequestRT = rt.intersection([
logEntriesBaseRequestRT,
rt.type({ before: rt.union([logEntriesCursorRT, rt.literal('last')]) }),
rt.type({ before: rt.union([logEntryCursorRT, rt.literal('last')]) }),
]);

export const logEntriesAfterRequestRT = rt.intersection([
logEntriesBaseRequestRT,
rt.type({ after: rt.union([logEntriesCursorRT, rt.literal('first')]) }),
rt.type({ after: rt.union([logEntryCursorRT, rt.literal('first')]) }),
]);

export const logEntriesCenteredRequestRT = rt.intersection([
logEntriesBaseRequestRT,
rt.type({ center: logEntriesCursorRT }),
rt.type({ center: logEntryCursorRT }),
]);

export const logEntriesRequestRT = rt.union([
Expand Down Expand Up @@ -85,7 +85,7 @@ export const logEntryContextRT = rt.union([

export const logEntryRT = rt.type({
id: rt.string,
cursor: logEntriesCursorRT,
cursor: logEntryCursorRT,
columns: rt.array(logColumnRT),
context: logEntryContextRT,
});
Expand All @@ -104,8 +104,8 @@ export const logEntriesResponseRT = rt.type({
data: rt.intersection([
rt.type({
entries: rt.array(logEntryRT),
topCursor: rt.union([logEntriesCursorRT, rt.null]),
bottomCursor: rt.union([logEntriesCursorRT, rt.null]),
topCursor: rt.union([logEntryCursorRT, rt.null]),
bottomCursor: rt.union([logEntryCursorRT, rt.null]),
}),
rt.partial({
hasMoreBefore: rt.boolean,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
*/

import * as rt from 'io-ts';
import { logEntryCursorRT } from '../../log_entry';
import {
logEntriesBaseRequestRT,
logEntriesBeforeRequestRT,
logEntriesAfterRequestRT,
logEntriesCenteredRequestRT,
logEntryRT,
} from './entries';
import { logEntriesCursorRT } from './common';

export const LOG_ENTRIES_HIGHLIGHTS_PATH = '/api/log_entries/highlights';

Expand Down Expand Up @@ -58,8 +58,8 @@ export const logEntriesHighlightsResponseRT = rt.type({
entries: rt.array(logEntryRT),
}),
rt.type({
topCursor: logEntriesCursorRT,
bottomCursor: logEntriesCursorRT,
topCursor: logEntryCursorRT,
bottomCursor: logEntryCursorRT,
entries: rt.array(logEntryRT),
}),
])
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/infra/common/http_api/log_entries/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

export * from './common';
export * from './entries';
export * from './highlights';
export * from './item';
export * from './summary';
export * from './summary_highlights';
32 changes: 0 additions & 32 deletions x-pack/plugins/infra/common/http_api/log_entries/item.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
*/

import * as rt from 'io-ts';
import { logEntryCursorRT } from '../../log_entry';
import { logEntriesSummaryRequestRT, logEntriesSummaryBucketRT } from './summary';
import { logEntriesCursorRT } from './common';

export const LOG_ENTRIES_SUMMARY_HIGHLIGHTS_PATH = '/api/log_entries/summary_highlights';

Expand All @@ -24,7 +24,7 @@ export type LogEntriesSummaryHighlightsRequest = rt.TypeOf<
export const logEntriesSummaryHighlightsBucketRT = rt.intersection([
logEntriesSummaryBucketRT,
rt.type({
representativeKey: logEntriesCursorRT,
representativeKey: logEntryCursorRT,
}),
]);

Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/infra/common/log_entry/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
*/

export * from './log_entry';
export * from './log_entry_cursor';
21 changes: 21 additions & 0 deletions x-pack/plugins/infra/common/log_entry/log_entry_cursor.ts
Original file line number Diff line number Diff line change
@@ -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 * as rt from 'io-ts';
import { decodeOrThrow } from '../runtime_types';

export const logEntryCursorRT = rt.type({
Copy link
Member Author

@weltenwort weltenwort Nov 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Moving this out of the http directory to make it usable in search strategies as well.

time: rt.number,
tiebreaker: rt.number,
});

export type LogEntryCursor = rt.TypeOf<typeof logEntryCursorRT>;

export const getLogEntryCursorFromHit = (hit: { sort: [number, number] }) =>
decodeOrThrow(logEntryCursorRT)({
time: hit.sort[0],
tiebreaker: hit.sort[1],
});
35 changes: 30 additions & 5 deletions x-pack/plugins/infra/common/runtime_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,41 @@
import { fold } from 'fp-ts/lib/Either';
import { identity } from 'fp-ts/lib/function';
import { pipe } from 'fp-ts/lib/pipeable';
import { Errors, Type } from 'io-ts';
import { failure } from 'io-ts/lib/PathReporter';
import { RouteValidationFunction } from 'kibana/server';
import { Context, Errors, IntersectionType, Type, UnionType, ValidationError } from 'io-ts';
import type { RouteValidationFunction } from 'kibana/server';

type ErrorFactory = (message: string) => Error;

const getErrorPath = ([first, ...rest]: Context): string[] => {
if (typeof first === 'undefined') {
return [];
} else if (first.type instanceof IntersectionType) {
const [, ...next] = rest;
return getErrorPath(next);
} else if (first.type instanceof UnionType) {
const [, ...next] = rest;
return [first.key, ...getErrorPath(next)];
}

return [first.key, ...getErrorPath(rest)];
};

const getErrorType = ({ context }: ValidationError) =>
context[context.length - 1]?.type?.name ?? 'unknown';

const formatError = (error: ValidationError) =>
error.message ??
`in ${getErrorPath(error.context).join('/')}: ${JSON.stringify(
error.value
)} does not match expected type ${getErrorType(error)}`;

const formatErrors = (errors: ValidationError[]) =>
`Failed to validate: \n${errors.map((error) => ` ${formatError(error)}`).join('\n')}`;
Comment on lines +15 to +39
Copy link
Member Author

@weltenwort weltenwort Nov 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ This aims to improve the readability of the io-ts validation error messages.


export const createPlainError = (message: string) => new Error(message);

export const throwErrors = (createError: ErrorFactory) => (errors: Errors) => {
throw createError(failure(errors).join('\n'));
throw createError(formatErrors(errors));
};

export const decodeOrThrow = <DecodedValue, EncodedValue, InputValue>(
Expand All @@ -33,7 +58,7 @@ export const createValidationFunction = <DecodedValue, EncodedValue, InputValue>
pipe(
runtimeType.decode(inputValue),
fold<Errors, DecodedValue, ValdidationResult<DecodedValue>>(
(errors: Errors) => badRequest(failure(errors).join('\n')),
(errors: Errors) => badRequest(formatErrors(errors)),
(result: DecodedValue) => ok(result)
)
);
26 changes: 26 additions & 0 deletions x-pack/plugins/infra/common/search_strategies/common/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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 rt from 'io-ts';

const genericErrorRT = rt.type({
type: rt.literal('generic'),
message: rt.string,
});

const shardFailureErrorRT = rt.type({
type: rt.literal('shardFailure'),
shardInfo: rt.type({
shard: rt.number,
index: rt.string,
node: rt.string,
}),
message: rt.string,
});

export const searchStrategyErrorRT = rt.union([genericErrorRT, shardFailureErrorRT]);

export type SearchStrategyError = rt.TypeOf<typeof searchStrategyErrorRT>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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 rt from 'io-ts';
import { logEntryCursorRT } from '../../log_entry';
import { jsonArrayRT } from '../../typed_json';
import { searchStrategyErrorRT } from '../common/errors';

export const LOG_ENTRY_SEARCH_STRATEGY = 'infra-log-entry';

export const logEntrySearchRequestParamsRT = rt.type({
sourceId: rt.string,
logEntryId: rt.string,
});

export type LogEntrySearchRequestParams = rt.TypeOf<typeof logEntrySearchRequestParamsRT>;

const logEntryFieldRT = rt.type({
field: rt.string,
value: jsonArrayRT,
});

export type LogEntryField = rt.TypeOf<typeof logEntryFieldRT>;

export const logEntryRT = rt.type({
id: rt.string,
index: rt.string,
fields: rt.array(logEntryFieldRT),
key: logEntryCursorRT,
});

export type LogEntry = rt.TypeOf<typeof logEntryRT>;

export const logEntrySearchResponsePayloadRT = rt.intersection([
rt.type({
data: rt.union([logEntryRT, rt.null]),
}),
rt.partial({
errors: rt.array(searchStrategyErrorRT),
}),
]);

export type LogEntrySearchResponsePayload = rt.TypeOf<typeof logEntrySearchResponsePayloadRT>;
4 changes: 2 additions & 2 deletions x-pack/plugins/infra/public/components/log_stream/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import React, { useMemo, useCallback, useEffect } from 'react';
import { noop } from 'lodash';
import { euiStyled } from '../../../../observability/public';

import { LogEntriesCursor } from '../../../common/http_api';
import { LogEntryCursor } from '../../../common/log_entry';

import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
import { LogSourceConfigurationProperties, useLogSource } from '../../containers/logs/log_source';
Expand All @@ -28,7 +28,7 @@ export interface LogStreamProps {
startTimestamp: number;
endTimestamp: number;
query?: string;
center?: LogEntriesCursor;
center?: LogEntryCursor;
highlight?: string;
height?: string | number;
columns?: LogColumnDefinition[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe('LogEntryActionsMenu component', () => {
const elementWrapper = mount(
<ProviderWrapper>
<LogEntryActionsMenu
logItem={{
logEntry={{
Copy link
Member Author

@weltenwort weltenwort Nov 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Trying to avoid the unclear logItem terminology.

fields: [{ field: 'host.ip', value: ['HOST_IP'] }],
id: 'ITEM_ID',
index: 'INDEX',
Expand Down Expand Up @@ -58,7 +58,7 @@ describe('LogEntryActionsMenu component', () => {
const elementWrapper = mount(
<ProviderWrapper>
<LogEntryActionsMenu
logItem={{
logEntry={{
fields: [{ field: 'container.id', value: ['CONTAINER_ID'] }],
id: 'ITEM_ID',
index: 'INDEX',
Expand Down Expand Up @@ -88,7 +88,7 @@ describe('LogEntryActionsMenu component', () => {
const elementWrapper = mount(
<ProviderWrapper>
<LogEntryActionsMenu
logItem={{
logEntry={{
fields: [{ field: 'kubernetes.pod.uid', value: ['POD_UID'] }],
id: 'ITEM_ID',
index: 'INDEX',
Expand Down Expand Up @@ -118,7 +118,7 @@ describe('LogEntryActionsMenu component', () => {
const elementWrapper = mount(
<ProviderWrapper>
<LogEntryActionsMenu
logItem={{
logEntry={{
fields: [
{ field: 'container.id', value: ['CONTAINER_ID'] },
{ field: 'host.ip', value: ['HOST_IP'] },
Expand Down Expand Up @@ -154,7 +154,7 @@ describe('LogEntryActionsMenu component', () => {
const elementWrapper = mount(
<ProviderWrapper>
<LogEntryActionsMenu
logItem={{
logEntry={{
fields: [],
id: 'ITEM_ID',
index: 'INDEX',
Expand Down Expand Up @@ -188,7 +188,7 @@ describe('LogEntryActionsMenu component', () => {
const elementWrapper = mount(
<ProviderWrapper>
<LogEntryActionsMenu
logItem={{
logEntry={{
fields: [{ field: 'trace.id', value: ['1234567'] }],
id: 'ITEM_ID',
index: 'INDEX',
Expand Down Expand Up @@ -219,7 +219,7 @@ describe('LogEntryActionsMenu component', () => {
const elementWrapper = mount(
<ProviderWrapper>
<LogEntryActionsMenu
logItem={{
logEntry={{
fields: [
{ field: 'trace.id', value: ['1234567'] },
{ field: '@timestamp', value: [timestamp] },
Expand Down Expand Up @@ -252,7 +252,7 @@ describe('LogEntryActionsMenu component', () => {
const elementWrapper = mount(
<ProviderWrapper>
<LogEntryActionsMenu
logItem={{
logEntry={{
fields: [],
id: 'ITEM_ID',
index: 'INDEX',
Expand Down
Loading