Skip to content

Commit

Permalink
Add actions for log details (#151)
Browse files Browse the repository at this point in the history
This commit adds actions to the log details in the ClickHouse plugin, so
that users can filter for values based on the value of a log line and
that they can toggle a specific field directly from the details.

This commit also improves how we check if a field exists when it is not
a default field.
  • Loading branch information
ricoberger authored Sep 16, 2021
1 parent d3c74c0 commit 578f597
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ NOTE: As semantic versioning states all 0.y.z releases can contain breaking chan
- [#136](https://github.com/kobsio/kobs/pull/136): Allow custom order for the returned logs and add `!~` and `_exists_` operator for ClickHouse plugin.
- [#138](https://github.com/kobsio/kobs/pull/138): Add option to stream (follow) logs via WebSockets.
- [#149](https://github.com/kobsio/kobs/pull/149): Add SQL plugin to run queries against a configured SQL database instance. For now we are supporting the `clickhouse`, `postgres` and `mysql` driver.
- [#151](https://github.com/kobsio/kobs/pull/151): Add actions for in log details view of the ClickHouse, so that users can filter based on the value of a field.

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion plugins/clickhouse/pkg/instance/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ func handleExistsCondition(key string) string {
return fmt.Sprintf("%s IS NOT NULL", key)
}

return fmt.Sprintf("fields_string.value[indexOf(fields_string.key, '%s')] IS NOT NULL AND fields_number.value[indexOf(fields_number.key, '%s')] IS NOT NULL", key, key)
return fmt.Sprintf("(has(fields_string.key, '%s') = 1 OR has(fields_number.key, '%s') = 1)", key, key)
}

func parseOrder(order, orderBy string) string {
Expand Down
4 changes: 3 additions & 1 deletion plugins/clickhouse/src/components/page/Logs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ interface IPageLogsProps {
order: string;
orderBy: string;
query: string;
addFilter: (filter: string) => void;
selectField: (field: string) => void;
times: IPluginTimes;
}
Expand All @@ -42,6 +43,7 @@ const PageLogs: React.FunctionComponent<IPageLogsProps> = ({
order,
orderBy,
query,
addFilter,
selectField,
times,
}: IPageLogsProps) => {
Expand Down Expand Up @@ -141,7 +143,7 @@ const PageLogs: React.FunctionComponent<IPageLogsProps> = ({

<Card isCompact={true} style={{ maxWidth: '100%', overflowX: 'scroll' }}>
<CardBody>
<LogsDocuments pages={data.pages} fields={fields} />
<LogsDocuments pages={data.pages} fields={fields} addFilter={addFilter} selectField={selectField} />
</CardBody>
</Card>

Expand Down
7 changes: 6 additions & 1 deletion plugins/clickhouse/src/components/page/LogsToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
ToolbarToggleGroup,
} from '@patternfly/react-core';
import { FilterIcon, SearchIcon } from '@patternfly/react-icons';
import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';

import { IOptionsAdditionalFields, Options, TTime } from '@kobsio/plugin-core';
import { IOptions } from '../../utils/interfaces';
Expand Down Expand Up @@ -83,6 +83,11 @@ const LogsToolbar: React.FunctionComponent<ILogsToolbarProps> = ({
}
};

useEffect(() => {
setData({ ...data, query: query });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [query]);

return (
<Toolbar id="clickhouse-logs-toolbar" style={{ paddingBottom: '0px', zIndex: 300 }}>
<ToolbarContent style={{ padding: '0px' }}>
Expand Down
5 changes: 5 additions & 0 deletions plugins/clickhouse/src/components/page/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ const Page: React.FunctionComponent<IPluginPageProps> = ({ name, displayName, de
changeOptions({ ...options, fields: tmpFields });
};

const addFilter = (filter: string): void => {
changeOptions({ ...options, query: `${options.query} ${filter}` });
};

// useEffect is used to set the options every time the search location for the current URL changes. The URL is changed
// via the changeOptions function. When the search location is changed we modify the options state.
useEffect(() => {
Expand Down Expand Up @@ -83,6 +87,7 @@ const Page: React.FunctionComponent<IPluginPageProps> = ({ name, displayName, de
order={options.order}
orderBy={options.orderBy}
maxDocuments={options.maxDocuments}
addFilter={addFilter}
selectField={selectField}
times={options.times}
/>
Expand Down
11 changes: 9 additions & 2 deletions plugins/clickhouse/src/components/panel/LogsDocument.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,16 @@ import { formatTimeWrapper } from '../../utils/helpers';
interface ILogsDocumentProps {
document: IDocument;
fields?: string[];
addFilter?: (filter: string) => void;
selectField?: (field: string) => void;
}

const LogsDocument: React.FunctionComponent<ILogsDocumentProps> = ({ document, fields }: ILogsDocumentProps) => {
const LogsDocument: React.FunctionComponent<ILogsDocumentProps> = ({
document,
fields,
addFilter,
selectField,
}: ILogsDocumentProps) => {
const [isExpanded, setIsExpanded] = useState<boolean>(false);

const defaultActions = [
Expand Down Expand Up @@ -93,7 +100,7 @@ const LogsDocument: React.FunctionComponent<ILogsDocumentProps> = ({ document, f
<Tr isExpanded={isExpanded}>
<Td />
<Td colSpan={fields && fields.length > 0 ? fields.length + 1 : 2}>
{isExpanded && <LogsDocumentDetails document={document} />}
{isExpanded && <LogsDocumentDetails document={document} addFilter={addFilter} selectField={selectField} />}
</Td>
<Td />
</Tr>
Expand Down
22 changes: 13 additions & 9 deletions plugins/clickhouse/src/components/panel/LogsDocumentDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import React, { useState } from 'react';
import { Tab, TabTitleText, Tabs } from '@patternfly/react-core';
import { TableComposable, TableVariant, Tbody, Td, Tr } from '@patternfly/react-table';
import { TableComposable, TableVariant, Tbody } from '@patternfly/react-table';

import { Editor } from '@kobsio/plugin-core';
import { IDocument } from '../../utils/interfaces';
import LogsDocumentDetailsRow from './LogsDocumentDetailsRow';

export interface ILogsDocumentDetailsProps {
document: IDocument;
addFilter?: (filter: string) => void;
selectField?: (field: string) => void;
}

const LogsDocumentDetails: React.FunctionComponent<ILogsDocumentDetailsProps> = ({
document,
addFilter,
selectField,
}: ILogsDocumentDetailsProps) => {
const [activeTab, setActiveTab] = useState<string>('table');

Expand All @@ -26,14 +31,13 @@ const LogsDocumentDetails: React.FunctionComponent<ILogsDocumentDetailsProps> =
<TableComposable aria-label="Details" variant={TableVariant.compact} borders={false}>
<Tbody>
{Object.keys(document).map((key) => (
<Tr key={key}>
<Td noPadding={true} dataLabel="Key">
<b>{key}</b>
</Td>
<Td className="pf-u-text-wrap pf-u-text-break-word" noPadding={true} dataLabel="Value">
<div style={{ whiteSpace: 'pre-wrap' }}>{document[key]}</div>
</Td>
</Tr>
<LogsDocumentDetailsRow
key={key}
documentKey={key}
documentValue={document[key]}
addFilter={addFilter}
selectField={selectField}
/>
))}
</Tbody>
</TableComposable>
Expand Down
88 changes: 88 additions & 0 deletions plugins/clickhouse/src/components/panel/LogsDocumentDetailsRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { Button, Tooltip } from '@patternfly/react-core';
import { ColumnsIcon, SearchIcon, SearchMinusIcon, SearchPlusIcon } from '@patternfly/react-icons';
import React, { useState } from 'react';
import { Td, Tr } from '@patternfly/react-table';

export interface ILogsDocumentDetailsRowProps {
documentKey: string;
documentValue: string;
addFilter?: (filter: string) => void;
selectField?: (field: string) => void;
}

const LogsDocumentDetailsRow: React.FunctionComponent<ILogsDocumentDetailsRowProps> = ({
documentKey,
documentValue,
addFilter,
selectField,
}: ILogsDocumentDetailsRowProps) => {
const [showActions, setShowActions] = useState<boolean>(false);

return (
<Tr onMouseEnter={(): void => setShowActions(true)} onMouseLeave={(): void => setShowActions(false)}>
{addFilter && selectField && (
<Td noPadding={true} dataLabel="Actions" style={{ width: '75px' }}>
{showActions && (
<div>
<Tooltip content={<div>Filter for value</div>}>
<Button
style={{ padding: '0', paddingRight: '3px' }}
variant="plain"
aria-label="Filter for value"
isSmall={true}
onClick={(): void => addFilter(` _and_ ${documentKey}='${documentValue}'`)}
>
<SearchPlusIcon />
</Button>
</Tooltip>

<Tooltip content={<div>Filter out value</div>}>
<Button
style={{ padding: '0', paddingRight: '3px' }}
variant="plain"
aria-label="Filter out value"
isSmall={true}
onClick={(): void => addFilter(` _and_ ${documentKey}!='${documentValue}'`)}
>
<SearchMinusIcon />
</Button>
</Tooltip>

<Tooltip content={<div>Filter for field present</div>}>
<Button
style={{ padding: '0', paddingRight: '3px' }}
variant="plain"
aria-label="Filter for field present"
isSmall={true}
onClick={(): void => addFilter(` _and_ _exists_ ${documentKey}`)}
>
<SearchIcon />
</Button>
</Tooltip>

<Tooltip content={<div>Toggle field in table</div>}>
<Button
style={{ padding: '0', paddingRight: '3px' }}
variant="plain"
aria-label="Toggle field in table"
isSmall={true}
onClick={(): void => selectField(documentKey)}
>
<ColumnsIcon />
</Button>
</Tooltip>
</div>
)}
</Td>
)}
<Td noPadding={true} dataLabel="Key">
<b>{documentKey}</b>
</Td>
<Td className="pf-u-text-wrap pf-u-text-break-word" noPadding={true} dataLabel="Value">
<div style={{ whiteSpace: 'pre-wrap' }}>{documentValue}</div>
</Td>
</Tr>
);
};

export default LogsDocumentDetailsRow;
17 changes: 15 additions & 2 deletions plugins/clickhouse/src/components/panel/LogsDocuments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,16 @@ import LogsDocument from './LogsDocument';
interface ILogsDocumentsProps {
pages: ILogsData[];
fields?: string[];
addFilter?: (filter: string) => void;
selectField?: (field: string) => void;
}

const LogsDocuments: React.FunctionComponent<ILogsDocumentsProps> = ({ pages, fields }: ILogsDocumentsProps) => {
const LogsDocuments: React.FunctionComponent<ILogsDocumentsProps> = ({
pages,
fields,
addFilter,
selectField,
}: ILogsDocumentsProps) => {
return (
<TableComposable aria-label="Logs" variant={TableVariant.compact} borders={false}>
<Thead>
Expand All @@ -28,7 +35,13 @@ const LogsDocuments: React.FunctionComponent<ILogsDocumentsProps> = ({ pages, fi
{pages.map((page, pageIndex) =>
page.documents
? page.documents.map((document, documentIndex) => (
<LogsDocument key={`${pageIndex}_${documentIndex}`} document={document} fields={fields} />
<LogsDocument
key={`${pageIndex}_${documentIndex}`}
document={document}
fields={fields}
addFilter={addFilter}
selectField={selectField}
/>
))
: null,
)}
Expand Down

0 comments on commit 578f597

Please sign in to comment.