Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow integer log levels #3

Merged
merged 18 commits into from
Mar 14, 2023
Merged

Conversation

jonathanjfshaw
Copy link
Contributor

@jonathanjfshaw jonathanjfshaw commented Jan 5, 2023

This is an alternative implementation to wimleers#1

Fixes #2

@jonathanjfshaw
Copy link
Contributor Author

I've used union types for string|int, which require php 8. Not sure whether you want to make a php 8 release, or drop the explicit type.

Copy link
Owner

@colinodell colinodell left a comment

Choose a reason for hiding this comment

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

Overall I think this approach is on the right track! Besides a few nits, I think the only other thing this PR needs is some tests showing that the level mapping works properly.

With regards to PHP 7.4: I'm fine with dropping support for that and bumping the minimum version to ^8.0. We can then release this as a minor version update since the changes would be backward-compatible.

src/TestLogger.php Outdated Show resolved Hide resolved
src/TestLogger.php Outdated Show resolved Hide resolved
Comment on lines 156 to 178
if (\preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) {
$levelNames = \implode('|', \array_map('ucfirst', \array_keys($this->levelMap)));
if (\preg_match('/(.*)(' . $levelNames . ')(.*)/', $method, $matches) > 0) {
Copy link
Owner

Choose a reason for hiding this comment

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

I'm guessing the idea behind this change is that a user may have instantiated the TestLogger without defining all of those levels in their map? If so, I think my preference would be to revert this change and add some validation to the constructor to ensure that the map contains all log levels. That would ensure that all of the magic methods defined on lines 15-54 are always available and guaranteed to never throw a BadMethodCallException.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually this was motivated by DRY. It smelled to me to repeat the name information twice in the class. I have kept this code, but added the requested validation to the __construct() method. Happy to remove this code if you prefer the simplicity of explicit repetition instead of DRY.

@jonathanjfshaw
Copy link
Contributor Author

All tests and checks are green now, I think this is good to go. Only outstanding question is the DRY vs explicit issue in your review point discussed above.

@jonathanjfshaw
Copy link
Contributor Author

@colinodell Any chance of this getting merged? Drupal are still keen to adopt this package.

@colinodell
Copy link
Owner

Sorry for the delay here! Life has been quite busy lately :)

After giving this a thorough review, I think I'd like to remove the $levelMap functionality. On the surface it seems potentially useful, but it leads to unexpected behavior like this:

$levelMap = [
    LogLevel::EMERGENCY => 0,
    LogLevel::ALERT => 1,
    LogLevel::CRITICAL => 2,
    LogLevel::ERROR => 3,
    LogLevel::WARNING => 4,
    LogLevel::NOTICE => 5,
    LogLevel::INFO => 6,
    LogLevel::DEBUG => 7,
];

$logger = new TestLogger($levelMap);

$logger->debug('foo');
$logger->log(LogLevel::DEBUG, 'bar');
$logger->log(7, 'baz');

$this->assertTrue($logger->hasDebugThatContains('foo')); // FAILS
$this->assertTrue($logger->hasDebugThatContains('bar')); // FAILS
$this->assertTrue($logger->hasDebugThatContains('baz')); // passes

After re-reading section 1.1 of PSR-3, it seems like 7 and LogLevel::DEBUG should pass into the logger as completely distinct, unrelated values. But the level map conflates the two. I can see how that mapping might be useful in a system that doesn't use LogLevel constants at all, but from this library's perspective, I think we should avoid the assumption that people will always use ONLY the constants or ONLY custom levels. So while I'm sure we could find a smart implementation that would work in all cases, I feel like doing so would contradict the intent of the spec.

Yes, this would mean that Drupal would not be able to leverage the magic methods. But all of the other methods would work just fine with int log levels, which I think would be sufficient.

WDYT?

If you think this is fine, I'd be happy to make the final changes to the PR and get this merged/released ASAP :)

@jonathanjfshaw
Copy link
Contributor Author

Thanks for looking @colinodell . I think removing the level map and not being able to use the magic methods will cause Drupal no problems at all.

I've asked @wimleers the Drupal expert who detected the present issue to give this MR a final check.

@colinodell
Copy link
Owner

Sounds good! I've updated the code in this PR accordingly and look forward to hearing the results of that final check :)

Copy link
Owner

@colinodell colinodell left a comment

Choose a reason for hiding this comment

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

Ready to merge once feedback is received

@wimleers
Copy link

Yep, that seems reasonable to me too!

This PR looks fine to me, with the sole exception that bumping the required PHP version in anything else than a major version release AFAICT violates https://semver.org/ 😅 If you feel strongly about not tagging a new major version, fine, but … I'm fairly sure this would make the Drupal maintainers feel uneasy because if this happens again at some point in the future, then it technically breaks backwards compatibility for Drupal 🤓

"php": "^7.4 || ^8.0",
"php": "^8.0",

Choose a reason for hiding this comment

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

According to semver this means it should be version 2.0.0? 🤔

Copy link

Choose a reason for hiding this comment

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

semver doesn't tie you to a particular PHP version support cycle, it's up to the project to determine what counts as a breaking API change and document it, and if PHP version increases don't count, then they don't.

Copy link
Owner

Choose a reason for hiding this comment

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

Correct, a bump in dependencies is not a BC break - see https://github.com/semver/semver/blob/d460299638d3d2af6fcf809285dc6a469e15742a/semver.md#what-should-i-do-if-i-update-my-own-dependencies-without-changing-the-public-api

The only reason we might need to release this as a new major version would be if we BC in the public API. Although the API signature of TestLogger did change, all of those changes are backward-compatible:

  • The class is final, so we don't need to worry about child classes suddenly becoming incompatible
  • The only type changes were parameter type widening. The new string|int types will continue to accept string inputs with no changes needed by end users.

I'm fairly sure this would make the Drupal maintainers feel uneasy because if this happens again at some point in the future, then it technically breaks backwards compatibility for Drupal

This shouldn't be an issue as Composer will prevent the installation of newer versions with incompatible dependencies.

For example, this change will be released as v1.2.0, so I imagine Drupal will use a constraint like ^1.2. If I were to release v1.3.0 in the future with a new minimum PHP version of 8.2.0, Composer would still allow v1.2.0 on PHP 8.0 and 8.1 - only users on 8.2 could install v1.3.0. And because it would be a minor version bump and we follow semver, there would be no breaking changes to the API. This means that both versions would function identically as far as Drupal is concerned.

Bumping dependencies (including minimum PHP versions) in a minor release is done by many other large PHP projects, including Doctrine: https://www.doctrine-project.org/2017/07/25/php-7.1-requirement-and-composer.html

TL;DR: Everything will "just work" :)

@colinodell colinodell merged commit 28b1e4a into colinodell:main Mar 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Stop assuming LogLevel::* levels, because \Psr\Log\LoggerInterface::log()does not require it either
4 participants