Skip to content
Console
Browse documentation
Guide

Search logs, traces, and metrics

Ask your agent to search raw telemetry when a customer story or Issue needs a deeper service-level investigation.

On this page

Raw signals are the deeper layer beneath the customer and Issue workflows. Use them when the problem has no customer, when correlation is incomplete, or when you need service-wide context. Your agent searches and reads the signals. You open the trace page it links to when you want to check a trace yourself.

Ask your agent

Search the orders-api logs from the last 30 minutes for errors. Find one trace that failed, show me its critical path, and give me the link to the trace.

Job MCP tool or action CLI command
Search logs by text, service, level, trace, customer, or a typed field filter search_logs anectico logs query
Follow new logs as they arrive, continuously no MCP tool; use the stream route below anectico logs tail
Take a bounded, resumable sample of new logs read_log_tail anectico logs tail-read
Cluster recurring log templates get_log_patterns anectico logs patterns
Count logs grouped by a field get_log_patterns with a group-by field anectico logs aggregate
List the log fields you can filter on list_log_fields anectico logs fields
Read one exact log, and the lines around it get_log_entry, get_log_context anectico logs get, anectico logs context
Find traces by service, operation, customer, duration, or errors search_traces anectico traces search
Read one trace, and its critical path get_trace, get_trace_critical_path anectico traces get, anectico traces critical-path
Span statistics per service and operation aggregate_traces anectico traces aggregate
See the service dependency map get_service_map anectico traces service-map
Search traces, metrics, and logs with one query unified_search anectico search unified
Find everything correlated with a trace or a query correlate_signals anectico search correlate
Read service health and metrics get_service_health, query_metrics anectico metrics query

The logs tools need logs:read. The trace tools need traces:read. unified_search and correlate_signals need search:read. Metrics need metrics:read. MCP calls also need mcp:read.

Read the window before the result

Every technical search, aggregate, discovery and metric read reports read_coverage (data.read_coverage in MCP; read_coverage in CLI JSON). Each source has its normalized requested_interval, actual effective_interval, retention_floor, outcome, availability and reason. Times are UTC with full sub-second precision; intervals include the start and exclude the end: [start, end).

For partially_covered, report results over the effective interval and say the earlier part was not read. For outside_retention, there is no effective interval and nothing was read; an empty page is not evidence of zero errors, zero spend or no activity. Unknown history and failed sources also cannot establish absence. Read coverage is separate from row truncation, withheld fields and intake sampling: upstream_completeness remains unknown. A service map or forecast may also narrow its window for a read_limit; use its effective bounds.

Exact reads disclose the history floor. They return outside_retention without object content when an authorized record clock or supplied trace window proves expiry. A missing ID whose clock is unknown remains not found. There is no archive or recovery for expired history; internal backups are not a customer recovery feature. See read coverage.

What your agent gets back

search_logs returns up to 50 lines per page. Log bodies are truncated, and each line carries its trace correlation. When since is absent, MCP search_logs supplies its own explicit 24-hour window and says so in its output. search_traces returns up to 20 traces per page, each with its root operation and ID, and each with its own url. The search headline uses the service's matching count when counted; otherwise it explicitly describes this page and says the total was not counted. data.count is always the rows shown. Follow pagination.next_cursor as cursor with the same filters and window while pagination.has_more is true. A returned page is not the whole matching set.

Exact trace reads return at most the earliest 1,000 spans by start time in the selected retained data. get_trace shows at most 40 of those spans, choosing error spans first and then the slowest remaining spans; it can omit errors even within that prefix. It also returns the url of the trace page.

get_trace omits raw span attributes, resource attributes and model-call fields for every credential. Content scope does not add them to its projection. Use anectico traces get <trace-id> -o json for the full returned span fields; content remains subject to agents:content:read and the project's content policy. Omitted fields are not evidence that nothing was captured.

