Skip to content

Commit

Permalink
[Logs UI] Allow custom columns in the <LogStream /> component (#83802)
Browse files Browse the repository at this point in the history
  • Loading branch information
Alejandro Fernández Gómez authored Nov 23, 2020
1 parent 10afcf0 commit 378d89b
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 63 deletions.
2 changes: 2 additions & 0 deletions x-pack/plugins/infra/common/http_api/log_entries/entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import * as rt from 'io-ts';
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 @@ -19,6 +20,7 @@ export const logEntriesBaseRequestRT = rt.intersection([
rt.partial({
query: rt.union([rt.string, rt.null]),
size: rt.number,
columns: rt.array(logSourceColumnConfigurationRT),
}),
]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const logSourceFieldColumnConfigurationRT = rt.strict({
]),
});

const logSourceColumnConfigurationRT = rt.union([
export const logSourceColumnConfigurationRT = rt.union([
logSourceTimestampColumnConfigurationRT,
logSourceMessageColumnConfigurationRT,
logSourceFieldColumnConfigurationRT,
Expand Down
30 changes: 30 additions & 0 deletions x-pack/plugins/infra/public/components/log_stream/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,36 @@ By default the `<LogStream />` uses the `"default"` source confiuration, but if
<LogStream startTimestamp={startTimestamp} endTimestamp={endTimestamp} sourceId="my_source" />
```

### Custom columns

It is possible to change what columns are loaded without creating a whole new source configuration. To do so the component supports the `columns` prop. The default configuration can be replicated as follows.

```tsx
<LogStream
startTimestamp={...}
endTimetsamp={...}
columns={[
{ type: 'timestamp' },
{ type: 'field', field: 'event.dataset' }
{ type: 'message' },
]}
/>
```

There are three column types:

<table>
<tr>
<td>`type: "timestamp"`
<td>The configured timestamp field. Defaults to `@timestamp`.
<tr>
<td>`type: "message"`
<td>The value of the `message` field if it exists. If it doesn't, the component will try to recompose the original log line using values of other fields.
<tr>
<td>`type: "field"`
<td>A specific field specified in the `field` property.
</table>

### Considerations

As mentioned in the prerequisites, the component relies on `kibana-react` to access kibana's core services. If this is not the case the component will throw an exception when rendering. We advise to use an `<EuiErrorBoundary>` in your component hierarchy to catch this error if necessary.
34 changes: 31 additions & 3 deletions x-pack/plugins/infra/public/components/log_stream/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@ import { euiStyled } from '../../../../observability/public';
import { LogEntriesCursor } from '../../../common/http_api';

import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
import { useLogSource } from '../../containers/logs/log_source';
import { LogSourceConfigurationProperties, useLogSource } from '../../containers/logs/log_source';
import { useLogStream } from '../../containers/logs/log_stream';

import { ScrollableLogTextStreamView } from '../logging/log_text_stream';

const PAGE_THRESHOLD = 2;

type LogColumnDefinition =
| { type: 'timestamp' }
| { type: 'message' }
| { type: 'field'; field: string };

export interface LogStreamProps {
sourceId?: string;
startTimestamp: number;
Expand All @@ -26,6 +31,7 @@ export interface LogStreamProps {
center?: LogEntriesCursor;
highlight?: string;
height?: string | number;
columns?: LogColumnDefinition[];
}

export const LogStream: React.FC<LogStreamProps> = ({
Expand All @@ -36,7 +42,13 @@ export const LogStream: React.FC<LogStreamProps> = ({
center,
highlight,
height = '400px',
columns,
}) => {
const customColumns = useMemo(
() => (columns ? convertLogColumnDefinitionToLogSourceColumnDefinition(columns) : undefined),
[columns]
);

// source boilerplate
const { services } = useKibana();
if (!services?.http?.fetch) {
Expand Down Expand Up @@ -74,6 +86,7 @@ Read more at https://github.com/elastic/kibana/blob/master/src/plugins/kibana_re
endTimestamp,
query,
center,
columns: customColumns,
});

// Derived state
Expand All @@ -83,8 +96,8 @@ Read more at https://github.com/elastic/kibana/blob/master/src/plugins/kibana_re
const isLoadingMore = pageLoadingState === 'loading';

const columnConfigurations = useMemo(() => {
return sourceConfiguration ? sourceConfiguration.configuration.logColumns : [];
}, [sourceConfiguration]);
return sourceConfiguration ? customColumns ?? sourceConfiguration.configuration.logColumns : [];
}, [sourceConfiguration, customColumns]);

const streamItems = useMemo(
() =>
Expand Down Expand Up @@ -163,6 +176,21 @@ const LogStreamContent = euiStyled.div<{ height: string }>`
height: ${(props) => props.height};
`;

function convertLogColumnDefinitionToLogSourceColumnDefinition(
columns: LogColumnDefinition[]
): LogSourceConfigurationProperties['logColumns'] {
return columns.map((column) => {
switch (column.type) {
case 'timestamp':
return { timestampColumn: { id: '___#timestamp' } };
case 'message':
return { messageColumn: { id: '___#message' } };
case 'field':
return { fieldColumn: { id: `___#${column.field}`, field: column.field } };
}
});
}

