Skip to content

Add details about stream in the query editor #12

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

Merged
merged 1 commit into from
Jan 11, 2023
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
2 changes: 1 addition & 1 deletion dist/img/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion dist/module.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/module.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions dist/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"id": "parseable-datasource",
"logs": true,
"info": {
"description": "Parseable is a log observability platform that helps you collect, parse, and analyze your logs. This plugin allows you to query your logs in Grafana.",
"description": "Parseable is a logging platform that helps you collect, parse, and analyze your logs. This plugin allows you to visualize your Parseable logs in Grafana.",
"author": {
"name": "Parseable Team <[email protected]>"
},
Expand All @@ -24,7 +24,7 @@
{"name": "Log Dashboard", "path": "img/log-visualisation.png"}
],
"version": "1.0.0",
"updated": "2023-01-10"
"updated": "2023-01-11"
},
"routes": [
{
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "parseable",
"version": "1.0.0",
"description": "Y",
"description": "Parseable is a logging platform that helps you collect, parse, and analyze your logs. This plugin allows you to visualize your Parseable logs in Grafana.",
"scripts": {
"build": "webpack -c ./.config/webpack/webpack.config.ts --env production",
"dev": "webpack -w -c ./.config/webpack/webpack.config.ts --env development",
Expand All @@ -18,7 +18,7 @@
"build-storybook": "build-storybook"
},
"author": "Parseable",
"license": "Affero General Public License v3.0",
"license": "AGPL-3.0-only",
"devDependencies": {
"@babel/core": "^7.16.7",
"@grafana/e2e": "9.2.5",
Expand Down
Binary file modified releases/parseable-datasource-1.0.0.zip
Binary file not shown.
2 changes: 1 addition & 1 deletion releases/parseable-datasource-1.0.0.zip.sha1
Original file line number Diff line number Diff line change
@@ -1 +1 @@
a106522fa8af91dc1277c63ee19a0a0b8bd3b3bf
dd08ae43a151ee69e30f74592a34e9726373fc4d
87 changes: 78 additions & 9 deletions src/components/QueryEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { ComponentType, ChangeEvent, useState } from 'react';
import { LegacyForms, AsyncSelect, Label, InlineField, InlineFieldRow } from '@grafana/ui';
import { QueryEditorProps, SelectableValue } from '@grafana/data';
import { DataSource } from '../datasource';
import { Fields, MyDataSourceOptions, MyQuery } from '../types';
import { SchemaFields, MyDataSourceOptions, MyQuery } from '../types';

const { FormField } = LegacyForms;

Expand Down Expand Up @@ -30,14 +30,18 @@ export const QueryEditor: ComponentType<Props> = ({ datasource, onChange, onRunQ

const [value, setValue] = useState<SelectableValue<string>>();
const [schema = '', setSchema] = React.useState<string | number>();
const [count = '', setEventCount] = React.useState<string | number>();
const [jsonsize = '', setJsonSize] = React.useState<string | number>();
const [parquetsize = '', setParquetSize] = React.useState<string | number>();
const [streamname = '', setStreamName] = React.useState<string | number>();
//const [fielder, setFielder] = React.useState<string | number>();

const loadSchemaOptions = React.useCallback((value) => {
const loadStreamSchema = React.useCallback((value) => {
if (value) {
return datasource.listSchema(value).then(
return datasource.streamSchema(value).then(
(result) => {
if (result.fields) {
const schema = result.fields.map((data: Fields) => (data.name));
const schema = result.fields.map((data: SchemaFields) => (data.name));
const schemaToText = schema.join(", ")
setSchema(schemaToText);
return schema;
Expand All @@ -52,6 +56,31 @@ export const QueryEditor: ComponentType<Props> = ({ datasource, onChange, onRunQ
return '';
}, [datasource, schema]);

const loadStreamStats = React.useCallback((value) => {
if (value) {
return datasource.streamStats(value).then(
(result) => {
if (result.ingestion) {
const count = result.ingestion.count;
const jsonsize = result.ingestion.size;
const parquetsize = result.storage?.size;
const streamname = result.stream;
setJsonSize(jsonsize);
setParquetSize(parquetsize);
setStreamName(streamname);
setEventCount(count);
return count;
}
return count;
},
(response) => {
throw new Error(response.statusText);
}
);
}
return '';
}, [datasource, count]);

const onQueryTextChange = (event: ChangeEvent<HTMLInputElement>) => {
onChange({ ...query, queryText: event.target.value });
};
Expand All @@ -65,15 +94,21 @@ export const QueryEditor: ComponentType<Props> = ({ datasource, onChange, onRunQ
}, [onRunQuery, queryText])

React.useEffect(() => {
loadSchemaOptions(value)
}, [loadSchemaOptions, value]);
loadStreamSchema(value)
}, [loadStreamSchema, value]);

React.useEffect(() => {
loadStreamStats(value)
}, [loadStreamStats, value]);

return (
<>
<div className="gf-form">
<InlineField>
<Label >
StreamName:
<div style={{ width: 'fit-content', color: 'blue' }}>
Select a log stream:
</div>
<div style={{ width: 200 + 'px', marginRight: '20px', marginLeft: '20px' }}>
<AsyncSelect
loadOptions={loadAsyncOptions}
Expand All @@ -89,8 +124,42 @@ export const QueryEditor: ComponentType<Props> = ({ datasource, onChange, onRunQ

<InlineFieldRow>
<div>
<Label>
<div style={{ width: 'fit-content', textAlign: 'center', color: 'blue'}}>
Stream {streamname} details
</div>
</Label>
<Label>
<div style={{ width: 'fit-content', color: 'blue' }}>
Columns:
</div>
<div style={{ width: 'fit-content' }}>
{ schema}
</div>
</Label>
<Label>
<div style={{ width: 'fit-content', color: 'blue' }}>
Total events ingested:
</div>
<div style={{ width: 'fit-content' }}>
{ count}
</div>
</Label>
<Label>
<div style={{ width: 'fit-content', color: 'blue' }}>
Total ingested data size:
</div>
<div style={{ width: 'fit-content' }}>
{ jsonsize}
</div>
</Label>
<Label>
Schema: {schema}
<div style={{ width: 'fit-content', color: 'blue' }}>
Total compressed data stored:
</div>
<div style={{ width: 'fit-content' }}>
{ parquetsize}
</div>
</Label>
</div>
</InlineFieldRow>
Expand All @@ -102,7 +171,7 @@ export const QueryEditor: ComponentType<Props> = ({ datasource, onChange, onRunQ
value={queryText || ''}
onChange={onQueryTextChange}
label="SQL Query"
tooltip="Enter the search SQL query here."
tooltip="Enter the search SQL query here (use column names as displayed above)"
/>
</>
);
Expand Down
30 changes: 28 additions & 2 deletions src/datasource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import {
StreamName,
StreamList,
//Fields,
ListSchemaResponse
StreamSchemaResponse,
StreamStatsResponse
} from './types';
export class DataSource extends DataSourceApi<MyQuery, MyDataSourceOptions> {
url: string;
Expand Down Expand Up @@ -149,7 +150,32 @@ export class DataSource extends DataSourceApi<MyQuery, MyDataSourceOptions> {
);
}

async listSchema(streamname: StreamName): Promise<ListSchemaResponse> {
async streamStats(streamname: StreamName): Promise<StreamStatsResponse> {
if (streamname) {
return lastValueFrom(
this.doFetch({
url: this.url + '/api/v1/logstream/' + streamname.value + '/stats',
method: 'GET',
}).pipe(
map((response) =>
(typeof response.data === 'object' && !isNull(response.data))
? response.data
: {}
),
catchError((err) => {
console.error(err);
return of({
status: 'error',
message: err.statusText
})

}))
)
}
return {}
}

async streamSchema(streamname: StreamName): Promise<StreamSchemaResponse> {
if (streamname) {
return lastValueFrom(
this.doFetch({
Expand Down
2 changes: 1 addition & 1 deletion src/img/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"id": "parseable-datasource",
"logs": true,
"info": {
"description": "Parseable is a log observability platform that helps you collect, parse, and analyze your logs. This plugin allows you to query your logs in Grafana.",
"description": "Parseable is a logging platform that helps you collect, parse, and analyze your logs. This plugin allows you to visualize your Parseable logs in Grafana.",
"author": {
"name": "Parseable Team <[email protected]>"
},
Expand Down
24 changes: 22 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,40 @@ export interface StreamName {
value: String | Number;
}

export interface ListSchemaResponse {
export interface StreamSchemaResponse {
fields?: [] | undefined;
status?: string;
message?: string;
}

export interface Fields {
export interface StreamStatsResponse {
ingestion?: Ingestion;
storage?: Storage;
time?: string;
stream?: string;
status?: string;
message?: string;
}

export interface SchemaFields {
name?: string;
data_type?: "Utf8";
nullable?: boolean;
dict_id?: number;
dict_is_ordered?: boolean;
}

export interface Ingestion {
count?: number;
format?: string;
size?: string;
}

export interface Storage {
format?: string;
size?: string;
}

export interface Schema {
schema?: string[];
}
Expand Down