Check partial, returned_span_count, total_span_count, and spans_shown before drawing a conclusion. If the 1,000-span read is partial, the MCP envelope has status: partial, its truncation counts shown spans against the true total, and whole-trace count, duration, service count, error count and status are absent because they are unknown. Errors and decisive ordinary spans may exist outside the returned prefix. A complete read still reports exact whole-trace summary values even when MCP shows only 40 spans. anectico traces get shows the returned and total counts with a partial warning; -o json preserves the span response. These detail reads have no continuation cursor for the omitted spans.

Linked-consumer discovery is also bounded to 64 traces. linked_traces_truncated: true means that causal graph is incomplete, even if fewer than 1,000 spans were returned. The full matching span count is then unknown: REST uses total_span_count: 0; MCP omits that total and reports status: partial. Critical-path analysis is withheld for either cap. Each shown span carries its own trace_id, span_id and parent reference; use the owning trace ID for a linked-span follow-up. The CLI table keeps those IDs and labels unknown totals explicitly.

Search counts are exact for the complete selected project, time window, and filter set, whatever the current page is. The API does not return server-computed highlight ranges. Field discovery is deliberately bounded: values_truncated and fields_truncated say when more values or structured keys exist.

Open the proof

A trace has a proof page at /view/trace/:traceId. It is read-only. It names its project, and its one action is Copy link. Use the url that search_traces, get_trace or get_trace_critical_path returns. See Proof pages. Log lines and metric series have no page. Ask the agent to quote the query, the window, and the log ID, so that you can run the same search again.

Logs

Begin with a short window and a service, trace ID, customer ID, or error level.

Use live tail for active reproduction, not for historical analysis. A stream that is paused or dropped resumes from its opaque server cursor and backfills the logs that arrived in the gap. A change of project, environment, query, service, or severity is a new subscription: start it without the old cursor. An agent cannot hold a stream open inside one tool call; it takes a bounded sample instead.

From the CLI:

anectico logs query --service orders-api --since 30m
anectico logs query --since 2026-08-02T21:58:18Z --until 2026-08-02T22:07:15Z
anectico logs tail --service orders-api --level error
anectico logs tail --distinct-id buyer@acme.example
anectico logs tail-read --service orders-api --level error --wait 10 --limit 50

logs query accepts positive lookback durations or exact RFC 3339 bounds. Exact bounds are normalized to UTC. Include Z or an explicit numeric offset, and make --until later than --since.

Log search time windows

The REST search endpoint POST /api/v1/logs/search/advanced and the underlying SearchLogs RPC default to the last hour when time_range is omitted, null, or empty. The start is inclusive and the end exclusive at the stored timestamp precision: [now - 1h, now). This excludes older and future-dated logs from the default search. It is not a one-hour maximum.

For a partial time_range, an omitted end becomes the server’s current time. An omitted start becomes one hour before the resolved end, also when that end is historical. Explicit bounds are not shortened to one hour. Your effective retention still limits how far back a search can read. A window that is entirely before retention returns an empty terminal page. Its read_coverage says whether the window was read completely, narrowed to retained history or outside retention; an empty page is a measured zero only when coverage is complete or partial. See read coverage.

Log search, pattern, field, and aggregate reads use timestamps through 2262-04-11T23:47:16.854775807Z, inclusive. A supplied bound after that maximum is refused with HTTP 400 InvalidArgument, naming time_range.start or time_range.end. Retrying the same bound cannot succeed. This also applies to the CLI’s exact --since and --until values. After defaults and retention clamping, bounds sent to storage must be on or after 1900-01-01T00:00:00Z. Older starts still advance to your retention floor, and windows that are entirely before retention still return empty results.

Trace search and detail, trace aggregates, service health, RED metrics and maps, agent-run lists, LLM cost reads, error-group reads, and mixed telemetry search and correlation use the same maximum timestamp and field-specific refusal. Person and account timelines also reject unsupported windows and page cursors. Their window end stays inclusive, including the maximum itself. An explicit ancient timeline end returns an empty page.

Metric-only reads, and searches that select only metrics, keep nanosecond precision and use the same maximum, 2262-04-11T23:47:16.854775807Z. Metric windows also include the start and exclude the end. Later bounds are refused with the same field-specific 400.

