Problem statement
When creating logs, in some places we want to include the request IP - here are some examples:
- https://github.com/wikimedia/mediawiki/blob/bc54403f9c1d779bf9174d40159fca303eb28d6a/includes/user/PasswordReset.php#L320
- https://gerrit.wikimedia.org/g/mediawiki/core/+/747c1cac86bd4d11cd51b3405d7741003bb7f964/includes/api/ApiMain.php#1995
- https://github.com/wikimedia/mediawiki-extensions-OATHAuth/blob/1533d0ab1372a3c39f1259c07863f9759bbbb5b5/src/Key/TOTPKey.php#L172
- https://gerrit.wikimedia.org/g/mediawiki/core/+/747c1cac86bd4d11cd51b3405d7741003bb7f964/includes/auth/AuthManager.php#907
- https://gerrit.wikimedia.org/g/mediawiki/core/+/747c1cac86bd4d11cd51b3405d7741003bb7f964/includes/editpage/Constraint/SpamRegexConstraint.php#106
- https://gerrit.wikimedia.org/g/mediawiki/extensions/WikimediaEvents/+/0be4784eafe21b299dea5ecad2d38cfc356548e3/includes/BlockMetrics/BlockMetricsHooks.php#126
We solve this by:
- sometimes we have easy access to $request variable and we just use it by $request->getIP() or $this->getRequest()->getIP().
- sometimes we just call RequestContext::getMain()->getRequest() to get the global WebRequest
- sometimes we pass the IP directly to the class, and in case something is wrong we use that $reqIP to add to the log context
- sometimes we fetch Request from things that Request does not belong to, like User object.
In general - having the possibility to include the IP (and maybe any other based on the current request) is something common. In other places, it's usually handled by solutions like Monolog WebProcessor that includes the Client IP with every single log. We don't want the IP in every single log - but when we want it - things get difficult.
Proposal
Implement a placeholder Processor and inject it into our Monolog logger.
This would allow us to define placeholders in the $context array without having to specify the value. Then, when a message gets processed, the PlaceHolder processor iterates over the context array and replaces all placeholders.
Code example:
$this->logger->info( "An example log message", [ 'client_ip' => PlaceholderProcessor::CLIENT_IP ] );
This way we would get the possibility to log request-related information without having to inject those over the way, simplify the code a bit, and also it would help with the removal of the User::getRequest() method, which is also often used to retrieve the client IP.
Kudos to @daniel for the initial proposal for this idea.