Sunday, December 6, 2015

Hijacking the logger, so rude!

Last Friday could be my happiest day in 2015 because I've figured out how could I override log4j logger in unit test. Take the use case below as an example, I'll have the logger being declared as private static member:
import org.apache.log4j.Logger;

public class Helper {
   private static Logger jdbcLogger = Logger.getLogger("jdbcLogger");

This was a nightmare because JDBC logger is very high depends on the database's availability, especially when I've been told to freeze any update into the database. End up I create a local database just for this purpose, by doing so, I'm required to reconfigure the JDBC connection in my source code. This still isn't a clean solution as I don't want Jenkins to reconfigure the database configuration in the raw code. Then I start to think about the hijacking way, this is what I did to hijack the logger:
private void hijackJdbcLogger() {
   Logger l = Whitebox.getInternalState(Helper.class, "jdbcLogger");

   Whitebox.setInternalState(Helper.class, "jdbcLogger", l);
But later I found out this solution has a flaw where jdbcLogger must first initialize by Helper class before I can change its value. At least I have something surprise me where I mock the whole logger:
public static void setUpBeforeClass() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
   Logger mockLogger = Logger.getLogger("unitTestLogger");

   Field logger = ReportHelper.class.getDeclaredField("jdbcLogger");
   logger.set(null, mockLogger);
Now I feel the perfection with this solution design. Again, in rule of design, hijacking is very rude, don't do this. When something has declared as private, meaning it is not suppose to be exposed to the outside world. Code review is indeed a higher priority in such case.

No comments: