Skip to content
Open
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
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# Changelog

## 0.6.0

### What's Changed

- React 19 compatibility for Grafana 13
- Add quick filters with value autocomplete and clearer filter operations
- Add trace search and full trace rendering for OpenTelemetry trace indexes, with Grafana trace frames, service node graph, span events, exception stack traces, status/warning handling, service tags, peer service metadata, and stable per-service node colors
- Add trace-to-logs and log-to-trace correlation links between separate Quickwit logs and traces datasources
- Add datasource configuration fields for related logs/traces datasources
- Add configurable filter autocomplete chain mode (no chain, sampled chain, full chain)
- Add configurable filter autocomplete value limit (defaults to 1000, `0` for unlimited)
- Show useful default log messages for OTEL logs when no message field is configured
- Migrate e2e tests from Cypress to Playwright and add Quickwit datasource e2e test
- Add Grafana version matrix to e2e tests
- Fix Shift-Enter keymapping on latest Grafana versions
- Fix missing `AND` when adding ad hoc filters
- Document Grafana 12.1+ plugin installation with `GF_PLUGINS_PREINSTALL_SYNC`
- Handle Grafana 13 overlays in e2e tests
- Bump Go and Node dependencies to fix high vulnerabilities
- Bump `grafana/plugin-actions/is-compatible` GitHub action

## 0.5.0

### What's Changed
Expand Down
46 changes: 35 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@

The Quickwit data source plugin allows you to query and visualize Quickwit data from within Grafana.

## 🎉 What's New in v0.5.0

- **Grafana 11.x Support**
- **Fixed Adhoc Filters**: Improved adhoc filters feature for dynamic query building
- **Enhanced Stability**: Various bug fixes and improvements
## 🎉 What's New in v0.6.0

- **Grafana 12.1+ and 13 Support** (React 19 compatibility)
- **Trace support** for OpenTelemetry trace indexes, with service node graph, span events, exception stacks, and trace/logs correlation links
- **Quick filters** with value autocomplete and clearer filter operations
- **Configurable autocomplete** chain mode and value limit
- **Better OTEL log display** when no message field is configured
- **Playwright e2e tests** replacing Cypress, with a Grafana version matrix
- **Fixed Shift-Enter keymapping** on latest Grafana versions
- **Fixed ad hoc filters** when appending filters to existing queries
- **Security updates**: Go and Node dependency bumps

