Multiple WARs sharing the same logback.xml

2019-09-10 14:34发布

问题:

Is the following logback setup safe and good practice.

I have Multiple WARs (deployed WebSphere 8.5.5) and want them to share a single logback.xml

-Dlogback.configurationFile=/opt/logback.xml -Dlogback.ContextSelector=JNDI

The logback.xml uses a SiftingAppender with JNDIBasedContextDiscriminator so each WAR gets its own log file.

<appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
    <discriminator class="ch.qos.logback.classic.sift.JNDIBasedContextDiscriminator">
        <defaultValue>unknown</defaultValue>
    </discriminator>
    <sift>
        <appender name="FILE-${contextName}" class="ch.qos.logback.core.FileAppender">
            <file>/var/log/${contextName}.log</file>
            <encoder>
                <pattern>%-50(%level %logger{35}) cn=%contextName - %msg%n</pattern>
            </encoder>
        </appender>
    </sift>
</appender>

Each WAR web.xml will have contextName:

<env-entry>
  <description>JNDI logging context for this app</description>
  <env-entry-name>logback/context-name</env-entry-name>
  <env-entry-type>java.lang.String</env-entry-type>
  <env-entry-value>ContextNameWebAppA</env-entry-value>
</env-entry>

回答1:

I don't know if using the Jndi discriminator is safe or a good practice, but it seems to be the way Logback solves this issue : http://logback.qos.ch/manual/loggingSeparation.html
They indicate that the performance can be better in adding this to your configuration :

<filter>
  <filter-name>LoggerContextFilter</filter-name>
  <filter-class>ch.qos.logback.classic.selector.servlet.LoggerContextFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>LoggerContextFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

On an other hand, I can share what I'm trying to do to avoid setting the system properties logback.ContextSelector=JNDI.

I use instead the MDCBasedDiscriminator which will get the discriminating value defined with MDC.put(key,value).
The MDC map is available as a thread local variable, so it must be set for every thread initiated by the web server.
For this initialisation I used a javax.servlet.Filter placed before other filters, this filter will put the correct value in MDC.

I don't think this is better than what you did, but it's an alternative to the JNDI property, the problem is that the shutting down log are in the unknown.log.

Here is some code :

public class WarLoggingFilter implements Filter {
    private static final String WAR_NAME_ATTRIBUTE = "WAR_NAME";
    private String warName;

    @Override
    public void init(final FilterConfig filterConfig) throws ServletException {
        warName = filterConfig.getInitParameter(WAR_NAME_ATTRIBUTE);
    }

    @Override
    public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
            throws IOException, ServletException {
        insertIntoMDC(warName);
        chain.doFilter(request, response);
    }

    private void clearMDC() {
        MDC.remove(WAR_NAME_ATTRIBUTE);
    }

    private static void insertIntoMDC(final String warName) {
        MDC.put(WAR_NAME_ATTRIBUTE, warName);
    }

    @Override
    public void destroy() {
        clearMDC();
    }


    /**
     * Register this filter in the servlet context. Adds the necessary init
     * parameter.
     *
     * @param warName
     * @param servletContext
     */
    public static void registerMe(final String warName, final ServletContext servletContext) {
        // MDC for the startup thread
        insertIntoMDC(warName);
        // MCD for next threads
        final Dynamic addFilter = servletContext.addFilter(warName, WarLoggingFilter.class);
        addFilter.setInitParameter(WarLoggingFilter.WAR_NAME_ATTRIBUTE, warName);
        addFilter.addMappingForUrlPatterns(null, false, "/*");

    }

}

And the logback file :

<appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
        <discriminator class="ch.qos.logback.classic.sift.MDCBasedDiscriminator">
            <key>WAR_NAME</key>
            <defaultValue>unknown</defaultValue>
        </discriminator>
        <sift>
            <appender name="FILE-${WAR_NAME}" class="ch.qos.logback.core.FileAppender">
                <file>/tmp/${WAR_NAME}.log</file>
                <encoder>
                    <pattern>%date{ISO8601} %-5level %logger{30}\(%line\) - %message%n</pattern>
                </encoder>
            </appender>
        </sift>
    </appender>

And the registration can be for example in a spring security initializer :

public class MySecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

    /**
     * Invoked before the springSecurityFilterChain is added.
     *
     * @param servletContext
     *            the {@link ServletContext}
     */
    @Override
    protected void beforeSpringSecurityFilterChain(final ServletContext servletContext) {
        // Tell logback to log this web app events in a separate file
        WarLoggingFilter.registerMe("my_webapp", servletContext);
    }


标签: war logback