
Problem/Motivation
I started dipping my toes into autowiring yesterday and one of the first things I butted up against was how to autowire loggers.
If a module needs to log something, most of the time it will do something like this:
services:
logger.channel.my_module:
parent: logger.channel_base
arguments: ['my_module']
my_module.service:
class: Drupal\my_module\Foo
arguments: ['@logger.channel.my_module']
I discovered in my own discussions internally that we can use calls
in the service to avoid having to pass the logger into the constructor by using LoggerAwareTrait
my_module.service:
class: Drupal\my_module\Foo
calls:
- [setLogger, ['@logger.channel.my_module']]
That works well, but what if we automated it?
We could detect services implementing LoggerAwareInterface and autoconfigure a logger for the module and automate the calls part of the service.
Proposed resolution
Add a compiler pass that finds services implementing LoggerAwareInterface and autoconfigure a logger for them using setLogger with a logger service that matches the service's module name.
Remaining tasks
Agree
Code
Test
User interface changes
API changes
Data model changes
Release notes snippet
Issue fork drupal-3395032
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
- 3395032-add-autoconfigure-for
changes, plain diff MR !5060
Comments
Comment #2
acbramley CreditAttribution: acbramley at PreviousNext for Service NSW commentedComment #3
dpiThe main technical challenge would be having different logger instances (channels) autowired per module.
Comment #4
dpiFor those new to autoconfigure, we have an instance in core as of #3221128: Enable service autoconfiguration for core event subscribers
Change record: https://www.drupal.org/node/3357408
Comment #6
kim.pepperHad a quick look at this, and it doesn't look like we know which module the services.yml is being loaded from. By the time we get to
CoreServiceProvider
we have a fully merged container definition.This would make it hard to auto configure
file.services.yml
with a logger service idlogger.channel.file
for example.One option is to loop through the service definitions and try to guess a prefix?
Comment #7
kim.pepperI was looking at
\Drupal\Core\CoreServiceProvider::register()
and adding something like:Comment #8
longwaveYamlFileLoader::parseDefinitions()
appears to add a_provider
tag to each service with the name of the module.See also #3295542-10: Use logger channel services consistently, instead of the factory object where I suggested a similar idea.
Comment #9
kim.pepperNice!
Comment #10
kim.pepperHaving a look at this.
Comment #12
kim.pepperTried an approach of first adding a
logger_aware
tag to any service with a class implementing\Psr\Log\LoggerAwareInterface
by using +Then a
\Drupal\Core\DependencyInjection\Compiler\LoggerAwarePass
that looks for those tagged services.Not finding my test service at the moment, so I thought I'd push up what I have to see if others have ideas?
Comment #13
kim.pepperNeeded to add:
to
logger_aware_test.services.yml
.Comment #14
kim.pepperAdded a change record.
Comment #15
acbramley CreditAttribution: acbramley at PreviousNext for Service NSW commentedManually tested with services I was playing with before creating this issue and (after applying the other patch to allow autoconfigure) it's working really well. Nice one!
Comment #16
acbramley CreditAttribution: acbramley at PreviousNext for Service NSW commentedComment #17
joachim CreditAttribution: joachim as a volunteer commentedThe CR doesn't mention the 'logger_aware' tag, but the code looks like it's still using it.
Comment #18
longwaveOverall I like this approach; previously I considered automatically creating lots of loggers but that just bloats the container - allowing this to be opt-in simply by implementing an interface is really clean.
I don't think there are any big issues but just to make sure we considered it: are there BC concerns here where services might have previously implemented LoggerAwareInterface and now we have added behaviour to that? Should we also check that the setLogger() method call isn't already present, just in case?
Comment #19
acbramley CreditAttribution: acbramley at PreviousNext for Service NSW commentedAgreed we should probably he checking for existing calls, is it also possible existing class members called $logger could be overwritten? I suppose the interface/trait covers us there.
We should also add a before and after to the CR showing how you no longer need to inject the logger.
Comment #20
kim.pepperSorry, not sure I follow... We are only checking services that implement
LoggerAwareInterface
. ThesetLogger()
method will always be there if they implementLoggerAwareInterface
.Comment #21
kim.pepperUpdated CR
Comment #22
kim.pepperAh I think I follow now.
We just need to check if the definition already has a
setLogger
method call defined.Comment #23
longwaveI wonder if we should also detect LoggerInterface being used in service constructor arguments (as opposed to a setter method) and automatically create and inject a logger, if that argument is not yet wired - then we don't even need LoggerAwareInterface. Or should that be a followup?
Comment #24
longwaveThinking again we should only do #23 if
autowire: true
is set which probably means we have to wait for #3295751: Autowire core services that do not require explicit configurationComment #25
longwaveThis looks good to me as a self contained issue. I don't think we need to explicitly test that exception, nothing else to do here so this is RTBC.
Comment #26
larowlanOne minor issue with PHPCS
Comment #27
kim.pepperTests green ✅
Comment #30
larowlanCommitted to 11.x and backported to 10.2.x
Published CR