Home » apache-log4j-1.2.15 » org.apache » log4j » net » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    * 
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    * 
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   
   18   package org.apache.log4j.net;
   19   
   20   import java.util.Vector;
   21   import java.net.Socket;
   22   import java.net.ServerSocket;
   23   import java.net.SocketException;
   24   import java.io.ObjectOutputStream;
   25   import java.io.IOException;
   26   import java.io.InterruptedIOException;
   27   import java.net.InetAddress;
   28   
   29   import org.apache.log4j.helpers.LogLog;
   30   import org.apache.log4j.spi.LoggingEvent;
   31   import org.apache.log4j.AppenderSkeleton;
   32   
   33   /**
   34     Sends {@link LoggingEvent} objects to a set of remote log servers,
   35     usually a {@link SocketNode SocketNodes}.
   36       
   37     <p>Acts just like {@link SocketAppender} except that instead of
   38     connecting to a given remote log server,
   39     <code>SocketHubAppender</code> accepts connections from the remote
   40     log servers as clients.  It can accept more than one connection.
   41     When a log event is received, the event is sent to the set of
   42     currently connected remote log servers. Implemented this way it does
   43     not require any update to the configuration file to send data to
   44     another remote log server. The remote log server simply connects to
   45     the host and port the <code>SocketHubAppender</code> is running on.
   46     
   47     <p>The <code>SocketHubAppender</code> does not store events such
   48     that the remote side will events that arrived after the
   49     establishment of its connection. Once connected, events arrive in
   50     order as guaranteed by the TCP protocol.
   51   
   52     <p>This implementation borrows heavily from the {@link
   53     SocketAppender}.
   54   
   55     <p>The SocketHubAppender has the following characteristics:
   56     
   57     <ul>
   58     
   59     <p><li>If sent to a {@link SocketNode}, logging is non-intrusive as
   60     far as the log event is concerned. In other words, the event will be
   61     logged with the same time stamp, {@link org.apache.log4j.NDC},
   62     location info as if it were logged locally.
   63     
   64     <p><li><code>SocketHubAppender</code> does not use a layout. It
   65     ships a serialized {@link LoggingEvent} object to the remote side.
   66     
   67     <p><li><code>SocketHubAppender</code> relies on the TCP
   68     protocol. Consequently, if the remote side is reachable, then log
   69     events will eventually arrive at remote client.
   70     
   71     <p><li>If no remote clients are attached, the logging requests are
   72     simply dropped.
   73     
   74     <p><li>Logging events are automatically <em>buffered</em> by the
   75     native TCP implementation. This means that if the link to remote
   76     client is slow but still faster than the rate of (log) event
   77     production, the application will not be affected by the slow network
   78     connection. However, if the network connection is slower then the
   79     rate of event production, then the local application can only
   80     progress at the network rate. In particular, if the network link to
   81     the the remote client is down, the application will be blocked.
   82     
   83     <p>On the other hand, if the network link is up, but the remote
   84     client is down, the client will not be blocked when making log
   85     requests but the log events will be lost due to client
   86     unavailability. 
   87   
   88     <p>The single remote client case extends to multiple clients
   89     connections. The rate of logging will be determined by the slowest
   90     link.
   91       
   92     <p><li>If the JVM hosting the <code>SocketHubAppender</code> exits
   93     before the <code>SocketHubAppender</code> is closed either
   94     explicitly or subsequent to garbage collection, then there might
   95     be untransmitted data in the pipe which might be lost. This is a
   96     common problem on Windows based systems.
   97     
   98     <p>To avoid lost data, it is usually sufficient to {@link #close}
   99     the <code>SocketHubAppender</code> either explicitly or by calling
  100     the {@link org.apache.log4j.LogManager#shutdown} method before
  101     exiting the application.
  102     
  103     </ul>
  104        
  105     @author Mark Womack */
  106   
  107   public class SocketHubAppender extends AppenderSkeleton {
  108   
  109     /**
  110        The default port number of the ServerSocket will be created on. */
  111     static final int DEFAULT_PORT = 4560;
  112     
  113     private int port = DEFAULT_PORT;
  114     private Vector oosList = new Vector();
  115     private ServerMonitor serverMonitor = null;
  116     private boolean locationInfo = false;
  117     
  118     public SocketHubAppender() { }
  119   
  120     /**
  121        Connects to remote server at <code>address</code> and <code>port</code>. */
  122     public
  123     SocketHubAppender(int _port) {
  124       port = _port;
  125       startServer();
  126     }
  127   
  128     /**
  129        Set up the socket server on the specified port.  */
  130     public
  131     void activateOptions() {
  132       startServer();
  133     }
  134   
  135     /**
  136        Close this appender. 
  137        <p>This will mark the appender as closed and
  138        call then {@link #cleanUp} method. */
  139     synchronized
  140     public
  141     void close() {
  142       if(closed)
  143         return;
  144   
  145   	LogLog.debug("closing SocketHubAppender " + getName());
  146       this.closed = true;
  147       cleanUp();
  148   	LogLog.debug("SocketHubAppender " + getName() + " closed");
  149     }
  150   
  151     /**
  152        Release the underlying ServerMonitor thread, and drop the connections
  153        to all connected remote servers. */
  154     public 
  155     void cleanUp() {
  156       // stop the monitor thread
  157   	LogLog.debug("stopping ServerSocket");
  158       serverMonitor.stopMonitor();
  159       serverMonitor = null;
  160       
  161       // close all of the connections
  162   	LogLog.debug("closing client connections");
  163       while (oosList.size() != 0) {
  164         ObjectOutputStream oos = (ObjectOutputStream)oosList.elementAt(0);
  165         if(oos != null) {
  166           try {
  167           	oos.close();
  168           }
  169           catch(IOException e) {
  170           	LogLog.error("could not close oos.", e);
  171           }
  172           
  173           oosList.removeElementAt(0);     
  174         }
  175       }
  176     }
  177   
  178     /**
  179       Append an event to all of current connections. */
  180     public
  181     void append(LoggingEvent event) {
  182   	// if no event or no open connections, exit now
  183       if(event == null || oosList.size() == 0)
  184         return;
  185   
  186       // set up location info if requested
  187       if (locationInfo) {
  188       	event.getLocationInformation();	
  189       } 
  190   
  191   	// loop through the current set of open connections, appending the event to each
  192       for (int streamCount = 0; streamCount < oosList.size(); streamCount++) {    	
  193   
  194         ObjectOutputStream oos = null;
  195         try {
  196           oos = (ObjectOutputStream)oosList.elementAt(streamCount);
  197         }
  198         catch (ArrayIndexOutOfBoundsException e) {
  199           // catch this, but just don't assign a value
  200           // this should not really occur as this method is
  201           // the only one that can remove oos's (besides cleanUp).
  202         }
  203         
  204         // list size changed unexpectedly? Just exit the append.
  205         if (oos == null)
  206           break;
  207           
  208         try {
  209         	oos.writeObject(event);
  210         	oos.flush();
  211       	// Failing to reset the object output stream every now and
  212       	// then creates a serious memory leak.
  213       	// right now we always reset. TODO - set up frequency counter per oos?
  214       	oos.reset();
  215         }
  216         catch(IOException e) {
  217         	// there was an io exception so just drop the connection
  218         	oosList.removeElementAt(streamCount);
  219         	LogLog.debug("dropped connection");
  220         	
  221         	// decrement to keep the counter in place (for loop always increments)
  222         	streamCount--;
  223         }
  224       }
  225     }
  226     
  227     /**
  228        The SocketHubAppender does not use a layout. Hence, this method returns
  229        <code>false</code>. */
  230     public
  231     boolean requiresLayout() {
  232       return false;
  233     }
  234     
  235     /**
  236        The <b>Port</b> option takes a positive integer representing
  237        the port where the server is waiting for connections. */
  238     public
  239     void setPort(int _port) {
  240       port = _port;
  241     }
  242     
  243     /**
  244        Returns value of the <b>Port</b> option. */
  245     public
  246     int getPort() {
  247       return port;
  248     }
  249     
  250     /**
  251        The <b>LocationInfo</b> option takes a boolean value. If true,
  252        the information sent to the remote host will include location
  253        information. By default no location information is sent to the server. */
  254     public
  255     void setLocationInfo(boolean _locationInfo) {
  256       locationInfo = _locationInfo;
  257     }
  258     
  259     /**
  260        Returns value of the <b>LocationInfo</b> option. */
  261     public
  262     boolean getLocationInfo() {
  263       return locationInfo;
  264     }
  265     
  266     /**
  267       Start the ServerMonitor thread. */
  268     private
  269     void startServer() {
  270       serverMonitor = new ServerMonitor(port, oosList);
  271     }
  272     
  273     /**
  274       This class is used internally to monitor a ServerSocket
  275       and register new connections in a vector passed in the
  276       constructor. */
  277     private
  278     class ServerMonitor implements Runnable {
  279       private int port;
  280       private Vector oosList;
  281       private boolean keepRunning;
  282       private Thread monitorThread;
  283       
  284       /**
  285         Create a thread and start the monitor. */
  286       public
  287       ServerMonitor(int _port, Vector _oosList) {
  288         port = _port;
  289         oosList = _oosList;
  290         keepRunning = true;
  291         monitorThread = new Thread(this);
  292         monitorThread.setDaemon(true);
  293         monitorThread.start();
  294       }
  295       
  296       /**
  297         Stops the monitor. This method will not return until
  298         the thread has finished executing. */
  299       public
  300       synchronized
  301       void stopMonitor() {
  302         if (keepRunning) {
  303       	LogLog.debug("server monitor thread shutting down");
  304           keepRunning = false;
  305           try {
  306             monitorThread.join();
  307           }
  308           catch (InterruptedException e) {
  309             // do nothing?
  310           }
  311           
  312           // release the thread
  313           monitorThread = null;
  314       	LogLog.debug("server monitor thread shut down");
  315         }
  316       }
  317       
  318       /**
  319         Method that runs, monitoring the ServerSocket and adding connections as
  320         they connect to the socket. */
  321       public
  322       void run() {
  323         ServerSocket serverSocket = null;
  324         try {
  325           serverSocket = new ServerSocket(port);
  326           serverSocket.setSoTimeout(1000);
  327         }
  328         catch (Exception e) {
  329           LogLog.error("exception setting timeout, shutting down server socket.", e);
  330           keepRunning = false;
  331           return;
  332         }
  333   
  334         try {
  335       	try {
  336           	serverSocket.setSoTimeout(1000);
  337       	}
  338       	catch (SocketException e) {
  339             LogLog.error("exception setting timeout, shutting down server socket.", e);
  340             return;
  341       	}
  342         
  343       	while (keepRunning) {
  344             Socket socket = null;
  345             try {
  346               socket = serverSocket.accept();
  347             }
  348             catch (InterruptedIOException e) {
  349               // timeout occurred, so just loop
  350             }
  351             catch (SocketException e) {
  352               LogLog.error("exception accepting socket, shutting down server socket.", e);
  353               keepRunning = false;
  354             }
  355             catch (IOException e) {
  356               LogLog.error("exception accepting socket.", e);
  357             }
  358   	        
  359             // if there was a socket accepted
  360             if (socket != null) {
  361               try {
  362                 InetAddress remoteAddress = socket.getInetAddress();
  363                 LogLog.debug("accepting connection from " + remoteAddress.getHostName() 
  364   			   + " (" + remoteAddress.getHostAddress() + ")");
  365   	        	
  366                 // create an ObjectOutputStream
  367                 ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
  368   	            
  369                 // add it to the oosList.  OK since Vector is synchronized.
  370                 oosList.addElement(oos);
  371               }
  372               catch (IOException e) {
  373                 LogLog.error("exception creating output stream on socket.", e);
  374               }
  375             }
  376           }
  377         }
  378         finally {
  379       	// close the socket
  380       	try {
  381       		serverSocket.close();
  382       	}
  383       	catch (IOException e) {
  384       		// do nothing with it?
  385       	}
  386         }
  387       }
  388     }
  389   }
  390   

Save This Page
Home » apache-log4j-1.2.15 » org.apache » log4j » net » [javadoc | source]