Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tomcat WAR - Configure Logback to use app name in path

I have logback deployed in my war file lib folder and I have the following logback.xml in the classes folder.

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
  <property name="destination" value="${catalina.base:-./temp}/logs/${appName:-myapp}" />

  <appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${destination}.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <!-- rollover daily -->
      <fileNamePattern>${destination}-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
      <!-- Keep logs for 7 days -->
      <maxHistory>7</maxHistory>

      <timeBasedFileNamingAndTriggeringPolicy
            class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
        <!-- or whenever the file size reaches 100MB -->
        <maxFileSize>100MB</maxFileSize>
      </timeBasedFileNamingAndTriggeringPolicy>
    </rollingPolicy>
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="error">
    <appender-ref ref="ROLLING" />
  </root>
</configuration>

On line 3 I have some variable substitutions that create the path for my log file.

  <property name="destination" value="${catalina.base:-./temp}/logs/${appName:-myapp}" />

I want to make it so ${appName} evaluates to the current name of the war file as deployed.

So if my web apps folder looked like so

webapps
 - myapp.war
 - myapp-dev.war

The ${destination} property for myapp.war would evaluate to .../logs/myapp and myapp-dev.war would evaluate to .../logs/myapp-dev. Is there a JNDI property or something I can access to access the appName ?

I would like to avoid having to manually reconfigure the logger.

Thanks!

like image 305
HaxElit Avatar asked May 05 '12 19:05

HaxElit


People also ask

Where do I put the Logback file?

In a Spring Boot application, you can put the Logback. xml file in the resources folder. If your Logback. xml file is outside the classpath, you need to point to its location using the Logback.

Where is Logback xml for Tomcat?

By default, LogbackValve looks for a configuration file called logback-access. xml, in the same folder where server. xml is located, that is in $TOMCAT_HOME/conf/. This configuration file contains directives for configuring logback-access components.

What is Logback configuration?

Logback is one of the most widely used logging frameworks in the Java Community. It's a replacement for its predecessor, Log4j. Logback offers a faster implementation, provides more options for configuration, and more flexibility in archiving old log files.


2 Answers

EDIT 2013-06: I have made this Listener available as OSS on Maven Central. Check out the project homepage.

Yes, this is feasible. First of all, you can always rely on catalina.base because without it Tomcat won't run. In order to inject the context name as property. Write a context listener which will put the context name into the JNDI context and remove at shutdown. After you have done that, You can retrieve the value with JNDI directly with logback. There is direct support for that. Write that in the the contextName element and you're done.

I have implemented this on my own already and it works for all of my projects. I can share the entire code on monday if you or someone else is interested.

Edit, here is the code:

import org.apache.catalina.Context;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.deploy.ContextEnvironment;
import org.apache.commons.lang.StringUtils;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;

public class LogbackContextNameListener implements LifecycleListener {

    private static final Log logger = LogFactory
            .getLog(LogbackContextNameListener.class);
    private Context context;

    private String name = "logback/contextName";

    @Override
    public void lifecycleEvent(LifecycleEvent le) {

        if (le.getLifecycle() instanceof Context)
            context = (Context) le.getLifecycle();
        else
            return;

        if (le.getType().equals(Lifecycle.START_EVENT)) {
            ContextEnvironment ce = new ContextEnvironment();
            ce.setName(getName());
            ce.setOverride(false);
            ce.setType("java.lang.String");
            String value = StringUtils.remove(context.getServletContext()
                    .getContextPath(), '/');
            ce.setValue(value);
            logger.debug(String.format("Adding env entry '%s' with value '%s'",
                    getName(), value));
            context.getNamingResources().addEnvironment(ce);
        }

        if (le.getType().equals(Lifecycle.STOP_EVENT)) {
            logger.debug(String.format("Removing env entry '%s'", getName()));
            context.getNamingResources().removeEnvironment(name);
        }

    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        if (StringUtils.isEmpty(name))
            throw new IllegalArgumentException(
                    "Parameter 'name' cannot be empty");

        this.name = name;
    }

}

A suitable config looks like this:

<configuration scan="true" scanPeriod="30 minutes">

    <insertFromJNDI env-entry-name="java:comp/env/logback/contextName" as="contextName" />
    <contextName>${contextName}</contextName>

    <appender name="FILE"
        class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${catalina.base}/logs/${CONTEXT_NAME}.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- daily rollover -->
            <fileNamePattern>${catalina.base}/logs/${CONTEXT_NAME}.log.%d.gz</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%-27(%d{HH:mm:ss.SSS} [%.-12thread]) %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="INFO"><!-- WARN -->
        <appender-ref ref="FILE" />
    </root>

</configuration>

This works flawlessly in Tomcat 6. I guess, it will run on Tomcat 7 w/o changes.

like image 123
Michael-O Avatar answered Sep 30 '22 13:09

Michael-O


This builds on Michael-O's answer. Considering that catalina.base is always a defined system property when running under Tomcat, we only have to worry about defining appName. Logback offers support for retrieving variables from JNDI. If appName is defined in JNDI, your configuration file becomes:

<configuration>
  <!-- retrieve appName from JNDI to set the variable appName -->
  <insertFromJNDI env-entry-name="java:comp/env/appName" as="appName" />
  <!-- let the context name be the applicaiton name -->
  <contextName>${appName}</contextName>

  <property name="destination" 
            value="${catalina.base:-./temp}/logs/${CONTEXT_NAME:-myapp}" />

  <appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${destination}.log</file>
    ... remainder of config file omitted for brevity  
  </appender>
</configuration>

I'd like to mention that you could also just define appName directly in logback.xml instead of in JNDI. (After all, the logback.xml file ships with your web-app where its name is already established. However, your question explicitly excludes this hypothesis.) Thus, you logback.xml file could be simplified to:

<configuration>
  <contextName>the_name_of_your_webapp</contextName>
  <property name="destination" 
            value="${catalina.base:-./temp}/logs/${CONTEXT_NAME:-myapp}" />
   ... the rest omitted for brevity
</configuration? 

BTW, once you find a satisfactory solution, do not hesitate to share it by posting on logback-user list.

like image 37
Ceki Avatar answered Sep 30 '22 13:09

Ceki