All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.enterprise.server.logging.GFFileHandler Maven / Gradle / Ivy

There is a newer version: 4.1.2.181
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2009-2013 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
// Portions Copyright [2016] [Payara Foundation and/or its affiliates]

package com.sun.enterprise.server.logging;

import com.sun.common.util.logging.GFLogRecord;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.text.FieldPosition;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.ResourceBundle;
import java.util.Vector;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.ErrorManager;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.StreamHandler;

import javax.inject.Inject;
import javax.inject.Singleton;

import org.glassfish.api.logging.Task;
import org.glassfish.config.support.TranslatedConfigView;
import org.glassfish.hk2.api.PostConstruct;
import org.glassfish.hk2.api.PreDestroy;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.internal.api.ServerContext;
import org.glassfish.server.ServerEnvironmentImpl;
import org.jvnet.hk2.annotations.ContractsProvided;
import org.jvnet.hk2.annotations.Optional;
import org.jvnet.hk2.annotations.Service;

import com.sun.appserv.server.util.Version;
import com.sun.common.util.logging.BooleanLatch;
import com.sun.enterprise.admin.monitor.callflow.Agent;
import com.sun.enterprise.module.bootstrap.EarlyLogHandler;
import com.sun.enterprise.util.LocalStringManagerImpl;
import com.sun.enterprise.util.io.FileUtils;
import com.sun.enterprise.v3.logging.AgentFormatterDelegate;
import java.io.FileInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * GFFileHandler publishes formatted log Messages to a FILE.
 *
 * @AUTHOR: Jerome Dochez
 * @AUTHOR: Carla Mott
 */
@Service
@Singleton
@ContractsProvided({GFFileHandler.class, java.util.logging.Handler.class, 
    LogEventBroadcaster.class, LoggingRuntime.class})
