FileDocCategorySizeDatePackage
FileandSyslogHandler.javaAPI DocGlassfish v2 API21294Fri May 04 22:35:44 BST 2007com.sun.enterprise.server.logging

FileandSyslogHandler

public class FileandSyslogHandler extends StreamHandler
FileandSyslogHandler publishes formatted log Messages to a FILE.
AUTHOR:
Hemanth Puttaswamy _REVISIT_: TODO: 1. Ability to lock log files to get exclusive write access

Fields Summary
private MeteredStream
meter
private final AMXLoggingHook
mAMXLoggingHook
private Date
date
private static final String
LOGS_DIR
private String
logFileName
private String
absoluteFileName
private static boolean
isInitialized
private LogMBean
logMBean
private com.sun.enterprise.config.serverbeans.LogService
logService
private static final int
WARNING
private static final int
SEVERE
private static final String
INSTANCE_ROOT_PROPERTY
private static final String
LOGGING_MAX_HISTORY_FILES
private static final int
MINIMUM_FILE_ROTATION_VALUE
private int
limitForFileRotation
private Object
fileUpdateLock
private boolean
rotationInProgress
private static final FileandSyslogHandler
thisInstance
private LinkedList
pendingLogRecordList
private static boolean
rotationRequested
private static final String
LOG_ROTATE_DATE_FORMAT
private static final SimpleDateFormat
logRotateDateFormatter
private static Vector
_recentErrors
private static final int
MAX_RECENT_ERRORS
Constructors Summary
protected FileandSyslogHandler()
The Default constructor creates the File and sets the UniformLogFormatter.

        mAMXLoggingHook    = AMXLoggingHook.getInstance();
        try {
            setFormatter( new UniformLogFormatter( ) );
        } catch( Exception e ) {
            new ErrorManager().error( 
                "FATAL ERROR: COULD NOT INSTANTIATE FILE AND SYSLOG HANDLER", 
                e, ErrorManager.GENERIC_FAILURE );
        }
    
Methods Summary
private static synchronized voidaddRecentErrorMessage(java.lang.String error)

        if (_recentErrors.size() < MAX_RECENT_ERRORS) {
            _recentErrors.add(error);
        } else {
            _recentErrors.removeElementAt(0);
            _recentErrors.add(MAX_RECENT_ERRORS - 1, error);
        }
    
voidchangeFileName(java.lang.String fileName)
This method is invoked from LogManager.reInitializeLoggers() to change the location of the file.

        // If the file name is same as the current file name, there
        // is no need to change the filename
        if( fileName.trim().equals( absoluteFileName ) ) {
            return;
        }
        synchronized( this ) { 
            super.flush( );
            super.close();
            try {
                openFile( fileName );
                // Change the file for LogViewer
                LogFilter.setLogFile( new LogFile( fileName ) ); 
                absoluteFileName = fileName;
            } catch( IOException ix ) {
                new ErrorManager().error( 
                    "FATAL ERROR: COULD NOT OPEN LOG FILE. " +
                    "Please Check to make sure that the directory for " +
                    "Logfile exists. Currently reverting back to use the " +
                    " default server.log", ix, ErrorManager.OPEN_FAILURE );
                try {
                    // Reverting back to the old server.log
                    openFile( absoluteFileName );
                } catch( Exception e ) {
                    new ErrorManager().error( 
                        "FATAL ERROR: COULD NOT RE-OPEN SERVER LOG FILE. ", e,
                        ErrorManager.OPEN_FAILURE ); 
                }
            }
        }
    
public voidcleanUpHistoryLogFiles()
cleanup the history log file based on JVM system property "max_history_files". If it is defined with valid number, we only keep that number of history logfiles; If "max_history_files" is defined without value, then default that number to be 10; If "max_history_files" is defined with value 0, no histry log file will be keeped; and server.log is the only log file.

        String nStr = System.getProperty(LOGGING_MAX_HISTORY_FILES);
        if (nStr==null) return;

        int maxHistryFiles = 10;
        if (!"".equals(nStr)) {
            try {
                maxHistryFiles = Integer.parseInt(nStr);
            } catch (NumberFormatException e) {};
        }
        if (maxHistryFiles<0) return;

        File   dir  = new File(absoluteFileName).getParentFile();
        if (dir==null) return;

        File[] 	fset = dir.listFiles();
        ArrayList candidates = new ArrayList();
        for (int i=0; fset!=null && i<fset.length; i++) {
            if ( !logFileName.equals(fset[i].getName()) &&
                 fset[i].isFile() && 
                 fset[i].getName().startsWith(logFileName) ) {
                 candidates.add(fset[i].getAbsolutePath() );
            }
        }
        if (candidates.size() <= maxHistryFiles) return;

        Object[] pathes = candidates.toArray();
        java.util.Arrays.sort(pathes);
        try {
            for (int i=0; i<pathes.length-maxHistryFiles; i++) {
		new File((String)pathes[i]).delete();
            }
        } catch (Exception e) {
            new ErrorManager().error("FATAL ERROR: COULD NOT DELETE LOG FILE..", 
                                     e, ErrorManager.GENERIC_FAILURE );
        }
    
