Elasticsearch / OpenSearch logs monitoring with Topologix
Monitoring & Observability
Application logs stored in Elasticsearch 7.x / 8.x or OpenSearch 1.x / 2.x: per-service error counts with error types, the log error baselines and new-type warnings of the Graylog All Services source, or a windowed count of a Lucene query. The field names are configurable — the ECS names (service.name, log.level, …) are the defaults. (This is not the HTTP / JSON integration's Elasticsearch pack, which reads a cluster's own health.)
Requirements
- HTTP(S) reach to the cluster's REST port (default 9200) from the Topologix server or — for an on-prem cluster — from a proxy agent 1.265.0 or later. A proxy-polled source on an older proxy is refused at save ("needs proxy 1.265.0 or later").
- Elasticsearch 7.x / 8.x (self-managed or Elastic Cloud) or OpenSearch 1.x / 2.x. OpenSearch Serverless is not supported.
- Authentication: an Elasticsearch API key (the encoded value — base64 of
id:api_key— sent asAuthorization: ApiKey …), Basic (username + password), a Bearer token (an OpenSearch JWT, an Elasticsearch service-account token) or None for a cluster with security disabled. OpenSearch has no API keys: use Basic or Bearer. - Privileges: read and view_index_metadata on the log index patterns. The cluster monitor privilege is optional — with it the Test shows the cluster health. Example role:
{"indices":[{"names":["logs-*","filebeat-*"],"privileges":["read","view_index_metadata"]}],"cluster":["monitor"]}. - Self-signed HTTPS is accepted by unticking Verify TLS certificate.
1 · Set up the integration
- Open the Integrations pageClick Integrations in the toolbar and pick Elasticsearch / OpenSearch logs from the catalogue (Monitoring & Observability).
- Enter the URL and the credentialsE.g.
https://es.example.local:9200; choose API key, Basic, Bearer token or None. Every credential field is masked and sealed at rest. - Index patternsComma-separated; empty =
logs-*,filebeat-*,logstash-*. Every source of the integration reads these unless it names its own. - Field mapping (optional)Empty = ECS: timestamp
@timestamp, serviceservice.name, levellog.level, messagemessage, hosthost.name, typeerror.type, traceIdtrace.id. On a saved integration Load fields from the cluster reads_field_capsover the patterns and offers the names (aggregatable first); Use suggested fills the empty inputs. A field you group by (service, error type, host) must be a keyword field — for a text field pick its.keywordsub-field. Enter-for an optional field (host, error type, trace id) the logs do not have. Examples: Logstashservice→service.keyword, Kuberneteskubernetes.labels.app, Serilogfields.Application+ levellevel. - Create, then TestClick Create, reopen the integration and click Test:
GET /(flavour + version),_cat/healthand_field_capsover the patterns — “Connected — Elasticsearch 8.13.4 (green) · 12 indices match logs-*,filebeat-*,logstash-*” or “OpenSearch 2.11.1 (yellow) · …”, with four rows: Connection, Cluster health, Indices, Field mapping. No matching index, a missing field or a text field turns it amber with the reason (the.keywordadvice for a text field).
2 · Add it to a flow
- Select the service nodeClick the node, open Metrics, then + Add and choose the Elasticsearch integration.
- Pick the modeErrors per service (the log-baseline source) or Count a query.
- Narrow it (optional)A Lucene query (Elasticsearch query-string syntax, ≤ 500 characters; a bare word searches the message field) and the index patterns. Advanced: the error-line query that replaces the level rule, the sample lines per service (default 10, 1–50) and a field-mapping override.
- Alarm and saveErrors per service: the log-error baseline rule (baseline or a count threshold, N of M, new error types) and the optional RECAP correlation. Count: the threshold field. Click Add Source; the source stores the resolved index patterns and field mapping.
3 · The two modes
Errors per service
Error lines per service over each check, with the log error baselines of the Graylog All Services source — usual count per weekday and hour, breach rule, new-type warnings, the banner (Elasticsearch Errors), the Alerts page and the Catalogue Logs section. The window is the polling interval (30 s to 24 h).
- The level rule: ONE lenient
query_string— the level field holds one ofemergemergencyalertcritcriticalfatalerrerrorsevere(lower, UPPER or Capitalised) or a syslog number ≤ 3. Lenient matters: a list of words against a numeric level field would make Elasticsearch answer 400 for the whole search. Advanced → Error lines are replaces the rule with your own Lucene. - Two searches per check: (1) a
size:0search over the window — terms on the service field (Max services, default 200; lines without the field count as(unknown)), terms on the error-type field (10 per service) and the newest sample lines per service; (2) a cardinality of the service field over the last hour for Services reporting (if it fails, the counts stand and a caveat says so). - Error types are the error-type field's values (exact); lines without it are typed by fingerprints of the sampled lines (marked approx), with the same noise guard as Graylog. Services past Max services are one
(other)row with a caveat. A service seen in the last 24 hours with no errors reads a measured 0. - Both the 8.x nested and the 7.x flattened field names are read; numeric levels 0–7 and OpenTelemetry severity numbers 9–24 are understood. Sample lines are redacted (on the proxy, before they leave the network) and kept for View logs.
Count a query
The number of log lines matching a Lucene query over each check — one search, the exact total. Errors only adds the level rule. Alarm on it with the threshold field (the source health rule reads value).
No value is not zero
Every failure reads no value with the reason, never 0: a 401 / 403 (“needs the read and view_index_metadata privileges”), index patterns that match no index (“No index matches logs-* — check the index patterns”), every shard failing, an unreachable cluster. A partial shard failure keeps the count and names it in a caveat.
- A text service / error-type / host field cannot be grouped: the source reads no value with “service.name is a text field: pick its .keyword sub-field (service.name.keyword) in the field mapping” — the Test row says the same before you add a source.
Catalogue and AI
The integration's services (the service field's values of the last 24 hours) appear in the Catalogue; an Errors-per-service source adds them to its Logs section. The runtime host of a service is probed from host.ip / the host field / kubernetes.pod.name / kubernetes.node.name. The AI log auto-scan and the per-source Analyze with AI stay Graylog-only.
Searching
Beyond the counts, the same integration is searched live — every search is ONE absolute window (gte / lt in epoch milliseconds) over the source's index patterns and field mapping (else the integration's), a service / host / trace id is an exact filter (the field or its .keyword, then only the exact value is kept), a text-field error is retried once on the .keyword sub-field, and every line is redacted (on the proxy, then with the organisation's rules). Through a proxy the searches need 1.265.1 or later — an older proxy is told “update the proxy” and asked nothing.
- View logs (the card, the banner rows, the Catalogue Logs section): the lines cached by the last checks first, else one search of the service's error lines over the window (≤ 7 days, ≤ 200 lines) — with Ack, groups and errors-only as for Graylog.
- The monitoring bot (
search_logs, sourceelasticsearch): a service, host or trace id over the asked window (≤ 24 h, 100 lines, the coverage names the indices, the query and the total); text alone is a fleet search — the exact matching-line count per host (the busiest 20, and how many lines are on other hosts) plus the newest 20 lines, over up to 7 days. - Forensics → Advanced → Elasticsearch (Lucene): your Lucene over the page's window (≤ 31 days and the Admin range), newest first with a histogram, ≤ 1000 lines (Admin → Forensics → Elasticsearch lines), on the integration's wired index patterns unless Elasticsearch: any index is on. KQL is Kibana's own language and is not accepted. The Logs section reads the service's lines the same way.
- Change reports: the warn-or-worse lines of each changed service (≤ 1 day, ≤ 100 a service) beside Graylog's, within the same 25 reads a pass; the report names “Elasticsearch · <integration>”.
- Zipkin / OTLP traces: pick the integration under Augment with logs (Graylog or Elasticsearch) — the trace viewer's “Show logs” reads the lines carrying the trace id (the mapped trace field,
trace.idby default) over the last hour, ≤ 100 lines. Set up monitoring can link it too, so each node's “Search logs & traces” lists the service's lines.
Good to know
- Every read is an ABSOLUTE window (
gte/ltin epoch milliseconds from the poller's clock), so a proxy and the server count the same window. - Index patterns accept letters, digits and
_ . * , : + -(a remote clustercluster:logs-*works); a missing index is not an error by itself — the shards guard tells "no index matches" apart from a quiet window. - Keep one mapping per pattern set: when the patterns match indices that map the SAME field differently (a keyword
host.namein one, a text one in another), the indices with the text mapping fail their shards — counts and fleet host counts then cover only the shards that answered, and the answer says so (“cover only the shards that answered … narrow the index patterns”). Give such indices their own source / patterns, or map the field as keyword everywhere.
More Monitoring & Observability integrations
See it on your own infrastructure
Book a demo and we will walk you through Topologix with your systems in mind: on our cloud, or self-hosted in your own network.