axiom.util.FileLogger.java Source code

Java tutorial

Introduction

Here is the source code for axiom.util.FileLogger.java

Source

/*
 * Helma License Notice
 *
 * The contents of this file are subject to the Helma License
 * Version 2.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://adele.helma.org/download/helma/license.txt
 *
 * Copyright 1998-2003 Helma Software. All Rights Reserved.
 *
 * $RCSfile: FileLogger.java,v $
 * $Author: hannes $
 * $Revision: 1.10 $
 * $Date: 2006/02/09 14:09:48 $
 */

package axiom.util;

import org.apache.commons.logging.Log;

import java.io.*;
import java.text.*;
import java.util.*;
import java.util.zip.GZIPOutputStream;

/**
 * An extended Logger that writes to a file and rotates files each midnight.
 *
 * @author Stefan Pollach
 * @author Daniel Ruthardt
 * @author Hannes Wallnoefer
 */
public class FileLogger extends Logger implements Log {

    // fields used for logging to files
    private String name;
    private File logdir;
    private File logfile;

    // number format for log file rotation
    DecimalFormat nformat = new DecimalFormat("000");
    DateFormat aformat = new SimpleDateFormat("yyyy-MM-dd");

    // timestamp of last log message
    long lastMessage;

    /**
     * Create a file logger. The actual file names do have numbers appended and are
     * rotated every x bytes.
     */
    protected FileLogger(String directory, String name) {
        this.name = name;
        logdir = new File(directory);
        // make logdir have an absolute path in case it doesn't already
        if (!logdir.isAbsolute())
            logdir = logdir.getAbsoluteFile();
        logfile = new File(logdir, name + ".log");

        if (!logdir.exists()) {
            logdir.mkdirs();
        }

        // create a synchronized list for log entries since different threads may
        // attempt to modify the list at the same time
        entries = Collections.synchronizedList(new LinkedList());

        lastMessage = System.currentTimeMillis();
    }

    /**
     * Open the file and get a writer to it. This will either rotate the log files
     * or it will return a writer that appends to an existing file.
     */
    private synchronized void openFile() {
        try {
            if (logfile.exists() && (logfile.lastModified() < lastMidnight())) {
                // rotate if a log file exists and is NOT from today
                File archive = rotateLogFile();
                // gzip rotated log file in a separate thread
                if (archive != null) {
                    new GZipper(archive).start();
                }
            }
            // create a new log file, appending to an existing file
            writer = new PrintWriter(new FileWriter(logfile.getAbsolutePath(), true), false);
        } catch (IOException iox) {
            System.err.println("Error creating log " + name + ": " + iox);
        }
    }

    /**
     * Actually closes the file writer of a log.
     */
    synchronized void closeFile() {
        if (writer != null) {
            try {
                writer.close();
            } catch (Exception ignore) {
                ignore.printStackTrace(System.out);
            } finally {
                writer = null;
            }
        }
    }

    /**
     * This is called by the runner thread to perform actual output.
     */
    synchronized void write() {
        if (entries.isEmpty()) {
            return;
        }

        try {
            int l = entries.size();

            if (writer == null || !logfile.exists()) {
                // open/create the log file if necessary
                openFile();
            }

            for (int i = 0; i < l; i++) {
                String entry = (String) entries.remove(0);
                writer.println(entry);
            }

            writer.flush();
        } catch (Exception x) {
            int size = entries.size();

            if (size > 1000) {
                // more than 1000 entries queued plus exception - something
                // is definitely wrong with this logger. Write a message to std err and
                // discard queued log entries.
                System.err.println("Error writing log file " + this + ": " + x);
                System.err.println("Discarding " + size + " log entries.");
                entries.clear();
            }
        }
    }

    /**
     *  Rotate log files, closing the file writer and renaming the old
     *  log file. Returns the renamed log file for zipping, or null if
     *  the log file couldn't be rotated.
     *
     *  @return the old renamed log file, or null
     */
    protected synchronized File rotateLogFile() throws IOException {
        // if the logger is not file based do nothing.
        if (logfile == null) {
            return null;
        }

        closeFile();

        // only backup/rotate if the log file is not empty,
        if (logfile.exists() && (logfile.length() > 0)) {
            String today = aformat.format(new Date());
            int ct = 0;

            // first append just the date
            String archname = name + "-" + today + ".log";
            File archive = new File(logdir, archname);
            File zipped = new File(logdir, archname + ".gz");

            // increase counter until we find an unused log archive name, checking
            // both unzipped and zipped file names
            while (archive.exists() || zipped.exists()) {
                // for the next try we append a counter
                String archidx = (ct > 999) ? Integer.toString(ct) : nformat.format(++ct);

                archname = name + "-" + today + "-" + archidx + ".log";
                archive = new File(logdir, archname);
                zipped = new File(logdir, archname + ".gz");
            }

            if (logfile.renameTo(archive)) {
                return archive;
            } else {
                System.err.println("Error rotating log file " + canonicalName + ". Will append to old file.");
            }
        }

        // no log file rotated
        return null;
    }

    /**
     * Return a string representation of this Logger
     */
    public String toString() {
        return "FileLogger[" + name + "]";
    }

    /**
     *  Return an object  which identifies  this logger.
     */
    public String getName() {
        return name;
    }

    /**
     * Append a message to the log.
     */
    public void log(String msg) {
        lastMessage = System.currentTimeMillis();

        // it's enough to render the date every second
        if ((lastMessage - 1000) > dateLastRendered) {
            renderDate();
        }

        entries.add(dateCache + msg);
    }

    /**
     *
     *
     * @return the timestamp for last midnight in millis
     */
    private static long lastMidnight() {
        return Logging.nextMidnight() - 86400000;
    }

    /**
     * a Thread class that zips up a file, filename will stay the same.
     */
    static class GZipper extends Thread {
        List files;
        final static int BUFFER_SIZE = 8192;

        public GZipper(List files) {
            this.files = files;
            setPriority(MIN_PRIORITY);
        }

        public GZipper(File file) {
            files = new ArrayList(1);
            files.add(file);
            setPriority(MIN_PRIORITY);
        }

        public void run() {
            Iterator it = files.iterator();
            File file = null;

            while (it.hasNext()) {
                try {
                    file = (File) it.next();
                    File zipped = new File(file.getAbsolutePath() + ".gz");
                    GZIPOutputStream zip = new GZIPOutputStream(new FileOutputStream(zipped));
                    BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
                    byte[] b = new byte[BUFFER_SIZE];
                    int len;

                    while ((len = in.read(b, 0, BUFFER_SIZE)) != -1) {
                        zip.write(b, 0, len);
                    }

                    zip.close();
                    in.close();
                    file.delete();
                } catch (Exception e) {
                    System.err.println("Error gzipping " + file);
                    System.err.println(e.toString());
                }
            }
        }
    }

}