Files
zeekay 1d8454b827 docs: say /v1/ where the docs mean our own API
Left at /api/, because these are not ours to rename:
- the vendor endpoint catalogues under products/warehouse_sources
- the ingress route table in docs/internal/feature-flags, which really does
  send /api/* to Django
- ActiveCampaign's own /api/3 path segment
- the billing service's routes in the Vercel sequence diagrams
- the Slack event and callback URLs, which are registered in the Slack app
  config and have to byte-match what the code still builds
2026-08-08 18:40:35 -07:00

13 KiB
Raw Permalink Blame History

Query log entries with filtering by severity, service name, date range, search term, and structured attribute filters. Supports cursor-based pagination. The response schema (see the tool's typed output) lists every returned field — prefer severity_text over severity_number / level, and be aware that trace_id and span_id return zero-padded strings rather than null when unset.

Use logs-attributes-list and logs-attribute-values-list to discover available attributes before building filters.

Workflow — follow this order every time

  1. Discover services first. Call logs-attribute-values-list with key: "service.name" and attribute_type: "resource" to see available services.
  2. Explore resource attributes. Call logs-attributes-list with attribute_type: "resource" to discover resource-level attributes (e.g. k8s.pod.name, k8s.namespace.name). Then call logs-attribute-values-list with attribute_type: "resource" for relevant attributes to validate what data exists.
  3. Explore log attributes if needed. Call logs-attributes-list (defaults to log attributes) and logs-attribute-values-list to discover log-level attributes.
  4. Size the total volume with logs-count. Call logs-count with the discovered serviceNames and filters. If it exceeds query-logs's max limit of 1000 — or if the user's question is about when something happened — continue to step 5.
  5. Find where the volume sits with logs-count-ranges. Call logs-count-ranges to get time-bucketed counts. Each bucket carries explicit date_from/date_to you can pass straight back as the next call's dateRange to drill into a sub-range. Recurse up to 3–4 levels to narrow onto a spike or a specific window. Stop when the bucket width drops below your precision goal (e.g. 1 minute).
  6. Only then query logs. Once the count is in range and the window is right-sized, call query-logs with serviceNames and any additional filters.

Many cheap calls (attribute/value queries, counts, count-ranges) beat one expensive query-logs. Prefer thorough exploration over speculative log searches.

CRITICAL: Be minimalist. Only include filters and settings that are essential to answer the user's specific question. Default settings are usually sufficient unless the user explicitly requests customization.

MANDATORY: Never call query-logs without setting serviceNames or at least one log_resource_attribute filter. Unfiltered log queries are too broad, expensive, and noisy. If the user hasn't specified a service, use the workflow above to discover services first, then ask or infer.

MANDATORY: Always pass query.dateRange explicitly (e.g. { "date_from": "-1h" }). Omitting it fails with a 400 parse_error ("Input should be a valid dictionary or instance of DateRange") — the server does not fall back to a default window.

All parameters must be nested inside a query object.

Data narrowing

Property filters

Use property filters via the query.filterGroup field to narrow results. Only include property filters when they are essential to directly answer the user's question.

When using a property filter, you should:

  • Choose the right type. Log property types are:
    • log — filters the log body/message. Use key "message" for this type.
    • log_attribute — filters log-level attributes (e.g. "k8s.container.name", "http.method").
    • log_resource_attribute — filters resource-level attributes (e.g. k8s labels, deployment info).
  • Use logs-attributes-list to discover available attribute keys before building filters.
  • Use logs-attribute-values-list to discover valid values for a specific attribute key.
  • Find the suitable operator for the value type (see supported operators below).

Important: The logs-attributes-list and logs-attribute-values-list tools default to attribute_type: "log" (log-level attributes). To search resource-level attributes (e.g. k8s.pod.name, k8s.namespace.name), you must explicitly pass attribute_type: "resource". Forgetting this will return log-level attributes when you intended resource-level ones.

Supported operators:

  • String: exact, is_not, icontains, not_icontains, regex, not_regex
  • Numeric: exact, gt, lt
  • Date: is_date_exact, is_date_before, is_date_after
  • Existence (no value needed): is_set, is_not_set

The value field accepts a string, number, or array of strings depending on the operator. Omit value for is_set/is_not_set.

Filtering logs by a Insights person

When the user references a person — by distinct_id, name, email, or via a prior persons-retrieve call — filter logs to that person via log_attribute filters. The attribute keys are configurable per project (they default to ["insightsDistinctId"]); read logs_distinct_id_attribute_keys from the /v1/projects/:id/logs_config/ endpoint and use those as the filter keys.

If a person has multiple distinct_ids, pass the array as the filter value with operator exact (matches any of them):

{
  "query": {
    "serviceNames": ["<service>"],
    "filterGroup": [
      {
        "key": "insightsDistinctId",
        "operator": "exact",
        "type": "log_attribute",
        "value": ["<distinct_id_1>", "<distinct_id_2>"]
      }
    ]
  }
}

Entries in the filterGroup array are combined with AND, so never put two distinct-id keys in one call — a log only carries one of them. When the team has multiple keys configured, run one query per key and merge the results.

Do not invent a different attribute key based on what looks plausible — use the configured keys. If the configured keys return zero results, the customer's logs pipeline may not stamp person identity at all; tell the user rather than guessing.

Time period

Use the query.dateRange field to control the time window — pass it on every call. If the question doesn't mention time, use the last hour: { "date_from": "-1h" }. Examples of relative dates: -1h, -6h, -1d, -7d, -30d.

Parameters

All parameters go inside query.

query.severityLevels

Filter by log severity: trace, debug, info, warn, error, fatal. Omit to include all levels.

This filter is an exact match against the response's severity_text field using these six lowercase buckets — it is not a numeric range and not case-insensitive. If a service ingests non-canonical severity strings (e.g. "ERROR", "Warning", "err"), severityLevels: ["error"] will not match them and you will get zero rows. When a severity filter returns nothing unexpectedly, discover the actual stored values with logs-attribute-values-list { key: "severity_text" } and either filter on the value you find or fall back to a searchTerm. See "Severity fields in the response" below for the severity_text / severity_number / level mapping.

query.serviceNames

Filter by service names. Use logs-attribute-values-list with key: "service.name" and attribute_type: "resource" to discover available services.

query.searchTerm

Full-text search across log bodies. Use this when the user is looking for specific text in log messages.

query.orderBy

Sort by timestamp: latest (default) or earliest.

query.filterGroup

A list of property filters to narrow results. Each filter specifies key, operator, type (log/log_attribute/log_resource_attribute), and optionally value. See the "Property filters" section above.

query.dateRange

Date range to filter results. Required in practice: omitting it fails with a 400 parse_error, so always pass it (use { "date_from": "-1h" } when the question doesn't mention time).

  • date_from: Start of the range. Accepts ISO 8601 timestamps or relative formats: -1h, -6h, -1d, -7d, -30d.
  • date_to: End of the range. Same format. Omit or null for "now".

query.limit

Maximum number of results (1-1000). Defaults to 100.

query.after

Cursor for pagination. Use the nextCursor value from the previous response.

query.excludeAttributes

Set true to drop the per-log attributes and resource_attributes maps from results (the maps stay present but empty). These maps can hold large values, so excluding them keeps big result sets compact — set it when you only need body, severity_text, timestamp, and service-level fields and not the full attribute maps. Defaults to false.

Examples

List recent error logs

{
  "query": {
    "severityLevels": ["error", "fatal"],
    "serviceNames": ["<service>"],
    "dateRange": { "date_from": "-1h" }
  }
}

Search for a specific log message

{
  "query": {
    "searchTerm": "connection refused",
    "serviceNames": ["<service>"],
    "dateRange": { "date_from": "-6h" }
  }
}

Filter logs from a specific service

{
  "query": {
    "serviceNames": ["api-gateway"],
    "dateRange": { "date_from": "-1d" }
  }
}

Filter by a log attribute

{
  "query": {
    "serviceNames": ["<service>"],
    "filterGroup": [{ "key": "http.status_code", "operator": "exact", "type": "log_attribute", "value": "500" }],
    "dateRange": { "date_from": "-1d" }
  }
}

Combine severity and attribute filters

{
  "query": {
    "severityLevels": ["error"],
    "filterGroup": [
      { "key": "k8s.container.name", "operator": "exact", "type": "log_resource_attribute", "value": "web" }
    ],
    "dateRange": { "date_from": "-12h" }
  }
}

Filter by log body content using property filter

{
  "query": {
    "serviceNames": ["<service>"],
    "filterGroup": [{ "key": "message", "operator": "icontains", "type": "log", "value": "timeout" }],
    "dateRange": { "date_from": "-1h" }
  }
}

Check if an attribute exists

{
  "query": {
    "serviceNames": ["<service>"],
    "filterGroup": [{ "key": "trace_id", "operator": "is_set", "type": "log_attribute" }],
    "dateRange": { "date_from": "-1h" }
  }
}

Severity fields in the response

Each returned log row carries three overlapping severity fields. Read and report severity_text; treat the other two as redundant:

Field What it is Use it for
severity_text Canonical severity string. Prefer this. Filtering (severityLevels), grouping, and anything you show a user.
severity_number OpenTelemetry numeric severity (1–24). Redundant with the text. Sorting by exact severity, or interop with OTel tooling.
level Datastore alias for severity_text. Redundant. Ignore — prefer severity_text.

severity_number maps to the severityLevels buckets by OTel range. Use this when you only have a number and need the bucket, or vice-versa:

Bucket severity_number range Canonical severity_text
trace 1–4 trace
debug 5–8 debug
info 9–12 info
warn 13–16 warn
error 17–20 error
fatal 21–24 fatal

When the user asks for "warnings and above", that is severityLevels: ["warn", "error", "fatal"] — there is no numeric >= operator on the top-level severity filter.

If the query fails (500 / timeout)

A query-logs call that returns a 500 almost always means the query scanned too much data and timed out server-side — it is rarely a bug in your filters. Do not retry the same call. Instead, narrow and re-size:

  1. Shorten dateRange (e.g. -1h instead of -1d).
  2. Add serviceNames or a log_resource_attribute filter to reduce the scan.
  3. Size the volume with logs-count, then locate the busy window with logs-count-ranges, before pulling rows again.

Reminders

  • Always set serviceNames or a resource attribute filter. Never run a broad unfiltered log query.
  • Limit dateRange to at most -1d (24 hours) unless the user explicitly requests a longer range.
  • When using logs-attributes-list or logs-attribute-values-list, remember they default to attribute_type: "log". Pass attribute_type: "resource" to search resource-level attributes.
  • Ensure that any property filters are directly relevant to the user's question. Avoid unnecessary filtering.
  • Use logs-attributes-list and logs-attribute-values-list to discover attributes before guessing filter keys/values.
  • Prefer searchTerm for simple text matching; use filterGroup with type log and key message for regex or exact matching.