feat(router): logger improvements, access logs - #1283
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces structured logging and request correlation to the router, replacing tracing-tree with tracing-appender and implementing request ID generation and extraction via sonyflake and HTTP headers. The review feedback focuses on performance and style guide compliance, specifically recommending the use of lazy formatting for trace IDs to avoid heap allocations on the hot path, adding a descriptive error message to an expect call, removing a redundant type conversion, and avoiding an unnecessary Vec allocation when creating target filters.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
🐋 This PR was built and pushed to the following Docker images: Image Names: Platforms: Image Tags: Docker metadata{
"buildx.build.provenance/linux/amd64": {
"builder": {
"id": "https://github.com/graphql-hive/router/actions/runs/30208149531/attempts/1"
},
"buildType": "https://mobyproject.org/buildkit@v1",
"materials": [
{
"uri": "pkg:docker/docker/dockerfile@1.22",
"digest": {
"sha256": "4a43a54dd1fedceb30ba47e76cfcf2b47304f4161c0caeac2db1c61804ea3c91"
}
},
{
"uri": "pkg:docker/gcr.io/distroless/cc-debian12@latest?platform=linux%2Famd64",
"digest": {
"sha256": "e8e7ee4b8b106d4c5fde9e422a321b2b8a2d5cca546c97adcce927f3e1d36e36"
}
}
],
"invocation": {
"configSource": {
"entryPoint": "router.Dockerfile"
},
"parameters": {
"frontend": "gateway.v0",
"args": {
"cmdline": "docker/dockerfile:1.22",
"label:org.opencontainers.image.created": "2026-07-26T15:35:32.474Z",
"label:org.opencontainers.image.description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
"label:org.opencontainers.image.licenses": "MIT",
"label:org.opencontainers.image.revision": "ebd2267e07f094d8ffff141a2466714f698bb062",
"label:org.opencontainers.image.source": "https://github.com/graphql-hive/router",
"label:org.opencontainers.image.title": "router",
"label:org.opencontainers.image.url": "https://github.com/graphql-hive/router",
"label:org.opencontainers.image.vendor": "theguild",
"label:org.opencontainers.image.version": "pr-1283",
"source": "docker/dockerfile:1.22"
},
"locals": [
{
"name": "context"
},
{
"name": "dockerfile"
}
],
"root": {
"configSource": {
"path": "router.Dockerfile"
},
"request": {
"args": {
"label:org.opencontainers.image.created": "2026-07-26T15:35:32.474Z",
"label:org.opencontainers.image.description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
"label:org.opencontainers.image.licenses": "MIT",
"label:org.opencontainers.image.revision": "ebd2267e07f094d8ffff141a2466714f698bb062",
"label:org.opencontainers.image.source": "https://github.com/graphql-hive/router",
"label:org.opencontainers.image.title": "router",
"label:org.opencontainers.image.url": "https://github.com/graphql-hive/router",
"label:org.opencontainers.image.vendor": "theguild",
"label:org.opencontainers.image.version": "pr-1283",
"vcs:localdir:context": ".",
"vcs:localdir:dockerfile": "docker",
"vcs:revision": "ebd2267e07f094d8ffff141a2466714f698bb062",
"vcs:source": "https://github.com/graphql-hive/router"
}
}
},
"compatibilityVersion": 30
},
"environment": {
"github_actor": "dotansimha",
"github_actor_id": "3680083",
"github_event_name": "pull_request",
"github_event_payload": {
"action": "synchronize",
"after": "bf21c72004395639f5d1b19a60b1fc2d419d27f2",
"before": "e3323c1d8f3705c27d91e1cd743d78e048ee5dd0",
"enterprise": {
"avatar_url": "https://avatars.githubusercontent.com/b/187753?v=4",
"created_at": "2024-07-02T08:52:28Z",
"description": "",
"html_url": "https://github.com/enterprises/the-guild",
"id": 187753,
"name": "The Guild",
"node_id": "E_kgDOAALdaQ",
"slug": "the-guild",
"updated_at": "2026-07-11T07:16:45Z",
"website_url": "https://the-guild.dev/"
},
"number": 1283,
"organization": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"description": "Schema registry, analytics and gateway for GraphQL federation and other GraphQL APIs.",
"events_url": "https://api.github.com/orgs/graphql-hive/events",
"hooks_url": "https://api.github.com/orgs/graphql-hive/hooks",
"id": 182742256,
"issues_url": "https://api.github.com/orgs/graphql-hive/issues",
"login": "graphql-hive",
"members_url": "https://api.github.com/orgs/graphql-hive/members{/member}",
"node_id": "O_kgDOCuRs8A",
"public_members_url": "https://api.github.com/orgs/graphql-hive/public_members{/member}",
"repos_url": "https://api.github.com/orgs/graphql-hive/repos",
"url": "https://api.github.com/orgs/graphql-hive"
},
"pull_request": {
"_links": {
"comments": {
"href": "https://api.github.com/repos/graphql-hive/router/issues/1283/comments"
},
"commits": {
"href": "https://api.github.com/repos/graphql-hive/router/pulls/1283/commits"
},
"html": {
"href": "https://github.com/graphql-hive/router/pull/1283"
},
"issue": {
"href": "https://api.github.com/repos/graphql-hive/router/issues/1283"
},
"review_comment": {
"href": "https://api.github.com/repos/graphql-hive/router/pulls/comments{/number}"
},
"review_comments": {
"href": "https://api.github.com/repos/graphql-hive/router/pulls/1283/comments"
},
"self": {
"href": "https://api.github.com/repos/graphql-hive/router/pulls/1283"
},
"statuses": {
"href": "https://api.github.com/repos/graphql-hive/router/statuses/bf21c72004395639f5d1b19a60b1fc2d419d27f2"
}
},
"active_lock_reason": null,
"additions": 3096,
"assignee": null,
"assignees": [],
"author_association": "MEMBER",
"auto_merge": null,
"base": {
"label": "graphql-hive:main",
"ref": "main",
"repo": {
"allow_auto_merge": false,
"allow_forking": true,
"allow_merge_commit": false,
"allow_rebase_merge": false,
"allow_squash_merge": true,
"allow_update_branch": true,
"archive_url": "https://api.github.com/repos/graphql-hive/router/{archive_format}{/ref}",
"archived": false,
"assignees_url": "https://api.github.com/repos/graphql-hive/router/assignees{/user}",
"blobs_url": "https://api.github.com/repos/graphql-hive/router/git/blobs{/sha}",
"branches_url": "https://api.github.com/repos/graphql-hive/router/branches{/branch}",
"clone_url": "https://github.com/graphql-hive/router.git",
"collaborators_url": "https://api.github.com/repos/graphql-hive/router/collaborators{/collaborator}",
"comments_url": "https://api.github.com/repos/graphql-hive/router/comments{/number}",
"commits_url": "https://api.github.com/repos/graphql-hive/router/commits{/sha}",
"compare_url": "https://api.github.com/repos/graphql-hive/router/compare/{base}...{head}",
"contents_url": "https://api.github.com/repos/graphql-hive/router/contents/{+path}",
"contributors_url": "https://api.github.com/repos/graphql-hive/router/contributors",
"created_at": "2024-11-20T16:16:12Z",
"default_branch": "main",
"delete_branch_on_merge": true,
"deployments_url": "https://api.github.com/repos/graphql-hive/router/deployments",
"description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
"disabled": false,
"downloads_url": "https://api.github.com/repos/graphql-hive/router/downloads",
"events_url": "https://api.github.com/repos/graphql-hive/router/events",
"fork": false,
"forks": 16,
"forks_count": 16,
"forks_url": "https://api.github.com/repos/graphql-hive/router/forks",
"full_name": "graphql-hive/router",
"git_commits_url": "https://api.github.com/repos/graphql-hive/router/git/commits{/sha}",
"git_refs_url": "https://api.github.com/repos/graphql-hive/router/git/refs{/sha}",
"git_tags_url": "https://api.github.com/repos/graphql-hive/router/git/tags{/sha}",
"git_url": "git://github.com/graphql-hive/router.git",
"has_discussions": false,
"has_downloads": false,
"has_issues": true,
"has_pages": false,
"has_projects": false,
"has_pull_requests": true,
"has_wiki": false,
"homepage": "https://the-guild.dev/graphql/hive/docs/router",
"hooks_url": "https://api.github.com/repos/graphql-hive/router/hooks",
"html_url": "https://github.com/graphql-hive/router",
"id": 891604244,
"is_template": false,
"issue_comment_url": "https://api.github.com/repos/graphql-hive/router/issues/comments{/number}",
"issue_events_url": "https://api.github.com/repos/graphql-hive/router/issues/events{/number}",
"issues_url": "https://api.github.com/repos/graphql-hive/router/issues{/number}",
"keys_url": "https://api.github.com/repos/graphql-hive/router/keys{/key_id}",
"labels_url": "https://api.github.com/repos/graphql-hive/router/labels{/name}",
"language": "Rust",
"languages_url": "https://api.github.com/repos/graphql-hive/router/languages",
"license": {
"key": "mit",
"name": "MIT License",
"node_id": "MDc6TGljZW5zZTEz",
"spdx_id": "MIT",
"url": "https://api.github.com/licenses/mit"
},
"merge_commit_message": "PR_TITLE",
"merge_commit_title": "MERGE_MESSAGE",
"merges_url": "https://api.github.com/repos/graphql-hive/router/merges",
"milestones_url": "https://api.github.com/repos/graphql-hive/router/milestones{/number}",
"mirror_url": null,
"name": "router",
"node_id": "R_kgDONSTNFA",
"notifications_url": "https://api.github.com/repos/graphql-hive/router/notifications{?since,all,participating}",
"open_issues": 63,
"open_issues_count": 63,
"owner": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
"followers_url": "https://api.github.com/users/graphql-hive/followers",
"following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
"gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/graphql-hive",
"id": 182742256,
"login": "graphql-hive",
"node_id": "O_kgDOCuRs8A",
"organizations_url": "https://api.github.com/users/graphql-hive/orgs",
"received_events_url": "https://api.github.com/users/graphql-hive/received_events",
"repos_url": "https://api.github.com/users/graphql-hive/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/graphql-hive",
"user_view_type": "public"
},
"private": false,
"pull_request_creation_policy": "all",
"pulls_url": "https://api.github.com/repos/graphql-hive/router/pulls{/number}",
"pushed_at": "2026-07-26T15:24:58Z",
"releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
"size": 10515,
"squash_merge_commit_message": "PR_BODY",
"squash_merge_commit_title": "PR_TITLE",
"ssh_url": "git@github.com:graphql-hive/router.git",
"stargazers_count": 95,
"stargazers_url": "https://api.github.com/repos/graphql-hive/router/stargazers",
"statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/{sha}",
"subscribers_url": "https://api.github.com/repos/graphql-hive/router/subscribers",
"subscription_url": "https://api.github.com/repos/graphql-hive/router/subscription",
"svn_url": "https://github.com/graphql-hive/router",
"tags_url": "https://api.github.com/repos/graphql-hive/router/tags",
"teams_url": "https://api.github.com/repos/graphql-hive/router/teams",
"topics": [
"apollo-federation",
"federation",
"federation-gateway",
"graphql",
"graphql-federation",
"router"
],
"trees_url": "https://api.github.com/repos/graphql-hive/router/git/trees{/sha}",
"updated_at": "2026-07-26T09:14:02Z",
"url": "https://api.github.com/repos/graphql-hive/router",
"use_squash_pr_title_as_default": true,
"visibility": "public",
"watchers": 95,
"watchers_count": 95,
"web_commit_signoff_required": false
},
"sha": "13643d6165e76cf3442b9dbbf1eb365b9b523702",
"user": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
"followers_url": "https://api.github.com/users/graphql-hive/followers",
"following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
"gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/graphql-hive",
"id": 182742256,
"login": "graphql-hive",
"node_id": "O_kgDOCuRs8A",
"organizations_url": "https://api.github.com/users/graphql-hive/orgs",
"received_events_url": "https://api.github.com/users/graphql-hive/received_events",
"repos_url": "https://api.github.com/users/graphql-hive/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/graphql-hive",
"user_view_type": "public"
}
},
"body": "Closes https://github.com/graphql-hive/router/issues/755 \r\nCloses https://github.com/graphql-hive/router/issues/765 \r\nCloses https://github.com/graphql-hive/router/issues/503 \r\n\r\nDocs PR: https://github.com/graphql-hive/docs/pull/147 \r\nDocs preview: https://a507d5b7-hive-platform-docs.theguild.workers.dev/graphql/hive/docs/router/observability/logging\r\n\r\n## Background \r\n\r\nExtracted only the crucial changes from https://github.com/graphql-hive/router/pull/775 and re-implements major parts of it. \r\n\r\n## Changes in this PR \r\n\r\n### Disable logging from internal crates \r\n\r\nA new config flag `log.log_internals` (default: `false`) has been added, that creates a `Targets` filter. It has a hardcoded list of log targets (mostly `ntex`) to remove and avoid printing. Most of `ntex` internal logging are noise, and we would like to avoid logging it, even when `debug` level is set. \r\n\r\nA user can enable these by setting `log.log_internals=true`, or by setting `LOG_INTERNALS=true` in the env. \r\n\r\n### Improved existing log lines \r\n\r\nThis PR also iterated all of the existing log lines (specifically the debug/info/warn/error levels) and adjusted the following:\r\n\r\n* User-based errors (like validation/parsing/invalid requests) are logged as warning, as they do not imply a router error. \r\n* Router-based/internal-errors (like failure to load schema or apply an internal process in the pipeline) are printed as errors level.\r\n* All log lines have `target` configured explicitly, so log lines are now much more clear and easy to read or filter.\r\n* Everything related to the router workflow/pipeline is printed as `debug`. No log lines are printed on the hotpath, expect for the summary (see below).\r\n* No nested `fields` or nested structued are printed, to allow log ingestors to process logs easily. \r\n* Added new log lines in decision areas of the Router, all under `DEBUG` level. \r\n\r\n### Structured Logging \r\n\r\nI've iterated all existing log lines and modified their attributes to be printed as-is, instead of printing it using a raw string. This allow JSON logging to be printed nicely and inspected by tools like DataDog or Grafana. \r\n\r\nThe `message` of each log line is only there to indicate and identify the even, and no log message contains information about the event. All data and fields are tracked as standalone fields, and they are not nested. \r\n\r\n### Performance impact \r\n\r\n`tracing` crate internal processes can be heavy. To avoid a major perf regression, while still allowing usersto get meaningful logs that are all-or-nothing, we've implement several mechanism and applied decisions to find the right balance:\r\n\r\n- We do not use `tracing`'s internal request-id correlation mechanism that is based on `span`s - this has a major impact since it records too many data items. Instead, we have a task-local implementation that tracks exactly what we need. \r\n- Custom logs formatters are in charge of correlation the log line with the request. \r\n- JSON log formatter is implemented from scratch, without performing any serialization (no serde / sonic_rs or any other itermediate representation). \r\n- The default log level is `INFO` and it prints only the request summary (see below), everything else is `DEBUG`. \r\n- All log lines that are lower than `DEBUG` in our code ( = `TRACE`) are dropped at compile time.\r\n\r\n### Custom log formatters \r\n\r\nFor `json` logging, we've implemented a custom formatter that does not construct any object, and avoid any JSON object allocations, or serialization. This means, that we construct the JSON output log lines, with a simple `buffer` that's also shared to avoid re-allocation for every log line. \r\n\r\nFor `text` logging, we use the `compact` format, but avoid re-allocation of new objects when possible.\r\n\r\nThe new, custom, log formatters are also in charge of injecting the request identifiers into every log line. \r\n\r\n### Access Logs / Request Summary\r\n\r\nWhile running the request, we attached a `RequestSummary` object that has a set of pre-defined attributes that are relevant for a request summary: \r\n\r\n```rs\r\npub struct RequestSummary {\r\n pub client_name: OnceLock<String>,\r\n pub client_version: OnceLock<String>,\r\n pub operation_name: OnceLock<String>,\r\n pub operation_type: OnceLock<&'static str>,\r\n pub operation_hash: OnceLock<String>,\r\n pub persisted_document_id: OnceLock<String>,\r\n pub subgraph_requests: AtomicU32,\r\n pub involved_subgraphs: Mutex<HashSet<String>>,\r\n pub error_count: AtomicU32,\r\n pub partial_response: AtomicBool,\r\n pub response_code: OnceLock<&'static str>,\r\n pub response_mode: OnceLock<&'static str>,\r\n pub status_code: AtomicU16,\r\n pub payload_bytes: AtomicI64,\r\n pub duration_ms: AtomicU64,\r\n}\r\n```\r\n\r\nDuring execution, a task-local struct is attached to the request execution, and different part of the codebase can `record` information and hints about the execution. I tried to avoid any allocations where possible, but we can't avoid them all due to the need to use `'static` lifetime for the task-local variable. \r\n\r\nOnce a request completes, a single log line is printed on the `INFO` level, summarizes the request overall: \r\n\r\n> `2026-07-22T06:03:46.293394Z INFO router::request: operation_name=“IntrospectionQuery” operation_hash=“1a9246e236afb66a7cccaf18c07c38f2\" subgraph_requests=0 involved_subgraphs=“” error_count=0 partial_response=false response_mode=“dual” status_code=200 payload_bytes=17302 duration_ms=3 request_id=629431863395501115` \r\n\r\n### Logs Correlation\r\n\r\nIn order to implement full request correlation, every incoming request is being inspected for both request-id and traceparent information. Both can be configured, or fully disabled from the config. \r\n\r\n```yaml\r\nlog:\r\n correlation:\r\n id_header: x-request-id\r\n trace_propagation: true\r\n```\r\n\r\n<img width=\"1587\" height=\"180\" alt=\"image\" src=\"https://github.com/user-attachments/assets/0c0f5d28-f947-46ae-8b62-70990eec5c70\" />\r\n\r\nIn order to do that, we create a tokio task-specific variable that holds the request identifiers extracted from the request. Then the task-local data is passed to the request processing `Future`. \r\n\r\nIf a request doesn't have a `X-Request-ID` header, then Router will generate one for it, so logs lines are always correlated. \r\n\r\n### Subscriptions support \r\n\r\nSame as HTTP, subscriptions also extract correlations and attached them, either based on the initial message headers, or based on the per-operation headers. \r\n\r\nWe do not print a log line for the initial HTTP part of Subscriptions, and we consider every operation execution on the subscriptions channel as a separate execution when it comes to requst-id correlation. \r\n\r\nRequest correlation is based on the headers that can be passed over the init message, or subscribe message. \r\n\r\nThe request-id and request-summary are being passed to the execution thread (as `Arc`) of subscriptions, so they can also correlate log lines and report summary details without effort.\r\n\r\nFor stream-based HTTP requests, I've implemented mechanism to \"bind\"/attach the summary and request-identifier to the response, so when `ntex` finally drops the response, it will `Drop` the summary object that will print the correct summary information. \r\nAlso, `duration_ms` for stream-based reports the final time of the request, since it was staretd. For `payload_bytes` it will be an accumulation of all bytes sent in all events. \r\n\r\n### Improved log filter\r\n\r\nThe existing `log.log_filter` (or `LOG_FILTER` env var) can now mute noise from the router itself, by doing something like `LOG_FILTER=router::supergraph=off`. This allow more granular control over specific targets and parts of the Router logs. \r\n\r\n### Non-blocking stdout logging \r\n\r\nInstead of using the default `stdout`, we are now using `tracing-appender`'s solution for non-blocking writing https://docs.rs/tracing-appender/latest/tracing_appender/#non-blocking-writer.\r\n\r\nAlso, the output is buffered so it won't print every character to the `stdout` immediately, to prevent over-flushing of data to the stdout. \r\n\r\n### Testing \r\n\r\nAdded some e2e to cover the important log lines, log attributes, req-id correlations, and also to test and cover the impact on OTEL (due to the \"fake\" span we create for the logging). \r\n\r\n## Breaking Changes \r\n\r\n* Log level `trace` no longer available \r\n* Log formats `pretty`, `tree` and `pretty-compact` no longer available, only `text` and `json` are available now.\r\n\r\n## TODO \r\n\r\n- [x] apply core changes from previous PR\r\n- [x] changes to code to make it more logging-friendly\r\n- [x] http request logging\r\n- [x] subgraph calls logging\r\n- [x] graphql layer logging\r\n- [x] env vars control \r\n- [x] bring back env-based log filter\r\n- [x] `trace` level in development only\r\n- [x] e2e testing\r\n- [x] subscriptions \r\n- [x] figure out perf regression\r\n- [x] subgraph call target \r\n- [x] subgraph http logging \r\n- [x] schema id in the log line \r\n- [x] subscriptions support (correlation over `spawn`)\r\n- [x] stream support (`duration_ms` + `payload_bytes`) \r\n- [x] sonyflake?\r\n- [x] changeset\r\n- [x] docs ",
"changed_files": 80,
"closed_at": null,
"comments": 4,
"comments_url": "https://api.github.com/repos/graphql-hive/router/issues/1283/comments",
"commits": 27,
"commits_url": "https://api.github.com/repos/graphql-hive/router/pulls/1283/commits",
"created_at": "2026-07-15T18:25:59Z",
"deletions": 820,
"diff_url": "https://github.com/graphql-hive/router/pull/1283.diff",
"draft": false,
"head": {
"label": "graphql-hive:minilog",
"ref": "minilog",
"repo": {
"allow_auto_merge": false,
"allow_forking": true,
"allow_merge_commit": false,
"allow_rebase_merge": false,
"allow_squash_merge": true,
"allow_update_branch": true,
"archive_url": "https://api.github.com/repos/graphql-hive/router/{archive_format}{/ref}",
"archived": false,
"assignees_url": "https://api.github.com/repos/graphql-hive/router/assignees{/user}",
"blobs_url": "https://api.github.com/repos/graphql-hive/router/git/blobs{/sha}",
"branches_url": "https://api.github.com/repos/graphql-hive/router/branches{/branch}",
"clone_url": "https://github.com/graphql-hive/router.git",
"collaborators_url": "https://api.github.com/repos/graphql-hive/router/collaborators{/collaborator}",
"comments_url": "https://api.github.com/repos/graphql-hive/router/comments{/number}",
"commits_url": "https://api.github.com/repos/graphql-hive/router/commits{/sha}",
"compare_url": "https://api.github.com/repos/graphql-hive/router/compare/{base}...{head}",
"contents_url": "https://api.github.com/repos/graphql-hive/router/contents/{+path}",
"contributors_url": "https://api.github.com/repos/graphql-hive/router/contributors",
"created_at": "2024-11-20T16:16:12Z",
"default_branch": "main",
"delete_branch_on_merge": true,
"deployments_url": "https://api.github.com/repos/graphql-hive/router/deployments",
"description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
"disabled": false,
"downloads_url": "https://api.github.com/repos/graphql-hive/router/downloads",
"events_url": "https://api.github.com/repos/graphql-hive/router/events",
"fork": false,
"forks": 16,
"forks_count": 16,
"forks_url": "https://api.github.com/repos/graphql-hive/router/forks",
"full_name": "graphql-hive/router",
"git_commits_url": "https://api.github.com/repos/graphql-hive/router/git/commits{/sha}",
"git_refs_url": "https://api.github.com/repos/graphql-hive/router/git/refs{/sha}",
"git_tags_url": "https://api.github.com/repos/graphql-hive/router/git/tags{/sha}",
"git_url": "git://github.com/graphql-hive/router.git",
"has_discussions": false,
"has_downloads": false,
"has_issues": true,
"has_pages": false,
"has_projects": false,
"has_pull_requests": true,
"has_wiki": false,
"homepage": "https://the-guild.dev/graphql/hive/docs/router",
"hooks_url": "https://api.github.com/repos/graphql-hive/router/hooks",
"html_url": "https://github.com/graphql-hive/router",
"id": 891604244,
"is_template": false,
"issue_comment_url": "https://api.github.com/repos/graphql-hive/router/issues/comments{/number}",
"issue_events_url": "https://api.github.com/repos/graphql-hive/router/issues/events{/number}",
"issues_url": "https://api.github.com/repos/graphql-hive/router/issues{/number}",
"keys_url": "https://api.github.com/repos/graphql-hive/router/keys{/key_id}",
"labels_url": "https://api.github.com/repos/graphql-hive/router/labels{/name}",
"language": "Rust",
"languages_url": "https://api.github.com/repos/graphql-hive/router/languages",
"license": {
"key": "mit",
"name": "MIT License",
"node_id": "MDc6TGljZW5zZTEz",
"spdx_id": "MIT",
"url": "https://api.github.com/licenses/mit"
},
"merge_commit_message": "PR_TITLE",
"merge_commit_title": "MERGE_MESSAGE",
"merges_url": "https://api.github.com/repos/graphql-hive/router/merges",
"milestones_url": "https://api.github.com/repos/graphql-hive/router/milestones{/number}",
"mirror_url": null,
"name": "router",
"node_id": "R_kgDONSTNFA",
"notifications_url": "https://api.github.com/repos/graphql-hive/router/notifications{?since,all,participating}",
"open_issues": 63,
"open_issues_count": 63,
"owner": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
"followers_url": "https://api.github.com/users/graphql-hive/followers",
"following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
"gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/graphql-hive",
"id": 182742256,
"login": "graphql-hive",
"node_id": "O_kgDOCuRs8A",
"organizations_url": "https://api.github.com/users/graphql-hive/orgs",
"received_events_url": "https://api.github.com/users/graphql-hive/received_events",
"repos_url": "https://api.github.com/users/graphql-hive/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/graphql-hive",
"user_view_type": "public"
},
"private": false,
"pull_request_creation_policy": "all",
"pulls_url": "https://api.github.com/repos/graphql-hive/router/pulls{/number}",
"pushed_at": "2026-07-26T15:24:58Z",
"releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
"size": 10515,
"squash_merge_commit_message": "PR_BODY",
"squash_merge_commit_title": "PR_TITLE",
"ssh_url": "git@github.com:graphql-hive/router.git",
"stargazers_count": 95,
"stargazers_url": "https://api.github.com/repos/graphql-hive/router/stargazers",
"statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/{sha}",
"subscribers_url": "https://api.github.com/repos/graphql-hive/router/subscribers",
"subscription_url": "https://api.github.com/repos/graphql-hive/router/subscription",
"svn_url": "https://github.com/graphql-hive/router",
"tags_url": "https://api.github.com/repos/graphql-hive/router/tags",
"teams_url": "https://api.github.com/repos/graphql-hive/router/teams",
"topics": [
"apollo-federation",
"federation",
"federation-gateway",
"graphql",
"graphql-federation",
"router"
],
"trees_url": "https://api.github.com/repos/graphql-hive/router/git/trees{/sha}",
"updated_at": "2026-07-26T09:14:02Z",
"url": "https://api.github.com/repos/graphql-hive/router",
"use_squash_pr_title_as_default": true,
"visibility": "public",
"watchers": 95,
"watchers_count": 95,
"web_commit_signoff_required": false
},
"sha": "bf21c72004395639f5d1b19a60b1fc2d419d27f2",
"user": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
"followers_url": "https://api.github.com/users/graphql-hive/followers",
"following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
"gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/graphql-hive",
"id": 182742256,
"login": "graphql-hive",
"node_id": "O_kgDOCuRs8A",
"organizations_url": "https://api.github.com/users/graphql-hive/orgs",
"received_events_url": "https://api.github.com/users/graphql-hive/received_events",
"repos_url": "https://api.github.com/users/graphql-hive/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/graphql-hive",
"user_view_type": "public"
}
},
"html_url": "https://github.com/graphql-hive/router/pull/1283",
"id": 4062814180,
"issue_url": "https://api.github.com/repos/graphql-hive/router/issues/1283",
"labels": [],
"locked": false,
"maintainer_can_modify": false,
"merge_commit_sha": "57391e94824b20a53535babe7cbfe7aa8e8d5d36",
"mergeable": null,
"mergeable_state": "unknown",
"merged": false,
"merged_at": null,
"merged_by": null,
"milestone": null,
"node_id": "PR_kwDONSTNFM7yKZ_k",
"number": 1283,
"patch_url": "https://github.com/graphql-hive/router/pull/1283.patch",
"rebaseable": null,
"requested_reviewers": [
{
"avatar_url": "https://avatars.githubusercontent.com/u/8167190?v=4",
"events_url": "https://api.github.com/users/kamilkisiela/events{/privacy}",
"followers_url": "https://api.github.com/users/kamilkisiela/followers",
"following_url": "https://api.github.com/users/kamilkisiela/following{/other_user}",
"gists_url": "https://api.github.com/users/kamilkisiela/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/kamilkisiela",
"id": 8167190,
"login": "kamilkisiela",
"node_id": "MDQ6VXNlcjgxNjcxOTA=",
"organizations_url": "https://api.github.com/users/kamilkisiela/orgs",
"received_events_url": "https://api.github.com/users/kamilkisiela/received_events",
"repos_url": "https://api.github.com/users/kamilkisiela/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/kamilkisiela/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kamilkisiela/subscriptions",
"type": "User",
"url": "https://api.github.com/users/kamilkisiela",
"user_view_type": "public"
},
{
"avatar_url": "https://avatars.githubusercontent.com/in/946600?v=4",
"events_url": "https://api.github.com/users/Copilot/events{/privacy}",
"followers_url": "https://api.github.com/users/Copilot/followers",
"following_url": "https://api.github.com/users/Copilot/following{/other_user}",
"gists_url": "https://api.github.com/users/Copilot/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/apps/copilot-pull-request-reviewer",
"id": 175728472,
"login": "Copilot",
"node_id": "BOT_kgDOCnlnWA",
"organizations_url": "https://api.github.com/users/Copilot/orgs",
"received_events_url": "https://api.github.com/users/Copilot/received_events",
"repos_url": "https://api.github.com/users/Copilot/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/Copilot/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/Copilot/subscriptions",
"type": "Bot",
"url": "https://api.github.com/users/Copilot",
"user_view_type": "public"
}
],
"requested_teams": [],
"review_comment_url": "https://api.github.com/repos/graphql-hive/router/pulls/comments{/number}",
"review_comments": 65,
"review_comments_url": "https://api.github.com/repos/graphql-hive/router/pulls/1283/comments",
"state": "open",
"statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/bf21c72004395639f5d1b19a60b1fc2d419d27f2",
"title": "feat(router): logger improvements, access logs ",
"updated_at": "2026-07-26T15:25:00Z",
"url": "https://api.github.com/repos/graphql-hive/router/pulls/1283",
"user": {
"avatar_url": "https://avatars.githubusercontent.com/u/3680083?v=4",
"events_url": "https://api.github.com/users/dotansimha/events{/privacy}",
"followers_url": "https://api.github.com/users/dotansimha/followers",
"following_url": "https://api.github.com/users/dotansimha/following{/other_user}",
"gists_url": "https://api.github.com/users/dotansimha/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/dotansimha",
"id": 3680083,
"login": "dotansimha",
"node_id": "MDQ6VXNlcjM2ODAwODM=",
"organizations_url": "https://api.github.com/users/dotansimha/orgs",
"received_events_url": "https://api.github.com/users/dotansimha/received_events",
"repos_url": "https://api.github.com/users/dotansimha/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/dotansimha/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/dotansimha/subscriptions",
"type": "User",
"url": "https://api.github.com/users/dotansimha",
"user_view_type": "public"
}
},
"repository": {
"allow_forking": true,
"archive_url": "https://api.github.com/repos/graphql-hive/router/{archive_format}{/ref}",
"archived": false,
"assignees_url": "https://api.github.com/repos/graphql-hive/router/assignees{/user}",
"blobs_url": "https://api.github.com/repos/graphql-hive/router/git/blobs{/sha}",
"branches_url": "https://api.github.com/repos/graphql-hive/router/branches{/branch}",
"clone_url": "https://github.com/graphql-hive/router.git",
"collaborators_url": "https://api.github.com/repos/graphql-hive/router/collaborators{/collaborator}",
"comments_url": "https://api.github.com/repos/graphql-hive/router/comments{/number}",
"commits_url": "https://api.github.com/repos/graphql-hive/router/commits{/sha}",
"compare_url": "https://api.github.com/repos/graphql-hive/router/compare/{base}...{head}",
"contents_url": "https://api.github.com/repos/graphql-hive/router/contents/{+path}",
"contributors_url": "https://api.github.com/repos/graphql-hive/router/contributors",
"created_at": "2024-11-20T16:16:12Z",
"custom_properties": {
"vanta_production_branch_name": "main"
},
"default_branch": "main",
"deployments_url": "https://api.github.com/repos/graphql-hive/router/deployments",
"description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
"disabled": false,
"downloads_url": "https://api.github.com/repos/graphql-hive/router/downloads",
"events_url": "https://api.github.com/repos/graphql-hive/router/events",
"fork": false,
"forks": 16,
"forks_count": 16,
"forks_url": "https://api.github.com/repos/graphql-hive/router/forks",
"full_name": "graphql-hive/router",
"git_commits_url": "https://api.github.com/repos/graphql-hive/router/git/commits{/sha}",
"git_refs_url": "https://api.github.com/repos/graphql-hive/router/git/refs{/sha}",
"git_tags_url": "https://api.github.com/repos/graphql-hive/router/git/tags{/sha}",
"git_url": "git://github.com/graphql-hive/router.git",
"has_discussions": false,
"has_downloads": false,
"has_issues": true,
"has_pages": false,
"has_projects": false,
"has_pull_requests": true,
"has_wiki": false,
"homepage": "https://the-guild.dev/graphql/hive/docs/router",
"hooks_url": "https://api.github.com/repos/graphql-hive/router/hooks",
"html_url": "https://github.com/graphql-hive/router",
"id": 891604244,
"is_template": false,
"issue_comment_url": "https://api.github.com/repos/graphql-hive/router/issues/comments{/number}",
"issue_events_url": "https://api.github.com/repos/graphql-hive/router/issues/events{/number}",
"issues_url": "https://api.github.com/repos/graphql-hive/router/issues{/number}",
"keys_url": "https://api.github.com/repos/graphql-hive/router/keys{/key_id}",
"labels_url": "https://api.github.com/repos/graphql-hive/router/labels{/name}",
"language": "Rust",
"languages_url": "https://api.github.com/repos/graphql-hive/router/languages",
"license": {
"key": "mit",
"name": "MIT License",
"node_id": "MDc6TGljZW5zZTEz",
"spdx_id": "MIT",
"url": "https://api.github.com/licenses/mit"
},
"merges_url": "https://api.github.com/repos/graphql-hive/router/merges",
"milestones_url": "https://api.github.com/repos/graphql-hive/router/milestones{/number}",
"mirror_url": null,
"name": "router",
"node_id": "R_kgDONSTNFA",
"notifications_url": "https://api.github.com/repos/graphql-hive/router/notifications{?since,all,participating}",
"open_issues": 63,
"open_issues_count": 63,
"owner": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
"followers_url": "https://api.github.com/users/graphql-hive/followers",
"following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
"gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/graphql-hive",
"id": 182742256,
"login": "graphql-hive",
"node_id": "O_kgDOCuRs8A",
"organizations_url": "https://api.github.com/users/graphql-hive/orgs",
"received_events_url": "https://api.github.com/users/graphql-hive/received_events",
"repos_url": "https://api.github.com/users/graphql-hive/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/graphql-hive",
"user_view_type": "public"
},
"private": false,
"pull_request_creation_policy": "all",
"pulls_url": "https://api.github.com/repos/graphql-hive/router/pulls{/number}",
"pushed_at": "2026-07-26T15:24:58Z",
"releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
"size": 10515,
"ssh_url": "git@github.com:graphql-hive/router.git",
"stargazers_count": 95,
"stargazers_url": "https://api.github.com/repos/graphql-hive/router/stargazers",
"statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/{sha}",
"subscribers_url": "https://api.github.com/repos/graphql-hive/router/subscribers",
"subscription_url": "https://api.github.com/repos/graphql-hive/router/subscription",
"svn_url": "https://github.com/graphql-hive/router",
"tags_url": "https://api.github.com/repos/graphql-hive/router/tags",
"teams_url": "https://api.github.com/repos/graphql-hive/router/teams",
"topics": [
"apollo-federation",
"federation",
"federation-gateway",
"graphql",
"graphql-federation",
"router"
],
"trees_url": "https://api.github.com/repos/graphql-hive/router/git/trees{/sha}",
"updated_at": "2026-07-26T09:14:02Z",
"url": "https://api.github.com/repos/graphql-hive/router",
"visibility": "public",
"watchers": 95,
"watchers_count": 95,
"web_commit_signoff_required": false
},
"sender": {
"avatar_url": "https://avatars.githubusercontent.com/u/3680083?v=4",
"events_url": "https://api.github.com/users/dotansimha/events{/privacy}",
"followers_url": "https://api.github.com/users/dotansimha/followers",
"following_url": "https://api.github.com/users/dotansimha/following{/other_user}",
"gists_url": "https://api.github.com/users/dotansimha/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/dotansimha",
"id": 3680083,
"login": "dotansimha",
"node_id": "MDQ6VXNlcjM2ODAwODM=",
"organizations_url": "https://api.github.com/users/dotansimha/orgs",
"received_events_url": "https://api.github.com/users/dotansimha/received_events",
"repos_url": "https://api.github.com/users/dotansimha/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/dotansimha/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/dotansimha/subscriptions",
"type": "User",
"url": "https://api.github.com/users/dotansimha",
"user_view_type": "public"
}
},
"github_job": "docker",
"github_ref": "refs/pull/1283/merge",
"github_ref_name": "1283/merge",
"github_ref_protected": "false",
"github_ref_type": "branch",
"github_repository": "graphql-hive/router",
"github_repository_id": "891604244",
"github_repository_owner": "graphql-hive",
"github_repository_owner_id": "182742256",
"github_run_attempt": "1",
"github_run_id": "30208149531",
"github_run_number": "4644",
"github_runner_arch": "X64",
"github_runner_environment": "github-hosted",
"github_runner_image_os": "ubuntu24",
"github_runner_image_version": "20260720.247.2",
"github_runner_name": "GitHub Actions 1000933918",
"github_runner_os": "Linux",
"github_runner_tracking_id": "github_cde20512-a663-4ffd-b6e2-d12f494b3652",
"github_server_url": "https://github.com",
"github_triggering_actor": "dotansimha",
"github_workflow": "build-router",
"github_workflow_ref": "graphql-hive/router/.github/workflows/build-router.yaml@refs/pull/1283/merge",
"github_workflow_sha": "ebd2267e07f094d8ffff141a2466714f698bb062",
"platform": "linux/amd64"
}
}
},
"buildx.build.provenance/linux/arm64": {
"builder": {
"id": "https://github.com/graphql-hive/router/actions/runs/30208149531/attempts/1"
},
"buildType": "https://mobyproject.org/buildkit@v1",
"materials": [
{
"uri": "pkg:docker/docker/dockerfile@1.22",
"digest": {
"sha256": "4a43a54dd1fedceb30ba47e76cfcf2b47304f4161c0caeac2db1c61804ea3c91"
}
},
{
"uri": "pkg:docker/gcr.io/distroless/cc-debian12@latest?platform=linux%2Farm64",
"digest": {
"sha256": "e8e7ee4b8b106d4c5fde9e422a321b2b8a2d5cca546c97adcce927f3e1d36e36"
}
}
],
"invocation": {
"configSource": {
"entryPoint": "router.Dockerfile"
},
"parameters": {
"frontend": "gateway.v0",
"args": {
"cmdline": "docker/dockerfile:1.22",
"label:org.opencontainers.image.created": "2026-07-26T15:35:32.474Z",
"label:org.opencontainers.image.description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
"label:org.opencontainers.image.licenses": "MIT",
"label:org.opencontainers.image.revision": "ebd2267e07f094d8ffff141a2466714f698bb062",
"label:org.opencontainers.image.source": "https://github.com/graphql-hive/router",
"label:org.opencontainers.image.title": "router",
"label:org.opencontainers.image.url": "https://github.com/graphql-hive/router",
"label:org.opencontainers.image.vendor": "theguild",
"label:org.opencontainers.image.version": "pr-1283",
"source": "docker/dockerfile:1.22"
},
"locals": [
{
"name": "context"
},
{
"name": "dockerfile"
}
],
"root": {
"configSource": {
"path": "router.Dockerfile"
},
"request": {
"args": {
"label:org.opencontainers.image.created": "2026-07-26T15:35:32.474Z",
"label:org.opencontainers.image.description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
"label:org.opencontainers.image.licenses": "MIT",
"label:org.opencontainers.image.revision": "ebd2267e07f094d8ffff141a2466714f698bb062",
"label:org.opencontainers.image.source": "https://github.com/graphql-hive/router",
"label:org.opencontainers.image.title": "router",
"label:org.opencontainers.image.url": "https://github.com/graphql-hive/router",
"label:org.opencontainers.image.vendor": "theguild",
"label:org.opencontainers.image.version": "pr-1283",
"vcs:localdir:context": ".",
"vcs:localdir:dockerfile": "docker",
"vcs:revision": "ebd2267e07f094d8ffff141a2466714f698bb062",
"vcs:source": "https://github.com/graphql-hive/router"
}
}
},
"compatibilityVersion": 30
},
"environment": {
"github_actor": "dotansimha",
"github_actor_id": "3680083",
"github_event_name": "pull_request",
"github_event_payload": {
"action": "synchronize",
"after": "bf21c72004395639f5d1b19a60b1fc2d419d27f2",
"before": "e3323c1d8f3705c27d91e1cd743d78e048ee5dd0",
"enterprise": {
"avatar_url": "https://avatars.githubusercontent.com/b/187753?v=4",
"created_at": "2024-07-02T08:52:28Z",
"description": "",
"html_url": "https://github.com/enterprises/the-guild",
"id": 187753,
"name": "The Guild",
"node_id": "E_kgDOAALdaQ",
"slug": "the-guild",
"updated_at": "2026-07-11T07:16:45Z",
"website_url": "https://the-guild.dev/"
},
"number": 1283,
"organization": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"description": "Schema registry, analytics and gateway for GraphQL federation and other GraphQL APIs.",
"events_url": "https://api.github.com/orgs/graphql-hive/events",
"hooks_url": "https://api.github.com/orgs/graphql-hive/hooks",
"id": 182742256,
"issues_url": "https://api.github.com/orgs/graphql-hive/issues",
"login": "graphql-hive",
"members_url": "https://api.github.com/orgs/graphql-hive/members{/member}",
"node_id": "O_kgDOCuRs8A",
"public_members_url": "https://api.github.com/orgs/graphql-hive/public_members{/member}",
"repos_url": "https://api.github.com/orgs/graphql-hive/repos",
"url": "https://api.github.com/orgs/graphql-hive"
},
"pull_request": {
"_links": {
"comments": {
"href": "https://api.github.com/repos/graphql-hive/router/issues/1283/comments"
},
"commits": {
"href": "https://api.github.com/repos/graphql-hive/router/pulls/1283/commits"
},
"html": {
"href": "https://github.com/graphql-hive/router/pull/1283"
},
"issue": {
"href": "https://api.github.com/repos/graphql-hive/router/issues/1283"
},
"review_comment": {
"href": "https://api.github.com/repos/graphql-hive/router/pulls/comments{/number}"
},
"review_comments": {
"href": "https://api.github.com/repos/graphql-hive/router/pulls/1283/comments"
},
"self": {
"href": "https://api.github.com/repos/graphql-hive/router/pulls/1283"
},
"statuses": {
"href": "https://api.github.com/repos/graphql-hive/router/statuses/bf21c72004395639f5d1b19a60b1fc2d419d27f2"
}
},
"active_lock_reason": null,
"additions": 3096,
"assignee": null,
"assignees": [],
"author_association": "MEMBER",
"auto_merge": null,
"base": {
"label": "graphql-hive:main",
"ref": "main",
"repo": {
"allow_auto_merge": false,
"allow_forking": true,
"allow_merge_commit": false,
"allow_rebase_merge": false,
"allow_squash_merge": true,
"allow_update_branch": true,
"archive_url": "https://api.github.com/repos/graphql-hive/router/{archive_format}{/ref}",
"archived": false,
"assignees_url": "https://api.github.com/repos/graphql-hive/router/assignees{/user}",
"blobs_url": "https://api.github.com/repos/graphql-hive/router/git/blobs{/sha}",
"branches_url": "https://api.github.com/repos/graphql-hive/router/branches{/branch}",
"clone_url": "https://github.com/graphql-hive/router.git",
"collaborators_url": "https://api.github.com/repos/graphql-hive/router/collaborators{/collaborator}",
"comments_url": "https://api.github.com/repos/graphql-hive/router/comments{/number}",
"commits_url": "https://api.github.com/repos/graphql-hive/router/commits{/sha}",
"compare_url": "https://api.github.com/repos/graphql-hive/router/compare/{base}...{head}",
"contents_url": "https://api.github.com/repos/graphql-hive/router/contents/{+path}",
"contributors_url": "https://api.github.com/repos/graphql-hive/router/contributors",
"created_at": "2024-11-20T16:16:12Z",
"default_branch": "main",
"delete_branch_on_merge": true,
"deployments_url": "https://api.github.com/repos/graphql-hive/router/deployments",
"description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
"disabled": false,
"downloads_url": "https://api.github.com/repos/graphql-hive/router/downloads",
"events_url": "https://api.github.com/repos/graphql-hive/router/events",
"fork": false,
"forks": 16,
"forks_count": 16,
"forks_url": "https://api.github.com/repos/graphql-hive/router/forks",
"full_name": "graphql-hive/router",
"git_commits_url": "https://api.github.com/repos/graphql-hive/router/git/commits{/sha}",
"git_refs_url": "https://api.github.com/repos/graphql-hive/router/git/refs{/sha}",
"git_tags_url": "https://api.github.com/repos/graphql-hive/router/git/tags{/sha}",
"git_url": "git://github.com/graphql-hive/router.git",
"has_discussions": false,
"has_downloads": false,
"has_issues": true,
"has_pages": false,
"has_projects": false,
"has_pull_requests": true,
"has_wiki": false,
"homepage": "https://the-guild.dev/graphql/hive/docs/router",
"hooks_url": "https://api.github.com/repos/graphql-hive/router/hooks",
"html_url": "https://github.com/graphql-hive/router",
"id": 891604244,
"is_template": false,
"issue_comment_url": "https://api.github.com/repos/graphql-hive/router/issues/comments{/number}",
"issue_events_url": "https://api.github.com/repos/graphql-hive/router/issues/events{/number}",
"issues_url": "https://api.github.com/repos/graphql-hive/router/issues{/number}",
"keys_url": "https://api.github.com/repos/graphql-hive/router/keys{/key_id}",
"labels_url": "https://api.github.com/repos/graphql-hive/router/labels{/name}",
"language": "Rust",
"languages_url": "https://api.github.com/repos/graphql-hive/router/languages",
"license": {
"key": "mit",
"name": "MIT License",
"node_id": "MDc6TGljZW5zZTEz",
"spdx_id": "MIT",
"url": "https://api.github.com/licenses/mit"
},
"merge_commit_message": "PR_TITLE",
"merge_commit_title": "MERGE_MESSAGE",
"merges_url": "https://api.github.com/repos/graphql-hive/router/merges",
"milestones_url": "https://api.github.com/repos/graphql-hive/router/milestones{/number}",
"mirror_url": null,
"name": "router",
"node_id": "R_kgDONSTNFA",
"notifications_url": "https://api.github.com/repos/graphql-hive/router/notifications{?since,all,participating}",
"open_issues": 63,
"open_issues_count": 63,
"owner": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
"followers_url": "https://api.github.com/users/graphql-hive/followers",
"following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
"gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/graphql-hive",
"id": 182742256,
"login": "graphql-hive",
"node_id": "O_kgDOCuRs8A",
"organizations_url": "https://api.github.com/users/graphql-hive/orgs",
"received_events_url": "https://api.github.com/users/graphql-hive/received_events",
"repos_url": "https://api.github.com/users/graphql-hive/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/graphql-hive",
"user_view_type": "public"
},
"private": false,
"pull_request_creation_policy": "all",
"pulls_url": "https://api.github.com/repos/graphql-hive/router/pulls{/number}",
"pushed_at": "2026-07-26T15:24:58Z",
"releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
"size": 10515,
"squash_merge_commit_message": "PR_BODY",
"squash_merge_commit_title": "PR_TITLE",
"ssh_url": "git@github.com:graphql-hive/router.git",
"stargazers_count": 95,
"stargazers_url": "https://api.github.com/repos/graphql-hive/router/stargazers",
"statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/{sha}",
"subscribers_url": "https://api.github.com/repos/graphql-hive/router/subscribers",
"subscription_url": "https://api.github.com/repos/graphql-hive/router/subscription",
"svn_url": "https://github.com/graphql-hive/router",
"tags_url": "https://api.github.com/repos/graphql-hive/router/tags",
"teams_url": "https://api.github.com/repos/graphql-hive/router/teams",
"topics": [
"apollo-federation",
"federation",
"federation-gateway",
"graphql",
"graphql-federation",
"router"
],
"trees_url": "https://api.github.com/repos/graphql-hive/router/git/trees{/sha}",
"updated_at": "2026-07-26T09:14:02Z",
"url": "https://api.github.com/repos/graphql-hive/router",
"use_squash_pr_title_as_default": true,
"visibility": "public",
"watchers": 95,
"watchers_count": 95,
"web_commit_signoff_required": false
},
"sha": "13643d6165e76cf3442b9dbbf1eb365b9b523702",
"user": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
"followers_url": "https://api.github.com/users/graphql-hive/followers",
"following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
"gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/graphql-hive",
"id": 182742256,
"login": "graphql-hive",
"node_id": "O_kgDOCuRs8A",
"organizations_url": "https://api.github.com/users/graphql-hive/orgs",
"received_events_url": "https://api.github.com/users/graphql-hive/received_events",
"repos_url": "https://api.github.com/users/graphql-hive/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/graphql-hive",
"user_view_type": "public"
}
},
"body": "Closes https://github.com/graphql-hive/router/issues/755 \r\nCloses https://github.com/graphql-hive/router/issues/765 \r\nCloses https://github.com/graphql-hive/router/issues/503 \r\n\r\nDocs PR: https://github.com/graphql-hive/docs/pull/147 \r\nDocs preview: https://a507d5b7-hive-platform-docs.theguild.workers.dev/graphql/hive/docs/router/observability/logging\r\n\r\n## Background \r\n\r\nExtracted only the crucial changes from https://github.com/graphql-hive/router/pull/775 and re-implements major parts of it. \r\n\r\n## Changes in this PR \r\n\r\n### Disable logging from internal crates \r\n\r\nA new config flag `log.log_internals` (default: `false`) has been added, that creates a `Targets` filter. It has a hardcoded list of log targets (mostly `ntex`) to remove and avoid printing. Most of `ntex` internal logging are noise, and we would like to avoid logging it, even when `debug` level is set. \r\n\r\nA user can enable these by setting `log.log_internals=true`, or by setting `LOG_INTERNALS=true` in the env. \r\n\r\n### Improved existing log lines \r\n\r\nThis PR also iterated all of the existing log lines (specifically the debug/info/warn/error levels) and adjusted the following:\r\n\r\n* User-based errors (like validation/parsing/invalid requests) are logged as warning, as they do not imply a router error. \r\n* Router-based/internal-errors (like failure to load schema or apply an internal process in the pipeline) are printed as errors level.\r\n* All log lines have `target` configured explicitly, so log lines are now much more clear and easy to read or filter.\r\n* Everything related to the router workflow/pipeline is printed as `debug`. No log lines are printed on the hotpath, expect for the summary (see below).\r\n* No nested `fields` or nested structued are printed, to allow log ingestors to process logs easily. \r\n* Added new log lines in decision areas of the Router, all under `DEBUG` level. \r\n\r\n### Structured Logging \r\n\r\nI've iterated all existing log lines and modified their attributes to be printed as-is, instead of printing it using a raw string. This allow JSON logging to be printed nicely and inspected by tools like DataDog or Grafana. \r\n\r\nThe `message` of each log line is only there to indicate and identify the even, and no log message contains information about the event. All data and fields are tracked as standalone fields, and they are not nested. \r\n\r\n### Performance impact \r\n\r\n`tracing` crate internal processes can be heavy. To avoid a major perf regression, while still allowing usersto get meaningful logs that are all-or-nothing, we've implement several mechanism and applied decisions to find the right balance:\r\n\r\n- We do not use `tracing`'s internal request-id correlation mechanism that is based on `span`s - this has a major impact since it records too many data items. Instead, we have a task-local implementation that tracks exactly what we need. \r\n- Custom logs formatters are in charge of correlation the log line with the request. \r\n- JSON log formatter is implemented from scratch, without performing any serialization (no serde / sonic_rs or any other itermediate representation). \r\n- The default log level is `INFO` and it prints only the request summary (see below), everything else is `DEBUG`. \r\n- All log lines that are lower than `DEBUG` in our code ( = `TRACE`) are dropped at compile time.\r\n\r\n### Custom log formatters \r\n\r\nFor `json` logging, we've implemented a custom formatter that does not construct any object, and avoid any JSON object allocations, or serialization. This means, that we construct the JSON output log lines, with a simple `buffer` that's also shared to avoid re-allocation for every log line. \r\n\r\nFor `text` logging, we use the `compact` format, but avoid re-allocation of new objects when possible.\r\n\r\nThe new, custom, log formatters are also in charge of injecting the request identifiers into every log line. \r\n\r\n### Access Logs / Request Summary\r\n\r\nWhile running the request, we attached a `RequestSummary` object that has a set of pre-defined attributes that are relevant for a request summary: \r\n\r\n```rs\r\npub struct RequestSummary {\r\n pub client_name: OnceLock<String>,\r\n pub client_version: OnceLock<String>,\r\n pub operation_name: OnceLock<String>,\r\n pub operation_type: OnceLock<&'static str>,\r\n pub operation_hash: OnceLock<String>,\r\n pub persisted_document_id: OnceLock<String>,\r\n pub subgraph_requests: AtomicU32,\r\n pub involved_subgraphs: Mutex<HashSet<String>>,\r\n pub error_count: AtomicU32,\r\n pub partial_response: AtomicBool,\r\n pub response_code: OnceLock<&'static str>,\r\n pub response_mode: OnceLock<&'static str>,\r\n pub status_code: AtomicU16,\r\n pub payload_bytes: AtomicI64,\r\n pub duration_ms: AtomicU64,\r\n}\r\n```\r\n\r\nDuring execution, a task-local struct is attached to the request execution, and different part of the codebase can `record` information and hints about the execution. I tried to avoid any allocations where possible, but we can't avoid them all due to the need to use `'static` lifetime for the task-local variable. \r\n\r\nOnce a request completes, a single log line is printed on the `INFO` level, summarizes the request overall: \r\n\r\n> `2026-07-22T06:03:46.293394Z INFO router::request: operation_name=“IntrospectionQuery” operation_hash=“1a9246e236afb66a7cccaf18c07c38f2\" subgraph_requests=0 involved_subgraphs=“” error_count=0 partial_response=false response_mode=“dual” status_code=200 payload_bytes=17302 duration_ms=3 request_id=629431863395501115` \r\n\r\n### Logs Correlation\r\n\r\nIn order to implement full request correlation, every incoming request is being inspected for both request-id and traceparent information. Both can be configured, or fully disabled from the config. \r\n\r\n```yaml\r\nlog:\r\n correlation:\r\n id_header: x-request-id\r\n trace_propagation: true\r\n```\r\n\r\n<img width=\"1587\" height=\"180\" alt=\"image\" src=\"https://github.com/user-attachments/assets/0c0f5d28-f947-46ae-8b62-70990eec5c70\" />\r\n\r\nIn order to do that, we create a tokio task-specific variable that holds the request identifiers extracted from the request. Then the task-local data is passed to the request processing `Future`. \r\n\r\nIf a request doesn't have a `X-Request-ID` header, then Router will generate one for it, so logs lines are always correlated. \r\n\r\n### Subscriptions support \r\n\r\nSame as HTTP, subscriptions also extract correlations and attached them, either based on the initial message headers, or based on the per-operation headers. \r\n\r\nWe do not print a log line for the initial HTTP part of Subscriptions, and we consider every operation execution on the subscriptions channel as a separate execution when it comes to requst-id correlation. \r\n\r\nRequest correlation is based on the headers that can be passed over the init message, or subscribe message. \r\n\r\nThe request-id and request-summary are being passed to the execution thread (as `Arc`) of subscriptions, so they can also correlate log lines and report summary details without effort.\r\n\r\nFor stream-based HTTP requests, I've implemented mechanism to \"bind\"/attach the summary and request-identifier to the response, so when `ntex` finally drops the response, it will `Drop` the summary object that will print the correct summary information. \r\nAlso, `duration_ms` for stream-based reports the final time of the request, since it was staretd. For `payload_bytes` it will be an accumulation of all bytes sent in all events. \r\n\r\n### Improved log filter\r\n\r\nThe existing `log.log_filter` (or `LOG_FILTER` env var) can now mute noise from the router itself, by doing something like `LOG_FILTER=router::supergraph=off`. This allow more granular control over specific targets and parts of the Router logs. \r\n\r\n### Non-blocking stdout logging \r\n\r\nInstead of using the default `stdout`, we are now using `tracing-appender`'s solution for non-blocking writing https://docs.rs/tracing-appender/latest/tracing_appender/#non-blocking-writer.\r\n\r\nAlso, the output is buffered so it won't print every character to the `stdout` immediately, to prevent over-flushing of data to the stdout. \r\n\r\n### Testing \r\n\r\nAdded some e2e to cover the important log lines, log attributes, req-id correlations, and also to test and cover the impact on OTEL (due to the \"fake\" span we create for the logging). \r\n\r\n## Breaking Changes \r\n\r\n* Log level `trace` no longer available \r\n* Log formats `pretty`, `tree` and `pretty-compact` no longer available, only `text` and `json` are available now.\r\n\r\n## TODO \r\n\r\n- [x] apply core changes from previous PR\r\n- [x] changes to code to make it more logging-friendly\r\n- [x] http request logging\r\n- [x] subgraph calls logging\r\n- [x] graphql layer logging\r\n- [x] env vars control \r\n- [x] bring back env-based log filter\r\n- [x] `trace` level in development only\r\n- [x] e2e testing\r\n- [x] subscriptions \r\n- [x] figure out perf regression\r\n- [x] subgraph call target \r\n- [x] subgraph http logging \r\n- [x] schema id in the log line \r\n- [x] subscriptions support (correlation over `spawn`)\r\n- [x] stream support (`duration_ms` + `payload_bytes`) \r\n- [x] sonyflake?\r\n- [x] changeset\r\n- [x] docs ",
"changed_files": 80,
"closed_at": null,
"comments": 4,
"comments_url": "https://api.github.com/repos/graphql-hive/router/issues/1283/comments",
"commits": 27,
"commits_url": "https://api.github.com/repos/graphql-hive/router/pulls/1283/commits",
"created_at": "2026-07-15T18:25:59Z",
"deletions": 820,
"diff_url": "https://github.com/graphql-hive/router/pull/1283.diff",
"draft": false,
"head": {
"label": "graphql-hive:minilog",
"ref": "minilog",
"repo": {
"allow_auto_merge": false,
"allow_forking": true,
"allow_merge_commit": false,
"allow_rebase_merge": false,
"allow_squash_merge": true,
"allow_update_branch": true,
"archive_url": "https://api.github.com/repos/graphql-hive/router/{archive_format}{/ref}",
"archived": false,
"assignees_url": "https://api.github.com/repos/graphql-hive/router/assignees{/user}",
"blobs_url": "https://api.github.com/repos/graphql-hive/router/git/blobs{/sha}",
"branches_url": "https://api.github.com/repos/graphql-hive/router/branches{/branch}",
"clone_url": "https://github.com/graphql-hive/router.git",
"collaborators_url": "https://api.github.com/repos/graphql-hive/router/collaborators{/collaborator}",
"comments_url": "https://api.github.com/repos/graphql-hive/router/comments{/number}",
"commits_url": "https://api.github.com/repos/graphql-hive/router/commits{/sha}",
"compare_url": "https://api.github.com/repos/graphql-hive/router/compare/{base}...{head}",
"contents_url": "https://api.github.com/repos/graphql-hive/router/contents/{+path}",
"contributors_url": "https://api.github.com/repos/graphql-hive/router/contributors",
"created_at": "2024-11-20T16:16:12Z",
"default_branch": "main",
"delete_branch_on_merge": true,
"deployments_url": "https://api.github.com/repos/graphql-hive/router/deployments",
"description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
"disabled": false,
"downloads_url": "https://api.github.com/repos/graphql-hive/router/downloads",
"events_url": "https://api.github.com/repos/graphql-hive/router/events",
"fork": false,
"forks": 16,
"forks_count": 16,
"forks_url": "https://api.github.com/repos/graphql-hive/router/forks",
"full_name": "graphql-hive/router",
"git_commits_url": "https://api.github.com/repos/graphql-hive/router/git/commits{/sha}",
"git_refs_url": "https://api.github.com/repos/graphql-hive/router/git/refs{/sha}",
"git_tags_url": "https://api.github.com/repos/graphql-hive/router/git/tags{/sha}",
"git_url": "git://github.com/graphql-hive/router.git",
"has_discussions": false,
"has_downloads": false,
"has_issues": true,
"has_pages": false,
"has_projects": false,
"has_pull_requests": true,
"has_wiki": false,
"homepage": "https://the-guild.dev/graphql/hive/docs/router",
"hooks_url": "https://api.github.com/repos/graphql-hive/router/hooks",
"html_url": "https://github.com/graphql-hive/router",
"id": 891604244,
"is_template": false,
"issue_comment_url": "https://api.github.com/repos/graphql-hive/router/issues/comments{/number}",
"issue_events_url": "https://api.github.com/repos/graphql-hive/router/issues/events{/number}",
"issues_url": "https://api.github.com/repos/graphql-hive/router/issues{/number}",
"keys_url": "https://api.github.com/repos/graphql-hive/router/keys{/key_id}",
"labels_url": "https://api.github.com/repos/graphql-hive/router/labels{/name}",
"language": "Rust",
"languages_url": "https://api.github.com/repos/graphql-hive/router/languages",
"license": {
"key": "mit",
"name": "MIT License",
"node_id": "MDc6TGljZW5zZTEz",
"spdx_id": "MIT",
"url": "https://api.github.com/licenses/mit"
},
"merge_commit_message": "PR_TITLE",
"merge_commit_title": "MERGE_MESSAGE",
"merges_url": "https://api.github.com/repos/graphql-hive/router/merges",
"milestones_url": "https://api.github.com/repos/graphql-hive/router/milestones{/number}",
"mirror_url": null,
"name": "router",
"node_id": "R_kgDONSTNFA",
"notifications_url": "https://api.github.com/repos/graphql-hive/router/notifications{?since,all,participating}",
"open_issues": 63,
"open_issues_count": 63,
"owner": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
"followers_url": "https://api.github.com/users/graphql-hive/followers",
"following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
"gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/graphql-hive",
"id": 182742256,
"login": "graphql-hive",
"node_id": "O_kgDOCuRs8A",
"organizations_url": "https://api.github.com/users/graphql-hive/orgs",
"received_events_url": "https://api.github.com/users/graphql-hive/received_events",
"repos_url": "https://api.github.com/users/graphql-hive/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/graphql-hive",
"user_view_type": "public"
},
"private": false,
"pull_request_creation_policy": "all",
"pulls_url": "https://api.github.com/repos/graphql-hive/router/pulls{/number}",
"pushed_at": "2026-07-26T15:24:58Z",
"releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
"size": 10515,
"squash_merge_commit_message": "PR_BODY",
"squash_merge_commit_title": "PR_TITLE",
"ssh_url": "git@github.com:graphql-hive/router.git",
"stargazers_count": 95,
"stargazers_url": "https://api.github.com/repos/graphql-hive/router/stargazers",
"statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/{sha}",
"subscribers_url": "https://api.github.com/repos/graphql-hive/router/subscribers",
"subscription_url": "https://api.github.com/repos/graphql-hive/router/subscription",
"svn_url": "https://github.com/graphql-hive/router",
"tags_url": "https://api.github.com/repos/graphql-hive/router/tags",
"teams_url": "https://api.github.com/repos/graphql-hive/router/teams",
"topics": [
"apollo-federation",
"federation",
"federation-gateway",
"graphql",
"graphql-federation",
"router"
],
"trees_url": "https://api.github.com/repos/graphql-hive/router/git/trees{/sha}",
"updated_at": "2026-07-26T09:14:02Z",
"url": "https://api.github.com/repos/graphql-hive/router",
"use_squash_pr_title_as_default": true,
"visibility": "public",
"watchers": 95,
"watchers_count": 95,
"web_commit_signoff_required": false
},
"sha": "bf21c72004395639f5d1b19a60b1fc2d419d27f2",
"user": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
"followers_url": "https://api.github.com/users/graphql-hive/followers",
"following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
"gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/graphql-hive",
"id": 182742256,
"login": "graphql-hive",
"node_id": "O_kgDOCuRs8A",
"organizations_url": "https://api.github.com/users/graphql-hive/orgs",
"received_events_url": "https://api.github.com/users/graphql-hive/received_events",
"repos_url": "https://api.github.com/users/graphql-hive/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/graphql-hive",
"user_view_type": "public"
}
},
"html_url": "https://github.com/graphql-hive/router/pull/1283",
"id": 4062814180,
"issue_url": "https://api.github.com/repos/graphql-hive/router/issues/1283",
"labels": [],
"locked": false,
"maintainer_can_modify": false,
"merge_commit_sha": "57391e94824b20a53535babe7cbfe7aa8e8d5d36",
"mergeable": null,
"mergeable_state": "unknown",
"merged": false,
"merged_at": null,
"merged_by": null,
"milestone": null,
"node_id": "PR_kwDONSTNFM7yKZ_k",
"number": 1283,
"patch_url": "https://github.com/graphql-hive/router/pull/1283.patch",
"rebaseable": null,
"requested_reviewers": [
{
"avatar_url": "https://avatars.githubusercontent.com/u/8167190?v=4",
"events_url": "https://api.github.com/users/kamilkisiela/events{/privacy}",
"followers_url": "https://api.github.com/users/kamilkisiela/followers",
"following_url": "https://api.github.com/users/kamilkisiela/following{/other_user}",
"gists_url": "https://api.github.com/users/kamilkisiela/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/kamilkisiela",
"id": 8167190,
"login": "kamilkisiela",
"node_id": "MDQ6VXNlcjgxNjcxOTA=",
"organizations_url": "https://api.github.com/users/kamilkisiela/orgs",
"received_events_url": "https://api.github.com/users/kamilkisiela/received_events",
"repos_url": "https://api.github.com/users/kamilkisiela/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/kamilkisiela/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kamilkisiela/subscriptions",
"type": "User",
"url": "https://api.github.com/users/kamilkisiela",
"user_view_type": "public"
},
{
"avatar_url": "https://avatars.githubusercontent.com/in/946600?v=4",
"events_url": "https://api.github.com/users/Copilot/events{/privacy}",
"followers_url": "https://api.github.com/users/Copilot/followers",
"following_url": "https://api.github.com/users/Copilot/following{/other_user}",
"gists_url": "https://api.github.com/users/Copilot/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/apps/copilot-pull-request-reviewer",
"id": 175728472,
"login": "Copilot",
"node_id": "BOT_kgDOCnlnWA",
"organizations_url": "https://api.github.com/users/Copilot/orgs",
"received_events_url": "https://api.github.com/users/Copilot/received_events",
"repos_url": "https://api.github.com/users/Copilot/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/Copilot/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/Copilot/subscriptions",
"type": "Bot",
"url": "https://api.github.com/users/Copilot",
"user_view_type": "public"
}
],
"requested_teams": [],
"review_comment_url": "https://api.github.com/repos/graphql-hive/router/pulls/comments{/number}",
"review_comments": 65,
"review_comments_url": "https://api.github.com/repos/graphql-hive/router/pulls/1283/comments",
"state": "open",
"statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/bf21c72004395639f5d1b19a60b1fc2d419d27f2",
"title": "feat(router): logger improvements, access logs ",
"updated_at": "2026-07-26T15:25:00Z",
"url": "https://api.github.com/repos/graphql-hive/router/pulls/1283",
"user": {
"avatar_url": "https://avatars.githubusercontent.com/u/3680083?v=4",
"events_url": "https://api.github.com/users/dotansimha/events{/privacy}",
"followers_url": "https://api.github.com/users/dotansimha/followers",
"following_url": "https://api.github.com/users/dotansimha/following{/other_user}",
"gists_url": "https://api.github.com/users/dotansimha/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/dotansimha",
"id": 3680083,
"login": "dotansimha",
"node_id": "MDQ6VXNlcjM2ODAwODM=",
"organizations_url": "https://api.github.com/users/dotansimha/orgs",
"received_events_url": "https://api.github.com/users/dotansimha/received_events",
"repos_url": "https://api.github.com/users/dotansimha/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/dotansimha/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/dotansimha/subscriptions",
"type": "User",
"url": "https://api.github.com/users/dotansimha",
"user_view_type": "public"
}
},
"repository": {
"allow_forking": true,
"archive_url": "https://api.github.com/repos/graphql-hive/router/{archive_format}{/ref}",
"archived": false,
"assignees_url": "https://api.github.com/repos/graphql-hive/router/assignees{/user}",
"blobs_url": "https://api.github.com/repos/graphql-hive/router/git/blobs{/sha}",
"branches_url": "https://api.github.com/repos/graphql-hive/router/branches{/branch}",
"clone_url": "https://github.com/graphql-hive/router.git",
"collaborators_url": "https://api.github.com/repos/graphql-hive/router/collaborators{/collaborator}",
"comments_url": "https://api.github.com/repos/graphql-hive/router/comments{/number}",
"commits_url": "https://api.github.com/repos/graphql-hive/router/commits{/sha}",
"compare_url": "https://api.github.com/repos/graphql-hive/router/compare/{base}...{head}",
"contents_url": "https://api.github.com/repos/graphql-hive/router/contents/{+path}",
"contributors_url": "https://api.github.com/repos/graphql-hive/router/contributors",
"created_at": "2024-11-20T16:16:12Z",
"custom_properties": {
"vanta_production_branch_name": "main"
},
"default_branch": "main",
"deployments_url": "https://api.github.com/repos/graphql-hive/router/deployments",
"description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
"disabled": false,
"downloads_url": "https://api.github.com/repos/graphql-hive/router/downloads",
"events_url": "https://api.github.com/repos/graphql-hive/router/events",
"fork": false,
"forks": 16,
"forks_count": 16,
"forks_url": "https://api.github.com/repos/graphql-hive/router/forks",
"full_name": "graphql-hive/router",
"git_commits_url": "https://api.github.com/repos/graphql-hive/router/git/commits{/sha}",
"git_refs_url": "https://api.github.com/repos/graphql-hive/router/git/refs{/sha}",
"git_tags_url": "https://api.github.com/repos/graphql-hive/router/git/tags{/sha}",
"git_url": "git://github.com/graphql-hive/router.git",
"has_discussions": false,
"has_downloads": false,
"has_issues": true,
"has_pages": false,
"has_projects": false,
"has_pull_requests": true,
"has_wiki": false,
"homepage": "https://the-guild.dev/graphql/hive/docs/router",
"hooks_url": "https://api.github.com/repos/graphql-hive/router/hooks",
"html_url": "https://github.com/graphql-hive/router",
"id": 891604244,
"is_template": false,
"issue_comment_url": "https://api.github.com/repos/graphql-hive/router/issues/comments{/number}",
"issue_events_url": "https://api.github.com/repos/graphql-hive/router/issues/events{/number}",
"issues_url": "https://api.github.com/repos/graphql-hive/router/issues{/number}",
"keys_url": "https://api.github.com/repos/graphql-hive/router/keys{/key_id}",
"labels_url": "https://api.github.com/repos/graphql-hive/router/labels{/name}",
"language": "Rust",
"languages_url": "https://api.github.com/repos/graphql-hive/router/languages",
"license": {
"key": "mit",
"name": "MIT License",
"node_id": "MDc6TGljZW5zZTEz",
"spdx_id": "MIT",
"url": "https://api.github.com/licenses/mit"
},
"merges_url": "https://api.github.com/repos/graphql-hive/router/merges",
"milestones_url": "https://api.github.com/repos/graphql-hive/router/milestones{/number}",
"mirror_url": null,
"name": "router",
"node_id": "R_kgDONSTNFA",
"notifications_url": "https://api.github.com/repos/graphql-hive/router/notifications{?since,all,participating}",
"open_issues": 63,
"open_issues_count": 63,
"owner": {
"avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
"events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
"followers_url": "https://api.github.com/users/graphql-hive/followers",
"following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
"gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/graphql-hive",
"id": 182742256,
"login": "graphql-hive",
"node_id": "O_kgDOCuRs8A",
"organizations_url": "https://api.github.com/users/graphql-hive/orgs",
"received_events_url": "https://api.github.com/users/graphql-hive/received_events",
"repos_url": "https://api.github.com/users/graphql-hive/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/graphql-hive",
"user_view_type": "public"
},
"private": false,
"pull_request_creation_policy": "all",
"pulls_url": "https://api.github.com/repos/graphql-hive/router/pulls{/number}",
"pushed_at": "2026-07-26T15:24:58Z",
"releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
"size": 10515,
"ssh_url": "git@github.com:graphql-hive/router.git",
"stargazers_count": 95,
"stargazers_url": "https://api.github.com/repos/graphql-hive/router/stargazers",
"statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/{sha}",
"subscribers_url": "https://api.github.com/repos/graphql-hive/router/subscribers",
"subscription_url": "https://api.github.com/repos/graphql-hive/router/subscription",
"svn_url": "https://github.com/graphql-hive/router",
"tags_url": "https://api.github.com/repos/graphql-hive/router/tags",
"teams_url": "https://api.github.com/repos/graphql-hive/router/teams",
"topics": [
"apollo-federation",
"federation",
"federation-gateway",
"graphql",
"graphql-federation",
"router"
],
"trees_url": "https://api.github.com/repos/graphql-hive/router/git/trees{/sha}",
"updated_at": "2026-07-26T09:14:02Z",
"url": "https://api.github.com/repos/graphql-hive/router",
"visibility": "public",
"watchers": 95,
"watchers_count": 95,
"web_commit_signoff_required": false
},
"sender": {
"avatar_url": "https://avatars.githubusercontent.com/u/3680083?v=4",
"events_url": "https://api.github.com/users/dotansimha/events{/privacy}",
"followers_url": "https://api.github.com/users/dotansimha/followers",
"following_url": "https://api.github.com/users/dotansimha/following{/other_user}",
"gists_url": "https://api.github.com/users/dotansimha/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/dotansimha",
"id": 3680083,
"login": "dotansimha",
"node_id": "MDQ6VXNlcjM2ODAwODM=",
"organizations_url": "https://api.github.com/users/dotansimha/orgs",
"received_events_url": "https://api.github.com/users/dotansimha/received_events",
"repos_url": "https://api.github.com/users/dotansimha/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/dotansimha/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/dotansimha/subscriptions",
"type": "User",
"url": "https://api.github.com/users/dotansimha",
"user_view_type": "public"
}
},
"github_job": "docker",
"github_ref": "refs/pull/1283/merge",
"github_ref_name": "1283/merge",
"github_ref_protected": "false",
"github_ref_type": "branch",
"github_repository": "graphql-hive/router",
"github_repository_id": "891604244",
"github_repository_owner": "graphql-hive",
"github_repository_owner_id": "182742256",
"github_run_attempt": "1",
"github_run_id": "30208149531",
"github_run_number": "4644",
"github_runner_arch": "X64",
"github_runner_environment": "github-hosted",
"github_runner_image_os": "ubuntu24",
"github_runner_image_version": "20260720.247.2",
"github_runner_name": "GitHub Actions 1000933918",
"github_runner_os": "Linux",
"github_runner_tracking_id": "github_cde20512-a663-4ffd-b6e2-d12f494b3652",
"github_server_url": "https://github.com",
"github_triggering_actor": "dotansimha",
"github_workflow": "build-router",
"github_workflow_ref": "graphql-hive/router/.github/workflows/build-router.yaml@refs/pull/1283/merge",
"github_workflow_sha": "ebd2267e07f094d8ffff141a2466714f698bb062",
"platform": "linux/amd64"
}
}
},
"buildx.build.ref": "builder-c1264e41-3221-4661-b4f3-fac8e7706fa1/builder-c1264e41-3221-4661-b4f3-fac8e7706fa10/4ytthw2bbocbib724eltjmw8j",
"containerimage.descriptor": {
"mediaType": "application/vnd.oci.image.index.v1+json",
"digest": "sha256:4f7222d3dbd69e81ac289ed7f18ac4315fd46d7e4325bb47293a41a908c9466c",
"size": 1609
},
"containerimage.digest": "sha256:4f7222d3dbd69e81ac289ed7f18ac4315fd46d7e4325bb47293a41a908c9466c",
"image.name": "ghcr.io/graphql-hive/router:pr-1283,ghcr.io/graphql-hive/router:sha-ebd2267"
} |
d10d926 to
56cd095
Compare
There was a problem hiding this comment.
Pull request overview
This PR reworks the router’s logging subsystem to provide structured, correlatable logs (request-id + optional trace-id), reduce noisy internal-crate output by default, and add request-summary (“access log”) style emission, along with new e2e coverage around stdout log contents.
Changes:
- Added logging correlation configuration (
log.correlation) andlog.log_internalsfiltering, including env var overrides. - Implemented custom JSON/text
tracing_subscriberformatters that inject request identifiers into every log line without serde-based JSON serialization. - Expanded/normalized structured log targets across the router + SDK crates and added e2e tests for log shape and correlation.
Reviewed changes
Copilot reviewed 73 out of 76 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| package-lock.json | Marks some dependencies as peer to reflect package metadata changes. |
| lib/router-config/src/log.rs | Adds correlation + log_internals config; restricts formats/levels; adds LevelFilter conversion. |
| lib/router-config/src/env_overrides.rs | Adds LOG_INTERNALS override; restructures override logs with explicit targets/fields. |
| lib/internal/src/telemetry/traces/trace_batch_span_processor.rs | Adds explicit telemetry log target and improves structured error fields. |
| lib/internal/src/telemetry/traces/hive_console_exporter.rs | Normalizes telemetry exporter logs to use targets::TELEMETRY + structured fields. |
| lib/internal/src/telemetry/traces/compatibility.rs | Normalizes http compatibility exporter shutdown logs to telemetry target. |
| lib/internal/src/telemetry/mod.rs | Introduces telemetry::logging module and request-id extractor in TelemetryContext. |
| lib/internal/src/telemetry/metrics/setup.rs | Adds explicit telemetry target for metrics setup warnings. |
| lib/internal/src/telemetry/logging/utils.rs | Adds internal-crate target filtering + OTEL span filtering helpers. |
| lib/internal/src/telemetry/logging/targets.rs | Centralizes log target strings for consistent filtering/querying. |
| lib/internal/src/telemetry/logging/summary.rs | Implements RequestSummary task-local request summary/access-log emission. |
| lib/internal/src/telemetry/logging/request_id.rs | Adds request-id + trace-id extraction and task-local scoping utilities. |
| lib/internal/src/telemetry/logging/mod.rs | Exposes new logging submodules. |
| lib/internal/src/telemetry/logging/format_text.rs | Custom text formatter that appends request identifiers to each line. |
| lib/internal/src/telemetry/logging/format_json.rs | Custom JSON formatter writing JSON directly into a buffer (no serde object creation). |
| lib/internal/src/background_tasks/mod.rs | Adds explicit targets and structured fields for background task logs. |
| lib/internal/Cargo.toml | Adds sonyflake + chrono for request-id generation and timestamp formatting. |
| lib/hive-console-sdk/src/supergraph_fetcher/sync_fetcher.rs | Adds an explicit target + structured error logging. |
| lib/hive-console-sdk/src/supergraph_fetcher/mod.rs | Adds SUPERGRAPH_FETCHER_TARGET constant for consistent targeting. |
| lib/hive-console-sdk/src/persisted_documents.rs | Converts persisted-doc logs to structured debug/warn with a fixed target. |
| lib/hive-console-sdk/src/agent/usage_agent.rs | Removes stdout println!, adds structured logs + target constants. |
| lib/executor/src/projection/plan.rs | Adds explicit target usage for projection debug/warn logs. |
| lib/executor/src/headers/cache_control.rs | Adds explicit cache-control target and more structured fields. |
| lib/executor/src/executors/websocket.rs | Adds executor naming + structured websocket client logs. |
| lib/executor/src/executors/websocket_common.rs | Adds explicit websocket client target and structured error logs. |
| lib/executor/src/executors/websocket_client.rs | Adds websocket-client target and structured logs (plus new subscription trace field). |
| lib/executor/src/executors/subscription_buffer.rs | Adds subscriptions target and normalizes field names. |
| lib/executor/src/executors/map.rs | Adds structured demand-control + execution logs and executor name usage. |
| lib/executor/src/executors/http.rs | Adds executor naming and structured HTTP client logs; records subgraph info into summary. |
| lib/executor/src/executors/http_callback.rs | Adds executor naming and structured callback logging with explicit target. |
| lib/executor/src/executors/graphql_transport_ws.rs | Adds explicit websocket target for JSON serialization errors. |
| lib/executor/src/executors/common.rs | Extends SubgraphExecutor trait with executor_name(). |
| lib/executor/src/execution/plan.rs | Normalizes execution error logs/targets and records partial response in summary. |
| lib/executor/src/coprocessor/runtime.rs | Converts coprocessor logs to structured fields + explicit targets. |
| e2e/src/testkit/stdout.rs | Adds stdout capture helpers to assert log output lines/JSON in e2e tests. |
| e2e/src/testkit/mod.rs | Exposes the stdout capture testkit module. |
| e2e/src/telemetry/mod.rs | Adds telemetry logging e2e module. |
| e2e/src/telemetry/logging.rs | Adds e2e tests for log structure, correlation, and OTEL coexistence. |
| e2e/Cargo.toml | Adds gag dependency for capturing stdout in tests. |
| docs/README.md | Updates documented logging schema (format/level enums, correlation config, defaults). |
| Cargo.toml | Updates insta features, enables tracing compile-time level caps, adds tracing-appender. |
| Cargo.lock | Lockfile updates for new crates/features (gag, sonyflake, tracing-appender, etc.). |
| bin/router/src/telemetry.rs | Replaces prior logging setup with non-blocking writer + custom JSON/text formatters and filters. |
| bin/router/src/supergraph/storage.rs | Converts supergraph storage load errors to structured logs/targets. |
| bin/router/src/supergraph/mod.rs | Adds explicit supergraph target and structured loader creation logging. |
| bin/router/src/supergraph/hive.rs | Converts Hive supergraph loader logs to structured fields/targets. |
| bin/router/src/supergraph/file.rs | Converts file supergraph loader logs to structured fields/targets. |
| bin/router/src/storage/s3_runtime.rs | Converts s3 load warnings to error! with storage target. |
| bin/router/src/storage/mod.rs | Adds storage target and structured runtime creation logs. |
| bin/router/src/shared_state.rs | Adds subscriptions target to lag/drop logs. |
| bin/router/src/schema_state.rs | Normalizes supergraph lifecycle logs to explicit targets + structured fields. |
| bin/router/src/plugins/registry.rs | Normalizes plugin init logs with explicit plugin target + structured error fields. |
| bin/router/src/pipeline/websocket_server.rs | Adds structured websocket server logs and hooks in correlation/summary into subscription execution flow. |
| bin/router/src/pipeline/validation/mod.rs | Converts validation failure logging to warn+debug with structured fields/targets. |
| bin/router/src/pipeline/usage_reporting.rs | Adds hive usage reporting target + structured error logging. |
| bin/router/src/pipeline/persisted_documents/resolve/storage.rs | Adds persisted-documents target + structured logs for reload behavior. |
| bin/router/src/pipeline/persisted_documents/resolve/fs.rs | Adds persisted-documents target + structured watcher/reload logs. |
| bin/router/src/pipeline/parser.rs | Normalizes parse/minify failure logs with explicit parsing target and structured errors. |
| bin/router/src/pipeline/normalize.rs | Converts normalization trace logs into structured debug output. |
| bin/router/src/pipeline/mod.rs | Adds structured HTTP server + GraphQL execution logs and records request summary fields. |
| bin/router/src/pipeline/introspection_policy.rs | Changes introspection rejection to warn with explicit target. |
| bin/router/src/pipeline/http_callback.rs | Restructures callback errors (less message detail) and logs with subscriptions target. |
| bin/router/src/pipeline/header.rs | Adds ResponseMode::as_str() and structured Accept-header parse warnings. |
| bin/router/src/pipeline/execution_request.rs | Normalizes HTTP server warnings and persisted-doc missing-id logging target/level. |
| bin/router/src/pipeline/error.rs | Records response code into request summary on pipeline errors. |
| bin/router/src/pipeline/demand_control/runtime.rs | Adds demand-control target + structured logs for enforcement/measurement. |
| bin/router/src/pipeline/demand_control/formula.rs | Adds demand-control target to warnings. |
| bin/router/src/pipeline/csrf_prevention.rs | Adds HTTP server targeted warning on CSRF failure. |
| bin/router/src/pipeline/coerce_variables.rs | Adds coerce-variables target and structured fields. |
| bin/router/src/pipeline/authorization/mod.rs | Changes unauthorized reject logging to warn with authorization target. |
| bin/router/src/pipeline/active_subscriptions.rs | Adds subscriptions target to subscription lifecycle traces. |
| bin/router/src/lib.rs | Adds HTTP request start/end logs and wires correlation + request summary scoping into request handling. |
| bin/router/src/jwt/mod.rs | Normalizes JWT logs to structured fields with JWT target. |
| bin/router/src/jwt/jwks_manager.rs | Adds JWT target to logs and improves structured fields for JWKS polling/load failures. |
| bin/router/Cargo.toml | Replaces tracing-tree with tracing-appender dependency for non-blocking output. |
| apollo-router-workspace/bin/router/src/persisted_documents.rs | Lowers persisted-doc “found” log from info to debug. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
c8a3cce to
2d31051
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 77 out of 80 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
lib/internal/src/telemetry/logging/format_text.rs:42
- Text log output appends request_id/trace_id as raw values (
request_id=...). Even though request_id is sanitized, it still allows characters like=/+that can break simple key=value parsers; quoting/escaping these values in text output makes the log line robust for downstream parsing.
lib/internal/src/telemetry/logging/request_id.rs:49 RequestIdentifierExtractor::newpanics ifSonyflake::new()fails (e.g., missing/unsupported network interfaces or other platform constraints). Since this runs during telemetry/logging setup, this can crash the router at startup in some containerized/sandboxed environments. Consider handling the failure gracefully (log + fall back to a different generator such as ULID/UUID, or keepgenerator: Option<Sonyflake>and use the existing timestamp fallback without panicking). Also, the current timestamp fallback uses seconds, which can collide under concurrent load; using higher resolution (millis/nanos) would reduce collision risk.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 77 out of 80 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (3)
lib/internal/src/telemetry/logging/format_text.rs:43
request_id/trace_idare appended as raw text without any quoting/escaping. Even with upstream sanitization, values like=or/can make the final line ambiguous for downstream key=value parsers, and it diverges from howtracing_subscriberformats other string fields. Prefer emitting them as quoted/escaped values (e.g. via{:?}) to make the text format robust.
e2e/src/testkit/stdout.rs:20BufferRedirect::stdout()redirects stdout process-wide. Since Rust tests run in parallel by default, concurrent use ofStdoutLogCapturecan interleave/corrupt captured output and make logging e2e tests flaky. Consider serializing stdout capture with a global lock held for the lifetime ofStdoutLogCapture.
lib/internal/src/telemetry/logging/request_id.rs:49RequestIdentifierExtractor::newusesexpect(...)onSonyflake::new(). Sonyflake initialization can fail (e.g. missing/unsupported network interfaces in some container setups), which would crash the router during startup. Prefer avoiding a hard panic here (e.g. log a warning and fall back to timestamp-only IDs, or make initialization fallible and surface a configuration/runtime error).
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 77 out of 80 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (2)
lib/internal/src/telemetry/logging/request_id.rs:49
RequestIdentifierExtractor::newusesSonyflake::new().expect(...), which will panic and crash the router at startup if sonyflake cannot initialize (e.g., restricted/ephemeral network interface environments). This turns a correlation/logging feature into a hard availability risk; consider handling initialization failure and falling back to the timestamp-based ID path (or another non-panicking generator).
bin/router/src/lib.rs:736- The doc comment above
init_rustls_crypto_providersays repeated calls should "log a warning", but the implementation logs aterrorlevel. This makes an expected/handled condition look like a failure in production logs and alerting.
pub fn init_rustls_crypto_provider() {
if rustls::crypto::aws_lc_rs::default_provider()
.install_default()
.is_err()
{
error!(target: targets::TLS, "rustls crypto provider already installed, ignoring");
}
Closes #755
Closes #765
Closes #503
Docs PR: graphql-hive/docs#147
Docs preview: https://a507d5b7-hive-platform-docs.theguild.workers.dev/graphql/hive/docs/router/observability/logging
Background
Extracted only the crucial changes from #775 and re-implements major parts of it.
Changes in this PR
Disable logging from internal crates
A new config flag
log.log_internals(default:false) has been added, that creates aTargetsfilter. It has a hardcoded list of log targets (mostlyntex) to remove and avoid printing. Most ofntexinternal logging are noise, and we would like to avoid logging it, even whendebuglevel is set.A user can enable these by setting
log.log_internals=true, or by settingLOG_INTERNALS=truein the env.Improved existing log lines
This PR also iterated all of the existing log lines (specifically the debug/info/warn/error levels) and adjusted the following:
targetconfigured explicitly, so log lines are now much more clear and easy to read or filter.debug. No log lines are printed on the hotpath, expect for the summary (see below).fieldsor nested structued are printed, to allow log ingestors to process logs easily.DEBUGlevel.Structured Logging
I've iterated all existing log lines and modified their attributes to be printed as-is, instead of printing it using a raw string. This allow JSON logging to be printed nicely and inspected by tools like DataDog or Grafana.
The
messageof each log line is only there to indicate and identify the even, and no log message contains information about the event. All data and fields are tracked as standalone fields, and they are not nested.Performance impact
tracingcrate internal processes can be heavy. To avoid a major perf regression, while still allowing usersto get meaningful logs that are all-or-nothing, we've implement several mechanism and applied decisions to find the right balance:tracing's internal request-id correlation mechanism that is based onspans - this has a major impact since it records too many data items. Instead, we have a task-local implementation that tracks exactly what we need.INFOand it prints only the request summary (see below), everything else isDEBUG.DEBUGin our code ( =TRACE) are dropped at compile time.Custom log formatters
For
jsonlogging, we've implemented a custom formatter that does not construct any object, and avoid any JSON object allocations, or serialization. This means, that we construct the JSON output log lines, with a simplebufferthat's also shared to avoid re-allocation for every log line.For
textlogging, we use thecompactformat, but avoid re-allocation of new objects when possible.The new, custom, log formatters are also in charge of injecting the request identifiers into every log line.
Access Logs / Request Summary
While running the request, we attached a
RequestSummaryobject that has a set of pre-defined attributes that are relevant for a request summary:During execution, a task-local struct is attached to the request execution, and different part of the codebase can
recordinformation and hints about the execution. I tried to avoid any allocations where possible, but we can't avoid them all due to the need to use'staticlifetime for the task-local variable.Once a request completes, a single log line is printed on the
INFOlevel, summarizes the request overall:Logs Correlation
In order to implement full request correlation, every incoming request is being inspected for both request-id and traceparent information. Both can be configured, or fully disabled from the config.
In order to do that, we create a tokio task-specific variable that holds the request identifiers extracted from the request. Then the task-local data is passed to the request processing
Future.If a request doesn't have a
X-Request-IDheader, then Router will generate one for it, so logs lines are always correlated.Subscriptions support
Same as HTTP, subscriptions also extract correlations and attached them, either based on the initial message headers, or based on the per-operation headers.
We do not print a log line for the initial HTTP part of Subscriptions, and we consider every operation execution on the subscriptions channel as a separate execution when it comes to requst-id correlation.
Request correlation is based on the headers that can be passed over the init message, or subscribe message.
The request-id and request-summary are being passed to the execution thread (as
Arc) of subscriptions, so they can also correlate log lines and report summary details without effort.For stream-based HTTP requests, I've implemented mechanism to "bind"/attach the summary and request-identifier to the response, so when
ntexfinally drops the response, it willDropthe summary object that will print the correct summary information.Also,
duration_msfor stream-based reports the final time of the request, since it was staretd. Forpayload_bytesit will be an accumulation of all bytes sent in all events.Improved log filter
The existing
log.log_filter(orLOG_FILTERenv var) can now mute noise from the router itself, by doing something likeLOG_FILTER=router::supergraph=off. This allow more granular control over specific targets and parts of the Router logs.Non-blocking stdout logging
Instead of using the default
stdout, we are now usingtracing-appender's solution for non-blocking writing https://docs.rs/tracing-appender/latest/tracing_appender/#non-blocking-writer.Also, the output is buffered so it won't print every character to the
stdoutimmediately, to prevent over-flushing of data to the stdout.Testing
Added some e2e to cover the important log lines, log attributes, req-id correlations, and also to test and cover the impact on OTEL (due to the "fake" span we create for the logging).
Breaking Changes
traceno longer availablepretty,treeandpretty-compactno longer available, onlytextandjsonare available now.TODO
tracelevel in development onlyspawn)duration_ms+payload_bytes)