The returned page and total count use the same resolved absolute window within one request. Across page requests, send the same explicit start and end, so that a relative or default window does not move with the clock. This fixes the query window. It is not a database snapshot: ingestion, deletion, and a moving retention floor can still change results. These defaults do not apply to live tail or to exact log-ID and context reads.

The CLI’s logs query defaults to --since 1h. MCP search_logs supplies its own explicit 24-hour window when since is absent and says so in its output. Set the window explicitly when you compare results across surfaces.

Filter logs by field

Every log search accepts one typed field filter. It is combined with AND with the service, level, text query, environment, trace, and customer scope. The REST API, the CLI’s --filter, MCP search_logs and live tail all use the same filter. One filter selects the same logs, and the same total count, wherever you run it.

The CLI (--filter on logs query and logs tail) and MCP (the filter argument of search_logs) take a text form:

anectico logs query --since 6h \
  --filter 'service_name = checkout AND attributes.http.status_code >= 500 AND NOT exists(attributes.retry)'
anectico logs tail --filter '(level = error OR level = fatal) AND message matches /timeout after \d+ms/'
Condition Matches a log when
field = value the field is present and equal to the value
field != value the field is present and differs. A log without the field does not match; write NOT field = value to include it
field > 10, >=, <, <= the field's value is a number that compares as stated. A value that is not a number never matches
field contains text the field contains the text; ASCII letters match regardless of case
field matches /pattern/ the field matches the RE2 pattern. Matching is unanchored: /users/ matches anywhere, /^GET / only at the start. (?i) ignores case; write \/ for a slash inside /…/. A quoted string also works: matches "^GET "
exists(field) the field is present and not empty
NOT x, x AND y, x OR y, ( … ) NOT binds tighter than AND, and AND tighter than OR

A quoted value ("500" or '500') is always text. A bare number (500, -1.5, 2e3) is compared as a number, so attributes.code = 500 also matches a stored 500.0. Other bare words are text. Quote a value that contains spaces or is a keyword (AND, OR, NOT, exists, contains, matches). \", \', \\, \n and \t are the escapes inside quotes. Keywords are case-insensitive. Put a key that contains spaces or operator characters in backquotes: `attributes.order total` > 100.

Fields:

  • log fields: service_name (or service), severity (or level; warn means warning), severity_text, message, environment, trace_id, span_id, distinct_id, session_id, host (or hostname), pod_name, logger_name, source_file and source_line, which is numeric;
  • attributes.<key> for a log attribute and resource.<key> for a resource attribute, for example attributes.http.status_code;
  • a bare key such as http.status_code, which matches the key in either attributes or resource.

project_id, org_id, timestamp and log_id are not filter fields. Choose the project with project_id and the window with time_range. A filter can only narrow them. A customer attribute that happens to be named project_id is attributes.project_id.

Over REST, send the text form as filter_expression, or the same filter as a JSON tree in filter:

{
  "time_range": {"start": "2026-09-24T08:00:00Z", "end": "2026-09-24T14:00:00Z"},
  "filter": {"all": {"filters": [
    {"condition": {"field": "service_name", "operator": "LOG_FILTER_OPERATOR_EQUALS", "text": "checkout"}},
    {"condition": {"field": "attributes.http.status_code", "operator": "LOG_FILTER_OPERATOR_GREATER_OR_EQUAL", "number": 500}},
    {"not": {"condition": {"field": "attributes.retry", "operator": "LOG_FILTER_OPERATOR_EXISTS"}}}
  ]}}
}

A tree node is exactly one of all (AND), any (OR), not or condition. A condition has a field, an operator (LOG_FILTER_OPERATOR_EQUALS, _NOT_EQUALS, _EXISTS, _GREATER_THAN, _GREATER_OR_EQUAL, _LESS_THAN, _LESS_OR_EQUAL, _CONTAINS or _MATCHES) and a text or number value. EXISTS takes no value. Send one of filter and filter_expression, not both. The exact-match filters object is still accepted: each entry is an EQUALS condition combined with the filter by AND. For live tail, pass filter_expression or the URL-encoded JSON filter as a query parameter on /api/v1/stream/logs. A resume cursor only continues the filter it was issued for.

