1

I'm using Spring XML-based DI to create my classes.

Also I'm using log4j and I'm injecting org.apache.log4j.Logger as a constructor argument in each class which needs it.

So I have a lot of entries in Spring config like following:

<bean id="myClass" class="com.myProject.MyClass">
    ...
    <constructor-arg type="org.apache.log4j.Logger">
        <bean class="org.apache.log4j.Logger" factory-method="getLogger">
            <constructor-arg value="com.myProject.MyClass"/>
        </bean>
    </constructor-arg>
</bean>

Class code:

package com.myProject;

import org.apache.log4j.Logger;

public class MyClass {
    private final Logger logger;

    public MyClass (
            ...
            Logger logger) {
        ...
        this.logger = logger;
    }

    ...
}

Although it works perfectly, I wonder if there is any possibility to default constructor-arg of the logger to the class name, whose constructor the created logger should be passed to?

E.g. like this solution for Unity DI and log4net which are somewhat analogs in .Net.


Update:

I see two reasons of injecting Logger rather than initializing it inside the class.

First of all, statements like private final Logger logger = LogManager.getLogger(MyClass.class); are usually copy-pasted, so they are prone to mistake in the logger name.

Moverover, I'd like to cover error logging with unit tests, e.g. pass a mock of Logger into the constructor and than verify that after some erroneous action logger.Error has been called.

stop-cran
  • 4,229
  • 2
  • 30
  • 47
  • why don't you use **private static final Logger LOGGER = LogManager.getLogger(MyClass.class);** ? – Dirk Deyne Apr 30 '18 at 13:19
  • 1
    You can use project Lombok's annotation @Log to automatically inject logger if it's needed. Here is an example: https://projectlombok.org/features/log – Alexander Terekhov Apr 30 '18 at 13:24
  • @DirkDeyne i'd like to inject a logger, e.g. to mock it in unit-tests and verify an error is logged. – stop-cran Apr 30 '18 at 13:30
  • 1
    there are better ways to test if logging happens. [how-to-do-a-junit-assert-on-a-message-in-a-logger](https://stackoverflow.com/questions/1827677/how-to-do-a-junit-assert-on-a-message-in-a-logger) – Dirk Deyne Apr 30 '18 at 13:39

1 Answers1

1

Passing the logger as a constructor parameter is somewhat terrible. Also, your arguments against using a private ...blah... Logger are specious.

  1. "copy-paste is error prone." True copy-paste techniques are error prone, but when you want each logger to have a unique name (so as to identify the source of the log message), you need to declare a logger in each class.
  2. "I want to mock my loggers during unit tests." While it is not reasonable to replace a final member variable with a mock, it is ridiculously easy to to replace a not-final member variable with a mock. To this end, you should always declare class level loggers a follows: private static Logger ...blah. Specifically, don't use final loggers. Once the loggers are not final, use reflection to replace them with mocks.
  3. [New in 2023; hurry for learnin'] Use protected loggers. Declare your logger as static Logger ...blah then assign it to a mockLogger in the @BeforeEach or @BeforeAll method in your Junit (jupiter) unit test.
DwB
  • 37,124
  • 11
  • 56
  • 82