[FEATURE] Add OpenSearch datasource and log query plugin with PPL support#641
[FEATURE] Add OpenSearch datasource and log query plugin with PPL support#641Oliver-ke wants to merge 26 commits into
Conversation
Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
05cd7bf to
bc65cad
Compare
Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
The bound was being appended at the end of the user query, which fails once the user pipeline drops @timestamp from the schema (stats, fields, top). Tests now describe the intended ordering. Implementation follows. Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
…cker file Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
0963d7f to
496c296
Compare
Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
Signed-off-by: Azorji Kelechi Oliver <kelechioliver96@gmail.com>
…search-plugin chore(plugins): update with remote main branch
|
Hi @Oliver-ke, |
Sure, I'd share my testing.md file shortly |
|
TESTING.md Testing the OpenSearch pluginManual QA steps. Verifies datasource, log query, and trace → logs pivot. Prerequisites
1. Run unit testsInstall once at the workspace root so cd plugins
npm install
cd opensearch
npm test
npm run type-check
npm run lintValidate the plugin schemas with cd plugins/opensearch
percli plugin lint
percli plugin test-schemasGo SDK: cd plugins/opensearch
go test ./sdk/...Expect all green. No skipped suites. 2. Start a local OpenSearchUse the bundled compose file (also boots OpenSearch Dashboards on cd docs/examples
docker compose up -d
docker compose logs -f opensearch # watch boot, Ctrl+C once "started" appearsWait until ready: curl -s http://localhost:9200/_cluster/health | jq .statusExpect Teardown when done: docker compose down -v3. Seed sample log dataIndex two log documents with a shared curl -X POST "http://localhost:9200/otel-logs-2026.04.29/_doc" \
-H 'Content-Type: application/json' -d '{
"@timestamp": "2026-04-29T10:00:00Z",
"body": "request received",
"traceId": "abc123",
"severity": "INFO"
}'
curl -X POST "http://localhost:9200/otel-logs-2026.04.29/_doc" \
-H 'Content-Type: application/json' -d '{
"@timestamp": "2026-04-29T10:00:01Z",
"body": "request failed",
"traceId": "abc123",
"severity": "ERROR"
}'
curl -X POST "http://localhost:9200/otel-logs-2026.04.29/_refresh"Sanity-check PPL endpoint directly: curl -X POST "http://localhost:9200/_plugins/_ppl" \
-H 'Content-Type: application/json' \
-d '{"query":"source=otel-logs-* | where traceId='\''abc123'\''"}'Expect 2 datarows in response. 4. Build and serve the plugincd plugins/opensearch
npm run devDev server listens on 5. Register the datasource in PersesThe current Perses UI does not accept raw YAML for datasource creation, so create Option A — via the UI formNavigate to Admin → Global Datasources → Add Datasource. Fill in:
Save. Option B — via
|
| Case | Input | Expected |
|---|---|---|
| Empty query | `` | No request, no error toast |
| Bad PPL | source=foo | invalid |
Error banner with PPL error body (SyntaxCheckException) |
| Wrong index | source=does-not-exist |
Error banner with IndexNotFoundException from OpenSearch (the PPL endpoint returns 404; the plugin surfaces that as an OpenSearchPPLError) |
| Unreachable DS | stop docker, re-run query | Error banner, retry works after restart |
| Aggregation (stats) | source=otel-logs-* | stats count() by service |
Returns one row per service, no error |
| Field projection | source=otel-logs-* | fields service, body | head 10 |
Returns 10 rows with only those two columns |
7. Field overrides
Index a doc with non-standard field names:
curl -X POST "http://localhost:9200/legacy-logs/_doc" \
-H 'Content-Type: application/json' -d '{
"time": "2026-04-29T10:05:00Z",
"message": "legacy log line",
"trace_id": "abc123"
}'
curl -X POST "http://localhost:9200/legacy-logs/_refresh"In the query spec, set:
timestampField: time
messageField: message
query: source=legacy-logs | where trace_id='abc123'Expect row to render with time as timestamp and message as body. Without
the overrides, the row still renders without crashing: missing timestamp falls
back to epoch 0, and missing message falls back to a JSON dump of the row.
8. Trace → logs pivot
Load docs/examples/trace-to-logs.json as a dashboard. Requires a Tempo (or Jaeger) datasource also configured.
- Set
traceIdvariable toabc123. - Logs panel re-runs and shows the 2 seeded rows.
- Change variable to
xyz999. Logs panel goes empty without error. - Swap Tempo panel for Jaeger per
docs/examples/README.md. Logs panel unchanged.
9. Cleanup
cd plugins/opensearch/docs/examples
docker compose down -v
looks like a comprehensive test plan |
|
Hi @Oliver-ke, first of all great work! I have tested against a local OpenSearch with the opensearch_dashboards_sample_data_logs sample. I did not review the source-code. I have only tested the functionality and below are my findings. Bugs/Improvements
Screenshot 1
Screenshot 2Working after I add Timestamp field (optional) placeholder @timestamp implies the plugin will fall back gracefully if left blank. it doesn't
6.. PPL Query field is a plain textarea. It has no syntax highlighting, no autocomplete, no Query Examples disclosure (Please compare ClickHouse query editor in the same panel type).
http://localhost:9200/_cat/indices?v
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
green open otel-v1-apm-service-map-sample Nio8tHEHTWK3uNI4PHmyXA 1 0 49 0 17.7kb 17.7kb
yellow open otel-logs-2026.04.29 _3tNZKIVTU2eseJhMHb_cw 1 1 4 0 17kb 17kb
green open otel-v1-apm-span-sample 14q5nFcqQq-OwewznwFAZQ 1 0 13061 0 6.1mb 6.1mb
green open .ql-datasources KdnzV0EoT96Y1XpY9NfQ7A 1 0 0 0 208b 208b
green open top_queries-2026.05.22-84957 hi43N36cRxqNUGtFImwVSg 1 0 16 2 104.1kb 104.1kb
green open ss4o_metrics-otel-sample 2Z4CkH5JTGWQXW6NZgHc7A 1 0 39923 0 4mb 4mb
green open .kibana_1 uKgkLwTLThyWFoSS5ztn2Q 1 0 246 1 155.6kb 155.6kb
green open ss4o_logs-otel-sample EkQueqJiQJmMWszbrNRe-Q 1 0 16286 0 5.5mb 5.5mb
green open .plugins-ml-config QmvGWldgQ_-5TnMuA35KeQ 1 0 1 0 4kb 4kb
green open .opensearch-observability BqmQfOVnSNaeth8GcjBEkw 1 0 0 0 208b 208b
green open opensearch_dashboards_sample_data_logs OgFiObqIR4GAuT_GPovfFA 1 0 14074 0 7.4mb 7.4mb
green open opensearch_dashboards_sample_data_flights 1jp6tGfmSuqBK1YUjKYmNA 1 0 13059 0 5.5mb 5.5mb
green open opensearch_dashboards_sample_data_ecommerce dxMt1PfqSP-9-4G5Cd-WbQ 1 0 4675 0 3.9mb 3.9mb8 Error banner reads OpenSearch PPL request failed (400): Invalid Query — the actual details field from the response (e.g. can't resolve Symbol(name=@timestamp)) is dropped, removing the important info.
|
@ibakshay Thanks for the feedback. |
…ins into feature/opensearch-plugin chore: update with remote branch
| "@emotion/react": "^11.7.1", | ||
| "@emotion/styled": "^11.6.0", | ||
| "@hookform/resolvers": "^3.2.0", | ||
| "@perses-dev/components": "^0.54.0-beta.1", |
There was a problem hiding this comment.
last version is 0.54.0-beta.3 and please remove the usage of @perses-dev/core as it is deprecated and replace mostly by @perses-dev/spec and @perses-dev/client
There was a problem hiding this comment.
@Nexucis Package deps updated.
Doing another round of e2e. Meanwhile, open for full review.
There was a problem hiding this comment.
Thank you, @Oliver-ke. Did you also address my feedback?
There was a problem hiding this comment.
Yes, @ibakshay, I did, I was able to cover the items in scope for this PR.
Signed-off-by: Kelechi Oliver Azorji <kelechioliver96@gmail.com>
6c643e3 to
fbddd72
Compare
|
@shahrokni @Gladorme @jgbernalp can you please review the code ? Thank you ! |
|
Hi @Oliver-ke, seems like my suggestions in this comment are not worked on. All my comments are still valid. Can you please check and let me know. Thank you. |
|
@ibakshay Had to go through your feedbacks again. The following feedback (3, 8, 10, 11) has been clearly addressed on this PR. While 1, 2, and 6 have been partially addressed due to the increasing scope. Issues 4 and 7 also deferred as they result in a larger scope, while 5 and 9 are outside the plugin repo (Perses hosted UI). In summary, all feedback in scope for this PR, at least for the initial release, has been addressed. Further enhancements can be added in the coming PR's. |
…search-plugin chore: update with upstream remote
…ins into feature/opensearch-plugin chore: update with remote feature branch
I think I did not build properly before. Everything works as it is supposed to be now. Thank you for the clarification. |
…search-plugin chore: update with remote main branch
Signed-off-by: Kelechi Oliver Azorji <kelechioliver96@gmail.com>
9435174 to
073bda2
Compare
|
Resolved all outstanding comments. Bump perses version |
…search-plugin chore: update with remote main
|
Any new feedback on this PR? |
| }; | ||
|
|
||
| export interface LogQueryPlugin<Spec = UnknownSpec> extends Plugin<Spec> { | ||
| getLogData: (spec: Spec, ctx: LogQueryContext) => Promise<LogQueryResult>; |
There was a problem hiding this comment.
This interface is duplicated, we should import it instead from @perses-dev/shared as is missing important abort signals. https://github.com/perses/shared/blob/bc8729625dcaf7223c749084ce024917cd958975/plugin-system/src/model/log-queries.ts
| return { timestamp, line, labels }; | ||
| }); | ||
|
|
||
| return { entries, totalCount: entries.length }; |
There was a problem hiding this comment.
is this the total count or the count of results that were returned?
There was a problem hiding this comment.
Yeah, so this is the count of returned rows, not a total. PPL does not return a separate total-hits field, so this reflects what the query returned
| "@perses-dev/components": "^0.54.0-beta.3", | ||
| "@perses-dev/dashboards": "^0.54.0-beta.3", | ||
| "@perses-dev/explore": "^0.54.0-beta.3", | ||
| "@perses-dev/plugin-system": "^0.54.0-beta.3", | ||
| "@perses-dev/spec": "^0.2.0-beta.2", |
There was a problem hiding this comment.
This needs an update, sorry for that but we have published several betas with bug fixes and new features.
| "@perses-dev/components": "^0.54.0-beta.3", | |
| "@perses-dev/dashboards": "^0.54.0-beta.3", | |
| "@perses-dev/explore": "^0.54.0-beta.3", | |
| "@perses-dev/plugin-system": "^0.54.0-beta.3", | |
| "@perses-dev/spec": "^0.2.0-beta.2", | |
| "@perses-dev/components": "^0.54.0-beta.8", | |
| "@perses-dev/dashboards": "^0.54.0-beta.8", | |
| "@perses-dev/explore": "^0.54.0-beta.8", | |
| "@perses-dev/plugin-system": "^0.54.0-beta.8", | |
| "@perses-dev/client": "^0.54.0-beta.8", | |
| "@perses-dev/spec": "^0.2.0-beta.6", |
| "lint": "eslint src --ext .ts,.tsx", | ||
| "test": "cross-env LC_ALL=C TZ=UTC jest", | ||
| "type-check": "tsc --noEmit" | ||
| }, |
There was a problem hiding this comment.
We need to add the apache 2 license field
|
|
||
| const startIso = start.toISOString(); | ||
| const endIso = end.toISOString(); | ||
| const bound = `where \`${timestampField}\` >= '${startIso}' and \`${timestampField}\` <= '${endIso}'`; |
There was a problem hiding this comment.
not sure how likely would be, but since this is user-editable field it can contain ticks which might create an invalid or potential insecure query.
There was a problem hiding this comment.
I added escapeIdentifier() function, which just basically replaces backticks
Signed-off-by: Kelechi Oliver Azorji <kelechioliver96@gmail.com>
|
PR has been updated based on feedbacks |





Description
Adds an OpenSearch datasource + log query plugin
These changes includes:
remaining columns (e.g. traceId) as labels.
opensearch/docs/examples/trace-to-logs.json (Tempo + OpenSearch; Jaeger swap documented).
and 2 Go tests (builder JSON shape + omitempty for optional fields).
Screenshots
DataSource

Checklist
[<catalog_entry>] <commit message>naming convention using one of thefollowing
catalog_entryvalues:FEATURE,ENHANCEMENT,BUGFIX,BREAKINGCHANGE,DOC,IGNORE.UI Changes