public static synchronized voidclearRecentErrorMessages()

        _recentErrors = new Vector();
    
protected AMXLoggingHookcreateAMXLoggingHook()
Returns the AMXLoggingHook to use for this logger.

This method is here primarily so a subclass can override it if needed.

return
AMXLoggingHook to use

        return new AMXLoggingHook();
    
public java.lang.StringcreateFileName()
Creates the File under the specified instance directory

        ServerContext sc = ApplicationServer.getServerContext();
        String instDir = "";
        if (sc != null) {
            instDir = sc.getInstanceEnvironment().getInstancesRoot();
        } else {
            instDir = System.getProperty( INSTANCE_ROOT_PROPERTY );
        }
        String[] names = {instDir, LOGS_DIR, getLogFileName() };
        // Create an absolute log filename 
        return StringUtils.makeFilePath(names, false);
    
java.lang.StringgetAbsoluteLogFileName()
A simple getter to access the complete fileName used by this FileHandler.

        return absoluteFileName;
    
public static synchronized com.sun.enterprise.server.logging.FileandSyslogHandlergetInstance()


          
        return thisInstance;
    
protected java.lang.StringgetLogFileName()

        return logFileName;
    
public static synchronized java.util.VectorgetRecentErrorMessages()

    
         
        return _recentErrors;
    
private voidopenFile(java.lang.String fileName)
Creates the file and initialized MeteredStream and passes it on to Superclass (java.util.logging.StreamHandler).

        File file = new File( fileName );
        FileOutputStream fout = new FileOutputStream( fileName, true );
        BufferedOutputStream bout = new BufferedOutputStream( fout );
        meter = new MeteredStream( bout, file.length() ); 
        setOutputStream( meter );
    
private voidplanBLogRotate(java.lang.String originalFileName, java.lang.String renamedFileName)
This method is solely provided as a plan B for Windows. On Windows the file rename fails if there are open handles to the file. We want to make sure that the log rotation succeeds in this case also. Basically we copy the contents of the file instead of renaming.

        FileOutputStream fo = null;
        FileInputStream fi = null;
        try {
            fo = new FileOutputStream( renamedFileName );
            fi = new FileInputStream( originalFileName );

            int BUF_SIZE = 4096;

            byte[] buffer = new byte[BUF_SIZE];
            int i = -1;
            do {
                i = fi.read( buffer );
                // Reached EOF. 
                if( i == -1 ) { break; }
                if( i == BUF_SIZE ) {
                    fo.write( buffer );
                } else {
                    // We have less number of bytes to read than the BUF_SIZE
                    // So, just create a temporary buffer with the exact
                    // number of bytes read and write that to the rotated
                    // file.
                    byte[] tempBuffer = new byte[i];
                    int j = 0;
                    for( j = 0; j < i; j++ ) {
                        tempBuffer[j] = buffer[j];
                    }
                    fo.write( tempBuffer );
                } 
            } while( true );
        } catch( Exception e ) {
           // If we fail in Plan B. Too bad, we should not
           // log a message here as it may lead to recursion.
        } finally {
            try {
                fo.close( );
                fi.close( );
            } catch( Exception e ) { }
        }
    
