You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4j-user@logging.apache.org by tobias van reuien <to...@freenet.de> on 2006/03/28 15:51:01 UTC

Strange behaviour of JoranConfigurator

Hi,

im using tomcat 5 and a servlet with load-on-startup=1 that should initialize my
log4J at web-app start. By putting a file-watchdog on the file, it should be
monitored for changes, wich works good.

But:
A) By setting log4j debug=true, theres a sax-parse-exception telling that
standard "premature end of file"-error. The xml-configuration is exactly similar
to the one on the official log4j-site.

B)By using my own rollingfile-appender, it works fine. I created a rolling file
appender that rolls daily at midnight, AND every seven days, so you'll always
have the last 6days + the current days logging file. Works nice on my local
WinXP, but the repacement does not work on the Debian Server. Looks like the
RollingFileAppender.rollOver-Method is never called.








Here's the code

----SERVLET---------------------------------------
public void init( ServletConfig config ) throws ServletException
    {
        super.init( config );
        FileWatchdog d = new FileWatchdog();
        try
        {
            System.out.println( "Servlet " + getClass().getName() + "      
initializing logging." );
            String s = getServletContext().getRealPath( "/" );
            String s1 = getInitParameter( "initFile" );
            String s2 = getInitParameter( "interval" );
            int interval = Integer.parseInt( s2 );

            if( s1 != null )
            {
                JoranConfigurator jC = new JoranConfigurator();
                jC.doConfigure( s + s1, LogManager.getLoggerRepository() );
                d.setLoggerRepository( LogManager.getLoggerRepository() );
                d.setFile( s + s1 );
                d.setInterval( interval );
                d.setConfigurator( jC.getClass().getName() );
                d.activateOptions();
                //esentially, if we do not dumErrors we'll never see if smthng
went wrong.
                jC.dumpErrors();
            }
            else
            {
                Logger.getRootLogger().log(
                        Level.ERROR,
                        "[MLogger.init] log4j initFile is null. Given params:
[initFile]:" + s1 + " [interval]:"
                                + interval );
            }
        }
        catch( Exception e )
        {
            Logger.getRootLogger().log( Level.ERROR, "[MLogger.init] Exception
occured! Reason: " + e.getMessage() );
            System.err.println( "[MLogger.init] Exception occured. Reason: " );
            e.printStackTrace();
        }

    }








------File appender--------------------------
public class MoccaRollingFileAppender implements Appender, OptionHandler
{

    /**
     * It is assumed and enforced that errorHandler is never null.
     * 
     * @deprecated as of 1.3
     */
    private final org.apache.log4j.spi.ErrorHandler            errorHandler    
= new org.apache.log4j.helpers.OnlyOnceErrorHandler();

    /**
     The default maximum file size is 10MB.
     */
    private long                                               maxFileSize     
= 10 * 1024 * 1024;

    /**
     There is one backup file by default.
     */
    private int                                                maxBackupIndex   = 1;

    /**
     *  Nested new rolling file appender.
     */
    private final org.apache.log4j.rolling.RollingFileAppender rfa             
= new org.apache.log4j.rolling.RollingFileAppender();

    private String                                             fileName        
= null;

    private String                                             datePattern     
= "'.'yyyy-MM-dd";

    private String                                             fileNamePattern 
= null;

    private String                                             m_suffix        
= null;

    int                                                        m_placeHolderPos = 0;

    /**
     The default constructor simply calls its {@link
     FileAppender#FileAppender parents constructor}.  */
    public MoccaRollingFileAppender()
    {
    //  rfa.setFile(fileName);
    //  activateOptions();
    }

    /**
     Instantiate a RollingFileAppender and open the file designated by
     <code>filename</code>. The opened filename will become the ouput
     destination for this appender.

     <p>If the <code>append</code> parameter is true, the file will be
     appended to. Otherwise, the file desginated by
     <code>filename</code> will be truncated before being opened.
     */
    public MoccaRollingFileAppender(

    final Layout layout, final String filename, final boolean append ) throws
IOException
    {
        System.out.println( "[MoccaRollingFileAppender.MoccaRollingFileAppender]" );

        System.out.println( "---MoccaRollingFileAppender---" );
        System.out.println( "---Initializing: fileName:" + filename + " append:
" + append );
        rfa.setLayout( layout );
        rfa.setFile( filename );
        rfa.setAppend( append );

        activateOptions();
    }

    /**
     Instantiate a FileAppender and open the file designated by
     <code>filename</code>. The opened filename will become the output
     destination for this appender.

     <p>The file will be appended to.  */
    public MoccaRollingFileAppender( final Layout layout, final String filename
) throws IOException
    {
        System.out.println( "[MoccaRollingFileAppender.MoccaRollingFileAppender]" );

        System.out.println( "---MoccaRollingFileAppender---" );
        System.out.println( "---Initializing: fileName:" + filename );
        rfa.setLayout( layout );
        rfa.setFile( fileName );
        activateOptions();
    }

    /**
     Returns the value of the <b>MaxBackupIndex</b> option.
     */
    public int getMaxBackupIndex()
    {
        return maxBackupIndex;
    }

    /**
     Get the maximum size that the output file is allowed to reach
     before being rolled over to backup files.

     @since 1.1
     */
    public long getMaximumFileSize()
    {
        return maxFileSize;
    }

    /**
     Set the maximum number of backup files to keep around.

     <p>The <b>MaxBackupIndex</b> option determines how many backup
     files are kept before the oldest is erased. This option takes
     a positive integer value. If set to zero, then there will be no
     backup files and the log file will be truncated when it reaches
     <code>MaxFileSize</code>.
     */
    public void setMaxBackupIndex( int maxBackups )
    {
        this.maxBackupIndex = maxBackups;
    }

    /**
     Set the maximum size that the output file is allowed to reach
     before being rolled over to backup files.

     <p>This method is equivalent to {@link #setMaxFileSize} except
     that it is required for differentiating the setter taking a
     <code>long</code> argument from the setter taking a
     <code>String</code> argument by the JavaBeans {@link
     java.beans.Introspector Introspector}.

     @see #setMaxFileSize(String)
     */
    public void setMaximumFileSize( long maxFileSize )
    {
        this.maxFileSize = maxFileSize;
    }

    /**
     Set the maximum size that the output file is allowed to reach
     before being rolled over to backup files.

     <p>In configuration files, the <b>MaxFileSize</b> option takes an
     long integer in the range 0 - 2^63. You can specify the value
     with the suffixes "KB", "MB" or "GB" so that the integer is
     interpreted being expressed respectively in kilobytes, megabytes
     or gigabytes. For example, the value "10KB" will be interpreted
     as 10240.
     */
    public void setMaxFileSize( String value )
    {
        maxFileSize = OptionConverter.toFileSize( value, maxFileSize + 1 );
    }

    /**
     The <b>DatePattern</b> takes a string in the same format as
     expected by {@link java.text.SimpleDateFormat}. This options determines the
     rollover schedule.
     */
    public void setDatePattern( String pattern )
    {
        datePattern = pattern;
    }

    /** 
     * Returns the value of the <b>DatePattern</b> option. 
     * By default, the pattern is set to ".yyyy-MM-dd" meaning daily rollover.
     */
    public String getDatePattern()
    {
        return datePattern;
    }

    public void setFileNamePattern( String pattern )
    {
        m_placeHolderPos = pattern.indexOf( "%" );
        m_suffix = pattern.substring( pattern.lastIndexOf( "." ) + 1,
pattern.length() );

        fileNamePattern = pattern;
    }

    public String getFileNamePattern()
    {
        return fileNamePattern;
    }

    /**
     * Prepares RollingFileAppender for use.
     */
    public void activateOptions()
    {
        MoccaRollingPolicy policy = new MoccaRollingPolicy();
        StringBuffer pattern = new StringBuffer( rfa.getFile() );
        boolean inLiteral = false;
        boolean inPattern = false;
        for( int i = 0; i < datePattern.length(); i++ )
        {
            if( datePattern.charAt( i ) == '\'' )
            {
                inLiteral = !inLiteral;
                if( inLiteral && inPattern )
                {
                    pattern.append( "}" );
                    inPattern = false;
                }
            }
            else
            {
                if( !inLiteral && !inPattern )
                {
                    pattern.append( "%d{" );
                    inPattern = true;
                }

                pattern.append( datePattern.charAt( i ) );
            }
        }

        if( inPattern )
        {
            pattern.append( "}" );
        }

        policy.setInsertionPos( m_placeHolderPos );
        policy.setFileNamePattern( pattern.toString() );
        policy.activateOptions();
        rfa.setTriggeringPolicy( policy );
        rfa.setRollingPolicy( policy );
        rfa.activateOptions();

    }

    /**
     * Add a filter to the end of the filter list.
     *
     * @since 0.9.0
     */
    public void addFilter( final Filter newFilter )
    {
        rfa.addFilter( newFilter );
    }

    /**
     * Returns the head Filter. The Filters are organized in a linked list and
     * so all Filters on this Appender are available through the result.
     *
     * @return the head Filter or null, if no Filters are present
     *
     * @since 1.1
     */
    public Filter getFilter()
    {

        return rfa.getFilter();
    }

    /**
     * Clear the list of filters by removing all the filters in it.
     *
     * @since 0.9.0
     */
    public void clearFilters()
    {

        rfa.clearFilters();
    }

    /**
     * Release any resources allocated within the appender such as file handles,
     * network connections, etc.
     *
     * <p>
     * It is a programming error to append to a closed appender.
     * </p>
     *
     * @since 0.8.4
     */
    public void close()
    {

        rfa.close();
    }

    /**
     * Is this appender closed?
     *
     * @since 1.3
     */
    public boolean isClosed()
    {

        return rfa.isClosed();
    }

    /**
     * Is this appender in working order?
     *
     * @since 1.3
     */
    public boolean isActive()
    {

        return rfa.isActive();
    }

    /**
     * Log in <code>Appender</code> specific way. When appropriate, Loggers will
     * call the <code>doAppend</code> method of appender implementations in
     * order to log.
     */
    public void doAppend( final LoggingEvent event )
    {

        rfa.doAppend( event );
    }

    /**
     * Get the name of this appender. The name uniquely identifies the appender.
     */
    public String getName()
    {
        return rfa.getName();
    }

    /**
     * Set the {@link Layout} for this appender.
     *
     * @since 0.8.1
     */
    public void setLayout( final Layout layout )
    {
        //    System.out.println("Setting layout: "+layout);
        rfa.setLayout( layout );
    }

    /**
     * Returns this appenders layout.
     *
     * @since 1.1
     */
    public Layout getLayout()
    {
        return rfa.getLayout();
    }

    /**
     * Set the name of this appender. The name is used by other components to
     * identify this appender.
     *
     * @since 0.8.1
     */
    public void setName( final String name )
    {
        rfa.setName( name );
    }

    public void setLoggerRepository( final LoggerRepository repository ) throws
IllegalStateException
    {
        rfa.setLoggerRepository( repository );
    }

    /**
     The <b>File</b> property takes a string value which should be the
     name of the file to append to.

     <p><font color="#DD0044"><b>Note that the special values
     "System.out" or "System.err" are no longer honored.</b></font>

     <p>Note: Actual opening of the file is made when {@link
     #activateOptions} is called, not when the options are set.  */
    public void setFile( final String file )
    {
        fileName = file;
        rfa.setFile( file );
        activateOptions();

    }

    /**
     Returns the value of the <b>Append</b> option.
     */
    public boolean getAppend()
    {
        return rfa.getAppend();
    }

    /** Returns the value of the <b>File</b> option. */
    public String getFile()
    {
        return rfa.getFile();
    }

    /**
     Get the value of the <b>BufferedIO</b> option.

     <p>BufferedIO will significatnly increase performance on heavily
     loaded systems.

     */
    public boolean getBufferedIO()
    {
        return rfa.getBufferedIO();
    }

    /**
     Get the size of the IO buffer.
     */
    public int getBufferSize()
    {
        return rfa.getBufferSize();
    }

    /**
     The <b>Append</b> option takes a boolean value. It is set to
     <code>true</code> by default. If true, then <code>File</code>
     will be opened in append mode by {@link #setFile setFile} (see
     above). Otherwise, {@link #setFile setFile} will open
     <code>File</code> in truncate mode.

     <p>Note: Actual opening of the file is made when {@link
     #activateOptions} is called, not when the options are set.
     */
    public void setAppend( final boolean flag )
    {
        rfa.setAppend( flag );
    }

    /**
     The <b>BufferedIO</b> option takes a boolean value. It is set to
     <code>false</code> by default. If true, then <code>File</code>
     will be opened and the resulting {@link java.io.Writer} wrapped
     around a {@link java.io.BufferedWriter}.

     BufferedIO will significatnly increase performance on heavily
     loaded systems.

     */
    public void setBufferedIO( final boolean bufferedIO )
    {
        rfa.setBufferedIO( bufferedIO );
    }

    /**
     Set the size of the IO buffer.
     */
    public void setBufferSize( final int bufferSize )
    {
        rfa.setBufferSize( bufferSize );
    }

    /**
     Implements the usual roll over behaviour.

     <p>If <code>MaxBackupIndex</code> is positive, then files
     {<code>File.1</code>, ..., <code>File.MaxBackupIndex -1</code>}
     are renamed to {<code>File.2</code>, ...,
     <code>File.MaxBackupIndex</code>}. Moreover, <code>File</code> is
     renamed <code>File.1</code> and closed. A new <code>File</code> is
     created to receive further log output.

     <p>If <code>MaxBackupIndex</code> is equal to zero, then the
     <code>File</code> is truncated with no backup files created.

     */
    public// synchronization not necessary since doAppend is alreasy synched
    void rollOver()
    {   rfa.rollover();
    }

    /**
     * Return the hardcoded <code>OnlyOnceErrorHandler</code> for this Appender.
     * <code>ErrorHandler</code>'s are no longer utilized as of version 1.3.
     *
     * @since 0.9.0
     * @deprecated As of 1.3
     */
    public final org.apache.log4j.spi.ErrorHandler getErrorHandler()
    {

        return this.errorHandler;
    }

    /**
     * Ignored as of 1.3
     *
     * @since 0.9.0
     * @deprecated As of 1.3
     */
    public final void setErrorHandler( org.apache.log4j.spi.ErrorHandler eh )
    {
        ; //ignore
    }

    /**
     * Gets whether appender requires a layout.
     * @return false
     */
    public boolean requiresLayout()
    {
        return false;
    }

    class MoccaRollingPolicy extends RollingPolicyBase implements TriggeringPolicy
    {

        final String   dayMappings[] = new String[] { "Sunday", "Monday",
"Tuesday", "Wednesday", "Thursday", "Friday",
                                             "Saturday" };

        /**
         * Time for next determination if time for rollover.
         */
        private long   nextCheck     = 0;

        /**
         * File name at last rollover.
         */
        private String lastFileName  = null;

        /**
         * Length of any file type suffix (.gz, .zip).
         */
        private int    suffixLength  = 0;

        int            insertionPos  = 0;

        /**
         * Constructs a new instance.
         */
        public MoccaRollingPolicy()
        {}

        /**
         * Prepares instance of use.
         */
        public void activateOptions()
        {
            super.activateOptions();

            PatternConverter dtc = getDatePatternConverter();

            if( dtc == null )
            {
                throw new IllegalStateException( "FileNamePattern [" +
getFileNamePattern()
                        + "] does not contain a valid date format specifier" );
            }

            long n = System.currentTimeMillis();
            StringBuffer buf = new StringBuffer();
            formatFileName( new Date( n ), buf );
            lastFileName = buf.toString();

            suffixLength = 0;

            if( lastFileName.endsWith( ".gz" ) )
            {
                suffixLength = 3;
            }
            else if( lastFileName.endsWith( ".zip" ) )
            {
                suffixLength = 3;
            }
        }

        public void setInsertionPos( int pos )
        {
            insertionPos = pos;
        }

        /**
         * {@inheritDoc}
         */
        public RolloverDescription initialize(

        final String currentActiveFile, final boolean append )
        {   long n = System.currentTimeMillis();
            nextCheck = ( ( n / 1000 ) + 1 ) * 1000;

            StringBuffer buf = new StringBuffer();
            buf = formatFileName( new Date( n ) );
            lastFileName = buf.toString();

            //
            //  RollingPolicyBase.activeFileName duplicates RollingFileAppender.file
            //    and should be removed.
            //
            if( activeFileName != null )
            {     return new RolloverDescriptionImpl( activeFileName, append,
null, null );
            }
            else if( currentActiveFile != null )
            {    return new RolloverDescriptionImpl( currentActiveFile, append,
null, null );
            }
            else
            {
                return new RolloverDescriptionImpl( lastFileName.substring( 0,
lastFileName.length() - suffixLength ),
                        append, null, null );
            }
        }

        protected StringBuffer formatFileName( Date d )
        {
            Format formatter = new SimpleDateFormat( getDatePattern() );
            String dateAsString = formatter.format( d );
            StringBuffer activeFile = new StringBuffer( getFile() );
            activeFile = activeFile.insert( insertionPos, dateAsString );
            return activeFile;
        }

        protected void deleteLastLogFile( String lastBaseName, String
currentActiveName )
        {
            long millis = System.currentTimeMillis() - ( 7 * 24 * 60 * 60 * 1000 );
            Date lastDate = new Date( millis );

            Format formatter = new SimpleDateFormat( getDatePattern() );
            String dateAsString = formatter.format( lastDate );

            StringBuffer activeFile = new StringBuffer( getFile() );
            lastFileName = activeFile.insert( insertionPos, dateAsString
).toString();

            boolean success = ( new File( lastFileName ) ).delete();
            if( !success )
            {
                System.out
                        .println( "[MoccaRollingFileAppender.deleteLastLogFile]
Warning, cannot delete last file: "+lastFileName+". Check log-file order!" );
            }
            else
                System.out.println(
"[MoccaRollingFileAppender.deleteLastLogFile] deletion of file \"" + lastFileName
                        + "\" successful." );

        }

        /**
         * {@inheritDoc}
         */
        public RolloverDescription rollover( final String currentActiveFile )
        {
            long n = System.currentTimeMillis();
            nextCheck = ( ( n / 1000 ) + 1 ) * 1000;

            StringBuffer buf = new StringBuffer();
            formatFileName( new Date( n ), buf );
            String newFileName = buf.toString();
            newFileName = formatFileName( new Date( n ) ).toString();

            //
            //  if file names haven't changed, no rollover
            //
            if( newFileName.equals( lastFileName ) )
            {
                return null;
            }

            Action renameAction = null;
            Action compressAction = null;
            String lastBaseName = lastFileName.substring( 0,
lastFileName.length() - suffixLength );
            String nextActiveFile = newFileName.substring( 0,
newFileName.length() - suffixLength );

            //
            //   if currentActiveFile is not lastBaseName then
            //        active file name is not following file pattern
            //        and requires a rename plus maintaining the same name
            if( !currentActiveFile.equals( lastBaseName ) )
            {
                deleteLastLogFile( lastBaseName, currentActiveFile );
                renameAction = new FileRenameAction( new File( currentActiveFile
), new File( lastBaseName ), true );
                nextActiveFile = currentActiveFile;
            }

            if( suffixLength == 3 )
            {
                compressAction = new GZCompressAction( new File( lastBaseName ),
new File( lastFileName ), true,
                        getLogger() );
            }

            if( suffixLength == 4 )
            {
                compressAction = new ZipCompressAction( new File( lastBaseName
), new File( lastFileName ), true,
                        getLogger() );
            }

            lastFileName = newFileName;

            return new RolloverDescriptionImpl( nextActiveFile, false,
renameAction, compressAction );
        }

        /**
         * {@inheritDoc}
         */
        public boolean isTriggeringEvent( final Appender appender, final
LoggingEvent event, final String filename,
                final long fileLength )
        {
            return System.currentTimeMillis() >= nextCheck;
        }
    }

}









------XML-config-File--------------
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE  log4j:configuration>

<configuration xmlns='http://logging.apache.org/' debug="true">

  <!-- Create a rolling file appender called "Mocca7DaysLogger" for the Tomcat
system log -->
  <appender name="Mocca7DaysLogger"
class="com.is_teledata.mocca.logging.server.MoccaRollingFileAppender">
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n"/>
    </layout>
   <param  name="file" value="../logs/mocca.log"/>
    <param name="fileNamePattern" value="../logs/mocca%d.log"/>
</appender>

  <!-- Configure the root appender -->
 <root>
    <appender-ref ref="Mocca7DaysLogger"/>
    <level value="info"/>
  </root>


 

 <logger name="xyz">
    <level value="debug"/>
  </logger >

</configuration>



---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-user-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-user-help@logging.apache.org