Limits. A filter can have at most 64 nodes (conditions, NOTs and groups) and 8 levels of nesting. Each value can be at most 1,024 bytes. A matches pattern can be at most 256 bytes and must be valid RE2 of bounded complexity. Backreferences and lookaround are not RE2. The text form can be at most 4,096 bytes. A filtered search covers at most 31 days, and a filtered live tail can start at most 31 days back. A filtered read that would scan more than 100,000,000 log rows is refused, not answered from part of the data. Narrow the window, or add a service, level or environment. All of these return HTTP 400 InvalidArgument or FailedPrecondition, naming the rule. A syntax error gives the byte offset. An organization can run four filtered searches at once. Another one returns 429 and can be retried shortly.

Sensitive attributes. Filtering on an attribute or resource key needs the same access as reading it. Keys that can carry model prompts, completions or tool arguments, and any key outside Anectico’s operational allowlist, need agents:content:read and a project content policy that permits reading them. Without that access the whole search is refused with 400 ("filtering on a classified attribute requires content read access"), wherever the condition appears, including under NOT or OR and in exists(…). A count or a negated filter would otherwise reveal what the hidden value is. The condition is never silently dropped. A search that names no project cannot consult a project’s policy, so name the project to filter on such keys. Log fields such as service_name or message need only logs:read. The text query (query) works differently: without that access it searches only the message and the service name.

Bounded search filters include service, level, full-text query, environment, customer ID, project, and a typed field filter. The same field filter applies to live tail. Log results support newest-first or oldest-first timestamp ordering. Relevance ordering is not available. The CLI reconnects from the last processed opaque cursor when a stream drops, including logs durably received during the interruption. It uses bounded exponential backoff and suppresses repeated stable log IDs with a 10,000-entry per-run cache. Ctrl-C stops it.

Read one log and its patterns

get_log_entry reads one exact log by its canonical log_id. get_log_context reads the lines just before and after it. Search and unified-search log rows retain log_id and their owning project_id, alongside trace and span IDs. Pass the returned log and project IDs unchanged to an exact entry or context read, including when the search covered several projects. CLI query, bounded-tail and streaming output keep these identifiers; JSON keeps the same fields. If retention has removed the log, the read says so. get_log_patterns summarizes the repeated templates in the current project and window. MCP returns a bounded selection of up to 20 qualifying patterns, largest counts first. The full pattern total is unknown. total_logs counts the selected window's logs, not patterns, and can include logs that produced no returned pattern. CLI JSON total_patterns counts only returned patterns; increasing --limit selects more examples, up to 100, without establishing the full pattern population.

A template may also carry a small category — database, auth, external_call and nine others — and a Needs attention marker when it reads as a degraded or failing situation, not as ordinary traffic. Both are hints. Neither ever hides, filters or reorders anything. Labels do not alter which clusters survive the threshold and selection limit, or their count order.

Labels are worked out after a read. A template you see for the first time carries none, and picks one up the next time you read the patterns. A template that shows neither a category nor a marker means one of exactly two things: nothing has labeled it yet, or labeling was unavailable. When the reason is known, the result says so. The reasons are: your plan’s daily decision-scoring cap has been reached (see plans and limits); labeling is switched off for your organization; or the labeling service rejected your deployment’s credential. The last two do not clear on their own. A plain "unavailable" is retried on a later pass. There is no "routine" or "low severity" label anywhere in the result. "Not flagged" and "not looked at yet" cannot be told apart from the marker alone, and showing one as the other would be a judgment that the labeling is not accurate enough to make. A category is shown only when it is confident enough to be worth having. Below that, the template is left uncategorized, not labeled with a probable guess.

Take a bounded sample of new logs

read_log_tail (CLI: anectico logs tail-read) is the finite form of live tail. One call listens for new logs for up to wait_seconds and returns the ones that arrived, at most limit of them, with a cursor. Pass that cursor back unchanged as resume_cursor and the next call continues exactly where this one stopped, even if the first call found nothing. From the CLI:

