Package org.apache.juli

Source Code of org.apache.juli.FileHandler

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


package org.apache.juli;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.sql.Timestamp;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.ErrorManager;
import java.util.logging.Filter;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.SimpleFormatter;

/**
* Implementation of <b>Handler</b> that appends log messages to a file
* named {prefix}{date}{suffix} in a configured directory.
*
* <p>The following configuration properties are available:</p>
*
* <ul>
*   <li><code>directory</code> - The directory where to create the log file.
*    If the path is not absolute, it is relative to the current working
*    directory of the application. The Apache Tomcat configuration files usually
*    specify an absolute path for this property,
*    <code>${catalina.base}/logs</code>
*    Default value: <code>logs</code></li>
*   <li><code>rotatable</code> - If <code>true</code>, the log file will be
*    rotated on the first write past midnight and the filename will be
*    <code>{prefix}{date}{suffix}</code>, where date is yyyy-MM-dd. If <code>false</code>,
*    the file will not be rotated and the filename will be <code>{prefix}{suffix}</code>.
*    Default value: <code>true</code></li>
*   <li><code>prefix</code> - The leading part of the log file name.
*    Default value: <code>juli.</code></li>
*   <li><code>suffix</code> - The trailing part of the log file name. Default value: <code>.log</code></li>
*   <li><code>bufferSize</code> - Configures buffering. The value of <code>0</code>
*    uses system default buffering (typically an 8K buffer will be used). A
*    value of <code>&lt;0</code> forces a writer flush upon each log write. A
*    value <code>&gt;0</code> uses a BufferedOutputStream with the defined
*    value but note that the system default buffering will also be
*    applied. Default value: <code>-1</code></li>
*   <li><code>encoding</code> - Character set used by the log file. Default value:
*    empty string, which means to use the system default character set.</li>
*   <li><code>level</code> - The level threshold for this Handler. See the
*    <code>java.util.logging.Level</code> class for the possible levels.
*    Default value: <code>ALL</code></li>
*   <li><code>filter</code> - The <code>java.util.logging.Filter</code>
*    implementation class name for this Handler. Default value: unset</li>
*   <li><code>formatter</code> - The <code>java.util.logging.Formatter</code>
*    implementation class name for this Handler. Default value:
*    <code>java.util.logging.SimpleFormatter</code></li>
* </ul>
*
* @version $Id$
*/

