1 2 3 Previous Next 39 Replies Latest reply on Feb 3, 2013 10:23 AM by arnabsarkar

    TCLFilter

    iksrazal

      Hi all, I've read the docs and searched the archives but I still have a problem. I have two war's (penguin.war and wtf.war) and I want separate logs for each war. I've added these appenders in jboss-log4j.xml :

      <appender name="penguinLog" class="org.apache.log4j.FileAppender">
       <errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"></errorHandler>
       <param name="Append" value="false"/>
       <param name="File" value="${jboss.server.home.dir}/log/penguinLog.log"/>
       <layout class="org.apache.log4j.PatternLayout">
       <param name="ConversionPattern" value="%d %-5p %c{1} %m%n"/>
       </layout>
       <filter class="org.jboss.logging.filter.TCLFilter">
       <param name="AcceptOnMatch" value="true"/>
       <param name="DeployURL" value="penguin"/>
       </filter>
       </appender>
      
       <appender name="wtfLog" class="org.apache.log4j.FileAppender">
       <errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"></errorHandler>
       <param name="Append" value="false"/>
       <param name="File" value="${jboss.server.home.dir}/log/wtfLog.log"/>
       <layout class="org.apache.log4j.PatternLayout">
       <param name="ConversionPattern" value="%d %-5p %c{1} %m%n"/>
       </layout>
       <filter class="org.jboss.logging.filter.TCLFilter">
       <param name="AcceptOnMatch" value="true"/>
       <param name="DeployURL" value="wtf"/>
       </filter>
       </appender>
       <root>
       <appender-ref ref="CONSOLE"/>
       <appender-ref ref="FILE"/>
       <appender-ref ref="penguinLog"/>
       <appender-ref ref="wtfLog"/>
       </root>
      


      In case it matters, the war's reside in my dir /programs/deploy via this entry in jboss-5.0.0.GA/server/default/conf/bootstrap/profile-repository.xml :

      <property name="applicationURIs">
       <array elementClass="java.net.URI">
       <value>${jboss.server.home.url}deploy</value>
       <value>file:///programs/deploy</value>
       </array>
      </property>
      


      The result is that both penguinLog.log and wtfLog.log have the same exact content and file size. If I add:

      <filter class="org.apache.log4j.varia.DenyAllFilter"></filter>
      


      To either the penguinLog or wtfLog appenders, then that file size remains zero when jboss starts. Anyone see a problem with my config which prevents separate logs for each of my war's?


        • 1. Re: TCLFilter
          jpviragine

          iksrazal,

          Try to use:

          <param name="DeployURL" value="wtf-exp.war"/>


          note the use of -exp.war as described here: http://www.jboss.org/community/docs/DOC-12203

          Cheers,
          JP

          • 2. Re: TCLFilter
            iksrazal

            Hmm, I tried your suggestion and both logs still show the same content and file sizes. Is DOC-12203 updated for JBoss 5 ? I ask because, after running "updatedb" and "locate", there are no files or directories with *exp.war* anywhere on my system. Here's my latest config:

            <appender name="penguinLog" class="org.apache.log4j.FileAppender">
             <errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"></errorHandler>
             <param name="Append" value="false"/>
             <param name="File" value="${jboss.server.home.dir}/log/penguinLog.log"/>
             <layout class="org.apache.log4j.PatternLayout">
             <param name="ConversionPattern" value="%d %-5p %c{1} %m%n"/>
             </layout>
             <filter class="org.jboss.logging.filter.TCLFilter">
             <param name="AcceptOnMatch" value="true"/>
             <param name="DeployURL" value="penguin"/>
             </filter>
             </appender>
            
             <appender name="wtfLog" class="org.apache.log4j.FileAppender">
             <errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"></errorHandler>
             <param name="Append" value="false"/>
             <param name="File" value="${jboss.server.home.dir}/log/wtfLog.log"/>
             <layout class="org.apache.log4j.PatternLayout">
             <param name="ConversionPattern" value="%d %-5p %c{1} %m%n"/>
             </layout>
             <filter class="org.jboss.logging.filter.TCLFilter">
             <param name="AcceptOnMatch" value="true"/>
             <param name="DeployURL" value="wtf-exp.war"/>
             </filter>
             </appender>
            
             <root>
             <appender-ref ref="CONSOLE"/>
             <appender-ref ref="FILE"/>
             <appender-ref ref="penguinLog"/>
             <appender-ref ref="wtfLog"/>
             </root>
            


            Please help.

            • 3. Re: TCLFilter
              jpviragine

              Sorry, but I didn't test this in JBoss AS 5... :-(
              In AS 4 you can find the exploded archive in ${jboss.server.home.dir}/tmp/deploy

              • 4. Re: TCLFilter
                iksrazal

                I created a jira for this issue:

                https://jira.jboss.org/jira/browse/JBAS-6532

                • 5. Re: TCLFilter
                  jaikiran

                  Its actually a combination of the code in TCLFilter and the org.jboss.web.tomcat.service.WebCtxLoader's ENCLoader which is causing this issue. The TCLFilter has this piece of code

                  /** Start with the current thread context class loader
                   * @return true if the caller tcl has a url matching our deployURL
                   */
                   private boolean isMatchingTCL()
                   {
                   ClassLoader tcl = Thread.currentThread().getContextClassLoader();
                   if( matchSet.contains(tcl) )
                   return true;
                   if( missSet.contains(tcl) )
                   return false;
                  
                   // Search the class loader URLs for a match
                   ClassLoader cl = tcl;
                   boolean match = false;
                   while( cl != null )
                   {
                   URL[] urls = getClassLoaderURLs(cl);
                   for(int n = 0; n < urls.length; n ++)
                   {
                   URL u = urls[n];
                   String file = u.getFile();
                   if( file.indexOf(deployURL) > 0 )
                  
                  


                  And relies on this getClassLoaderURLs method (which relies on reflection) to see whether the current classloader corresponds to the deployURL:

                  /** Use reflection to access a URL[] getURLs method so that non-URLClassLoader
                   class loaders that support this method can provide info.
                   */
                   private static URL[] getClassLoaderURLs(ClassLoader cl)
                   {
                   URL[] urls = {};
                   try
                   {
                   Class returnType = urls.getClass();
                   Class[] parameterTypes = {};
                   Method getURLs = cl.getClass().getMethod("getURLs", parameterTypes);
                   if( returnType.isAssignableFrom(getURLs.getReturnType()) )
                   {
                   Object[] args = {};
                   urls = (URL[]) getURLs.invoke(cl, args);
                   }
                   if( urls == null || urls.length == 0 )
                   {
                   getURLs = cl.getClass().getMethod("getClasspath", parameterTypes);
                   if( returnType.isAssignableFrom(getURLs.getReturnType()) )
                   {
                   Object[] args = {};
                   urls = (URL[]) getURLs.invoke(cl, args);
                   }
                   }
                   }
                   catch(Exception ignore)
                   {
                   }
                   return urls;
                   }
                  


                  The .war application (ex: jmx-console.war) is loaded through the WebCtxLoader's ENCLoader. The ENCLoader has package static access:

                  public class WebCtxLoader
                   implements Lifecycle, Loader
                  {
                   private static final Logger log = Logger.getLogger(WebCtxLoader.class);
                   /**
                   * The ClassLoader used to scope the ENC
                   */
                   protected ClassLoader encLoader;
                  ... // removed irrelevant code intentionally
                  ... // removed irrelevant code intentionally
                  ... // removed irrelevant code intentionally
                  // This won't be accessible to the TCLFilter
                   /**
                   * A trival extension of URLClassLoader that uses an empty URL[] as its
                   * classpath so that all work is delegated to its parent.
                   */
                   static class ENCLoader extends URLClassLoader
                   {
                  ... // removed irrelevant code intentionally
                   public URL[] getURLs()
                   {
                   return urllist;
                   }
                   }
                  }
                  
                  


                  and hence is not available through reflection to the TCLFilter. As a result, the filter always has a empty set of URLs and hence the filtering fails.

                  Looking at the code in the TCLFilter and the ENCLoader (which extends URLClassloader), i guess the TCLFilter's getClassLoaderURLs(ClassLoader cl) method can be patched to do this:


                  /**
                   * First check if the cl is of type URLClassloader. If yes, then use the getURLs API without the
                   * need for reflection. If its not a URLClassloader type then go for reflection
                   * to access a URL[] getURLs method so that non-URLClassLoader
                   * class loaders that support this method can provide info.
                   */
                   private static URL[] getClassLoaderURLs(ClassLoader cl)
                   {
                   URL[] urls = {};
                   try
                   {
                   // FIrst check the type of cl, if URLClassloader type then don't use reflection
                   if (cl instance of URLClassLoader)
                   {
                   URLClassLoader urlClassLoader = (URLClassLoader) cl;
                   urls = urlClassLoader.getURLs();
                  
                   }
                  
                   else
                   { //let's rely on reflection
                   Class returnType = urls.getClass();
                   Class[] parameterTypes = {};
                   Method getURLs = cl.getClass().getMethod("getURLs", parameterTypes);
                   if( returnType.isAssignableFrom(getURLs.getReturnType()) )
                   {
                   Object[] args = {};
                   urls = (URL[]) getURLs.invoke(cl, args);
                   }
                   if( urls == null || urls.length == 0 )
                   {
                   getURLs = cl.getClass().getMethod("getClasspath", parameterTypes);
                   if( returnType.isAssignableFrom(getURLs.getReturnType()) )
                   {
                   Object[] args = {};
                   urls = (URL[]) getURLs.invoke(cl, args);
                   }
                   }
                   }
                   }
                   catch(Exception ignore)
                   {
                   }
                   return urls;
                   }
                  
                  


                  That should fix the problem. Then your log4j.xml can have this:

                  <appender name="App1Log" class="org.apache.log4j.FileAppender">
                  ....
                   <filter class="org.jboss.logging.filter.TCLFilter">
                   <param name="AcceptOnMatch" value="true"/>
                   <param name="DeployURL" value="jmx-console.war"/>
                   </filter>
                  
                   <!-- end the filter chain here -->
                   <filter class="org.apache.log4j.varia.DenyAllFilter"></filter>
                  
                   </appender>
                  
                  
                  


                  • 6. Re: TCLFilter
                    iksrazal

                    Wow, thanks jaikiran for the patch. I'm running our projects production releases on jboss 5 , so I'm unlikely to be able to test your patch and compile jboss 5 from source. Hopefully the patch gets the attention of the committers and they accept it.

                    • 7. Re: TCLFilter
                      m.wigge

                      Hi all, we've got the same Problem with our JBoss5. I saw that there is a Patch to download on Jira but I don't know how to install it. Can anyone please help me with it?

                      Thanks

                      • 8. Re: TCLFilter
                        jaikiran

                         

                        "mwigge" wrote:
                        I saw that there is a Patch to download on Jira but I don't know how to install it. Can anyone please help me with it?

                        Thanks


                        To use the patch, you will have to checkout the common-logging-log4j project trunk from SVN and apply this patch there and then build a jboss-logging-log4j.jar out of it. The jar can then be (re)placed in the %JBOSS_HOME%/lib folder.

                        Instead, i think i will build that jar from the patch and attach it to the JIRA. You can then replace it in the %JBOSS_HOME%/lib folder. I would however recommend that you maintain a backup of the existing jar before doing this. And remember, the jar attached to the JIRA is just for convenience and ultimately, the fix will be available after the patched is reviewed and applied in SVN.


                        • 9. Re: TCLFilter
                          jaikiran

                           

                          "jaikiran" wrote:

                          Instead, i think i will build that jar from the patch and attach it to the JIRA.


                          Done. The JIRA has the jar with the patch.

                          • 10. Re: TCLFilter
                            geofrank

                            Does anybody know a way to get this working on JBoss AS 5.1.0.GA?

                            And is there any progress on getting this issue fixed? I would say it's a very important feature to have in production. Like many others, I host multiple projects on each JBoss instance and need a way to provide the correct log to the correct developers.

                            • 11. Re: TCLFilter
                              vickyk

                               

                              "geofrank" wrote:
                              Does anybody know a way to get this working on JBoss AS 5.1.0.GA?

                              Give a try to this class
                              package org.jboss.logging.filter.jboss5;
                              
                              
                              
                              import org.apache.log4j.spi.Filter;
                              import org.apache.log4j.spi.LoggingEvent;
                              import org.jboss.classloader.spi.base.BaseClassLoader;
                              import org.jboss.util.collection.WeakSet;
                              
                              public class TCLFilter extends Filter {
                              
                               /** The set of TCLs seen to match DeployURL */
                               private WeakSet matchSet = new WeakSet();
                              
                               /** The set of TCLs seen to not match DeployURL */
                               private WeakSet missSet = new WeakSet();
                              
                               /** The deployment URL string fragment to match against */
                               private String deployURL;
                              
                               /** Whether a match should return ACCEPT or DENY */
                               private boolean acceptOnMatch = true;
                              
                               public boolean isAcceptOnMatch() {
                               return acceptOnMatch;
                               }
                              
                               public void setAcceptOnMatch(boolean acceptOnMatch) {
                               this.acceptOnMatch = acceptOnMatch;
                               }
                              
                               public String getDeployURL() {
                               return deployURL;
                               }
                              
                               public void setDeployURL(String deployURL) {
                               this.deployURL = deployURL;
                               }
                              
                               public int decide(LoggingEvent event) {
                               int ok = Filter.NEUTRAL;
                               if (acceptOnMatch == true) {
                               if (isMatchingTCL())
                               ok = Filter.ACCEPT;
                               } else {
                               if (isMatchingTCL())
                               ok = Filter.DENY;
                               }
                               return ok;
                               }
                              
                               /**
                               * Start with the current thread context class loader
                               *
                               * @return true if the caller tcl has a url matching our deployURL
                               */
                               private boolean isMatchingTCL() {
                               ClassLoader tcl = Thread.currentThread().getContextClassLoader();
                               if (matchSet.contains(tcl))
                               return true;
                               if (missSet.contains(tcl))
                               return false;
                               // Search the class loader URLs for a match
                               ClassLoader cl = tcl;
                               // JBoss5 specific Deployment ClassLoader type
                               BaseClassLoader baseClassLoader = null;
                               boolean match = false;
                               while (cl != null) {
                               if (cl instanceof BaseClassLoader) {
                               baseClassLoader = (BaseClassLoader) cl;
                               if(baseClassLoader.toString().indexOf(deployURL) > 0){
                               System.out.println("JBoss5 specific BaseClassLoader is "+baseClassLoader+" and is matched for "+ deployURL);
                               match = true;
                               break;
                               }
                               }
                               cl = cl.getParent();
                               }
                               if (match == true)
                               matchSet.add(tcl);
                               else
                               missSet.add(tcl);
                               System.out.println("JBoss5 specific BaseClassLoader is "+baseClassLoader);
                               return match;
                               }
                              }
                              


                              • 12. Re: TCLFilter
                                alesj

                                 

                                "vickyk" wrote:

                                Give a try to this class
                                if(baseClassLoader.toString().indexOf(deployURL) > 0){
                                


                                Wow, that's one ugly hack. ;-)

                                And it actually won't work, as BCL::toString only outputs name.
                                What you really wanted to use it toLongString.

                                But relying on string to do your dirty work is just too hackish.
                                Although original impl is not so far away either :-)
                                * http://anonsvn.jboss.org/repos/common/common-logging-log4j/trunk/src/main/java/org/jboss/logging/filter/TCLFilter.java

                                Let me try to think of something ...


                                • 13. Re: TCLFilter
                                  jaikiran

                                   

                                  "alesj" wrote:

                                  Let me try to think of something ...


                                  It's actually fixed https://jira.jboss.org/jira/browse/JBLOGGING-30. I forgot to create a component upgrade request for Branch_5_x to get a new version of JBoss logging.

                                  • 14. Re: TCLFilter
                                    alesj

                                     

                                    "jaikiran" wrote:

                                    It's actually fixed https://jira.jboss.org/jira/browse/JBLOGGING-30. I forgot to create a component upgrade request for Branch_5_x to get a new version of JBoss logging.

                                    Is this the fix?
                                    * http://anonsvn.jboss.org/repos/common/common-logging-log4j/trunk/src/main/java/org/jboss/logging/filter/TCLFilter.java

                                    How does this retrieve urls from BaseClassLoader?
                                    e.g. in non web wrapped ENCLoader (or something similar)
                                    Or is this just intended for web?


                                    1 2 3 Previous Next