It is available for installation directly from the
[Grafana catalog](https://grafana.com/grafana/plugins/quickwit-quickwit-datasource/) until version 0.4.5
Expand All @@ -17,21 +23,39 @@ or you can download the latest version and follow the

## Version compatibility

We recommend Grafana v10.X or v11.X.
We recommend Grafana v12.1+ or v13.

Quickwit 0.7 is compatible with 0.3.x versions only.

Quickwit 0.8 is compatible with 0.4.x and 0.5.x versions.
Quickwit 0.8 is compatible with 0.4.x, 0.5.x and 0.6.x versions.

- **v0.5.x** (Latest): Grafana 11.x with improved adhoc filters
- **v0.4.x**: Grafana 10.x
- **v0.6.x** (Latest): Grafana 12.1+ and 13 (React 19)
- **v0.5.x**: Grafana 11.x
- **v0.4.x**: Grafana 10.x
- **v0.3.x**: Grafana 9.x / Quickwit 0.7

## Installation

You can either download the plugin manually and unzip it into the plugin directory or use the env variable `GF_INSTALL_PLUGINS` to install it.
You can either download the plugin manually and unzip it into the plugin directory, or use a Grafana env variable to install it. Note that `GF_INSTALL_PLUGINS` is **deprecated since Grafana 12.1** — use `GF_PLUGINS_PREINSTALL_SYNC` instead on recent versions.

### 0.6.0 (Latest) for Quickwit 0.8 + Grafana 12.1+ / 13

Run `grafana` container with the env variable (format: `<plugin-id>@<version>@<url>`):

```bash
docker run -p 3000:3000 -e GF_PLUGINS_PREINSTALL_SYNC="quickwit-quickwit-datasource@0.6.0@https://github.com/quickwit-oss/quickwit-datasource/releases/download/v0.6.0/quickwit-quickwit-datasource-0.6.0.zip" grafana/grafana run
```

Or download the plugin manually and start Grafana

```bash
wget https://github.com/quickwit-oss/quickwit-datasource/releases/download/v0.6.0/quickwit-quickwit-datasource-0.6.0.zip
mkdir -p plugins
unzip quickwit-quickwit-datasource-0.6.0.zip -d plugins/quickwit-quickwit-datasource-0.6.0
docker run -p 3000:3000 -e GF_PATHS_PLUGINS=/data/plugins -v ${PWD}/plugins:/data/plugins grafana/grafana run
```

### 0.5.0 (Latest) for Quickwit 0.8 + Grafana 11
### 0.5.0 for Quickwit 0.8 + Grafana 11

Run `grafana` container with the env variable:

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "quickwit-datasource",
"version": "0.5.0",
"version": "0.6.0",
"description": "Quickwit datasource",
"scripts": {
"build": "webpack -c ./.config/webpack/webpack.config.ts --env production",
Expand Down
25 changes: 25 additions & 0 deletions src/QueryBuilder/elastic.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { getDataQuery } from './elastic';

describe('getDataQuery', () => {
it('uses the requested terms size for autocomplete queries', () => {
const query = getDataQuery({ field: 'status', size: 250 }, 'getTerms');

expect(query.bucketAggs?.[0].settings).toEqual(
expect.objectContaining({
size: '250',
shard_size: '250',
})
);
});

it('keeps zero as no terms limit', () => {
const query = getDataQuery({ field: 'status', size: 0 }, 'getTerms');

expect(query.bucketAggs?.[0].settings).toEqual(
expect.objectContaining({
size: '0',
shard_size: '0',
})
);
});
});
3 changes: 2 additions & 1 deletion src/QueryBuilder/elastic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ export function getDataQuery(queryDef: TermsQuery, refId: string): Elasticsearch

const bucketAggs: BucketAggregation[] = [];
if (queryDef.field) {
bucketAggs.push(getTermsAgg(queryDef.field, 100, 100, orderBy, order))
const size = queryDef.size ?? 100;
bucketAggs.push(getTermsAgg(queryDef.field, size, size, orderBy, order))
}

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ describe('Terms Settings Editor', () => {
query: '',
bucketAggs: [termsAgg],
metrics: [avg, derivative, topMetrics],
filters: [],
};

renderWithESProvider(<TermsSettingsEditor bucketAgg={termsAgg} />, { providerProps: { query } });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const query: ElasticsearchQuery = {
query: '',
metrics: [{ id: '1', type: 'count' }],
bucketAggs: [{ type: 'date_histogram', id: '2' }],
filters: []
};

describe('ElasticsearchQueryContext', () => {
Expand Down
6 changes: 4 additions & 2 deletions src/components/QueryEditor/ElasticsearchQueryContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ElasticsearchQuery } from '@/types';

import { createReducer as createBucketAggsReducer } from './BucketAggregationsEditor/state/reducer';
import { reducer as metricsReducer } from './MetricAggregationsEditor/state/reducer';
import { reducer as filtersReducer } from './FilterEditor/state/reducer';
import { aliasPatternReducer, queryReducer, initQuery, initExploreQuery } from './state';
import { getHook } from '@/utils/context';
import { Provider, useDispatch } from "react-redux";
Expand Down Expand Up @@ -64,10 +65,11 @@ export const ElasticsearchProvider = withStore(({
[onChange]
);

const reducer = combineReducers<Pick<ElasticsearchQuery, 'query' | 'alias' | 'metrics' | 'bucketAggs'>>({
const reducer = combineReducers<Pick<ElasticsearchQuery, 'query' | 'alias' | 'metrics' | 'filters' | 'bucketAggs'>>({
query: queryReducer,
alias: aliasPatternReducer,
metrics: metricsReducer,
filters: filtersReducer,
bucketAggs: createBucketAggsReducer(datasource.timeField),
});

Expand All @@ -78,7 +80,7 @@ export const ElasticsearchProvider = withStore(({
reducer
);

const isUninitialized = !query.metrics || !query.bucketAggs || query.query === undefined;
const isUninitialized = !query.metrics || !query.filters || !query.bucketAggs || query.query === undefined;

const [shouldRunInit, setShouldRunInit] = useState(isUninitialized);

Expand Down
50 changes: 50 additions & 0 deletions src/components/QueryEditor/FilterEditor/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { QueryFilter } from '@/types';

import { getPreviousAdHocFilters } from './index';

describe('FilterEditor helpers', () => {
it('returns only complete filters before the current filter', () => {
const filters: QueryFilter[] = [
{
id: 'first',
filter: { key: 'service', operator: '=', value: 'frontend' },
},
{
id: 'hidden',
hide: true,
filter: { key: 'cluster', operator: '=', value: 'prod' },
},
{
id: 'incomplete',
filter: { key: 'namespace', operator: '=', value: '' },
},
{
id: 'current',
filter: { key: 'attributes.grpc_message', operator: '=', value: '' },
},
{
id: 'later',
filter: { key: 'status', operator: '=', value: '500' },
},
];

expect(getPreviousAdHocFilters(filters, 'current')).toEqual([
{ key: 'service', operator: '=', value: 'frontend' },
]);
});

it('does not include previous term filters with whitespace values', () => {
const filters: QueryFilter[] = [
{
id: 'invalid-term',
filter: { key: 'message', operator: 'term', value: 'invalid token' },
},
{
id: 'current',
filter: { key: 'status', operator: '=', value: '' },
},
];

expect(getPreviousAdHocFilters(filters, 'current')).toEqual([]);
});
});
Loading
Loading