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-apilogs 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(orservice),severity(orlevel;warnmeanswarning),severity_text,message,environment,trace_id,span_id,distinct_id,session_id,host(orhostname),pod_name,logger_name,source_fileandsource_line, which is numeric; attributes.<key>for a log attribute andresource.<key>for a resource attribute, for exampleattributes.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_secondsseconds 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
400and 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=bmatches the valuea=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'scursor; 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) needsagents:content:readand 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_limiterror 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.