Hi @all,

I attached a MultiPipeLogger class.

The purpose in our project was to enable logging via SysLog on the local
machine, without using IP. For this purpose, we use the "logger" shell
tool, and open one pipe per supported SysLog priority. It will go to
productive use in our environment those days.

I also included a tester Class that tests all log priorities and the
pipe recreation.

I have the permission from our management to submit this classes for
inclusion into the main log4j project (after setting a sensible package
declaration).

Nevertheless, I rather expect to get some comments from this first
submission.


Thanks,
Markus

-- 
markus schaber | dipl. informatiker
logi-track ag | rennweg 14-16 | ch 8001 z�rich
phone +41-43-888 62 52 | fax +41-43-888 62 53
mailto:[EMAIL PROTECTED] | www.logi-track.com
/*
 * Logger Command Appender Class
 * 
 * $Id: MultipipeLogger.java,v 1.1 2004/04/08 10:52:05 schabi Exp $
 * 
 * (C) 2004 Markus Schaber, logi-track ag, Z�rich, Switzerland.
 * 
 * Submitted for inclusion into the apache.org log4j project, Copyright (C) The
 * Apache Software Foundation. All rights reserved.
 * 
 * This software is published under the terms of the Apache Software License
 * version 1.1, a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 */
package com.logitrack.sql.posinsertdaemon.log;

import java.io.IOException;

import org.apache.log4j.Appender;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Layout;
import org.apache.log4j.Logger;
import org.apache.log4j.WriterAppender;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;

/**
 * MultipipeLogger - allows to log via syslog "logger" command or other
 * processes that log stdin.
 * 
 * CAVEATS: Currently, when a pipe breaks, some log events will be lost until
 * successful restart of the pipe process, including the event that triggered
 * the successful recreation, and as it uses WriterAppenders internally, it
 * blocks when the pipe is full.
 * 
 * @author Markus Schaber, logi-track ag, Z�rich, Switzerland.
 *  
 */
public class MultipipeLogger extends AppenderSkeleton {

    protected String pipeCommand;
    protected String facility;
    protected final WriterAppender[] writers = new WriterAppender[8];
    protected final Process[] processes = new Process[8];
    protected final BrokenPipeHandler[] recreatePipes = new BrokenPipeHandler[8];

    boolean badcallmessage = true;
    private static final String[] levels = new String[] { "crit", "crit", "crit", "error", "warning", "warning",
            "info", "debug"};

    /**
     * simple constructor.
     * 
     * You have to set layout, pipecommand and facility and then call
     * activateOptions() before you can use it.
     */
    public MultipipeLogger() {
        super();
        for (int i = 0; i < recreatePipes.length; i++) {
            recreatePipes[i] = new BrokenPipeHandler(i);
        }
    }

    /**
     * This constructor produces an ready-to-use MultipipeLogger.
     * 
     * @param layout
     * @param pipecommand
     * @param facility
     */
    public MultipipeLogger(Layout layout, String pipecommand, String facility) {
        this();
        super.setLayout(layout);
        this.pipeCommand = pipecommand;
        this.facility = facility;
        this.activateOptions();
    }

    protected void append(LoggingEvent event) {
        int pri = event.getLevel().getSyslogEquivalent();
        if (pri < 0) {
            pri = 0;
        } else if (pri >= writers.length) {
            pri = writers.length - 1;
        }
        if (writers[pri] == null) { //We try to reinstantiate logger
            activateWriter(pri);
        }
        if (writers[pri] != null) { //maybe reinstantiation was not successfull
            writers[pri].doAppend(event);
        }
    }

    /**
     * Close this appenders - tries to stop all processes
     */
    public synchronized void close() {
        if (closed) {
            return;
        }

        for (int i = 0; i < writers.length; i++) {
            close(i);
        }

        closed = true;
    }

    /**
     * Derived appenders should override this method if option structure
     * requires it.
     */
    public synchronized void activateOptions() {
        close();
        for (int i = 0; i < writers.length; i++) {
            activateWriter(i);
        }
        closed = false;
    }

    protected void close(int i) {
        Process process = processes[i];
        if (writers[i] != null) {
            writers[i].close();
            writers[i] = null;
        }
        if (process != null) {
            try {
                process.getOutputStream().close();
            } catch (IOException E) {
                LogLog.warn("Error closing Stream for " + levels[i], E);
            }
            try {
                process.waitFor();
            } catch (InterruptedException E) {
                LogLog.warn("Error closing Process for " + levels[i], E);
            }
            processes[i] = null;
        }
    }

