Search code examples
jakarta-eelogginglog4jwarear

LOG4J - Separate log files for different wars in EAR


I have the following scenario: an EAR, with 3 WARs (sales module, intranet module and the main app - a lms system). Each WAR has it log4j.properties to configure individual logging files, to organize and make easier to spot problems on each system.

The problem is, the 3 wars share a dependency - a core jar, with my service layer (entities, repositories, services etc). The jar doesn't have a log4.properties, and all the logging made here goes to server.log. I want to avoid it.

When I used to deploy the wars separately, everything was fine - because the jar was within the war and together with the log4j.properties. So:

   Request -> Intranet.war -> Core JAR -> all logging here goes to intranet.log
   Request -> LMS.war -> Core JAR -> all logging here goes to lms.log
   Request -> Sales.war -> Core JAR -> all logging here goes to sales.log

Now I have only one core jar inside the lib/ folder in the EAR, shared by them all.

The question is: how can I still organize the log based on which war called the core?

I mean, if I call the intranet.war inside the EAR, all logging in core.jar still goes to intranet.log. Same for lms and sales.

I was thinking about a unique log4j.properties file to the whole EAR, but how can I separate in files? I could use package approach (xxx.lms.yyy goes to lms.log for instance), but the package in the core jar are shared.

Any ideas?

Thanks in advance!


Solution

  • I found a way to make it work with CDI and ThreadLocal.

    Each request has a thread associated, so I made a CDI Producer method which has the simple logic: is there a Logger in the current threadlocal? If yes, return it. If no, get one from SLF4J LoggerFactory, save it in the ThreadLocal and return it.

    Then, to obtain the logger in the wars AND in the core jar, I do:

    @Inject @MyQualifier private Logger logger; 
    //I created a specific qualifier because one of the frameworks I use also have a Logger Producer
    

    The Producer method:

    @Produces @MyQualifier
    public Logger getLogger(InjectionPoint ip){
    
        ThreadLocal<Logger> t = new ThreadLocal<>();
        if(t.get() == null) {
            Class<?> clazz = ip.getMember().getDeclaringClass();
            t.set(org.slf4j.LoggerFactory.getLogger(clazz));
        }
        return t.get();
    }
    

    Works like a charm! My services/daos classes are all @requestscoped, so for each request I have only one logger associated which is instantied back in the WAR - with the log4j.properties configured as I want for that context.

    There's, however, a scenario in which it doesn't work: @Schedule methods in EJBs. In this case the thread is "created "not in the war but already in the jar context. FOr these cases, I created another log4j.properties in the META-INF folder of the EAR. It's all working now.