public synchronized voidpublish(java.util.logging.LogRecord record)
Publishes the logrecord using the super class and checks to see if a file rotation is required.

        // This is provided to avoid the recursion. When Log Rotation
        // is called, there is a chance that any of the called routines
        // in the Log Rotation call path may log a message, which may
        // result in a recursion. Hence, we shall log those log messages
        // after the rotation is completed, so as to avoid the recursion.
        if( rotationInProgress ) {
	    pendingLogRecordList.addLast(record);
	    return;
	}
        // Important: There are issues with double instantiation of
        // FileandSyslogHandler if we move the createFile/openFile calls
        // to the constructor. For now, we will do this for the first 
        // publish call.
        if( meter == null ) {
            try {
                absoluteFileName = createFileName( );
                openFile( absoluteFileName );
            } catch( Exception e ) {
                throw new RuntimeException(
                    "Serious Error Couldn't open Log File" + e );
            }
        }
        super.publish( record );  
        flush( );
        if ( ( rotationRequested )  
        || ( ( limitForFileRotation > 0 )
        &&  ( meter.written >= limitForFileRotation ) ) )  
        {
            rotationInProgress = true;  
            // If we have written more than the limit set for the
            // file, or rotation requested from the Timer Task or LogMBean
            // start fresh with a new file after renaming the old file.
            rotate( );
            rotationInProgress = false; 
            rotationRequested = false;
	    while (pendingLogRecordList.size() != 0) {
		publish((LogRecord) pendingLogRecordList.removeFirst());
	    }
        } 
        
        if (mAMXLoggingHook != null) {
            mAMXLoggingHook.publish( record, getFormatter() );
        }
        
        int level = record.getLevel( ).intValue();
        // The processing here is to raise alarms if
        // alarm flag in domain.xml is on and the log level is SEVERE or
        // WARNING 
        if(( level != WARNING ) && (level != SEVERE)) {
            return;
        }
        
        //Any WARNING or SEVERE message is placed onto a singleton rolling 
        // error message list. This can be queried as part of a running 
        // server's status. This allows the last N error messages to be easily 
        // viewed without having to consult the log file.
        String logMessage = getFormatter().format(record);
        addRecentErrorMessage(logMessage);
        ErrorStatistics.singleton().updateStatistics(record); // error stats.
        
        if( logService == null ) { 
            logService = ServerLogManager.getLogService( );
        }
        // _REVISIT_: Deprecate Alarms flag
        if( logService == null ) return;
       
        // Raise an alarm if the logged message is a SEVERE or WARNING
        // message.
        if( record.getLevel( ).intValue() == WARNING ) {
            LogMBean.getInstance().raiseWarningAlarm( record );
        } else if( record.getLevel( ).intValue() == SEVERE ) {
            LogMBean.getInstance().raiseSevereAlarm( record );
        }
    
voidrequestRotation()
Request Rotation called from Rotation Timer Task or LogMBean

        synchronized( this ) {
            rotationRequested = true;
        }
    
private voidrotate()
A Simple rotate method to close the old file and start the new one when the limit is reached.

        final FileandSyslogHandler thisInstance = this; 
        java.security.AccessController.doPrivileged(
            new java.security.PrivilegedAction() {
                public Object run( ) {
                    thisInstance.flush( );
                    thisInstance.close();
                    StringBuffer renamedFileName = null;
                    try {
                        File oldFile = new File( absoluteFileName );
                        renamedFileName = 
                            new StringBuffer( absoluteFileName + "_" );
                                logRotateDateFormatter.format( 
                                    new Date(), renamedFileName, 
                                    new FieldPosition( 0 ) );
                        File rotatedFile = new File( 
                            renamedFileName.toString() );
                        boolean renameSuccess = oldFile.renameTo( rotatedFile );
                        if( !renameSuccess ) {
                            // If we don't succeed with file rename which
                            // most likely can happen on Windows because
                            // of multiple file handles opened. We go through
                            // Plan B to copy bytes explicitly to a renamed 
                            // file.
                            planBLogRotate(absoluteFileName, 
                                renamedFileName.toString( ) ); 
                            String freshServerLogFileName = createFileName( );
                            // We do this to make sure that server.log
                            // contents are flushed out to start from a 
                            // clean file again after the rename..
                            FileOutputStream fo = 
                                new FileOutputStream( freshServerLogFileName );
                            fo.close( );
                        }
                        openFile( createFileName( ) );
                        LogFilter.setLogFile( new LogFile( absoluteFileName) ); 
                        // This will ensure that the log rotation timer
                        // will be restarted if there is a value set
                        // for time based log rotation
                        LogRotationTimer.getInstance( ).restartTimer( );

                        cleanUpHistoryLogFiles();
                    } catch( IOException ix ) {
                        new ErrorManager().error( 
                            "FATAL ERROR: COULD NOT OPEN LOG FILE..", ix,
                            ErrorManager.OPEN_FAILURE );
                    }
                    return null;
                }
            }
        );
    
synchronized voidsetLimitForRotation(int rotationLimitInBytes)
A package private method to set the limit for File Rotation.

        if ((rotationLimitInBytes == 0) ||
	        (rotationLimitInBytes >= MINIMUM_FILE_ROTATION_VALUE )) {
            limitForFileRotation = rotationLimitInBytes;
        }