    protected void activateWriter(int i) {
        try {
            if ((i > 0) && (levels[i - 1].equals(levels[i])) && (writers[i - 1] != null)) {
                writers[i] = writers[i - 1];
                processes[i] = processes[i - 1];
            } else {
                Process p = Runtime.getRuntime().exec(createCommandString(i));
                processes[i] = p;
                writers[i] = new WriterAppender(layout, p.getOutputStream());
                writers[i].setErrorHandler(recreatePipes[i]);
            }
        } catch (IOException E) {
            LogLog.error("Cannot open pipe for " + levels[i], E);
        }
    }

    /**
     * @param i
     * @return
     */
    private String createCommandString(int i) {
        return pipeCommand + " " + facility + "." + levels[i];
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.log4j.Appender#requiresLayout()
     */
    public boolean requiresLayout() {
        return true;
    }

    /**
     * Bean Getter for pipeCommand
     * 
     * @return The content of pipeCommand.
     */
    public String getPipeCommand() {
        return pipeCommand;
    }

    /**
     * Bean Setter for pipeCommand
     * 
     * @param pipeCommand The new value for pipeCommand.
     */
    public void setPipeCommand(String pipeCommand) {
        this.pipeCommand = pipeCommand;

    }

    /**
     * Bean Getter for facility
     * 
     * @return The content of facility.
     */
    public String getFacility() {
        return facility;
    }

    /**
     * Bean Setter for facility
     * 
     * @param facility The new value for facility.
     */
    public void setFacility(String facility) {
        this.facility = facility;
    }

    /**
     * BrokenPipeHandler is triggered when the internally used WriterAppenders
     * fail, and it tries to recreate the broken pipes again.
     */

    protected class BrokenPipeHandler implements org.apache.log4j.spi.ErrorHandler {

        private int index;

        /**
         * default constructor
         * 
         * @param i The index of our Writer
         */
        protected BrokenPipeHandler(int i) {
            this.index = i;
        }

        public void setLogger(Logger logger) {
        // ignored
        }

        public void error(String message) {
            LogLog.warn("Error '" + message + " occured. Trying to recreate MultipipeLogger pipe " + index);
            recover();
        }

        public void error(String message, Exception e, int errorCode) {
            LogLog.warn("Error '" + message + "' occured with Errorcode " + errorCode
                    + ".\nTrying to recreate MultipipeLogger pipe " + index, e);
            recover();
        }

        public void error(String message, Exception e, int errorCode, LoggingEvent event) {
            LogLog.warn("Logging Event " + e + " caused a logging error:");
            error(message, e, errorCode);
        }

        private void recover() {
            try {
                close(index);
                activateWriter(index);
            } catch (Exception E) {
                LogLog.debug("Error while trying to recover from Broken Pipe Error", E);
            }
        }

        public void setAppender(Appender appender) {
        // ignored
        }

        public void setBackupAppender(Appender appender) {
        // ignored
        }

        public void activateOptions() {
        //ignored
        }
    }
}
/*
 * MultipipeLoggerTester.java Created on 08.04.2004 11:53:03 by schabi
 * 
 * $Id: MultipipeLoggerTester.java,v 1.1 2004/04/08 10:52:05 schabi Exp $
 * 
 * (C) 2004 Markus Schaber, logi-track ag, Z�rich, Switzerland.
 * 
 * Submitted for inclusion into the apache.org log4j project, Copyright (C) The
 * Apache Software Foundation. All rights reserved.
 * 
 * This software is published under the terms of the Apache Software License
 * version 1.1, a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 */

package com.logitrack.sql.posinsertdaemon.log;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

/**
 * Test class for the multipipe Logger.
 * 
 * It is not easy to create a sensible unit test for this as there is no easy,
 * platform-independent way to read the written syslogs back :-/
 * 
 * @author Markus Schaber, logi-track ag, Z�rich, Switzerland.
 *  
 */
public class MultipipeLoggerTester {

    /**
     * Shell commant to test MultipipeLogger
     * 
     * @param args
     */
    public static void main(String[] args) {

        System.out.println("Initializing Loging System");

        MultipipeLogger app = new MultipipeLogger(new org.apache.log4j.PatternLayout(
                org.apache.log4j.PatternLayout.TTCC_CONVERSION_PATTERN), "/usr/bin/logger -p", "user");
        app.setThreshold(Level.ALL);
        BasicConfigurator.configure(app);
        Logger log = Logger.getLogger(MultipipeLoggerTester.class);

        System.out.println("Simple logging tests");
        log.debug("Debug log test");
        log.info("Info log test");
        log.warn("Warn log test");
        log.error("Error log test");
        log.fatal("Fatal log test");

        System.out.println("failed backend recovety test");
        log.debug("Going to destroy logging backend 7");
        app.processes[7].destroy();
        log.debug("First message after destruction");
        log.debug("Second message after destruction");
    }

}

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to