public class FileHandler
    extends Handler {


    // ------------------------------------------------------------ Constructor

   
    public FileHandler() {
        this(null, null, null);
    }
   
   
    public FileHandler(String directory, String prefix, String suffix) {
        this.directory = directory;
        this.prefix = prefix;
        this.suffix = suffix;
        configure();
    }

    public FileHandler(String directory, String prefix, String suffix, boolean rotatable, int bufferSize) {
        this.directory = directory;
        this.prefix = prefix;
        this.suffix = suffix;
        this.rotatable = rotatable;
        this.bufferSize = bufferSize;
        configure();
    }

    // ----------------------------------------------------- Instance Variables


    /**
     * The directory in which log files are created.
     */
    private String directory;

    /**
     * The prefix that is added to log file filenames.
     */
    private String prefix;

    /**
     * The suffix that is added to log file filenames.
     */
    private String suffix;

    /**
     * Determines whether the logfile is rotatable
     */
    private Boolean rotatable;

    /**
     * Log buffer size.
     */
    private Integer bufferSize;

    /**
     * The as-of date for the currently open log file, or null if there is no
     * open log file.
     */
    private volatile String date = null;
   
    /**
     * The PrintWriter to which we are currently logging, if any.
     */
    private volatile PrintWriter writer = null;

    /**
     * Lock used to control access to the writer.
     */
    protected final ReadWriteLock writerLock = new ReentrantReadWriteLock();

    // --------------------------------------------------------- Public Methods


    /**
     * Format and publish a <tt>LogRecord</tt>.
     *
     * @param  record  description of the log event
     */
    @Override
    public void publish(LogRecord record) {

        if (!isLoggable(record)) {
            return;
        }

        final String tsDate = rotatable ?
            new Timestamp(System.currentTimeMillis()).toString().substring(0, 10) : "";
       
        try {
            writerLock.readLock().lock();
            // If the date has changed, switch log files
            // Construct the timestamp we will use, if requested
            // (... if not rotatable this will happen only once)
            if ( !tsDate.equals(date) ) {
                try {
                    // Update to writeLock before we switch
                    writerLock.readLock().unlock();
                    writerLock.writeLock().lock();

                    // Make sure another thread hasn't already done this
                    if ( !tsDate.equals(date) ) {
                        closeWriter();
                        date = tsDate;
                        openWriter();
                    }
                } finally {
                    writerLock.writeLock().unlock();
                    // Down grade to read-lock. This ensures the writer remains valid
                    // until the log message is written
                    writerLock.readLock().lock();
                }
            }

            String result = null;
            try {
                result = getFormatter().format(record);
            } catch (Exception e) {
                reportError(null, e, ErrorManager.FORMAT_FAILURE);
                return;
            }

            try {
                if (writer!=null) {
                    writer.write(result);
                    if (bufferSize == null || bufferSize < 0) {
                        writer.flush();
                    }
                } else {
                    reportError("FileHandler is closed or not yet initialized, unable to log ["+result+"]", null, ErrorManager.WRITE_FAILURE);
                }
            } catch (Exception e) {
                reportError(null, e, ErrorManager.WRITE_FAILURE);
                return;
            }
        } finally {
            writerLock.readLock().unlock();
        }
    }
   
   
    // -------------------------------------------------------- Private Methods


    /**
     * Close the currently open log file (if any).
     */
    @Override
    public void close() {
        closeWriter();
    }

    protected void closeWriter() {
       
        writerLock.writeLock().lock();
        try {
            if (writer == null)
                return;
            writer.write(getFormatter().getTail(this));
            writer.flush();
            writer.close();
            writer = null;
            date = null;
        } catch (Exception e) {
            reportError(null, e, ErrorManager.CLOSE_FAILURE);
        } finally {
            writerLock.writeLock().unlock();
        }
    }


    /**
     * Flush the writer.
     */
    @Override
    public void flush() {
       
        writerLock.readLock().lock();
        try {
            if (writer == null)
                return;
            writer.flush();
        } catch (Exception e) {
            reportError(null, e, ErrorManager.FLUSH_FAILURE);
        } finally {
            writerLock.readLock().unlock();
        }
       
    }
   
    /**
     * Configure from <code>LogManager</code> properties.
     */
    private void configure() {
       
        Timestamp ts = new Timestamp(System.currentTimeMillis());
        date = ts.toString().substring(0, 10);

        String className = this.getClass().getName(); //allow classes to override
       
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
       
        // Retrieve configuration of logging file name
        if (rotatable == null)
            rotatable = Boolean.parseBoolean(getProperty(className + ".rotatable", "true"));
        if (directory == null)
            directory = getProperty(className + ".directory", "logs");
        if (prefix == null)
            prefix = getProperty(className + ".prefix", "juli.");
        if (suffix == null)
            suffix = getProperty(className + ".suffix", ".log");
        if (bufferSize == null) {
            String sBufferSize = getProperty(className + ".bufferSize", null);
            try {
                bufferSize = sBufferSize != null ? Integer.parseInt(sBufferSize) : null;
            } catch (NumberFormatException ignore) { /* no op */ }
        }
        // Get encoding for the logging file
        String encoding = getProperty(className + ".encoding", null);
        if (encoding != null && encoding.length() > 0) {
            try {
                setEncoding(encoding);
            } catch (UnsupportedEncodingException ex) {
                // Ignore
            }
        }

        // Get logging level for the handler
        setLevel(Level.parse(getProperty(className + ".level", "" + Level.ALL)));

        // Get filter configuration
        String filterName = getProperty(className + ".filter", null);
        if (filterName != null) {
            try {
                setFilter((Filter) cl.loadClass(filterName).newInstance());
            } catch (Exception e) {
                // Ignore
            }
        }

        // Set formatter
        String formatterName = getProperty(className + ".formatter", null);
        if (formatterName != null) {
            try {
                setFormatter((Formatter) cl.loadClass(formatterName).newInstance());
            } catch (Exception e) {
                // Ignore and fallback to defaults
                setFormatter(new SimpleFormatter());
            }
        } else {
            setFormatter(new SimpleFormatter());
        }
       
        // Set error manager
        setErrorManager(new ErrorManager());
       
    }

   
    private String getProperty(String name, String defaultValue) {
        String value = LogManager.getLogManager().getProperty(name);
        if (value == null) {
            value = defaultValue;
        } else {
            value = value.trim();
        }
        return value;
    }
   
   
    /**
     * Open the new log file for the date specified by <code>date</code>.
     */
    public void open() {
        openWriter();
    }
   
    protected void openWriter() {
       
        // Create the directory if necessary
        final File dir = new File(directory);
        if ( !checkDir(dir) ) {
            writer = null; return;
        }
       
        // Open the current log file
        writerLock.writeLock().lock();
        String logFileName = prefix + (rotatable ? date : "") + suffix;
        try {
            File logFile = new File(dir.getAbsoluteFile(), logFileName);
            if ( !checkDir(logFile.getParentFile()) ) {
                writer = null; return;
            }
            String encoding = getEncoding();
            FileOutputStream fos = new FileOutputStream(logFile, true);
            OutputStream os = (bufferSize != null && bufferSize > 0) ?
                    new BufferedOutputStream(fos, bufferSize) : fos;
            writer = new PrintWriter(
                    (encoding != null) ? new OutputStreamWriter(os, encoding)
                                       : new OutputStreamWriter(os), false);
            writer.write(getFormatter().getHead(this));
        } catch (Exception e) {
            reportError(null, e, ErrorManager.OPEN_FAILURE);
            writer = null;
        } finally {
            writerLock.writeLock().unlock();
        }

    }

    private boolean checkDir(final File dir) {
        if ( !dir.mkdirs() && !dir.isDirectory() ) {
            reportError("Unable to create [" + dir + "]", null, ErrorManager.OPEN_FAILURE);
            return false;
        }
        return true;
    }

}
TOP

Related Classes of org.apache.juli.FileHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.