Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ NOTE: As semantic versioning states all 0.y.z releases can contain breaking chan
- [#288](https://github.com/kobsio/kobs/pull/288): [resources] Add support to show custom columns for a resource.
- [#289](https://github.com/kobsio/kobs/pull/289): Add an option to create a NetworkPolicy via the Helm chart.
- [#295](https://github.com/kobsio/kobs/pull/295): [sql] Add `columns` options to set the title and formation of the returned columns from a query.
- [#296](https://github.com/kobsio/kobs/pull/296): [sql] Add support to visualize data via line and area charts.

### Fixed

Expand Down
Binary file added docs/plugins/assets/sql-example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
173 changes: 151 additions & 22 deletions docs/plugins/sql.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,39 +29,168 @@ The following options can be used for a panel with the SQL plugin:

| Field | Type | Description | Required |
| ----- | ---- | ----------- | -------- |
| type | string | The type which should be used to visualize the data. Currently we only support the `table` value. | Yes |
| queries | [[]Query](#query) | A list of queries, which can be selected by the user. | Yes |
| type | string | The type which should be used to visualize the data. This can be `table` or `chart`. | Yes |
| queries | [[]Query](#query) | A list of queries, which can be selected by the user. This is required when the `type` is set to `table`. | No |
| chart | [Chart](#chart) | Settings to render the results of a query in a chart. This is required when the `type` is set to `chart`. | No |

### Query

| Field | Type | Description | Required |
| ----- | ---- | ----------- | -------- |
| name | string | A name for the SQL query, which is displayed in the select box. | Yes |
| query | string | The query which should be run against the configured SQL database. | Yes |
| columns | map<string, [Column](#column)> | A map of columns to format the returned data for a query. The key must match the returned column name. | Yes |
| columns | map<string, [Column](#column)> | A map of columns to format the returned data for a query. The key must match the returned column name. | No |

### Column

| Field | Type | Description | Required |
| ----- | ---- | ----------- | -------- |
| title | string | Set a title for the column. | No |
| format | string | Format the results for the column. This can be a string with `{% .value %}` as placeholder for the value or one of the following special keys: `time`. | No |
| unit | string | A unit which should be displayed behind the column value. If this is `time` we automatically try to auto format the column to the users local time. | No |

```yaml
---
apiVersion: kobs.io/v1
kind: Dashboard
spec:
rows:
- size: -1
panels:
- title: User Data
colSpan: 12
plugin:
name: sql
options:
type: table
queries:
- name: User Data
query: "SELECT * FROM example.users"
```
### Chart

| Field | Type | Description | Required |
| ----- | ---- | ----------- | -------- |
| type | string | The chart type. This could be `line` or `area`. | Yes |
| query | string | The query which which results should be used in the chart. | Yes |
| xAxisColumn | string | The column which should be used for the x axis. | Yes |
| xAxisType | string | The type for the x axis. This could be empty or `time`. | No |
| xAxisUnit | string | The unit which should be used for the x axis. | No |
| yAxisColumns | []string | A list of columns which should be shown for the y axis. | Yes |
| yAxisUnit | string | The unit for the y axis. | No |
| yAxisStacked | boolean | When this is `true` the values of the y axis are stacked. | No |
| legend | map<string, string> | A map of string pairs, to set the displayed title for a column in the legend. The key is the column name as returned by the query and the value is the shown title. | No |

## Examples

The following example uses a configured SQL which access the data from a [klogs](klogs.md) ClickHouse instance to show the difference between the duration and upstream service time from the Istio access logs.

??? note "Dashboard"

```yaml
---
apiVersion: kobs.io/v1
kind: Dashboard
metadata:
name: latency
namespace: kobs
spec:
rows:
- size: 3
panels:
- title: Raw Data
colSpan: 6
rowSpan: 2
plugin:
name: sql-klogs-clickhouse
options:
type: table
queries:
- name: Duration and Upstream Service Time
query: |
SELECT
toStartOfInterval(timestamp, INTERVAL 60 second) AS time,
avg(fields_number.value[indexOf(fields_number.key, 'content.duration')]) as avg_duration,
avg(fields_number.value[indexOf(fields_number.key, 'content.upstream_service_time')]) as avg_ust,
avg_duration - avg_ust as avg_diff
FROM
logs.logs
WHERE
timestamp >= FROM_UNIXTIME({% .__timeStart %})
AND timestamp <= FROM_UNIXTIME({% .__timeEnd %})
AND namespace='myservice'
AND app='myservice'
AND container_name='istio-proxy'
AND match(fields_string.value[indexOf(fields_string.key, 'content.upstream_cluster')], '^inbound.*')
GROUP BY
time
ORDER BY
time
columns:
time:
title: Time
unit: time
avg_duration:
title: Duration
unit: ms
avg_ust:
title: Upstream Service Time
unit: ms
avg_diff:
title: Difference
unit: ms

- title: Difference
colSpan: 6
plugin:
name: sql-klogs-clickhouse
options:
type: chart
chart:
type: line
query: |
SELECT
toStartOfInterval(timestamp, INTERVAL 60 second) AS time,
avg(fields_number.value[indexOf(fields_number.key, 'content.duration')]) - avg(fields_number.value[indexOf(fields_number.key, 'content.upstream_service_time')]) as avg_diff
FROM
logs.logs
WHERE
timestamp >= FROM_UNIXTIME({% .__timeStart %})
AND timestamp <= FROM_UNIXTIME({% .__timeEnd %})
AND namespace='myservice'
AND app='myservice'
AND container_name='istio-proxy'
AND match(fields_string.value[indexOf(fields_string.key, 'content.upstream_cluster')], '^inbound.*')
GROUP BY
time
ORDER BY
time
xAxisColumn: time
xAxisType: time
yAxisColumns:
- avg_diff
yAxisUnit: ms
yAxisStacked: false
legend:
avg_diff: Difference

- title: Duration vs Upstream Service Time
colSpan: 6
plugin:
name: sql-klogs-clickhouse
options:
type: chart
chart:
type: line
query: |
SELECT
toStartOfInterval(timestamp, INTERVAL 60 second) AS time,
avg(fields_number.value[indexOf(fields_number.key, 'content.duration')]) as avg_duration,
avg(fields_number.value[indexOf(fields_number.key, 'content.upstream_service_time')]) as avg_ust
FROM
logs.logs
WHERE
timestamp >= FROM_UNIXTIME({% .__timeStart %})
AND timestamp <= FROM_UNIXTIME({% .__timeEnd %})
AND namespace='myservice'
AND app='myservice'
AND container_name='istio-proxy'
AND match(fields_string.value[indexOf(fields_string.key, 'content.upstream_cluster')], '^inbound.*')
GROUP BY
time
ORDER BY
time
xAxisColumn: time
xAxisType: time
yAxisColumns:
- avg_duration
- avg_ust
yAxisUnit: ms
yAxisStacked: false
legend:
avg_duration: Duration
avg_ust: Upstream Service Time
```

![SQL Example](assets/sql-example.png)
37 changes: 20 additions & 17 deletions plugins/sql/src/components/page/PageSQL.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,29 @@ interface IPageSQLProps {
const PageSQL: React.FunctionComponent<IPageSQLProps> = ({ name, query }: IPageSQLProps) => {
const history = useHistory();

const { isError, isFetching, error, data, refetch } = useQuery<ISQLData, Error>(['sql/query', query], async () => {
try {
const response = await fetch(`/api/plugins/sql/${name}/query?query=${encodeURIComponent(query)}`, {
method: 'get',
});
const json = await response.json();
const { isError, isFetching, error, data, refetch } = useQuery<ISQLData, Error>(
['sql/query', name, query],
async () => {
try {
const response = await fetch(`/api/plugins/sql/${name}/query?query=${encodeURIComponent(query)}`, {
method: 'get',
});
const json = await response.json();

if (response.status >= 200 && response.status < 300) {
return json;
} else {
if (json.error) {
throw new Error(json.error);
if (response.status >= 200 && response.status < 300) {
return json;
} else {
throw new Error('An unknown error occured');
if (json.error) {
throw new Error(json.error);
} else {
throw new Error('An unknown error occured');
}
}
} catch (err) {
throw err;
}
} catch (err) {
throw err;
}
});
},
);

if (isFetching) {
return (
Expand All @@ -47,7 +50,7 @@ const PageSQL: React.FunctionComponent<IPageSQLProps> = ({ name, query }: IPageS
return (
<Alert
variant={AlertVariant.danger}
title="Could not get result for SQL query"
title="Could not get query results"
actionLinks={
<React.Fragment>
<AlertActionLink onClick={(): void => history.push('/')}>Home</AlertActionLink>
Expand Down
44 changes: 34 additions & 10 deletions plugins/sql/src/components/panel/Panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, { memo } from 'react';
import { IPluginPanelProps, PluginOptionsMissing } from '@kobsio/plugin-core';
import { IPanelOptions } from '../../utils/interfaces';
import SQL from './SQL';
import SQLChart from './SQLChart';

interface IPanelProps extends IPluginPanelProps {
options?: IPanelOptions;
Expand All @@ -15,22 +16,45 @@ export const Panel: React.FunctionComponent<IPanelProps> = ({
times,
options,
}: IPanelProps) => {
if (!options || !options.type) {
if (options && options.type === 'table' && options.queries) {
return <SQL name={name} title={title} description={description} queries={options.queries} />;
}

if (
options &&
options.type === 'chart' &&
options.chart &&
options.chart.type &&
options.chart.query &&
options.chart.xAxisColumn &&
options.chart.yAxisColumns
) {
return (
<PluginOptionsMissing
<SQLChart
name={name}
title={title}
message="Options for SQL panel are missing or invalid"
details="The panel doesn't contain the required options to render get the SQL data or the provided options are invalid."
documentation="https://kobs.io/plugins/sql"
description={description}
type={options.chart.type}
query={options.chart.query}
xAxisColumn={options.chart.xAxisColumn}
xAxisType={options.chart.xAxisType}
xAxisUnit={options.chart.xAxisUnit}
yAxisColumns={options.chart.yAxisColumns}
yAxisUnit={options.chart.yAxisUnit}
yAxisStacked={options.chart.yAxisStacked}
legend={options.chart.legend}
/>
);
}

if (options.type === 'table' && options.queries) {
return <SQL name={name} title={title} description={description} queries={options.queries} />;
}

return null;
return (
<PluginOptionsMissing
title={title}
message="Options for SQL panel are missing or invalid"
details="The panel doesn't contain the required options to render get the SQL data or the provided options are invalid."
documentation="https://kobs.io/plugins/sql"
/>
);
};

export default memo(Panel, (prevProps, nextProps) => {
Expand Down
4 changes: 2 additions & 2 deletions plugins/sql/src/components/panel/SQL.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const SQL: React.FunctionComponent<ISQLProps> = ({ name, title, description, que
const [selectedQueryIndex, setSelectedQueryIndex] = useState<number>(0);

const { isError, isFetching, isLoading, error, data, refetch } = useQuery<ISQLData, Error>(
['sql/query', queries, selectedQueryIndex],
['sql/query', name, queries, selectedQueryIndex],
async () => {
try {
if (!queries[selectedQueryIndex].query) {
Expand Down Expand Up @@ -104,7 +104,7 @@ const SQL: React.FunctionComponent<ISQLProps> = ({ name, title, description, que
<Alert
variant={AlertVariant.danger}
isInline={true}
title="Could not get logs"
title="Could not get query results"
actionLinks={
<React.Fragment>
<AlertActionLink onClick={(): Promise<QueryObserverResult<ISQLData, Error>> => refetch()}>
Expand Down
Loading