
Author: seb Date: Fri Nov 24 18:00:58 2006 New Revision: 1001 Added: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/net/SocketAppender.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/net/SocketAppenderBase.java Modified: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/net/SMTPAppender.java logback/trunk/logback-access/src/main/java/ch/qos/logback/access/spi/AccessEvent.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketAppender.java logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/net/SocketAppenderTestApp.java Log: Added an access specific implementation of SocketAppender. Moved to core module the common functionnalities Minor modifications on the other classes (format,...) Modified: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/net/SMTPAppender.java ============================================================================== --- logback/trunk/logback-access/src/main/java/ch/qos/logback/access/net/SMTPAppender.java (original) +++ logback/trunk/logback-access/src/main/java/ch/qos/logback/access/net/SMTPAppender.java Fri Nov 24 18:00:58 2006 @@ -18,21 +18,11 @@ import ch.qos.logback.core.net.SMTPAppenderBase; /** - * Send an e-mail when a specific access event occurs, typically on server errors. + * Send an e-mail when a specific access event occurs, typically when + * certain pages are accessed. * - * <p> - * The number of access events delivered in this e-mail depend on the value of - * <b>BufferSize</b> option. The <code>SMTPAppender</code> keeps only the - * last <code>BufferSize</code> access events in its cyclic buffer. This - * keeps memory requirements at a reasonable level while still delivering useful - * application context. - * <p> - * By default, the email is sent everytime an event has a status code of - * <em>500 (server error) or higher</em>. In order not to flood one's mailbox, - * an email will be sent only if the previous email was sent more that 24 hours ago. - * <p> - * This behaviour can be easily bypassed either by modifying this class, or by - * imlementing a new <code>EventEvaluator</code>. + * For more informations about this appender, please refer to the online manual at + * http://logback.qos.ch/manual/appenders.html#AccessSMTPAppender * <p> * @author Ceki Gülcü * @author Sébastien Pennec Added: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/net/SocketAppender.java ============================================================================== --- (empty file) +++ logback/trunk/logback-access/src/main/java/ch/qos/logback/access/net/SocketAppender.java Fri Nov 24 18:00:58 2006 @@ -0,0 +1,57 @@ +/** + * Logback: the generic, reliable, fast and flexible logging framework. + * + * Copyright (C) 1999-2006, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ + +// Contributors: Dan MacDonald <dan@redknee.com> +package ch.qos.logback.access.net; + +import java.net.InetAddress; + +import ch.qos.logback.core.net.SocketAppenderBase; + +/** + * Sends {@link AccessEvent} objects to a remote a log server, usually a + * {@link SocketNode}. + * + * For more informations about this appender, please refer to the online manual at + * http://logback.qos.ch/manual/appenders.html#AccessSocketAppender + * + * @author Ceki Gülcü + * @author Sébastien Pennec + * + */ + +public class SocketAppender extends SocketAppenderBase { + + public SocketAppender() { + } + + /** + * Connects to remote server at <code>address</code> and <code>port</code>. + */ + public SocketAppender(InetAddress address, int port) { + this.address = address; + this.remoteHost = address.getHostName(); + this.port = port; + } + + /** + * Connects to remote server at <code>host</code> and <code>port</code>. + */ + public SocketAppender(String host, int port) { + this.port = port; + this.address = getAddressByName(host); + this.remoteHost = host; + } + + @Override + protected void postProcessEvent(Object event) { + } + +} Modified: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/spi/AccessEvent.java ============================================================================== --- logback/trunk/logback-access/src/main/java/ch/qos/logback/access/spi/AccessEvent.java (original) +++ logback/trunk/logback-access/src/main/java/ch/qos/logback/access/spi/AccessEvent.java Fri Nov 24 18:00:58 2006 @@ -14,7 +14,8 @@ /** * The Access module's internal representation of logging events. When the - * RequestLogImpl instance is called to log then a <code>AccessEvent</code> + * logging component instance is called in the container to log then a + * <code>AccessEvent</code> * instance is created. This instance is passed around to the different logback * components. * Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketAppender.java ============================================================================== --- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketAppender.java (original) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketAppender.java Fri Nov 24 18:00:58 2006 @@ -11,14 +11,10 @@ // Contributors: Dan MacDonald <dan@redknee.com> package ch.qos.logback.classic.net; -import java.io.IOException; -import java.io.ObjectOutputStream; import java.net.InetAddress; -import java.net.Socket; import ch.qos.logback.classic.spi.LoggingEvent; -import ch.qos.logback.core.AppenderBase; -import ch.qos.logback.core.Layout; +import ch.qos.logback.core.net.SocketAppenderBase; /** * Sends {@link LoggingEvent} objects to a remote a log server, usually a @@ -29,41 +25,12 @@ * * @author Ceki Gülcü * @author Sébastien Pennec - * - * @since 0.8.4 */ -public class SocketAppender extends AppenderBase { - - /** - * The default port number of remote logging server (4560). - */ - static final int DEFAULT_PORT = 4560; +public class SocketAppender extends SocketAppenderBase { - /** - * The default reconnection delay (30000 milliseconds or 30 seconds). - */ - static final int DEFAULT_RECONNECTION_DELAY = 30000; - - /** - * We remember host name as String in addition to the resolved InetAddress so - * that it can be returned via getOption(). - */ - String remoteHost; - - InetAddress address; - int port = DEFAULT_PORT; - ObjectOutputStream oos; - int reconnectionDelay = DEFAULT_RECONNECTION_DELAY; boolean includeCallerData = false; - - private Connector connector; - - int counter = 0; - - // reset the ObjectOutputStream every 70 calls - private static final int RESET_FREQUENCY = 70; - + public SocketAppender() { } @@ -74,7 +41,6 @@ this.address = address; this.remoteHost = address.getHostName(); this.port = port; - // connect(address, port); } /** @@ -84,259 +50,17 @@ this.port = port; this.address = getAddressByName(host); this.remoteHost = host; - // connect(address, port); } - - // /** - // * Connect to the specified <b>RemoteHost</b> and <b>Port</b>. - // */ - // public void activateOptions() { - // connect(address, port); - // } - - /** - * Start this appender. - */ - public void start() { - int errorCount = 0; - if (port == 0) { - errorCount++; - addError("No port was configured for appender" + name + " For more information, please visit http://logback.qos.ch/codes.html#socket_no_port"); - } - - if (address == null) { - errorCount++; - addError("No remote address was configured for appender" + name + " For more information, please visit http://logback.qos.ch/codes.html#socket_no_host"); - } - - connect(address, port); - - if (errorCount == 0) { - this.started = true; - } - } - - /** - * Strop this appender. - * - * <p> - * This will mark the appender as closed and call then {@link #cleanUp} - * method. - */ - @Override - public void stop() { - if (!isStarted()) - return; - - this.started = false; - cleanUp(); - } - - /** - * Drop the connection to the remote host and release the underlying connector - * thread if it has been created - */ - public void cleanUp() { - if (oos != null) { - try { - oos.close(); - } catch (IOException e) { - addError("Could not close oos.", e); - } - oos = null; - } - if (connector != null) { - addInfo("Interrupting the connector."); - connector.interrupted = true; - connector = null; // allow gc - } - } - - void connect(InetAddress address, int port) { - if (this.address == null) - return; - try { - // First, close the previous connection if any. - cleanUp(); - oos = new ObjectOutputStream(new Socket(address, port).getOutputStream()); - } catch (IOException e) { - - String msg = "Could not connect to remote logback server at [" - + address.getHostName() + "]."; - if (reconnectionDelay > 0) { - msg += " We will try again later."; - fireConnector(); // fire the connector thread - } - addError(msg, e); - } - } - + @Override - protected void append(Object event) { - - if (event == null) - return; - - if (address == null) { - addError("No remote host is set for SocketAppender named \"" + this.name - + "\". For more information, please visit http://logback.qos.ch/codes.html#socket_no_host"); - return; - } - - if (oos != null) { - try { - if (includeCallerData) { - ((LoggingEvent)event).getCallerData(); - } - oos.writeObject(event); - // addInfo("=========Flushing."); - oos.flush(); - if (++counter >= RESET_FREQUENCY) { - counter = 0; - // Failing to reset the object output stream every now and - // then creates a serious memory leak. - // System.err.println("Doing oos.reset()"); - oos.reset(); - } - } catch (IOException e) { - oos = null; - addWarn("Detected problem with connection: " + e); - if (reconnectionDelay > 0) { - fireConnector(); - } - } - } - } - - void fireConnector() { - if (connector == null) { - addInfo("Starting a new connector thread."); - connector = new Connector(); - connector.setDaemon(true); - connector.setPriority(Thread.MIN_PRIORITY); - connector.start(); - } - } - - static InetAddress getAddressByName(String host) { - try { - return InetAddress.getByName(host); - } catch (Exception e) { - // addError("Could not find address of [" + host + "].", e); - return null; - } - } - - /** - * The <b>RemoteHost</b> option takes a string value which should be the host - * name of the server where a {@link SocketNode} is running. - */ - public void setRemoteHost(String host) { - address = getAddressByName(host); - remoteHost = host; - } - - /** - * Returns value of the <b>RemoteHost</b> option. - */ - public String getRemoteHost() { - return remoteHost; - } - - /** - * The <b>Port</b> option takes a positive integer representing the port - * where the server is waiting for connections. - */ - public void setPort(int port) { - this.port = port; - } - - /** - * Returns value of the <b>Port</b> option. - */ - public int getPort() { - return port; + protected void postProcessEvent(Object event) { + if (includeCallerData) { + ((LoggingEvent) event).getCallerData(); + } } public void setIncludeCallerData(boolean includeCallerData) { this.includeCallerData = includeCallerData; } - - - /** - * The <b>ReconnectionDelay</b> option takes a positive integer representing - * the number of milliseconds to wait between each failed connection attempt - * to the server. The default value of this option is 30000 which corresponds - * to 30 seconds. - * - * <p> - * Setting this option to zero turns off reconnection capability. - */ - public void setReconnectionDelay(int delay) { - this.reconnectionDelay = delay; - } - - /** - * Returns value of the <b>ReconnectionDelay</b> option. - */ - public int getReconnectionDelay() { - return reconnectionDelay; - } - - public Layout getLayout() { - return null; - } - - public void setLayout(Layout layout) { - } - - /** - * The Connector will reconnect when the server becomes available again. It - * does this by attempting to open a new connection every - * <code>reconnectionDelay</code> milliseconds. - * - * <p> - * It stops trying whenever a connection is established. It will restart to - * try reconnect to the server when previpously open connection is droppped. - * - * @author Ceki Gülcü - * @since 0.8.4 - */ - class Connector extends Thread { - - boolean interrupted = false; - - public void run() { - Socket socket; - while (!interrupted) { - try { - sleep(reconnectionDelay); - addInfo("Attempting connection to " + address.getHostName()); - socket = new Socket(address, port); - synchronized (this) { - oos = new ObjectOutputStream(socket.getOutputStream()); - connector = null; - addInfo("Connection established. Exiting connector thread."); - break; - } - } catch (InterruptedException e) { - addInfo("Connector interrupted. Leaving loop."); - return; - } catch (java.net.ConnectException e) { - addInfo("Remote host " + address.getHostName() - + " refused connection."); - } catch (IOException e) { - addInfo("Could not connect to " + address.getHostName() - + ". Exception is " + e); - } - } - // addInfo("Exiting Connector.run() method."); - } - - /** - * public void finalize() { LogLog.debug("Connector finalize() has been - * called."); } - */ - } } Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/net/SocketAppenderTestApp.java ============================================================================== --- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/net/SocketAppenderTestApp.java (original) +++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/net/SocketAppenderTestApp.java Fri Nov 24 18:00:58 2006 @@ -6,25 +6,25 @@ import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.core.util.StatusPrinter; - public class SocketAppenderTestApp { - public static void main(String[] args) { + public static void main(String[] args) { - Logger logger = (Logger) LoggerFactory.getLogger(SocketAppenderTestApp.class); - LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); - SocketAppender appender = new SocketAppender("localhost", 4560); - appender.setContext(lc); - appender.setName("socket"); - appender.start(); + Logger logger = (Logger) LoggerFactory + .getLogger(SocketAppenderTestApp.class); + LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); + SocketAppender appender = new SocketAppender("localhost", 4560); + appender.setContext(lc); + appender.setName("socket"); + appender.start(); - logger.addAppender(appender); + logger.addAppender(appender); - for (int i = 0; i <= 1000; i++) { - logger.debug("** Hello world. n=" + i); - } + for (int i = 0; i <= 1000; i++) { + logger.debug("** Hello world. n=" + i); + } - StatusPrinter.print(lc.getStatusManager()); + StatusPrinter.print(lc.getStatusManager()); - } + } } Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/net/SocketAppenderBase.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/net/SocketAppenderBase.java Fri Nov 24 18:00:58 2006 @@ -0,0 +1,310 @@ +/** + * Logback: the generic, reliable, fast and flexible logging framework. + * + * Copyright (C) 1999-2006, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ + +// Contributors: Dan MacDonald <dan@redknee.com> +package ch.qos.logback.core.net; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.net.InetAddress; +import java.net.Socket; + +import ch.qos.logback.core.AppenderBase; +import ch.qos.logback.core.Layout; + +/** + * + * This is the base class for module specific SocketAppender + * implementations. + * + * @author Ceki Gülcü + * @author Sébastien Pennec + */ + +public abstract class SocketAppenderBase extends AppenderBase { + + /** + * The default port number of remote logging server (4560). + */ + static final int DEFAULT_PORT = 4560; + + /** + * The default reconnection delay (30000 milliseconds or 30 seconds). + */ + static final int DEFAULT_RECONNECTION_DELAY = 30000; + + /** + * We remember host name as String in addition to the resolved InetAddress so + * that it can be returned via getOption(). + */ + protected String remoteHost; + + protected InetAddress address; + protected int port = DEFAULT_PORT; + protected ObjectOutputStream oos; + protected int reconnectionDelay = DEFAULT_RECONNECTION_DELAY; + + + private Connector connector; + + protected int counter = 0; + + // reset the ObjectOutputStream every 70 calls + private static final int RESET_FREQUENCY = 70; + + // /** + // * Connect to the specified <b>RemoteHost</b> and <b>Port</b>. + // */ + // public void activateOptions() { + // connect(address, port); + // } + + /** + * Start this appender. + */ + public void start() { + int errorCount = 0; + if (port == 0) { + errorCount++; + addError("No port was configured for appender" + name + " For more information, please visit http://logback.qos.ch/codes.html#socket_no_port"); + } + + if (address == null) { + errorCount++; + addError("No remote address was configured for appender" + name + " For more information, please visit http://logback.qos.ch/codes.html#socket_no_host"); + } + + connect(address, port); + + if (errorCount == 0) { + this.started = true; + } + } + + /** + * Strop this appender. + * + * <p> + * This will mark the appender as closed and call then {@link #cleanUp} + * method. + */ + @Override + public void stop() { + if (!isStarted()) + return; + + this.started = false; + cleanUp(); + } + + /** + * Drop the connection to the remote host and release the underlying connector + * thread if it has been created + */ + public void cleanUp() { + if (oos != null) { + try { + oos.close(); + } catch (IOException e) { + addError("Could not close oos.", e); + } + oos = null; + } + if (connector != null) { + addInfo("Interrupting the connector."); + connector.interrupted = true; + connector = null; // allow gc + } + } + + void connect(InetAddress address, int port) { + if (this.address == null) + return; + try { + // First, close the previous connection if any. + cleanUp(); + oos = new ObjectOutputStream(new Socket(address, port).getOutputStream()); + } catch (IOException e) { + + String msg = "Could not connect to remote logback server at [" + + address.getHostName() + "]."; + if (reconnectionDelay > 0) { + msg += " We will try again later."; + fireConnector(); // fire the connector thread + } + addError(msg, e); + } + } + + @Override + protected void append(Object event) { + + if (event == null) + return; + + if (address == null) { + addError("No remote host is set for SocketAppender named \"" + this.name + + "\". For more information, please visit http://logback.qos.ch/codes.html#socket_no_host"); + return; + } + + if (oos != null) { + try { + postProcessEvent(event); + oos.writeObject(event); + // addInfo("=========Flushing."); + oos.flush(); + if (++counter >= RESET_FREQUENCY) { + counter = 0; + // Failing to reset the object output stream every now and + // then creates a serious memory leak. + // System.err.println("Doing oos.reset()"); + oos.reset(); + } + } catch (IOException e) { + oos = null; + addWarn("Detected problem with connection: " + e); + if (reconnectionDelay > 0) { + fireConnector(); + } + } + } + } + + protected abstract void postProcessEvent(Object event); + + void fireConnector() { + if (connector == null) { + addInfo("Starting a new connector thread."); + connector = new Connector(); + connector.setDaemon(true); + connector.setPriority(Thread.MIN_PRIORITY); + connector.start(); + } + } + + protected static InetAddress getAddressByName(String host) { + try { + return InetAddress.getByName(host); + } catch (Exception e) { + // addError("Could not find address of [" + host + "].", e); + return null; + } + } + + /** + * The <b>RemoteHost</b> option takes a string value which should be the host + * name of the server where a {@link SocketNode} is running. + */ + public void setRemoteHost(String host) { + address = getAddressByName(host); + remoteHost = host; + } + + /** + * Returns value of the <b>RemoteHost</b> option. + */ + public String getRemoteHost() { + return remoteHost; + } + + /** + * The <b>Port</b> option takes a positive integer representing the port + * where the server is waiting for connections. + */ + public void setPort(int port) { + this.port = port; + } + + /** + * Returns value of the <b>Port</b> option. + */ + public int getPort() { + return port; + } + + + /** + * The <b>ReconnectionDelay</b> option takes a positive integer representing + * the number of milliseconds to wait between each failed connection attempt + * to the server. The default value of this option is 30000 which corresponds + * to 30 seconds. + * + * <p> + * Setting this option to zero turns off reconnection capability. + */ + public void setReconnectionDelay(int delay) { + this.reconnectionDelay = delay; + } + + /** + * Returns value of the <b>ReconnectionDelay</b> option. + */ + public int getReconnectionDelay() { + return reconnectionDelay; + } + + public Layout getLayout() { + return null; + } + + public void setLayout(Layout layout) { + } + + /** + * The Connector will reconnect when the server becomes available again. It + * does this by attempting to open a new connection every + * <code>reconnectionDelay</code> milliseconds. + * + * <p> + * It stops trying whenever a connection is established. It will restart to + * try reconnect to the server when previpously open connection is droppped. + * + * @author Ceki Gülcü + * @since 0.8.4 + */ + class Connector extends Thread { + + boolean interrupted = false; + + public void run() { + Socket socket; + while (!interrupted) { + try { + sleep(reconnectionDelay); + addInfo("Attempting connection to " + address.getHostName()); + socket = new Socket(address, port); + synchronized (this) { + oos = new ObjectOutputStream(socket.getOutputStream()); + connector = null; + addInfo("Connection established. Exiting connector thread."); + break; + } + } catch (InterruptedException e) { + addInfo("Connector interrupted. Leaving loop."); + return; + } catch (java.net.ConnectException e) { + addInfo("Remote host " + address.getHostName() + + " refused connection."); + } catch (IOException e) { + addInfo("Could not connect to " + address.getHostName() + + ". Exception is " + e); + } + } + // addInfo("Exiting Connector.run() method."); + } + + /** + * public void finalize() { LogLog.debug("Connector finalize() has been + * called."); } + */ + } + +}