// Allow for lazy loading
// eslint-disable-next-line import/no-default-export
export default LogStream;
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import { fetchLogEntries } from '../log_entries/api/fetch_log_entries';
import { useTrackedPromise } from '../../../utils/use_tracked_promise';
import { LogEntry, LogEntriesCursor } from '../../../../common/http_api';
import { useKibanaContextForPlugin } from '../../../hooks/use_kibana';
import { LogSourceConfigurationProperties } from '../log_source';

interface LogStreamProps {
sourceId: string;
startTimestamp: number;
endTimestamp: number;
query?: string;
center?: LogEntriesCursor;
columns?: LogSourceConfigurationProperties['logColumns'];
}

interface LogStreamState {
Expand Down Expand Up @@ -60,6 +62,7 @@ export function useLogStream({
endTimestamp,
query,
center,
columns,
}: LogStreamProps): LogStreamReturn {
const { services } = useKibanaContextForPlugin();
const [state, setState] = useSetState<LogStreamState>(INITIAL_STATE);
Expand Down Expand Up @@ -100,6 +103,7 @@ export function useLogStream({
startTimestamp,
endTimestamp,
query: parsedQuery,
columns,
...fetchPosition,
},
services.http.fetch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
LogEntriesItem,
LogEntriesCursor,
LogColumn,
LogEntriesRequest,
} from '../../../../common/http_api';
import {
InfraSourceConfiguration,
Expand Down Expand Up @@ -73,7 +74,8 @@ export class InfraLogEntriesDomain {
public async getLogEntriesAround(
requestContext: RequestHandlerContext,
sourceId: string,
params: LogEntriesAroundParams
params: LogEntriesAroundParams,
columnOverrides?: LogEntriesRequest['columns']
): Promise<{ entries: LogEntry[]; hasMoreBefore?: boolean; hasMoreAfter?: boolean }> {
const { startTimestamp, endTimestamp, center, query, size, highlightTerm } = params;

Expand All @@ -97,7 +99,8 @@ export class InfraLogEntriesDomain {
cursor: { before: center },
size: Math.floor(halfSize),
highlightTerm,
}
},
columnOverrides
);

/*
Expand Down Expand Up @@ -131,13 +134,16 @@ export class InfraLogEntriesDomain {
public async getLogEntries(
requestContext: RequestHandlerContext,
sourceId: string,
params: LogEntriesParams
params: LogEntriesParams,
columnOverrides?: LogEntriesRequest['columns']
): Promise<{ entries: LogEntry[]; hasMoreBefore?: boolean; hasMoreAfter?: boolean }> {
const { configuration } = await this.libs.sources.getSourceConfiguration(
requestContext.core.savedObjects.client,
sourceId
);

const columnDefinitions = columnOverrides ?? configuration.logColumns;

const messageFormattingRules = compileFormattingRules(
getBuiltinRules(configuration.fields.message)
);
Expand All @@ -155,7 +161,7 @@ export class InfraLogEntriesDomain {
return {
id: doc.id,
cursor: doc.cursor,
columns: configuration.logColumns.map(
columns: columnDefinitions.map(
(column): LogColumn => {
if ('timestampColumn' in column) {
return {
Expand Down
7 changes: 5 additions & 2 deletions x-pack/plugins/infra/server/routes/log_entries/entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const initLogEntriesRoute = ({ framework, logEntries }: InfraBackendLibs)
sourceId,
query,
size,
columns,
} = payload;

let entries;
Expand All @@ -47,7 +48,8 @@ export const initLogEntriesRoute = ({ framework, logEntries }: InfraBackendLibs)
query: parseFilterQuery(query),
center: payload.center,
size,
}
},
columns
));
} else {
let cursor: LogEntriesParams['cursor'];
Expand All @@ -66,7 +68,8 @@ export const initLogEntriesRoute = ({ framework, logEntries }: InfraBackendLibs)
query: parseFilterQuery(query),
cursor,
size,
}
},
columns
));
}

Expand Down
Loading

0 comments on commit 378d89b

Please sign in to comment.