Logging and monitoring setup in Cursor
PLLogging and monitoring setup in Cursor means having Cursor’s agent generate four observability layers plus dashboards for a service: structured JSON logs, Prometheus metrics, OpenTelemetry traces, and alert rules. The agent writes each layer quickly because the conventions are well documented; trust a layer only once a test, promtool, or a trace smoke test proves it.
This page is for developers who own a Node.js or TypeScript service and the tech leads who set the observability conventions for a team. The pager fires at 02:00 for “high error rate on orders API”. The logs say Internal Server Error with no request ID, no route, and no user, and nothing ties the spike on the dashboard to a line of code. The fix is not more logging. It is logs, metrics, and traces that share identifiers, and alerts that fire on user-facing symptoms.
What you get from an agent-built observability stack
Section titled “What you get from an agent-built observability stack”- A project rule that fixes your field names, label sets, and log levels before the agent writes any code.
- A
pinologger with request context, redaction, andtrace_idon every line. prom-clientmetrics with a bounded label set, plus a test that fails when someone adds an unbounded label.- OpenTelemetry tracing that loads before your app and propagates W3C
traceparentacross services. - Prometheus alert rules with unit tests in
promtool, so you know each alert fires before production does. - Cursor connected to Sentry or Grafana through MCP, so the agent can read real telemetry when you debug.
The examples use Express on Node.js. The prompts carry over to Fastify, NestJS, or Next.js route handlers once you swap the framework name and the route-pattern accessor (for example, Fastify’s routeOptions.url).
In what order should the agent build observability?
Section titled “In what order should the agent build observability?”Build the layers in dependency order: conventions, then logs, metrics, traces, alerts, and dashboards last. Alerts query metric names, and dashboards query both, so generating them first gives you rules that point at series that do not exist. Each step below ends with a gate. Do not start the next step until the gate is green.
-
Write the conventions down as a project rule. Without one, the agent picks field names per file:
requestIdin the middleware,req_idin the orders service,request_idin the tests. A rule makes every later prompt consistent. See project rules in Cursor for where rules live and how they load. -
Ask for a plan before code. Switch the agent to Plan Mode and have it inventory what already exists. Plan Mode “creates detailed implementation plans before writing any code” (Cursor docs, checked 2026-08-28), which keeps the agent from bolting a second logger onto a service that already has one.
-
Generate one layer per conversation. A fresh conversation per layer keeps the context small and the diff reviewable. Commit after each gate passes, so a bad layer reverts cleanly with
git revert.
Generate structured logging with request context
Section titled “Generate structured logging with request context”Logs are the first layer because every other layer references their identifiers. The prompt below asks for the logger, the request middleware, and the test that proves both in one pass.
Gate: the three tests pass, and the sample line contains request_id and no secret. The concurrency test is the one that matters: AsyncLocalStorage leakage between requests is invisible in a single-request manual check.
To add business context later, ask for a child logger per unit of work rather than new fields in every call: “In src/services/orders.ts, create a child logger with order_id and customer_id at the start of processOrder and use it for every log line in that function.” Business IDs belong in logs, where each line is one event, and never in metric labels.
Add Prometheus metrics without a cardinality explosion
Section titled “Add Prometheus metrics without a cardinality explosion”Metrics answer “how often and how slow” in aggregate. The label set is the real design decision. Each distinct combination of label values creates a new time series, so one user_id label turns a counter into a series per user.
Gate: the series count test passes. The unmatched case catches the most common generated bug: using req.path or req.originalUrl as the label, which works in a demo and creates one series per URL under a scanner’s traffic.
Wire OpenTelemetry tracing across services
Section titled “Wire OpenTelemetry tracing across services”Tracing links one user request across services and ties it back to the log lines through trace_id. The Node.js SDK has one hard rule the agent often misses: it must load before express, http, and database clients are imported, or auto-instrumentation patches nothing and you get empty traces with no error.
Gate: the smoke test passes. It proves three things you would otherwise check by hand across three tools: the SDK loaded in time, the logger reads the active span, and propagation works on outgoing calls. The package names above are current on npm as of 2026-09-26 (@opentelemetry/sdk-node 0.222.0, @opentelemetry/auto-instrumentations-node 0.80.0).
Generate alert rules and prove they fire
Section titled “Generate alert rules and prove they fire”Alert on symptoms users feel, such as error ratio and latency, not on causes such as CPU. The agent writes Prometheus rule syntax well. The gap is thresholds that were never exercised. promtool test rules closes that gap: it feeds synthetic series into the rules and asserts which alerts fire at which minute.
Run the gate in a terminal. The prom/prometheus image ships promtool, so you do not need a local Prometheus install:
docker run --rm -v "$PWD/monitoring:/m" --entrypoint promtool \ prom/prometheus check rules /m/alerts/api.yml /m/alerts/business.ymldocker run --rm -v "$PWD/monitoring:/m" --entrypoint promtool \ prom/prometheus test rules /m/alerts/tests/api_test.ymlGate: both commands exit 0. Add them to CI next to your unit tests so a later edit to a metric name breaks the build, not the pager. CI/CD with Cursor shows where that job goes.
Dashboards come last and are the lowest-risk layer, because a wrong panel misleads a person, while a wrong alert wakes one. Ask for a Grafana dashboard JSON with an environment and service template variable and one row each for traffic, errors, latency, and saturation, and tell the agent to use only the metric names in src/lib/metrics.ts. Import it into a staging Grafana and check that every panel returns data.
Let Cursor read your production telemetry through MCP
Section titled “Let Cursor read your production telemetry through MCP”Generated instrumentation is half the loop. The other half is the agent reading what that instrumentation reports when something breaks. Cursor reads MCP servers from .cursor/mcp.json in the project or ~/.cursor/mcp.json globally. Sentry’s official remote server uses OAuth, so the config holds no secret:
{ "mcpServers": { "sentry": { "url": "https://mcp.sentry.dev/mcp" } }}With that in place, a debugging prompt can start from real data instead of a pasted stack trace:
For metrics and logs, Grafana Labs publishes an official server (uvx mcp-grafana, PyPI 1.6.0 on 2026-09-26) that queries Prometheus and Loki; start it with --disable-write so the agent can read dashboards but not change them. Keep its service account token in your environment, never in a committed mcp.json. Setup for both servers across tools is in MCP setup for Cursor and observability MCP servers.
Cursor Automations can also start a Cloud Agent from a Sentry or PagerDuty event (Cursor docs, checked 2026-10-12). That is useful for a first-pass diagnosis on a pull request, but the fix still goes through your normal tests and review. See Cloud Agents and Automations before you wire an alert to an agent.
How do you verify generated observability code without reading it all?
Section titled “How do you verify generated observability code without reading it all?”Each layer has a behavioural check that runs in seconds. Together they replace a line-by-line review of a few hundred lines of instrumentation.
| Layer | What can go wrong silently | The check that catches it |
|---|---|---|
| Logs | Secrets logged; request_id leaks across requests | Redaction and concurrency tests on captured output |
| Metrics | Unbounded labels; wrong route label | Series-count test and allowed-label-keys test on app-defined metrics |
| Traces | SDK loads too late; no propagation | traceparent smoke test with an in-memory exporter |
| Alerts | Rule never fires, or fires on noise | promtool test rules with positive and negative cases |
| Dashboards | Panels query metrics that do not exist | Import into staging Grafana; every panel shows data |
The developer who opens the pull request owns the tests. The tech lead owns the project rule and signs off on the alert thresholds and severities, because those decide who gets woken up. What you still read by eye is short: the rule file, the label list, and the alert thresholds.
When generated observability code breaks
Section titled “When generated observability code breaks”Traces are empty, and nothing errors. The OpenTelemetry SDK was imported after express or http, so it patched nothing. A second cause, for an ESM build: the SDK loads first but the loader hook is missing, so ESM imports are never patched. Recovery: move the import to --import or --require in the start command, add --experimental-loader=@opentelemetry/instrumentation/hook.mjs if the app is ESM, as in the tracing prompt, and rerun the smoke test.
Metrics storage bills jump after a deploy. A label carries a raw URL, an ID, or an error message. Recovery: query your Prometheus for the series count per metric (count by (__name__)({__name__=~"http_.*"})), find the offending label, and add that label key to the forbidden list in the project rule so the next generation does not repeat it. The label-keys test keeps it out afterwards.
The agent wrote custom span middleware on top of auto-instrumentation. Every request produces two nested server spans with the same name. Recovery: delete the custom middleware and keep withSpan only for business steps. The tracing prompt says this up front because the agent adds it by default.
Attribute names do not match your backend’s queries. Generated code often uses the deprecated http.method and http.status_code attributes. Recovery: state the current conventions in the project rule, as in the conventions prompt, and ask the agent to migrate existing spans in one pass.
Alerts fire every day and get muted. Thresholds were chosen without data. Recovery: set new alerts to severity: ticket for the first two weeks, compare their firing history with real incidents, then promote only the ones that matched to page. Add a promtool case for every threshold you change.
Production logs are too verbose. The agent logged at info inside loops. Recovery: grep for logger calls inside for and map bodies, move them to debug, and confirm LOG_LEVEL defaults to info when unset, so production never logs debug unless someone sets it on purpose.