Logging¶
The Nautobot MCP Server writes all logs to stdout. Two settings control them, both covered in the Configuration Guide: LOG_LEVEL sets verbosity, and LOG_FORMAT chooses between human-readable and structured output.
Access Logging¶
The server writes one log line per tool call, recording the caller, which tool ran, how long it took, and whether it succeeded:
INFO nautobot_mcp_server.middleware tool=execute_graphql_query caller=user@example.com token_uuid=5dc9d21c-3148-4ce5-a2af-1af92818a4f4 duration_ms=182.4 outcome=ok
| Field | Meaning |
|---|---|
tool |
The capability that ran |
caller |
The Nautobot user behind the request |
token_uuid |
UUID of the API token record used — the object's identifier, not the secret token key itself |
duration_ms |
How long the call took |
outcome |
ok, or exception with the error type |
Two identifiers are recorded because they answer different questions. caller says who the line is about. token_uuid survives a username or email change, tells apart two tokens belonging to the same person, and is what you revoke if a credential is misused. The token key itself is never written to the logs, at any log level.
Identification is best-effort: if a user's Nautobot permissions do not allow them to read their own token record, they still get a fully working session and their calls are logged as caller=unidentified. Set LOG_LEVEL=WARNING or higher to suppress the per-call lines and keep only failures.
This is separate from Nautobot's own audit trail. Because every call is made with the user's own token, the same activity is also recorded in Nautobot, attributed to that user.
Shipping Logs to a Log Aggregator¶
All logs go to stdout, so the server does not manage log files, rotation, or destinations — whatever supervises the process collects them:
- systemd (the production install) captures stdout into the journal automatically, with no
StandardOutput=directive needed. From there, ship them with any journal-aware log collector, or addStandardOutput=append:/var/log/nautobot-mcp-server.logto the unit if your tooling expects a file — rotation is yours in that case, vialogrotate. - Containers hand stdout to the runtime, so an existing container log collector picks them up with no extra configuration.
Set LOG_FORMAT=json to emit one JSON object per line, with the access log's fields promoted to top-level keys so they can be filtered on without parsing the message:
{"ts":"2026-08-14T14:31:52.738931+00:00","level":"INFO","logger":"nautobot_mcp_server.middleware","msg":"tool=count_nautobot_objects caller=user@example.com token_uuid=5dc9d21c-3148-4ce5-a2af-1af92818a4f4 duration_ms=211.7 outcome=ok","tool":"count_nautobot_objects","caller":"user@example.com","caller_token_uuid":"5dc9d21c-3148-4ce5-a2af-1af92818a4f4","duration_ms":211.7,"outcome":"ok"}
That supports queries like "every call by this user in the last hour", "calls slower than two seconds", or "failures grouped by tool", without regexes.
Three things to know when configuring a collector:
- The web server keeps its own format. It configures its loggers independently, so its HTTP access lines (
INFO: 10.0.0.1:54321 - "POST /mcp HTTP/1.1" 200 OK) stay plain text even whenLOG_FORMAT=json. Expect a mix, and treat unparseable lines as plain text rather than dropping them. - In
textmode the access log is still machine-readable. The message iskey=valuepairs, so alogfmtparser applied to the message portion of%(asctime)s - %(name)s - %(levelname)s - %(message)srecovers the same fields. - Under systemd, JSON lines arrive nested. The journal stores each line as the
MESSAGEfield of a journal entry that has its own structure, so a collector reading the journal sees the JSON as a string and needs a parse stage to expand it.journalctl -o catprints the raw lines if you would rather tail them directly.
Reading the Logs¶
Where the logs appear depends on how the server is deployed:
# systemd (production install)
sudo journalctl -u nautobot-mcp-server -f
# Docker
docker logs -f <container>
For diagnosing a specific problem, LOG_LEVEL=DEBUG adds detail on token validation, client caching, and the Nautobot requests behind each tool call. See Troubleshooting for common issues.