# Debugging

> Work out what is happening in production by querying your logs, traces, and metrics from a connected agent, in the app, or in SQL.

Something is wrong in production and you need to know what. Everything you send to
Fixter is queryable seconds after it arrives, scoped to your workspace, and there are
three ways at it: your agent over MCP, the app, and SQL. They read the same data.

This is the debugging you drive. When an alert fires and you would rather it were
already done, that is [investigations](/using-fixter/investigations/).

## From your agent

A [connected agent](/setup/mcp/) queries Fixter over MCP, so
you ask in plain language and it decides which tools to call:

```
checkout-service started throwing 500s around 14:00. what changed?
```

It searches logs, pulls the traces behind the failures, and correlates them. The tools
it has are `logs`, `get_log`, `get_log_neighbors`, `spans`, `get_trace`,
`aggregate_spans`, `list_metrics`, `metrics`, `correlate`, `describe_schema`, and
`run_sql`. The [MCP tools reference](/reference/mcp-tools/#telemetry) covers each one.

You do not need to know the schema or write a query, and the answer arrives where you
are already working.

In Claude Code, the [plugin](/setup/claude-code-plugin/) adds a `diagnose` skill that
does this with a fixed method rather than leaving it to the model. Ask your agent to
look into the symptom and it runs:

```
why is checkout throwing 500s in production?
```

It routes off your symptom instead of always reaching for logs: an error message starts
in logs, "slow" or "timing out" starts in spans, a trace id goes straight to `correlate`.
Then it works across signals to root cause. Everything it calls is read-only.

## In the app

The [Logs page](https://app.fixter.dev/logs) is where you land after login:

- **The filter bar** suggests fields and values as you type, including your own log
  attributes, not just the built-in fields.
- **The histogram** charts log volume over time for the current filter.
- **Live tail** streams matching logs while you reproduce an issue or watch a deploy.
- **Alert on this** turns the query in front of you into an
  [alert rule](/using-fixter/alerting/).

<!-- fx-image: Logs page with a filtered search, histogram, and live tail running -->

Any log carrying a trace id opens the full trace view: every span of the request, timed
and nested, next to the other logs from that request. Traces are reached that way or by
trace id, and there is no separate trace browser. The
[Metrics page](https://app.fixter.dev/metrics) charts your series per service, and
exemplars link a sample back to the traces behind it, so a latency spike leads to the
slow requests themselves.

## In SQL

When a question needs to be exact, [QuerySQL](/reference/querysql/) reads `logs`,
`spans`, and `metrics` directly, with time bucketing, latency percentiles, and
full-text matching on top of ordinary SQL.

**Where you actually run it:**

- **In the app.** Switch the query bar on the [Logs page](https://app.fixter.dev/logs)
  into SQL mode and type it there.
- **Through your agent.** Usually you do not write SQL at all: you ask in plain
  language, and the agent writes and runs the statement for you with `run_sql`. This is
  the common case.
- **In an alert rule.** The slice a rule watches is a QuerySQL filter, so a query that
  found a problem once can become the [rule](/using-fixter/alerting/) that catches it
  next time.

```sql
SELECT service, count() AS errors
FROM logs
WHERE level IN ('ERROR', 'FATAL', 'CRITICAL')
GROUP BY service
ORDER BY errors DESC
```

`level` holds whatever your logging library calls things, so match a set rather than a
single value. The [data model](/reference/data-model/#logs) explains why.

**The time range is not part of the statement.** Each surface supplies it separately:
the Logs page from its range picker, the MCP tools from `from` and `to` sent alongside
the query. So the same statement answers for the last hour or the last month without
being edited.

## If you do not find what you expect

- **A field you send is not queryable.** Your own attributes become fields by name with
  no registration, but only once at least one record carrying them has arrived. Send a
  request that emits the attribute, then filter again.
- **A log has no trace view.** The trace view needs a `trace_id` on the record, so the
  request has to be instrumented for tracing and not only for logging.
- **Results look truncated in an agent.** MCP responses are capped so that one answer
  cannot flood the context window. Narrow the time range, or aggregate instead of
  listing.

Still stuck? Email [info@fixter.dev](mailto:info@fixter.dev) and one of our founders will help you fix your problem asap.

## Related

- [Telemetry](/using-fixter/telemetry/): what you are querying, and why each signal is there
- [Data model](/reference/data-model/): the fields on a log, span, and metric, and how long data is kept
- [Alerts](/using-fixter/alerting/): turning a repeated question into a notification
- [QuerySQL](/reference/querysql/): the full dialect