anectico logs tail-read --service orders-api --level error --wait 10 --limit 50
anectico logs tail-read --service orders-api --level error --resume-cursor "$CURSOR"

Through MCP, the read action takes the same arguments, for example {"project_id": "PROJECT_UUID", "wait_seconds": 10, "limit": 50, "resume_cursor": "OPAQUE"}, plus the filters service, level, query, environment, distinct_ids and the typed filter. It needs logs:read (and mcp:read for MCP). wait_seconds is 0 to 30 and defaults to 10; 0 is one immediate look, not a wait. limit is 1 to 100 and defaults to 50. A new subscription starts at the moment the call began; since (an RFC 3339 time) starts it earlier. The cursor remembers the start, so a resumed call ignores since.

What the result tells you:

Field Meaning
logs The records delivered, in delivery order. Each keeps the time it says it occurred.
resume_cursor Pass it back unchanged. It is always present, also when nothing arrived.
stop_reason idle_timeout: the wait ended. row_limit: you got limit records and more may be waiting. poll_budget: a polling round used its whole work allowance with records still waiting. Resume from the cursor in the last two cases.
armed_at, finished_at When the call began and when its last look at new logs ended.
arrival_lower_bound When Anectico started counting arrivals for this subscription. It never moves when you resume.
returned_count, effective_wait_seconds, effective_limit, polls_used What the call returned and what it used after defaults.
withheld_record_count How many returned records had content removed. Each names the withheld fields in its attributes.
read_coverage The oldest history this credential can read for logs.

Read it for what it is:

  • It follows arrival, not occurrence time. A log that happened an hour ago and reached Anectico after the call began is returned. A log that reached Anectico before the call began is not; search for it. The time on a record is when the sender says it happened; the cursor records when Anectico received it.
  • It is a sample, not a count. An empty result does not show that nothing was logged. A few logs can take up to legacy_settle_seconds seconds to become eligible and appear in a later call. Whether everything the application sent arrived is unknown.
  • The wait is not the call's total time. A 30-second wait plus the final checks takes up to about 35 seconds. Give the client at least 40 seconds; a client that hangs up after 30 seconds will drop a healthy call. The CLI does this for you.
  • The credential is checked while it waits and again before anything is returned. If the key is revoked or narrowed, or the project stops allowing the content, the call is refused and returns no records. A project policy that tightened after the wait began removes the protected fields instead of returning them.
  • A few reads at a time. One API key (or signed-in person) can have two waiting reads at once, and an organization four. More are refused with 429 (read_admission_busy) before anything runs; retry when one finishes.
  • Requests have size limits. A cursor is at most 16 KiB, a text query 4 KiB, a filter expression 4,096 bytes, the service, level and environment 1 KiB each, and the list of people 100 values or 16 KiB. A larger value is refused with 400 and no records.

Stream logs from your own client

For a custom SSE consumer:

curl -N \
  -H "X-Anectico-API-Key: $ANECTICO_API_KEY" \
  "https://app.anectico.com/api/v1/stream/logs?project_id=$ANECTICO_PROJECT&level=error"

Send Accept: text/event-stream in production clients. Each data: frame contains one JSON log with a stable log_id, the same source, runtime, and resource fields as historical search, and an opaque resume_cursor. The SSE id is that same cursor. Save it only after you process the frame, then reconnect with Last-Event-ID and bounded backoff. since is only an explicit initial time and is ignored once a cursor exists. Treat cursors as opaque and keep the same project and filters. Cursors are unsigned encoded positions, not credentials. Authenticated scope remains authoritative. Unauthorized or server-disabled streams need a credential or a server configuration change before you retry.

A live tail keeps re-checking the credential it opened with, on every five-second heartbeat. Revoking the key, freezing it, or narrowing its scopes ends the stream instead of letting it run on. The last thing you receive is an error frame with a reason and a terminal flag. unauthorized or forbidden marked terminal means that reconnecting cannot repair it, and your client should stop. A frame that is not marked terminal means that the credential only needs to be re-established: reconnect with backoff from your saved cursor. See Revoke agent access.

