Query log Functions via Netdata Cloud
This guide is part of the query-netdata-cloud skill.
Read the SKILL.md prerequisites first.
For the generic Function transport and the canonical protocol
reference, see query-functions.md. The
authoritative protocol spec is
<repo>/src/plugins.d/FUNCTION_UI_REFERENCE.md (specifically the
"Log Explorer Format" section).
Log Functions are the Log Explorer class of Functions
(has_history: true in their info response). They return a
time-windowed skim of a larger log dataset, with facets
(per-field value counts) for drill-down and an optional
histogram (bucketed counts over time) for context.
Three log Functions exist today, each backed by a different log source. Their request and response shapes follow the same standard envelope, but the journal-field set differs per source:
| Function | Source | Notes |
|---|---|---|
systemd-journal |
systemd journal namespaces (system, user, namespace-specific, remote-forwarded) | Linux nodes |
windows-events |
Windows event log channels | Windows nodes |
otel-logs |
OpenTelemetry logs ingested by the agent | Any node with the OTEL log receiver enabled |
Confirm which are registered on a node via the
function-listing endpoint in
query-functions.md. Field names below are
illustrative for systemd-journal; the same Function payload keys
(after, before, last, query, facets, histogram,
__logs_sources, ...) apply to the other two -- only the values
and column names differ per source.
Endpoint
POST /api/v2/nodes/{nodeId}/function?function=systemd-journal
Same shape as any other Function call. The body is the
systemd-journal Function's payload.
Discover the Function's parameters
Always start with info=true to confirm the current schema -- the
Function's parameter set evolves across agent versions.
TOKEN="YOUR_API_TOKEN"
NODE="YOUR_NODE_UUID"
read -r -d '' PAYLOAD <<'EOF'
{ "info": true }
EOF
curl -sS -X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $TOKEN" \
"https://app.netdata.cloud/api/v2/nodes/$NODE/function?function=systemd-journal" \
-d "$PAYLOAD"
The accepted_params array in the response tells you which keys the
agent currently accepts.
Body keys (current as of STATUS_FILE_VERSION = 28)
| Key | Type | Purpose |
|---|---|---|
info |
bool | Discovery only; do not combine with a real query |
after |
int | Unix ms timestamp; lower bound. Negative = relative seconds from before |
before |
int | Unix ms timestamp; upper bound. Negative = relative seconds from now |
last |
int | Page size (rows). Default 200 |
direction |
string | backward (default; newest first) or forward |
anchor |
int | Per-row cursor for pagination |
query |
string | Free-text search across journal fields |
facets |
string[] | Field names to group by (returns counts per value) |
histogram |
string | Field name to bucket-by-time |
__logs_sources |
string | Source selector. Common values: all, all-local-logs, all-local-system-logs, all-local-user-logs, all-local-namespaces, plus per-namespace strings like <namespace-name> for a specific journal namespace |
if_modified_since |
int | Tail mode -- skip if no new data |
data_only |
bool | Skip metadata for a faster query |
sampling |
int | Cap on rows scanned when search would otherwise be huge |
slice |
bool | Native backend filter (faster, less flexible) |
delta |
bool | Incremental histogram updates |
tail |
bool | Append-mode (combine with if_modified_since) |
info=true returns the current authoritative list; rely on it, not
this table, when in doubt.
Response shape
The response uses the standard Function envelope (top-level
keys status, v, type, help, accepted_params,
required_params, has_history, update_every, data, ...).
For log Functions, type is the source name (logs family
discriminator). Verified live against the agent-events node:
| Top-level key | Description |
|---|---|
status |
HTTP-style status integer (200 on success) |
v |
Function schema version |
type |
Family discriminator (carries logs-family value) |
help / accepted_params / required_params |
Discovery metadata (see query-functions.md) |
data |
Array of row arrays -- this is the result rows |
columns |
Object keyed by column name; per-column metadata: index (position in each row of data), name (display label), type (string / timestamp / integer / ...), visible, unique_key, sort, summary (count / min / max / sum / ...), filter (e.g. range), visualization, value_options (for transforms like datetime_usec) |
facets |
Array of facet records: {id, name, options[]} where each option is {id, name, count}. Use to drill down by field value. |
histogram |
If requested: time-bucketed counts. Object with chart, id, name, plus per-bucket data |
pagination |
Cursor info (anchor, direction, last, ...) for the next page |
default_charts |
Suggested chart configuration |
default_sort_column |
Recommended sort column |
available_histograms |
Field names that the agent can histogram-bucket |
_request |
Echo of the parsed request (defaults applied) |
versions |
Source/version map for cache invalidation |
last_modified |
Last-data timestamp |
expires |
Suggested cache expiry |
partial |
True if the result was capped by sampling or timeout |
message |
Optional info / warning string |
_journal_files / _fstat_caching / _sampling / _stats |
systemd-journal-specific debug counters |
Reading rows
data is an array of rows. Each row is itself an array whose
positions match columns.<key>.index. To pretty-print a single
row by column name:
jq '.columns as $c
| .data[0] as $row
| $c | to_entries
| sort_by(.value.index)
| map({(.key): $row[.value.index]})
| add' response.json
Multi-value field selections (AND-of-OR filtering)
The selections POST-payload key is a structured field-filter
mechanism the Netdata systemd-journal Function (powered by the
libnetdata facets engine) supports. It is distinct from raw
journalctl's KEY=value matches: a single field can carry
multiple allowed values, and multiple fields are AND'd.
Shape
selections is an object whose keys are journal field names
and whose values are arrays of allowed values:
{
"selections": {
"FIELD1": ["A", "B", "C"],
"FIELD2": ["D", "E"]
}
}
Semantics (verified at
<repo>/src/libnetdata/facets/logs_query_status.h:386-466):
- Between fields: AND. All listed fields must match.
- Between values for the same field: OR. Any one of the listed values matches.
So the example above is logically:
(FIELD1 in A, B, C) AND (FIELD2 in D, E)
Why this matters for performance
A namespace can hold tens of thousands to hundreds of thousands
of records per day. A bare query (FTS) scans every record's
indexed text fields. Structured selections matches use the
facet engine's per-field index, which is dramatically faster
once the time window is fixed.
Rule of thumb: narrow with selections first, then refine
with query (FTS) only as a residual narrower over the
already-sliced subset.
Reserved keys inside selections
__logs_sources(perLQS_PARAMETER_SOURCE,logs_query_status.h:407) is treated as the source-type filter (e.g.all-local-namespaces,<namespace-name>). Using it insideselectionsis equivalent to setting the top-level__logs_sourcesparameter.queryinsideselectionsis ignored (logs_query_status.h:398); use the top-levelquery.
Example: structured filter + FTS narrower
{
"after": -86400,
"before": 0,
"last": 500,
"__logs_sources": "agent-events",
"selections": {
"AE_AGENT_HEALTH": ["crash-first", "crash-loop", "crash-repeated", "crash-entered"],
"AE_AGENT_VERSION": ["v2.10.0", "v2.10.0-135-nightly"]
},
"query": "deadlock"
}
This selects the cross-product of crash-class records on those
two versions (index-resolved), then FTS-filters the result for
the substring deadlock. Index-friendly even on a
~200k-records-per-day namespace.
Anti-pattern (avoid)
{
"after": -604800,
"before": 0,
"__logs_sources": "agent-events",
"query": "SIGSEGV"
}
A 7-day FTS over the entire namespace with no structured
narrowing. Slow and costly on large namespaces. Always pair FTS
with at least one structured selections field.
Examples
Example 1: most recent 50 entries from a specific namespace
TOKEN="YOUR_API_TOKEN"
NODE="YOUR_NODE_UUID"
NAMESPACE="systemd" # or "agent-events", "any-namespace-name"
read -r -d '' PAYLOAD <<EOF
{
"after": -3600,
"before": 0,
"last": 50,
"direction": "backward",
"__logs_sources": "${NAMESPACE}"
}
EOF
curl -sS -X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $TOKEN" \
"https://app.netdata.cloud/api/v2/nodes/$NODE/function?function=systemd-journal" \
-d "$PAYLOAD"
Example 2: full-text search with histogram
TOKEN="YOUR_API_TOKEN"
NODE="YOUR_NODE_UUID"
read -r -d '' PAYLOAD <<'EOF'
{
"after": -86400,
"before": 0,
"last": 100,
"query": "OOM",
"histogram": "PRIORITY",
"facets": ["_SYSTEMD_UNIT", "PRIORITY"],
"__logs_sources": "all-local-system-logs"
}
EOF
curl -sS -X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $TOKEN" \
"https://app.netdata.cloud/api/v2/nodes/$NODE/function?function=systemd-journal" \
-d "$PAYLOAD"
Example 3: paginate forward from a known anchor
TOKEN="YOUR_API_TOKEN"
NODE="YOUR_NODE_UUID"
ANCHOR=1700000123456789 # cursor from previous response
read -r -d '' PAYLOAD <<EOF
{
"anchor": ${ANCHOR},
"direction": "forward",
"last": 200,
"__logs_sources": "all-local-logs"
}
EOF
curl -sS -X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $TOKEN" \
"https://app.netdata.cloud/api/v2/nodes/$NODE/function?function=systemd-journal" \
-d "$PAYLOAD"
Limits and gotchas
- Time bounds are unix-microseconds, not seconds, when given as
positive integers. Negative integers are relative seconds (
-3600= "one hour ago relative tobefore"). Mixing units is the most common bug. - Default cloud timeout is 120 s, but very large queries
(thousands of rows over weeks of data) can hit it. Narrow the
window or use
sampling. __logs_sourcesis required to scope to a specific journal namespace. Without it, the query targets all-local-logs which on a busy host can be hundreds of GB.- Permission: the cloud token must have a role that includes
log-read access (function tags include
logs).scope:allworks;scope:grafana-plugindoes NOT. - Response can be tens of MB when
facetsinclude high-cardinality fields (MESSAGE_ID,_BOOT_ID,_PID). Pick facets carefully.
Discovering a journal namespace
If the host runs journalctl --namespace=<name>, the same name is
the value of __logs_sources. The agent's info=true response
enumerates all visible sources under accepted_params._logs_sources
or under a required_params widget -- inspect that widget's
options[] to learn which sources the node actually exposes.