Recently, Spring added InjectionPoint
which allows the context to retrieve some information about where a bean is being injected.
One potential use for this would be to inject a slf4j Logger
. Since LoggerFactory.getLogger
wants you to pass the class in which the Logger will be used, it makes it tricky to inject since your configuration class can't know about this class ahead of time. This is now easy with InjectionPoint:
@Bean
@Scope("prototype")
Logger logger(InjectionPoint injectionPoint){
return LoggerFactory.getLogger(injectionPoint.getMethodParameter().getContainingClass());
}
Credit for the code/idea goes to this article.
It would be quite useful if this was added automatically by Spring, allowing Loggers to be injected into beans. This would make retrieving the logging dependency, equivalent to any other dependency, therefore making it very easy to swap out an implementation, perhaps for tests. It also makes code more consistent with no exception being made for Logging.
My main concern was whether there would be additional overhead with the Logger field now no longer being static. Slf4j has some discussion on this. It seems that there is overhead, but not significant overhead in most cases. I feel that the benefits outweigh this potential downside, which is easy to workaround by reverting back to a static, manually created logger.
Comment From: philwebb
I have mixed feelings about this for a number of reasons. I can certainly see the benefit if you're using field injection:
@Autowired
private Logger logger;
The problem is, we generally don't recommend filed injection. With constructor injection things become a little less nice:
private final Logger logger;
public MyClass(Logger logger) {
this.logger = logger;
}
I'm not sure this is much better than:
private static final Logger logger = LoggerFactory.getLogger(MyClass.class);
The static field also has the benefit of being available in static methods of the class, where as the bean can only be used on an instantiated class.
Comment From: philwebb
I've marked the issue for:team-attenttion
, let's see what others think.
Comment From: snicoll
I am not sure I get the argument of swapping the implementation and I don't like the idea of making Logger
a bean. This is surely a decision that a particular team/app can make but I don't see us promoting that idea.
Comment From: rupert-madden-abbott
I also have mixed feelings but to make the case for it slightly stronger:
-
When writing a library, especially, the logging is a key way of communicating with the developers who use your library. If you get a feature request "please could X log X in this circumstance", it is useful to be able to write a test that captures that request so you don't later accidentally regress it. At the moment, it is not trivial (although still possible) to verify that the logger output with a particular message.
-
I think it is more concise to declare the Logger statically and that field injection is more concise than constructor injection. I still think it is better to inject dependencies vs creating them internally and to do so via the constructor vs fields.
-
Injecting loggers would make beans more consistent and encourages dependency injection as the default. There is not this exception being made for the logging dependency. Are there good reasons not to inject a dependency? Yes, for any dependency. That doesn't mean it should be encouraged as the default.
-
If you have static methods, then you also need static dependencies and you won't be able to inject any of them. That goes for any dependency. However, if you are already injecting other dependencies, then you probably don't have static methods in that class. If you have classes full of static methods, then you probably aren't constructing them from your Spring context anyway and can revert back to having a static logger.
Comment From: bclozel
I don't feel comfortable with 1) exposing Logger
as a bean 2) field injection 3) treating Logger
as a dependency. As for the testing side of that argument, I think we're using OutputCapture
to achieve just that.
Comment From: wilkinsona
Sorry, but I agree with @bclozel, @snicoll, and @philwebb. I don't think we should add this to Boot.
Comment From: philwebb
Thanks anyway for the suggestion but we've decided not to proceed with this one.
Comment From: rupert-madden-abbott
@philwebb No problem, thanks for the consideration!
Comment From: mykola-dev
class MyClass {
private final Logger logger;
public MyClass(Logger logger) {
this.logger = logger;
}
}
This ugliness usually related to java. When you do it in Kotlin:
class MyClass(val logger:Logger){
...
}
Comment From: binkley
Coming in late to the discussion, in our Java projects we use Lombok for this:
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
So the actual class code is along the lines of:
public class Foo {
private final Logger logger;
// Other dependencies
}
which is rather succinct (though not quite as nice as Kotlin, I agree).
(Mostly I'm adding the comment not to reopen the issue, but as help for others coming across this issue as I did.)