Traces

Ask the agent to filter traces by service, operation, duration, errors, or customer identity. Then ask it to read one trace, its error spans, and its critical path (get_trace_critical_path), and then the correlated logs. The trace page shows the same timeline and error spans, with the Critical path and the correlated logs.

When the trace view does not include span events, the span drawer on the trace page says Event details are not available in this view. This does not mean that the span has no events or that an exception was lost. When event details are included, the drawer shows the events and their count. Events (0) and No events mean that the included event list is empty.

When you correlate a trace through REST or MCP (correlate_signals), each returned Issue’s event and affected-customer counts cover all its visible occurrences in the correlation time window, including occurrences on other traces. The anchor trace selects the Issues. It does not limit their impact counts to that one request. Narrow the window to compare impact over a shorter period. Occurrences without a customer identity count as events but do not increase the affected-customer count.

Trace-level filters are applied before pagination. Read the returned coverage and continuation even when a page is empty. An empty page with a continuation can be followed; a terminal empty page only describes retained, visible evidence in the effective window. It does not prove the request never happened upstream.

Search one canonical person's complete identity

Use person_id to search every current identifier of one canonical person in one project. You need traces:read, plus persons:read for this selector (mcp:read for MCP). No profile read permission is needed. The raw distinct_id selector (CLI --distinct-id) selects exactly one identifier and cannot be combined with a canonical person.

{"project_id":"PROJECT_UUID","person_id":"PERSON_UUID","since":"24h","sort":"start_time","limit":20}
anectico traces search --project PROJECT_UUID --person PERSON_UUID --since 24h --sort-by start_time --sort-order desc --limit 20

One trace touched by several identifiers appears once, keyed by project_id and trace_id. The result includes resolved_person_id, alias_count, identity_digest, project_id, requested_time_range, effective time_range, normalized selection and read_coverage. The digest binds the complete current identity without returning an alias dump; a profile edit alone does not change it. A complete empty identity returns no traces. Unknown or erased people return not found, unavailable identity resolution refuses the read, and more than 1,000 aliases returns an identity-limit refusal. No alias set is silently truncated.

The default order is newest first. To continue, resend the same person, project, filters, sort and limit with the owning next_cursor as MCP cursor or CLI --cursor. Omit since/until on continuation, or repeat the exact returned requested_time_range timestamps. The initial relative window is fixed; repeating a moving relative duration is a new window and is refused. MCP also accepts exact RFC3339 UTC since and until. The CLI preserves nanosecond timestamps.

Every page checks current identity and authority again. An identity change refuses continuation with a restart instruction; an erasure returns not found. A serving restart can also expire the continuation. Restart without a cursor when refused. Trace arrivals or deletions within the fixed window can still change later pages; this is a live read, not a frozen audience.

read_coverage distinguishes complete, partially_covered, outside_retention and unknown history. Complete means the requested retained, visible source was read; upstream completeness remains unknown. Withheld content, failed reads and response limits are separate from retention. Expired history has no promised archive or recovery. Only MCP returns the existing project-qualified trace proof links; no new viewing page is needed.

Filter traces by span attribute

Ask the agent for traces where a span attribute has an exact value, for example "checkout traces over gRPC in the last hour". MCP search_traces takes attributes, a map of exact, case-sensitive values, and POST /api/v1/traces/search takes the same map. The CLI takes a repeatable --attribute KEY=VALUE. The search_traces arguments and the CLI equivalent:

