A Prometheus exporter that runs custom OpenSearch queries on a schedule and exposes results as metrics. Designed for Kubernetes deployments where teams define their own queries via ConfigMaps.
- Go 1.26+ for building from source
- An OpenSearch cluster reachable over HTTPS (TLS only)
- Credentials with read access to the indices you want to query
- Query-focused: Only exposes metrics you define, no noise from cluster internals
- Secure: TLS-only with credential failover support
- Efficient: Concurrent background query execution, minimal footprint
- Resilient: Configurable error handling (preserve/drop/zero) per query
- Safe:
max_query_rangerejects queries that would hit warm/cold storage - Kubernetes-native: Health probes, env var expansion in config, ConfigMap-friendly
make build
make testEnvironment variables are expanded in config files ($VAR or ${VAR}).
opensearch_url: https://opensearch.example.com:9200
ca_cert_path: /certs/ca.crt
insecure: true
credentials:
- username: "$LOGS_USERNAME"
password: "$LOGS_PASSWORD"
- username: "$LOGS2_USERNAME"
password: "$LOGS2_PASSWORD"
timeout: 30s
max_query_range: 168h
# Optional - cluster stats collectors (all disabled by default)
collect_cluster_health: false
collect_nodes_stats: false
collect_indices_stats: false
queries: []Queries can be in the global config or in separate files loaded via -queries-dir:
queries:
- name: error_count
support_group: observability
service: logs
interval: 300s
indices: "logs-*"
on_error: preserve
on_missing: drop
query:
size: 0
query:
bool:
must:
- match: { level: "ERROR" }
- range: { "@timestamp": { gte: "now-5m" } }In order to validate your queries, update example file or create new *.yaml file in queries dir. After you prepare your quries run:
make validate-queries/config/
├── config.yaml # Connection settings
└── queries/ # Per-team query files
├── sre.yaml
├── security.yaml
└── platform.yaml
opensearch-query-exporter -config /config/config.yaml -queries-dir /config/queries/| Field | Required | Default | Description |
|---|---|---|---|
name |
yes | — | Query identifier, used in metric names |
support_group |
yes | — | Team owning this query (label on metrics) |
service |
yes | — | Service this query monitors (label on metrics) |
interval |
no | 300s | How often to run the query |
indices |
no | _all |
OpenSearch indices to query |
query |
yes | — | OpenSearch query body |
on_error |
no | drop |
What to do when query fails |
on_missing |
no | drop |
What to do when a metric disappears |
metrics |
no | — | Custom metric extraction from response |
| Strategy | on_error (query fails) |
on_missing (metric disappears) |
|---|---|---|
preserve |
Keep last successful value | Keep last known value |
drop |
Remove all metrics (default) | Remove missing metrics (default) |
zero |
Reset values to 0 | Set missing to 0 |
Setting max_query_range: 168h (7 days) rejects any query with a time range exceeding that limit at startup. This prevents queries from accidentally hitting warm/cold storage tiers.
-config string Configuration file path (default "config.yaml")
-queries-dir string Directory containing additional query files (*.yaml)
-listen-address string Metrics server address (default ":9206")
-opensearch-url string OpenSearch URL override
-insecure Skip TLS verification
-timeout duration Query timeout override
-log-level string Log level: debug, info, warn, error (default "info")
| Path | Description |
|---|---|
/metrics |
Prometheus metrics |
/healthz |
Liveness probe (always 200) |
/readyz |
Readiness probe (pings OpenSearch) |
opensearch_up- Cluster reachability (1=up, 0=down)
opensearch_query_{name}_hits- Total hits from queryopensearch_query_{name}_took_milliseconds- OpenSearch execution timeopensearch_query_success{query, support_group, service}- 1 if last run succeededopensearch_query_duration_seconds{query, support_group, service}- End-to-end duration
Automatically extracted from nested aggregations with bucket keys as labels:
opensearch_query_{name}_{agg}_{sub_agg}_doc_count{bucket_key="value"} 42
Enable via config:
collect_cluster_health: true- cluster status, shard counts, node countscollect_nodes_stats: true- per-node JVM, transport, thread poolscollect_indices_stats: true- aggregate index statistics
queries:
- name: custom_metrics
support_group: observability
service: api
query: { ... }
metrics:
- name: response_time_p99
path: aggregations.response_time.values.99.0
help: "99th percentile response time"
labels:
environment: "prod"
label_paths:
region: aggregations.by_region.keycmd/exporter/ Main application
pkg/config/ Configuration loading, validation, env expansion
pkg/opensearch/ HTTP client with TLS and credential failover
pkg/metrics/ Prometheus collector with background query execution
pkg/parser/ Response parsing (queries, aggregations, cluster stats)
This project is open to feature requests/suggestions, bug reports etc. via GitHub issues. Contribution and feedback are encouraged and always welcome. For more information about how to contribute, see our Contribution Guidelines.
If you find any bug that may be a security problem, please follow our instructions in the security policy on how to report it. Please do not create GitHub issues for security-related doubts or problems.
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone. By participating in this project, you agree to abide by its Code of Conduct at all times.
Copyright 2026 SAP SE or an SAP affiliate company and opensearch-query-exporter contributors. Please see our LICENSE for copyright and license information. Detailed information including third-party components and their licensing/copyright information is available via the REUSE tool.