Skip to content

feat: Add RequestId filter#10274

Open
patel-vansh wants to merge 15 commits into
codeigniter4:4.8from
patel-vansh:feat/request-id
Open

feat: Add RequestId filter#10274
patel-vansh wants to merge 15 commits into
codeigniter4:4.8from
patel-vansh:feat/request-id

Conversation

@patel-vansh
Copy link
Copy Markdown
Contributor

@patel-vansh patel-vansh commented Jun 3, 2026

Description
This PR adds RequestId filter which will improve debugging, traceability, as well as better logging for each request.

This filter is fully opt-in, that means there's no BC here. The filter line is commented so that users can easily add if needed.

Request id is generated during early lifecycle. Incoming header X-Request-ID will be prioritized over auto generation of request ids, so that CDN/web server/load balancer can generate request id for better traceability.

If the header is not present, the framework will create request id via bin2hex(random_bytes(16)) function, that means the framework-generated ids will be of length 32.

Why only bin2hex?
I initially thought of UUID v4, but due to lack of in-built support for UUID, I changed that to bin2hex. Adding a new external dependency for just this one thing would be less feasible, IMO. And also random_bytes(16) should provide enough randomness so collision would be highly unlikely.

If there is some other better way to generate id, then I would surely change to that.

Checklist:

  • Securely signed commits
  • Component(s) with PHPDoc blocks, only if necessary or adds value (without duplication)
  • Unit testing, with >80% coverage
  • User guide updated
  • Conforms to style guide

@github-actions github-actions Bot added the 4.8 PRs that target the `4.8` branch. label Jun 3, 2026
Copy link
Copy Markdown
Member

@michalsn michalsn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this. I agree that having a request ID can be useful. However, I am not sure we should add this directly to IncomingRequest. I would prefer to see this as documentation only, or provide an opt-in filter that's disabled by default. This is a path similar to those followed by Symfony and Laravel.

If we provide an example or filter, it should store the value in context()->set('request_id', $id) so $logGlobalContext can pick it up, set X-Request-ID on the response in after(), and validate incoming IDs: non-empty, bounded length, and safe characters only.

Let's see what others think.

@memleakd
Copy link
Copy Markdown
Contributor

memleakd commented Jun 3, 2026

Thanks for working on this. I agree that having a request ID is useful.

I also use this in my projects, but with a filter-based approach, and it has worked well in practice. I think that shape fits better here than adding it directly to IncomingRequest.

The filter can resolve or generate the ID early, validate any incoming X-Request-ID, store it in context()->set('request_id', $id) so $logGlobalContext can include it in logs, and expose the same ID back on the response as X-Request-ID.

This avoids keeping separate request-id state on the request object, keeps the feature opt-in, and makes the ID useful for logs, response headers, and support/debug workflows.

@patel-vansh
Copy link
Copy Markdown
Contributor Author

Thanks @michalsn and @memleakd for your reviews. I've changed the implementation. Added a filter which is completely opt-in, so there's no BC. This filter will set request_id key in Context class so that $logGlobalContext can automatically log request IDs.

@patel-vansh patel-vansh requested a review from michalsn June 4, 2026 10:23
@patel-vansh patel-vansh changed the title feat: Add Request ID to IncomingRequest feat: Add RequestId filter Jun 4, 2026
Copy link
Copy Markdown
Contributor

@memleakd memleakd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating this. I think the filter direction is much better. I added a few comments on things that still look worth checking.

Comment thread system/Filters/RequestId.php
$response->setHeader('X-Request-ID', context()->get('request_id'));

return null;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this handle the case where request_id is missing from context?

If only the after filter is enabled, or context is cleared before after() runs, this currently sets X-Request-ID to an empty value. It may be safer to either resolve/generate the ID here as a fallback, or skip setting the header when no valid ID exists.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think skipping would be better, changed according to that.

Comment thread tests/system/Filters/RequestIdTest.php
Comment thread user_guide_src/source/incoming/filters.rst Outdated
Comment thread user_guide_src/source/incoming/filters.rst Outdated
@patel-vansh patel-vansh requested a review from memleakd June 4, 2026 13:06
Copy link
Copy Markdown
Contributor

@memleakd memleakd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the updates. This looks much better now. Just two small things left for me:

Comment thread app/Config/Filters.php
Comment thread tests/system/Filters/RequestIdTest.php
Comment thread system/Config/Filters.php
@patel-vansh patel-vansh requested a review from memleakd June 5, 2026 03:47
Copy link
Copy Markdown
Contributor

@memleakd memleakd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed one last thing with the filter ordering. I think requestid should stay after pagecache in after. PageCache::after() stores response headers, so if requestid runs first, we may accidentally cache X-Request-ID.

@patel-vansh patel-vansh requested a review from memleakd June 5, 2026 08:50

$filter->before($request);

$requestId = context()->get('request_id');
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also add asserts here that the request has the header line, and its value matches the one from context?

$filter = new RequestId();
$request = service('request', null, false);

$existingRequestId = 'Abc@!#$';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a separate test where the existing id is more than 255 chars.

Framework-generated IDs are unique for practical purposes, however valid incoming IDs are reused.
It is added to the request's context and can be accessed via the ``request_id`` key.

The ID is generated via ``bin2hex(random_bytes(16))``, so it is a 32-character hexadecimal string.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an implementation detail. Do you need to expose that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4.8 PRs that target the `4.8` branch.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants