FileDocCategorySizeDatePackage
PEAccessLogValve.javaAPI DocGlassfish v2 API35053Wed Jul 25 17:58:04 BST 2007com.sun.enterprise.web

PEAccessLogValve

public final class PEAccessLogValve extends org.apache.catalina.valves.ValveBase implements org.apache.catalina.Lifecycle, Runnable

Implementation of the Valve interface that generates a web server access log with the detailed line contents matching a configurable pattern. The syntax of the available patterns is similar to that supported by the Apache mod_log_config module. As an additional feature, automatic rollover of log files at a specified interval is also supported.

This class uses a direct ByteBuffer to store and write logs.
author
Jean-Francois Arcand
author
Charlie J. Hunt

Fields Summary
private static final Logger
_logger
private static final String
COMMON_PATTERN
private static final String
COMBINED_PATTERN
private static final String
LOGGING_MAX_HISTORY_FILES
Name of the system property whose value specifies the max number of access log history files to keep. If this property has been specified without any value, a default value of 10 is used. Else, if it has been specified with a value of 0, no access log history files will be maintained, and the current access log file will be reset after each rotation. If undefined, all access log history files will be preserved.
private static final int
MIN_BUFFER_SIZE
The minimum size a buffer can have.
private String
directory
The directory in which log files are created.
protected static final String
info
The descriptive information about this implementation.
private String
prefix
The prefix that is added to log file filenames.
private boolean
rotatable
Should we rotate our log file?
private org.apache.catalina.util.StringManager
sm
The string manager for this package.
private boolean
started
Has this component been started yet?
private String
suffix
The suffix that is added to log file filenames.
private boolean
removeLeadingDotFromSuffix
If prefix ends in '.', and suffix starts with '.', we must remove the leading '.' from suffix when prefix and suffix are concatenated.
private String
dotLessSuffix
Suffix from which leading '.' has been removed if removeLeadingDotFromSuffix is true
private SimpleDateFormat
dateFormatter
A date formatter to format a Date into a date in the format "yyyy-MM-dd".
private boolean
resolveHosts
Resolve hosts.
private long
lastAccessLogCreationTime
Instant when the log daily rotation was last checked.
private String
condition
Are we doing conditional logging. default false.
private String
fileDateFormat
Date format to place in log file name. Use at your own risk!
protected FileChannel
fileChannel
The FileChannel used to write the access log.
FileOutputStream
fos
The stream used to store the logs.
private int
writeInterval
The interval (in seconds) between writing the logs
private int
rotationInterval
The interval between rotating the logs
private Thread
writerThread
The background writerThread.
private boolean
threadDone
The background writerThread completion semaphore.
private CharBuffer
charBuffer
The CharBuffer used to store the logs.
protected int
bufferSize
The byteBuffer used to store the log.
protected boolean
flushRealTime
If the writer interval is equals to zero, then always flush the direct byte buffer after every request.
private boolean
addDateStampToFirstAccessLogFile
Are we supposed to add datestamp to first access log file we create, or only after first rotation? If set to false, the current access log file will never have any date stamp: It will be moved to a date-stamped file upon rotation
private File
logFile
The current access log file.
private int
maxHistoryFiles
The maximum number of access log history files to keep
private boolean
deleteAllHistoryFiles
True if no access log history files are to be kept, false otherwise
private LinkedList
historyFiles
List of most recent access log history files (the size of this list is not to exceed maxHistoryFiles)
private com.sun.enterprise.web.accesslog.AccessLogFormatter
formatter
The access log formatter
private Object
lock
Simple lock
Constructors Summary
Methods Summary
public voidaddLifecycleListener(org.apache.catalina.LifecycleListener listener)
Add a lifecycle event listener to this component.

param
listener The listener to add


        lifecycle.addLifecycleListener(listener);

    
private synchronized voidclose()
Close the currently open log file (if any)


        try{            
            // Make sure the byteBuffer is clean
            log();
            fileChannel.close();
            fos.close();
        } catch (IOException ex){
            ;
        }
    
booleanconfigure(java.lang.String vsId, com.sun.enterprise.config.serverbeans.VirtualServer vsBean, com.sun.enterprise.config.serverbeans.HttpService httpService, com.sun.enterprise.config.serverbeans.Domain domain, com.sun.enterprise.instance.InstanceEnvironment instance, com.sun.enterprise.server.pluggable.WebContainerFeatureFactory fac, java.lang.String globalAccessLogBufferSize, java.lang.String globalAccessLogWriteInterval)


        setPrefix(vsId + fac.getDefaultAccessLogPrefix());

        boolean start = updateVirtualServerProperties(
            vsId, vsBean, domain, instance, globalAccessLogBufferSize,
            globalAccessLogWriteInterval);
        updateAccessLogAttributes(httpService, fac);

        return start;
    
public org.apache.catalina.LifecycleListener[]findLifecycleListeners()
Get the lifecycle listeners associated with this lifecycle. If this Lifecycle has no listeners registered, a zero-length array is returned.


        return lifecycle.findLifecycleListeners();

    
public intgeRotationInterval()
Return rotation interval

        
        return rotationInterval;       
    
public intgetBufferSize()
Return the direct ByteBuffer size

        return bufferSize;
    
public java.lang.StringgetCondition()
Return whether the attribute name to look for when performing conditional loggging. If null, every request is logged.


        return condition;

    
public java.lang.StringgetDirectory()
Return the directory in which we create log files.


        return (directory);

    
public java.lang.StringgetFileDateFormat()
Return the date format date based log rotation.

        return fileDateFormat;
    
public java.lang.StringgetInfo()
Return descriptive information about this implementation.


        return (this.info);

    
public java.lang.StringgetPrefix()
Return the log file prefix.


        return (prefix);

    
public java.lang.StringgetSuffix()
Return the log file suffix.


        return (suffix);

    
public intgetWriterInterval()
Return writerThread interval (seconds)

    

             
               
        return writeInterval;       
    
public intinvoke(org.apache.catalina.Request request, org.apache.catalina.Response response)
Log a message summarizing the specified request and response, according to the format specified by the pattern property.

param
request Request being processed
param
response Response being processed
param
context The valve context used to invoke the next valve in the current processing pipeline
exception
IOException if an input/output error has occurred
exception
ServletException if a servlet error has occurred


        if (formatter.needTimeTaken()) {
            request.setNote(Constants.REQUEST_START_TIME_NOTE,
                            Long.valueOf(System.currentTimeMillis()));
        }

        return INVOKE_NEXT;
    
public booleanisResolveHosts()
Get the value of the resolve hosts flag.


        return resolveHosts;

    
public booleanisRotatable()
Should we rotate the logs


        return rotatable;

    
booleanisStarted()

return
true if this access log valve has been started, false otherwise.

        return started;
    
public voidlog()
Log the specified message to the log file, switching files if the date has changed since the previous log call.

param
message Message to be logged
param
date the current Date object (so this method doesn't need to create a new one)

        
        if (rotatable){

            long systime = System.currentTimeMillis();
            if ((systime-lastAccessLogCreationTime) > (rotationInterval*1000)) {
                synchronized (this) {
                    systime = System.currentTimeMillis();
                    if ((systime-lastAccessLogCreationTime) >
                                                    (rotationInterval*1000)) {

                        // Rotate only if the formatted datestamps are
                        // different
                        String lastDateStamp = dateFormatter.format(
                            new Date(lastAccessLogCreationTime));
                        String newDateStamp = dateFormatter.format(
                            new Date(systime));

                        lastAccessLogCreationTime = systime;

                        if (!lastDateStamp.equals(newDateStamp)) {
                            close();
                            open(newDateStamp, false);
                        }
                    }
                }
            }
        }
        
        synchronized(lock){
            try{
                charBuffer.flip();
                ByteBuffer byteBuffer =
                    ByteBuffer.wrap(charBuffer.toString().getBytes());
                while (byteBuffer.hasRemaining()){
                    fileChannel.write(byteBuffer);
                }
                charBuffer.clear();
            } catch (IOException ex){
                ;
            }
        }

    
private synchronized voidopen(java.lang.String dateStamp, boolean firstAccessLogFile)
Open new access log file.

param
dateStamp The date stamp of the new access log file (if log rotation has been enabled)
param
firstAccessLogFile true if we are creating our first access log file, and false if we have rotated

        
        // Create the directory if necessary
        File dir = new File(directory);
        if (!dir.isAbsolute())
            dir = new File(System.getProperty("catalina.base"), directory);
        dir.mkdirs();

        // Open the current log file
        try {
            String pathname;
            // If no rotate - no need for dateStamp in fileName
            if (rotatable && addDateStampToFirstAccessLogFile) {
                pathname = dir.getAbsolutePath() + File.separator +
                            prefix + dateStamp + suffix;
            } else {
                if (removeLeadingDotFromSuffix) {
                    pathname = dir.getAbsolutePath() + File.separator +
                               prefix + dotLessSuffix;
                } else {
                    pathname = dir.getAbsolutePath() + File.separator +
                               prefix + suffix;
                }
            }
            
            if (rotatable
                    && !addDateStampToFirstAccessLogFile
                    && !firstAccessLogFile) {
                // Move current access log file, which has no date stamp,
                // to date-stamped file
                String dateStampedPathname = dir.getAbsolutePath()
                                        + File.separator
                                        + prefix + dateStamp + suffix;
                File renameToFile = new File(dateStampedPathname);
                if (!logFile.renameTo(renameToFile)) {
                    _logger.log(
                        Level.WARNING,
                        "peaccesslogvalve.unableToRenameLogFile",
                        new Object[] {
                            logFile.toString(), dateStampedPathname });
                }
                File removeFile = null;
                if (deleteAllHistoryFiles) {
                    removeFile = renameToFile;
                } else {
                    if (historyFiles != null) {
                        historyFiles.addLast(renameToFile);
                        if (historyFiles.size() > maxHistoryFiles) {
                            removeFile = historyFiles.removeFirst();
                        }
                    }
                }
                if (removeFile != null && !removeFile.delete()) {
                    _logger.log(Level.WARNING,
                                "peaccesslogvalve.unableToRemoveLogFile",
                                removeFile.toString());
                }
            }

            // Open the file and then get a channel from the stream
            logFile = new File(pathname);
            fos = new FileOutputStream(logFile, true);
            fileChannel = fos.getChannel();

        } catch (IOException e) {
            try{
                if ( fileChannel != null )
                    fileChannel.close();
            } catch (IOException ex){
                ;
            }
        } 

    
public voidpostInvoke(org.apache.catalina.Request request, org.apache.catalina.Response response)


        if (!started || (condition!=null &&
                null!=request.getRequest().getAttribute(condition))) {
             return;
        }
        
        synchronized (lock){
            // Reset properly the buffer in case of an unexpected
            // exception.
            if (charBuffer.position() == charBuffer.limit()){
                charBuffer.limit(charBuffer.capacity());
            }    

            int pos = charBuffer.position();
            // We've flushed our buffer to make room for the current request.
            // Now process the current request
            for (int i=0; i < 2; i++){
                try {
                    formatter.appendLogEntry(request, response, charBuffer);
                    charBuffer.put("\n");

                    if (flushRealTime){
                        log();
                    }
                    break;
                 } catch (BufferOverflowException ex) {  
                    charBuffer.position(pos);
                    log();
                    
                    if (i+1 == 2){
                        _logger.log(
                            Level.SEVERE,
                            "peaccesslogvalve.unableToWrite",
                            new Object[] {ex});   
                        return;
                    }
                    charBuffer = CharBuffer.allocate(MIN_BUFFER_SIZE);   
                }
            }
        }
    
public voidremoveLifecycleListener(org.apache.catalina.LifecycleListener listener)
Remove a lifecycle event listener from this component.

param
listener The listener to add


        lifecycle.removeLifecycleListener(listener);

    
public voidrun()
The background writerThread that checks for write the log.


        // Loop until the termination semaphore is set
        while (!threadDone) {
            threadSleep();
            log();
        }

    
public voidsetAddDateStampToFirstAccessLogFile(boolean add)
Are we supposed to add datestamp to first access log file we create, or only starting with first rotation?

        this.addDateStampToFirstAccessLogFile = add;
    
public voidsetBufferSize(int size)
Set the direct ByteBuffer size

        if ( size > 0 ){
            flushRealTime = false;
        }        
        bufferSize = size;
    
public voidsetCondition(java.lang.String condition)
Set the ServletRequest.attribute to look for to perform conditional logging. Set to null to log everything.

param
condition Set to null to log everything


        this.condition = condition;

    
public voidsetDirectory(java.lang.String directory)
Set the directory in which we create log files.

param
directory The new log file directory

        this.directory = directory;

    
public voidsetFileDateFormat(java.lang.String fileDateFormat)
Set the date format date based log rotation.

        this.fileDateFormat =  fileDateFormat;
    
public voidsetPattern(java.lang.String p)
Set the format pattern, first translating any recognized alias.

param
p The new pattern

        if (COMMON_PATTERN.equalsIgnoreCase(p)) {
            formatter = new CommonAccessLogFormatterImpl();
        } else if (COMBINED_PATTERN.equalsIgnoreCase(p)) {
            formatter = new CombinedAccessLogFormatterImpl();
        } else {
            formatter = new DefaultAccessLogFormatterImpl(p, getContainer());
        }
    
public voidsetPrefix(java.lang.String p)
Set the log file prefix.

param
prefix The new log file prefix


        prefix = p;

        if (prefix != null && suffix != null && prefix.endsWith(".")
                && suffix.startsWith(".")) {
            removeLeadingDotFromSuffix = true;
            dotLessSuffix = suffix.substring(1);
        } else {
            removeLeadingDotFromSuffix = false;
        }
    
public voidsetResolveHosts(boolean resolveHosts)
Set the resolve hosts flag.

param
resolveHosts The new resolve hosts value


        this.resolveHosts = resolveHosts;

    
public voidsetRotatable(boolean rotatable)
Set the value is we should we rotate the logs

param
rotatable true is we should rotate.


        this.rotatable = rotatable;

    
public voidsetRotationInterval(int t)
Set rotation interval

        rotationInterval = t;
    
public voidsetSuffix(java.lang.String s)
Set the log file suffix.

param
suffix The new log file suffix


        suffix = s;

        if (prefix != null && suffix != null && prefix.endsWith(".")
                && suffix.startsWith(".")) {
            removeLeadingDotFromSuffix = true;
            dotLessSuffix = suffix.substring(1);
        } else {
            removeLeadingDotFromSuffix = false;
        }
    
public voidsetWriterInterval(int t)
Set writerthread interval (seconds)

        if ( t > 0 ){
            flushRealTime = false;
        }
        writeInterval = t;
    
public voidstart()
Prepare for the beginning of active use of the public methods of this component. This method should be called after configure(), and before any of the public methods of the component are utilized.

exception
LifecycleException if this component detects a fatal error that prevents this component from being used


        // Validate and update our current component state
        if (started) {
            throw new LifecycleException
                (sm.getString("accessLogValve.alreadyStarted"));
        }

        lifecycle.fireLifecycleEvent(START_EVENT, null);

        deleteAllHistoryFiles = false;
        historyFiles = null;
        String prop = System.getProperty(LOGGING_MAX_HISTORY_FILES);
        if (prop != null) {
            maxHistoryFiles = 10;
            if (!"".equals(prop)) {
                try {
                    maxHistoryFiles = Integer.parseInt(prop);
                } catch (NumberFormatException e) {};
            }
	    if (maxHistoryFiles == 0) {
                deleteAllHistoryFiles = true;
            } else if (maxHistoryFiles > 0) {
                historyFiles = new LinkedList<File>();
            }
        }

        if (bufferSize <= 0) {
            bufferSize = MIN_BUFFER_SIZE;
        }

        charBuffer = CharBuffer.allocate(bufferSize);

        // Initialize the timeZone, Date formatters, and currentDate
        TimeZone tz = TimeZone.getDefault();

        if (fileDateFormat==null || fileDateFormat.length()==0)
            fileDateFormat = "yyyy-MM-dd";
        dateFormatter = new SimpleDateFormat(fileDateFormat);
        dateFormatter.setTimeZone(tz);

        long systime = System.currentTimeMillis();
        open(dateFormatter.format(new Date(systime)), true);
        lastAccessLogCreationTime = systime;

        if (!flushRealTime){
            // Start the background writer writerThread
            threadStart();
        }
        started = true;
   
public voidstop()
Gracefully terminate the active use of the public methods of this component. This method should be the last one called on a given instance of this component.

exception
LifecycleException if this component detects a fatal error that needs to be reported


        // Validate and update our current component state
        if (!started)
            throw new LifecycleException
                (sm.getString("accessLogValve.notStarted"));
        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
        started = false;
        
        if (!flushRealTime){
            // Stop the background writer thread
            threadStop();
        }
        
        close();

    
private voidthreadSleep()
Sleep for the duration specified by the writeInterval property.

        
        if (writerThread != null || writeInterval == 0)
            return;
        
        try {
            writerThread.sleep(writeInterval * 1000L);
        } catch (InterruptedException e) {
            ;
        }

    
private voidthreadStart()
Start the background writerThread that will periodically write access log


        if (writerThread != null || writeInterval == 0)
            return;

        threadDone = false;
        String threadName = "AccessLogWriter";
        writerThread = new Thread(this, threadName);
        writerThread.setDaemon(true);
        writerThread.start();

    
private voidthreadStop()
Stop the background writerThread that is periodically write logs


        if (writerThread == null || writeInterval == 0)
            return;

        threadDone = true;
        writerThread.interrupt();
        try {
            writerThread.join();
        } catch (InterruptedException e) {
            ;
        }

        writerThread = null;

    
voidupdateAccessLogAttributes(com.sun.enterprise.config.serverbeans.HttpService httpService, com.sun.enterprise.server.pluggable.WebContainerFeatureFactory fac)
Configures this accesslog valve with the accesslog related attributes of the domain.xml's and elements.


        HttpProtocol httpProtocol = httpService.getHttpProtocol();
        if (httpProtocol != null) {
            setResolveHosts(httpProtocol.isDnsLookupEnabled());
        } else {
            setResolveHosts(false);
        }

        AccessLog accessLogConfig = httpService.getAccessLog();

        // access-log format
        String format = null;
        if (accessLogConfig != null) {
            format = accessLogConfig.getFormat();
        } else {
	    format = AccessLog.getDefaultFormat();
        }
        setPattern(format);
                       
        // rotation-enabled
        if (accessLogConfig != null) {
            setRotatable(accessLogConfig.isRotationEnabled());
        } else {
	    setRotatable(Boolean.valueOf(
                AccessLog.getDefaultRotationEnabled()).booleanValue());
        }

        // rotation-interval
        int rotationInterval = 0;
        if (accessLogConfig != null) {
            String s = accessLogConfig.getRotationIntervalInMinutes();
            rotationInterval = Integer.parseInt(s) * 60;
        } else {
            rotationInterval = fac.getDefaultRotationIntervalInMinutes() * 60;
        }
        setRotationInterval(rotationInterval);

        // rotation-datestamp
        String rotationDateStamp = null;
        if (accessLogConfig != null) {
            rotationDateStamp = accessLogConfig.getRotationSuffix();
        } else {
            rotationDateStamp = fac.getDefaultAccessLogDateStampPattern();
        }
        if ("%YYYY;%MM;%DD;-%hh;h%mm;m%ss;s".equals(rotationDateStamp)) {
            /*
             * Modify the default rotation suffix pattern specified in the
             * sun-domain DTD in such a way that it is accepted by
             * java.text.SimpleDateFormat. We support only those patterns
             * accepted by java.text.SimpleDateFormat.
             */
            rotationDateStamp = "yyyyMMdd-HH'h'mm'm'ss's'";
        }
        setFileDateFormat(rotationDateStamp);

        // rotation-suffix
        setSuffix(fac.getDefaultAccessLogSuffix());

        setAddDateStampToFirstAccessLogFile(
            fac.getAddDateStampToFirstAccessLogFile());
    
booleanupdateVirtualServerProperties(java.lang.String vsId, com.sun.enterprise.config.serverbeans.VirtualServer vsBean, com.sun.enterprise.config.serverbeans.Domain domain, com.sun.enterprise.instance.InstanceEnvironment instance, java.lang.String globalAccessLogBufferSize, java.lang.String globalAccessLogWriteInterval)
Configures this accesslog valve with the accesslog related properties of the given bean.


        /*
         * Determine the virtual server's access log directory, which may be
         * specified in two places:
         *
         * 1.  <virtual-server>
         *       <http-access-log log-directory="..."/>
         *     </virtual-server>
         *
         * 2.  <virtual-server>
         *       <property name="accesslog" value="..."/>
         *     </virtual-server>
         *
         * If both have been specified, the latter takes precedence.
         */
        String accessLog = "access";
        if (vsBean.getElementPropertyByName(
                    Constants.ACCESS_LOG_PROPERTY) != null) {
            accessLog = vsBean.getElementPropertyByName(
                Constants.ACCESS_LOG_PROPERTY).getValue();
        } else if (vsBean.getHttpAccessLog() != null) {
            accessLog = vsBean.getHttpAccessLog().getLogDirectory();
        }
        if (accessLog == null) {
            return false;
        }

        File dir = new File(accessLog);
        if (!dir.isAbsolute()) {
            /*
             * If accesslog is relative, turn it into an absolute path by
             * prepending log-root of domain element
             */
            String logRoot = domain.getLogRoot();
            if (logRoot != null) {
                dir = new File(logRoot, accessLog);
            } else {
                dir = new File(instance.getInstancesRoot(), accessLog);
            }
        }
            
        if (_logger.isLoggable(Level.FINE)) {
            _logger.log(Level.FINE,
                        "Setting accesslog directory for virtual "
                        + "server '" + vsId + "' to "
                        + dir.getAbsolutePath());
        }

        setDirectory(dir.getAbsolutePath());

        // If the property is defined under virtual-server, override the one
        // defined under http-service.
        String acWriteInterval = globalAccessLogWriteInterval;
        if (vsBean.getElementPropertyByName(
                Constants.ACCESS_LOG_WRITE_INTERVAL_PROPERTY) != null){
            acWriteInterval = vsBean.getElementPropertyByName(
                Constants.ACCESS_LOG_WRITE_INTERVAL_PROPERTY).getValue();
        }
        if (acWriteInterval != null){
            try{
                setWriterInterval(Integer.parseInt(acWriteInterval));
            } catch (NumberFormatException ex){
                _logger.log(Level.WARNING,
                    "pewebcontainer.invalid_accessLog_writerInterval",
                    acWriteInterval);
            }
        }
         
        // If the property is defined under virtual-server, override the one
        // defined under http-service.
        String acBufferSize = globalAccessLogBufferSize;
        if (vsBean.getElementPropertyByName(
                Constants.ACCESS_LOG_BUFFER_SIZE_PROPERTY) != null){
            acBufferSize =  vsBean.getElementPropertyByName(
                Constants.ACCESS_LOG_BUFFER_SIZE_PROPERTY).getValue();
        }
        if (acBufferSize != null){
            try{
                setBufferSize(Integer.parseInt(acBufferSize));
            } catch (NumberFormatException ex){
                _logger.log(Level.WARNING,
                    "pewebcontainer.invalid_accessLog_bufferSize",
                    acBufferSize);
            }
        }

        return true;