{"project_id": "PROJECT_UUID", "attributes": {"http.route": "/checkout", "rpc.system": "grpc"}, "since": "1h", "sort": "start_time"}
anectico traces search --attribute http.route=/checkout --attribute rpc.system=grpc --since 1h
  • Every pair must be found on some span of the trace. Two pairs may sit on different spans of the same trace.
  • Values are compared byte for byte. An empty value matches an attribute recorded with an empty value.
  • A key must not be empty. Every surface refuses an empty key before searching.
  • In the CLI only the first = separates the key from the value, so --attribute note=a=b matches the value a=b. Repeating the same pair is one filter. The same key with two different values is refused.
  • The MCP result echoes the exact pairs it applied in data.attributes, marked as untrusted customer text. Send the same map with the next page's cursor; a different map is a different question, so start again without a cursor.
  • Route, method, status and other operational keys need only traces:read. A key that can hold captured content (a prompt, a custom attribute) needs agents:content:read and a named project. Without them the search is refused with "filtering on a classified attribute requires content read access". It is never answered without that filter.
  • An attribute search has fixed limits: 10 seconds, 10 million scanned spans, 128 MiB of memory and 64 MiB of temporary storage. A search over more than that is refused with a resource_limit error and returns no traces at all, never a shorter page. Narrow the window or add a service or other filter and ask again.

Filter by attribute in the search rather than reading trace detail and filtering it: trace detail shows a bounded selection of spans, so filtering it can miss matching traces.

For model calls and token cost, see Explain LLM spend per customer.

Metrics and services

Use service health, the service map, and metrics for symptoms that affect a broad service or have no customer identity, such as a queue backlog or infrastructure saturation. get_service_health reads the rate, errors, and latency of each service. get_service_map reads the dependencies. Its requested_window records the ask; time_range.start and time_range.end give the actual interval analyzed. window_narrowing_reasons lists retention or range_limit when that interval was narrowed; a map analyzes at most seven days. Use the effective interval for follow-up reads and comparisons.

A unified search reports failed_sources when a requested signal could not be read. Its MCP status remains partial even when all successful sources returned zero hits. An empty partial result cannot establish absence in the failed sources. With no name, query_metrics returns a bounded metric catalog. catalog_truncated: true means names were omitted before the local match filter; name_total is then absent because the matching count is unknown. An empty filtered result cannot prove that the metric does not exist. match filters only the returned catalog and cannot recover omitted names. Narrow window, or query an already-known exact metric name. When the catalog is complete, name_total counts the full matching set before MCP's display limit. CLI metrics list table output reports the count or unknown total; JSON retains truncated and total_count.

query_metrics reads one metric as a bucketed series. Metrics identify when and where behavior changed. Use the matching time window in traces and logs to explain why.

See Explore and compare metrics for aggregation semantics, dashboard queries, forecasts, CLI examples, and common misleading results.

Save repeatable work

To keep a bounded copy of a search's results, see Export data. Ask for a dashboard only for a question the team expects to ask again. Avoid dashboards that duplicate an existing customer, Issue, or service view. See Investigate service health and dashboards.

Keep the investigation connected

Whenever a raw signal contains a customer, Issue, trace, release, or replay reference, follow it instead of copying identifiers by hand. The value of Anectico is the connected path, not the number of query tools.

When live tail is empty

The stream carries newly ingested logs and bounded resumable catch-up. A bounded sample (logs tail-read) shows the same subscription once: an idle_timeout result with no records and a cursor means that nothing eligible arrived during the wait. Run anectico logs query --since 5m, remove filters, and confirm the active project before you investigate the streaming connection. Proxies that buffer or time out SSE can repeatedly end a healthy stream. The CLI resumes automatically, but custom clients must keep the last SSE id.

Grouped log counts keep service and severity selections. With get_log_patterns, pass service and level alongside group_by; with anectico logs aggregate, use --service and --level. Those selections apply before grouping, in the same way as template clustering. warn selects warning. Grouped reads return at most 1,000 groups; MCP displays at most 50. Full group and log totals are unknown. returned_group_logs is the subtotal over the groups the service returned, including those MCP does not display. Their percentages use that bounded subtotal and exclude omitted groups. Narrow the window and selections before comparing distributions.

Field, label and filter-value discovery returns a page with returned_count, has_more and next_cursor. Continue with MCP cursor or CLI --cursor and unchanged selections. list_log_fields / anectico logs fields and list_metric_labels / anectico metrics labels take exact start and end bounds when continuing; the read returns its resolved window. Their values are samples, and values_truncated names a cut. A source bound without a continuation cannot establish a complete inventory; narrow the supported project or window.