public class GFFileHandler extends StreamHandler implements 
PostConstruct, PreDestroy, LogEventBroadcaster, LoggingRuntime {

    private static final int DEFAULT_ROTATION_LIMIT_BYTES = 2000000;
    public static final int DISABLE_LOG_FILE_ROTATION_VALUE = 0;

    private final static LocalStringManagerImpl LOCAL_STRINGS = 
        new LocalStringManagerImpl(GFFileHandler.class);
    
    @Inject
    ServerContext serverContext;

    @Inject
    ServerEnvironmentImpl env;

    @Inject @Optional
    Agent agent;
    
    @Inject
    private ServiceLocator habitat;
    
    // This is a OutputStream to keep track of number of bytes
    // written out to the stream
    private MeteredStream meter;

    private static final String LOGS_DIR = "logs";
    private static final String LOG_FILE_NAME = "server.log";
    
    private static final String GZIP_EXTENSION = ".gz";

    private String absoluteServerLogName = null;

    private File absoluteFile = null;

    private int flushFrequency = 1;

    private int maxHistoryFiles = 10;


    private String gffileHandlerFormatter = "";
    private String currentgffileHandlerFormatter = "";

    // Initially the LogRotation will be off until the domain.xml value is read.
    private int limitForFileRotation = 0;

    private BlockingQueue pendingRecords = new ArrayBlockingQueue(5000);

    // Rotation can be done in 3 ways
    // 1. Based on the Size: Rotate when some Threshold number of bytes are 
    //    written to server.log
    // 2. Based on the Time: Rotate ever 'n' minutes, mostly 24 hrs
    // 3. Rotate now
    // For mechanisms 2 and 3 we will use this flag. The rotate() will always
    // be fired from the publish( ) method for consistency
    private AtomicBoolean rotationRequested = new AtomicBoolean(false);
    
    private Object rotationLock = new Object();

    private static final String LOG_ROTATE_DATE_FORMAT =
            "yyyy-MM-dd'T'HH-mm-ss";

    private static final SimpleDateFormat logRotateDateFormatter =
            new SimpleDateFormat(LOG_ROTATE_DATE_FORMAT);

    private static final String DEFAULT_LOG_FILE_FORMATTER_CLASS_NAME = UniformLogFormatter.class.getName();

    public static final int MINIMUM_ROTATION_LIMIT_VALUE = 500*1000;

    private BooleanLatch done = new BooleanLatch();
    private Thread pump;

    boolean dayBasedFileRotation = false;
    boolean compressLogs = false;

    private String RECORD_BEGIN_MARKER = "[#|";
    private String RECORD_END_MARKER = "|#]";
    private String RECORD_FIELD_SEPARATOR = "|";
    private String RECORD_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
    
    private List logEventListeners = new ArrayList();

    String recordBeginMarker;
    String recordEndMarker;
    String recordFieldSeparator;
    String recordDateFormat;

    String logFileProperty = "";

    public void postConstruct() {

        LogManager manager = LogManager.getLogManager();
        String cname = getClass().getName();

        logFileProperty = manager.getProperty(cname + ".file");
        if(logFileProperty==null || logFileProperty.trim().equals("")) {
            logFileProperty = env.getInstanceRoot().getAbsolutePath() + File.separator + LOGS_DIR + File.separator +
                    LOG_FILE_NAME;
        }

        String filename = TranslatedConfigView.getTranslatedValue(logFileProperty).toString();

        File serverLog = new File(filename);
        absoluteServerLogName = filename;
        if (!serverLog.isAbsolute()) {
            serverLog = new File(env.getDomainRoot(), filename);
            absoluteServerLogName = env.getDomainRoot() + File.separator + filename;
        }
        changeFileName(serverLog);

        // Reading just few lines of log file to get the log fomatter used.
        BufferedReader br = null;
        String strLine = "";
        int odlFormatter = 0;
        int uniformLogFormatter = 0;
        int otherFormatter = 0;
        boolean mustRotate = false;
        
        try {
            br = new BufferedReader(new FileReader(serverLog));            
            while ((strLine = br.readLine()) != null) {
                strLine = strLine.trim();
                if (!strLine.equals("")) {
                    if (LogFormatHelper.isUniformFormatLogHeader(strLine)) {  // for ufl formatter
                        uniformLogFormatter++;
                    } else if (LogFormatHelper.isODLFormatLogHeader(strLine)) { 
                        // for ODL formatter
                        odlFormatter++;
                    } else {
                        otherFormatter++;  // for other formatter
                    }

                    // Rotate on startup for custom log files
                    if (otherFormatter > 0) {
                        mustRotate = true;
                    }
                    // Read only first log record line and break out of the loop
                    break;
                }
            }
        } catch (Exception e) {
            ErrorManager em = getErrorManager();
            if (em != null) {
                em.error(e.getMessage(), e, ErrorManager.GENERIC_FAILURE);
            }
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch(Exception e) {}
            }
        }

        if (odlFormatter > 0) {
            currentgffileHandlerFormatter = "com.sun.enterprise.server.logging.ODLLogFormatter";
        } else if (uniformLogFormatter > 0) {
            currentgffileHandlerFormatter = "com.sun.enterprise.server.logging.UniformLogFormatter";
        }

        // start the Queue consumer thread.
        initializePump();
        
        LogRecord lr = new LogRecord(Level.INFO, LogFacade.GF_VERSION_INFO);
        lr.setParameters(new Object[]{ Version.getFullVersion()});        
        lr.setResourceBundle(ResourceBundle.getBundle(LogFacade.LOGGING_RB_NAME));
        lr.setThreadID((int) Thread.currentThread().getId());
        lr.setLoggerName(LogFacade.LOGGING_LOGGER_NAME);
        EarlyLogHandler.earlyMessages.add(lr);

        String propValue = null;
        propValue = manager.getProperty(cname + ".rotationOnDateChange");
        boolean rotationOnDateChange = false;
        if (propValue != null) {
            rotationOnDateChange = Boolean.parseBoolean(propValue);
        }
        if (rotationOnDateChange) {

            dayBasedFileRotation = true;

            Long rotationTimeLimitValue = 0L;

            int MILLIS_IN_DAY = 1000 * 60 * 60 * 24;
            Date date = new Date();
            SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yy");

            long systime = System.currentTimeMillis();
            String nextDate = dateFormat.format(date.getTime() + MILLIS_IN_DAY);
            Date nextDay = null;
            try {
                nextDay = dateFormat.parse(nextDate);
            } catch (ParseException e) {
                nextDay = new Date();
                lr = new LogRecord(Level.WARNING, LogFacade.DATE_PARSING_FAILED);
                lr.setParameters(new Object[]{nextDate});
                lr.setResourceBundle(ResourceBundle.getBundle(LogFacade.LOGGING_RB_NAME));
                lr.setThreadID((int) Thread.currentThread().getId());
                lr.setLoggerName(LogFacade.LOGGING_LOGGER_NAME);
                EarlyLogHandler.earlyMessages.add(lr);
            }
            long nextsystime = nextDay.getTime();

            rotationTimeLimitValue = nextsystime - systime;

            Task rotationTask = new Task() {
                public Object run() {
                    rotate();
                    return null;
                }
            };

            LogRotationTimer.getInstance().startTimer(
                    new LogRotationTimerTask(rotationTask,
                            rotationTimeLimitValue / 60000));

        } else {

            Long rotationTimeLimitValue = 0L;
            try {
                propValue = manager.getProperty(cname + ".rotationTimelimitInMinutes");
                if (propValue != null) {
                    rotationTimeLimitValue = Long.parseLong(propValue);                    
                }
            } catch (NumberFormatException e) {
                lr = new LogRecord(Level.WARNING, LogFacade.INVALID_ATTRIBUTE_VALUE);
                lr.setParameters(new Object[]{propValue, "rotationTimelimitInMinutes"});
                lr.setResourceBundle(ResourceBundle.getBundle(LogFacade.LOGGING_RB_NAME));
                lr.setThreadID((int) Thread.currentThread().getId());
                lr.setLoggerName(LogFacade.LOGGING_LOGGER_NAME);
                EarlyLogHandler.earlyMessages.add(lr);
            }

            if (rotationTimeLimitValue > 0) {

                Task rotationTask = new Task() {
                    public Object run() {
                        rotate();
                        return null;
                    }
                };

                LogRotationTimer.getInstance().startTimer(
                        new LogRotationTimerTask(rotationTask,
                                rotationTimeLimitValue));
            } 
        }

        // Also honor the size based rotation if configured.
        Integer rotationLimitAttrValue = DEFAULT_ROTATION_LIMIT_BYTES;
        try {
            propValue = manager.getProperty(cname + ".rotationLimitInBytes");
            if (propValue != null) {
                rotationLimitAttrValue = Integer.parseInt(propValue);
            }
        } catch (NumberFormatException e) {
            lr = new LogRecord(Level.WARNING, LogFacade.INVALID_ATTRIBUTE_VALUE);
            lr.setParameters(new Object[]{propValue, "rotationLimitInBytes"});
            lr.setResourceBundle(ResourceBundle.getBundle(LogFacade.LOGGING_RB_NAME));
            lr.setThreadID((int) Thread.currentThread().getId());
            lr.setLoggerName(LogFacade.LOGGING_LOGGER_NAME);
            EarlyLogHandler.earlyMessages.add(lr);
        }
        // We set the LogRotation limit here. The rotation limit is the
        // Threshold for the number of bytes in the log file after which
        // it will be rotated.
        if (rotationLimitAttrValue >= MINIMUM_ROTATION_LIMIT_VALUE || rotationLimitAttrValue == DISABLE_LOG_FILE_ROTATION_VALUE ) {
            setLimitForRotation(rotationLimitAttrValue);        
        }

        //setLevel(Level.ALL);
        propValue = manager.getProperty(cname + ".flushFrequency");
        if (propValue != null) {
            try {
                flushFrequency = Integer.parseInt(propValue);
            } catch (NumberFormatException e) {
                lr = new LogRecord(Level.WARNING, LogFacade.INVALID_ATTRIBUTE_VALUE);
                lr.setParameters(new Object[]{propValue, "flushFrequency"});
                lr.setResourceBundle(ResourceBundle.getBundle(LogFacade.LOGGING_RB_NAME));
                lr.setThreadID((int) Thread.currentThread().getId());
                lr.setLoggerName(LogFacade.LOGGING_LOGGER_NAME);
                EarlyLogHandler.earlyMessages.add(lr);
            }
        }
        if (flushFrequency <= 0)
            flushFrequency = 1;

        propValue = manager.getProperty(cname + ".maxHistoryFiles");
        try {
            if (propValue != null) {
                maxHistoryFiles = Integer.parseInt(propValue);
            }
        } catch (NumberFormatException e) {
            lr = new LogRecord(Level.WARNING, LogFacade.INVALID_ATTRIBUTE_VALUE);
            lr.setParameters(new Object[]{propValue, "maxHistoryFiles"});
            lr.setResourceBundle(ResourceBundle.getBundle(LogFacade.LOGGING_RB_NAME));
            lr.setThreadID((int) Thread.currentThread().getId());
            lr.setLoggerName(LogFacade.LOGGING_LOGGER_NAME);
            EarlyLogHandler.earlyMessages.add(lr);
        }

        if (maxHistoryFiles < 0)
            maxHistoryFiles = 10;

        propValue = manager.getProperty(cname + ".compressOnRotation");
        boolean compressionOnRotation = false;
        if (propValue != null) {
            compressionOnRotation = Boolean.parseBoolean(propValue);
        }
        if (compressionOnRotation) {
            compressLogs = true;
        }

        String formatterName = manager.getProperty(cname + ".formatter");
        formatterName = (formatterName == null) ? DEFAULT_LOG_FILE_FORMATTER_CLASS_NAME : formatterName; 
        
        // Below snapshot of the code is used to rotate server.log file on startup. It is used to avoid different format
        // log messages logged under same server.log file.
        gffileHandlerFormatter = formatterName;
        if (mustRotate) {
            rotate();
        } else if (gffileHandlerFormatter != null
                && !gffileHandlerFormatter
                        .equals(currentgffileHandlerFormatter)) {
            rotate();
        }
        
        String excludeFields = manager.getProperty(LogManagerService.EXCLUDE_FIELDS_PROPERTY);
        boolean multiLineMode = Boolean.parseBoolean(manager.getProperty(LogManagerService.MULTI_LINE_MODE_PROPERTY));
                
        if (UniformLogFormatter.class.getName().equals(formatterName)) {
            configureUniformLogFormatter(excludeFields, multiLineMode);
        } else if (ODLLogFormatter.class.getName().equals(formatterName)) {
            configureODLFormatter(excludeFields, multiLineMode);
        } else {
            // Custom formatter is configured in logging.properties
            // Check if the user specified formatter is in play else
            // log an error message
            Formatter currentFormatter = this.getFormatter();
            if (currentFormatter == null || !currentFormatter.getClass().getName().equals(formatterName)) {
                Formatter formatter = findFormatterService(formatterName);
                if (formatter == null) {
                    lr = new LogRecord(Level.SEVERE, LogFacade.INVALID_FORMATTER_CLASS_NAME);
                    lr.setParameters(new Object[]{formatterName});
                    lr.setThreadID((int) Thread.currentThread().getId());
                    lr.setResourceBundle(ResourceBundle.getBundle(LogFacade.LOGGING_RB_NAME));
                    lr.setLoggerName(LogFacade.LOGGING_LOGGER_NAME);
                    EarlyLogHandler.earlyMessages.add(lr);
                    // Fall back to the GlassFish default
                    configureDefaultFormatter(excludeFields, multiLineMode);                                    
                } else {
                    setFormatter(formatter);
                }
            }
        }
        
        formatterName = this.getFormatter().getClass().getName();
        lr = new LogRecord(Level.INFO, LogFacade.LOG_FORMATTER_INFO);        
        lr .setParameters(new Object[] {formatterName});
        lr.setResourceBundle(ResourceBundle.getBundle(LogFacade.LOGGING_RB_NAME));
        lr.setThreadID((int) Thread.currentThread().getId());
        lr.setLoggerName(LogFacade.LOGGING_LOGGER_NAME);
        EarlyLogHandler.earlyMessages.add(lr);
    }

    Formatter findFormatterService(String formatterName) {
        List formatterServices = habitat.getAllServices(Formatter.class);
        for (Formatter formatter : formatterServices) {
            if (formatter.getClass().getName().equals(formatterName)) {
                return formatter;
            }
        }
        return null;
    }

    private void configureDefaultFormatter(String excludeFields,
            boolean multiLineMode) {
        configureUniformLogFormatter(excludeFields, multiLineMode);
    }

    private void configureODLFormatter(String excludeFields, boolean multiLineMode) {
        // this loop is used for ODL formatter
        ODLLogFormatter formatterClass = null;
        // set the formatter
        if (agent != null) {
            formatterClass = new ODLLogFormatter(new AgentFormatterDelegate(agent));
            setFormatter(formatterClass);
        } else {
            formatterClass = new ODLLogFormatter();
            setFormatter(formatterClass);
        }
        formatterClass.setExcludeFields(excludeFields);
        formatterClass.setMultiLineMode(multiLineMode);
        formatterClass.setLogEventBroadcaster(this);        
    }

    private void configureUniformLogFormatter(String excludeFields, boolean multiLineMode) {
        LogManager manager = LogManager.getLogManager();
        String cname = getClass().getName();
        // this loop is used for UFL formatter
        UniformLogFormatter formatterClass = null;
        // set the formatter
        if (agent != null) {
            formatterClass = new UniformLogFormatter(new AgentFormatterDelegate(agent));
            setFormatter(formatterClass);
        } else {
            formatterClass = new UniformLogFormatter();
            setFormatter(formatterClass);
        }

        formatterClass.setExcludeFields(excludeFields);
        formatterClass.setMultiLineMode(multiLineMode);
        formatterClass.setLogEventBroadcaster(this);

        if (formatterClass != null) {
            recordBeginMarker = manager.getProperty(cname + ".logFormatBeginMarker");
            if (recordBeginMarker == null || ("").equals(recordBeginMarker)) {
                recordBeginMarker = RECORD_BEGIN_MARKER;
            }

            recordEndMarker = manager.getProperty(cname + ".logFormatEndMarker");
            if (recordEndMarker == null || ("").equals(recordEndMarker)) {
                recordEndMarker = RECORD_END_MARKER;
            }

            recordFieldSeparator = manager.getProperty(cname + ".logFormatFieldSeparator");
            if (recordFieldSeparator == null || ("").equals(recordFieldSeparator) || recordFieldSeparator.length() > 1) {
                recordFieldSeparator = RECORD_FIELD_SEPARATOR;
            }

            recordDateFormat = manager.getProperty(cname + ".logFormatDateFormat");
            if (recordDateFormat != null && !("").equals(recordDateFormat)) {
                SimpleDateFormat sdf = new SimpleDateFormat(recordDateFormat);
                try {
                    sdf.format(new Date());
                } catch (Exception e) {
                    recordDateFormat = RECORD_DATE_FORMAT;
                }
            } else {
                recordDateFormat = RECORD_DATE_FORMAT;
            }

            formatterClass.setRecordBeginMarker(recordBeginMarker);
            formatterClass.setRecordEndMarker(recordEndMarker);
            formatterClass.setRecordDateFormat(recordDateFormat);
            formatterClass.setRecordFieldSeparator(recordFieldSeparator);
        }        
    }
    
    void initializePump() {
        pump = new Thread() {
            public void run() {
                while (!done.isSignalled()) {
                    try {
                        log();
                    } catch (Exception e) {
                        // GLASSFISH-19125
                        // Continue the loop without exiting
                    }
                }
            }
        };
        pump.setName("GFFileHandler log pump");
        pump.setDaemon(true);
        pump.start();        
    }

    public void preDestroy() {
        // stop the Queue consummer thread.
        if (LogFacade.LOGGING_LOGGER.isLoggable(Level.FINE)) {
            LogFacade.LOGGING_LOGGER.fine("Logger handler killed");            
        }
        done.tryReleaseShared(1);
        pump.interrupt();

        // drain and return
        final int size = pendingRecords.size();
        if (size > 0) {
            Collection records = new ArrayList(size);
            pendingRecords.drainTo(records, size);
            for (LogRecord record : records) {
                super.publish(record);
            }
        }

    }

    /**
     * This method is invoked from LogManager.reInitializeLoggers() to
     * change the location of the file.
     */
    void changeFileName(File file) {
        // If the file name is same as the current file name, there
        // is no need to change the filename
        if (file.equals(absoluteFile)) {
            return;
        }
        synchronized (rotationLock) {
            super.flush();
            super.close();
            try {
                openFile(file);
                absoluteFile = file;
            } 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(absoluteFile);
                } catch (Exception e) {
                    new ErrorManager().error(
                            "FATAL ERROR: COULD NOT RE-OPEN SERVER LOG FILE. ", e,
                            ErrorManager.OPEN_FAILURE);
                }
            }
        }
    }


    /**
     * A simple getter to access the current log file written by
     * this FileHandler.
     */
    public File getCurrentLogFile() {
        return absoluteFile;
    }
    
    /**
     * A package private method to set the limit for File Rotation.
     */
    private synchronized void setLimitForRotation(int rotationLimitInBytes) {
        limitForFileRotation = rotationLimitInBytes;
    }

    // NOTE: This private class is copied from java.util.logging.FileHandler
    // A metered stream is a subclass of OutputStream that
    //   (a) forwards all its output to a target stream
    //   (b) keeps track of how many bytes have been written

    private static final class MeteredStream extends OutputStream {

        private volatile boolean isOpen = false;

        OutputStream out;
        long written;

        MeteredStream(OutputStream out, long written) {
            this.out = out;
            this.written = written;
            isOpen = true;
        }

        public void write(int b) throws IOException {
            out.write(b);
            written++;
        }

        public void write(byte buff[]) throws IOException {
            out.write(buff);
            written += buff.length;
        }

        public void write(byte buff[], int off, int len) throws IOException {
            out.write(buff, off, len);
            written += len;
        }

        public void flush() throws IOException {
            out.flush();
        }

        public void close() throws IOException {
            if (isOpen) {
                isOpen = false;
                flush();
                out.close();
            }

        }
    }

    /**
     * Creates the file and initialized MeteredStream and passes it on to
     * Superclass (java.util.logging.StreamHandler).
     */
    private void openFile(File file) throws IOException {
        // check that the parent directory exists.
        File parent = file.getParentFile();
        if (!parent.exists() && !parent.mkdirs()) {
            throw new IOException(LOCAL_STRINGS.getLocalString("parent.dir.create.failed", 
                    "Failed to create the parent dir {0}", parent.getAbsolutePath()));
        }
        FileOutputStream fout = new FileOutputStream(file, true);
        BufferedOutputStream bout = new BufferedOutputStream(fout);
        meter = new MeteredStream(bout, file.length());
        setOutputStream(meter);        
    }

    /**
     * Request Rotation called from Rotation Timer Task or LogMBean
     */
    void requestRotation() {
        rotationRequested.set(true);
    }

    /**
     * cleanup the history log file based on attributes set under logging.properties file".
     * 

* 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, any number of history files are kept. */ public void cleanUpHistoryLogFiles() { if (maxHistoryFiles == 0) return; synchronized (rotationLock) { File dir = absoluteFile.getParentFile(); if (dir == null) return; File[] fset = dir.listFiles(); ArrayList candidates = new ArrayList(); for (int i = 0; fset != null && i < fset.length; i++) { if (!LOG_FILE_NAME.equals(fset[i].getName()) && fset[i].isFile() && fset[i].getName().startsWith(LOG_FILE_NAME)) { candidates.add(fset[i].getAbsolutePath()); } } if (candidates.size() <= maxHistoryFiles) return; Object[] pathes = candidates.toArray(); java.util.Arrays.sort(pathes); try { for (int i = 0; i < pathes.length - maxHistoryFiles; i++) { File logFile = new File((String) pathes[i]); boolean delFile = logFile.delete(); if (!delFile) { throw new IOException("Could not delete log file: " + logFile.getAbsolutePath()); } } } catch (Exception e) { new ErrorManager().error( "FATAL ERROR: COULD NOT DELETE LOG FILE.", e, ErrorManager.GENERIC_FAILURE); } } } /** * A Simple rotate method to close the old file and start the new one * when the limit is reached. */ public void rotate() { final GFFileHandler thisInstance = this; java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Object run() { synchronized (thisInstance.rotationLock) { if (thisInstance.meter != null && thisInstance.meter.written <= 0) { return null; } thisInstance.flush(); thisInstance.close(); try { if (!absoluteFile.exists()) { File creatingDeletedLogFile = new File( absoluteFile.getAbsolutePath()); if (creatingDeletedLogFile.createNewFile()) { absoluteFile = creatingDeletedLogFile; } } else { File oldFile = absoluteFile; StringBuffer renamedFileName = new StringBuffer( absoluteFile + "_"); 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. FileUtils.copy(absoluteFile, rotatedFile); File freshServerLogFile = getLogFileName(); // 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( freshServerLogFile); fo.close(); } FileOutputStream oldFileFO = new FileOutputStream( oldFile); oldFileFO.close(); openFile(getLogFileName()); absoluteFile = getLogFileName(); // This will ensure that the log rotation timer // will be restarted if there is a value set // for time based log rotation if (dayBasedFileRotation) { LogRotationTimer.getInstance() .restartTimerForDayBasedRotation(); } else { LogRotationTimer.getInstance() .restartTimer(); } if (compressLogs) { boolean compressed = gzipFile(rotatedFile); if (compressed) { boolean deleted = rotatedFile.delete(); if (!deleted) { throw new IOException("Could not delete uncompressed log file: " + rotatedFile.getAbsolutePath()); } } else { throw new IOException("Could not compress log file: " + rotatedFile.getAbsolutePath()); } } cleanUpHistoryLogFiles(); } } catch (IOException ix) { new ErrorManager().error("Error, could not rotate log file", ix, ErrorManager.GENERIC_FAILURE); } return null; } } } ); } /** * 5005 * Retrieves the LogRecord from our Queue and store them in the file */ public void log() { LogRecord record; // take is blocking so we take one record off the queue try { record = pendingRecords.take(); super.publish(record); } catch (InterruptedException e) { return; } // now try to read more. we end up blocking on the above take call if nothing is in the queue Vector v = new Vector(); int msgs = pendingRecords.drainTo(v, flushFrequency); for (int j = 0; j < msgs; j++) { super.publish(v.get(j)); } flush(); if ((rotationRequested.get()) || ((limitForFileRotation > 0) && (meter.written >= limitForFileRotation))) { // 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. synchronized (rotationLock) { rotate(); rotationRequested.set(false); } } } /** * Publishes the logrecord storing it in our queue */ public void publish(LogRecord record) { // the queue has shutdown, we are not processing any more records if (done.isSignalled()) { return; } // JUL LogRecord does not capture thread-name. Create a wrapper to // capture the name of the logging thread so that a formatter can // output correct thread-name if done asynchronously. Note that // this fix is limited to records published through this handler only. // *** // PAYARA-406 Check if the LogRecord passed in is already a GFLogRecord, // and just cast the passed record if it is GFLogRecord recordWrapper; if (record.getClass().getSimpleName().equals("GFLogRecord")) { recordWrapper = (GFLogRecord) record; // Check there is actually a set thread name if (recordWrapper.getThreadName() == null) { recordWrapper.setThreadName(Thread.currentThread().getName()); } } else { recordWrapper = new GFLogRecord(record); // set the thread id to be the current thread that is logging the message recordWrapper.setThreadName(Thread.currentThread().getName()); } try { pendingRecords.add(recordWrapper); } catch (IllegalStateException e) { // queue is full, start waiting. try { pendingRecords.put(recordWrapper); } catch (InterruptedException e1) { // too bad, record is lost... } } Formatter formatter = this.getFormatter(); if (!(formatter instanceof LogEventBroadcaster)) { LogEvent logEvent = new LogEventImpl(record); informLogEventListeners(logEvent); } } protected File getLogFileName() { // return new File(new File(env.getDomainRoot(),LOGS_DIR), logFileName); return new File(absoluteServerLogName); } public boolean addLogEventListener(LogEventListener listener) { if (logEventListeners.contains(listener)) { return false; } return logEventListeners.add(listener); } public boolean removeLogEventListener(LogEventListener listener) { return logEventListeners.remove(listener); } public void informLogEventListeners(LogEvent logEvent) { for (LogEventListener listener : logEventListeners) { listener.messageLogged(logEvent); } } private boolean gzipFile(File infile) { boolean status = false; try ( FileInputStream fis = new FileInputStream(infile); FileOutputStream fos = new FileOutputStream(infile.getCanonicalPath() + GZIP_EXTENSION); GZIPOutputStream gzos = new GZIPOutputStream(fos); ) { byte[] buffer = new byte[1024]; int len; while ((len=fis.read(buffer)) != -1 ) { gzos.write(buffer, 0, len); } gzos.finish(); status = true; } catch (IOException ix) { new ErrorManager().error("Error gzipping log file", ix, ErrorManager.GENERIC_FAILURE); } return status; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy