View Javadoc

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.io.*;
21  import java.net.*;
22  import java.util.*;
23  import org.apache.log4j.Layout;
24  import org.apache.log4j.spi.LoggingEvent;
25  import org.apache.log4j.AppenderSkeleton;
26  import org.apache.log4j.helpers.LogLog;
27  
28  /***
29    <p>The TelnetAppender is a log4j appender that specializes in
30    writing to a read-only socket.  The output is provided in a
31    telnet-friendly way so that a log can be monitored over TCP/IP.
32    Clients using telnet connect to the socket and receive log data.
33    This is handy for remote monitoring, especially when monitoring a
34    servlet.
35  
36    <p>Here is a list of the available configuration options:
37  
38    <table border=1>
39     <tr>
40     <th>Name</th>
41     <th>Requirement</th>
42     <th>Description</th>
43     <th>Sample Value</th>
44     </tr>
45  
46     <tr>
47     <td>Port</td>
48     <td>optional</td>
49     <td>This parameter determines the port to use for announcing log events.  The default port is 23 (telnet).</td>
50     <td>5875</td>
51     </table>
52  
53     @author <a HREF="mailto:jay@v-wave.com">Jay Funnell</a>
54  */
55  
56  public class TelnetAppender extends AppenderSkeleton {
57  
58    private SocketHandler sh;
59    private int port = 23;
60  
61    /*** 
62        This appender requires a layout to format the text to the
63        attached client(s). */
64    public boolean requiresLayout() {
65      return true;
66    }
67  
68    /*** all of the options have been set, create the socket handler and
69        wait for connections. */
70    public void activateOptions() {
71      try {
72        sh = new SocketHandler(port);
73        sh.start();
74      }
75      catch(Exception e) {
76        e.printStackTrace();
77      }
78      super.activateOptions();
79    }
80  
81    public
82    int getPort() {
83      return port;
84    }
85  
86    public
87    void setPort(int port) {
88      this.port = port;
89    }
90  
91  
92    /*** shuts down the appender. */
93    public void close() {
94      if (sh != null) {
95          sh.close();
96          try {
97              sh.join();
98          } catch(InterruptedException ex) {
99          }
100     }
101   }
102 
103   /*** Handles a log event.  For this appender, that means writing the
104     message to each connected client.  */
105   protected void append(LoggingEvent event) {
106     sh.send(this.layout.format(event));
107     if(layout.ignoresThrowable()) {
108       String[] s = event.getThrowableStrRep();
109       if (s != null) {
110 	int len = s.length;
111 	for(int i = 0; i < len; i++) {
112 	  sh.send(s[i]);
113 	  sh.send(Layout.LINE_SEP);
114 	}
115       }
116     }
117   }
118 
119   //---------------------------------------------------------- SocketHandler:
120 
121   /*** The SocketHandler class is used to accept connections from
122       clients.  It is threaded so that clients can connect/disconnect
123       asynchronously. */
124   protected class SocketHandler extends Thread {
125 
126     private Vector writers = new Vector();
127     private Vector connections = new Vector();
128     private ServerSocket serverSocket;
129     private int MAX_CONNECTIONS = 20;
130 
131     public void finalize() {
132         close();
133     }
134       
135     /*** make sure we close all network connections when this handler is destroyed. */
136     public void close() {
137       for(Enumeration e = connections.elements();e.hasMoreElements();) {
138         try {
139           ((Socket)e.nextElement()).close();
140         } catch(Exception ex) {
141         }
142       }
143 
144       try {
145         serverSocket.close();
146       } catch(Exception ex) {
147       }
148     }
149 
150     /*** sends a message to each of the clients in telnet-friendly output. */
151     public void send(String message) {
152       Enumeration ce = connections.elements();
153       for(Enumeration e = writers.elements();e.hasMoreElements();) {
154         Socket sock = (Socket)ce.nextElement();
155         PrintWriter writer = (PrintWriter)e.nextElement();
156         writer.print(message);
157         if(writer.checkError()) {
158           // The client has closed the connection, remove it from our list:
159           connections.remove(sock);
160           writers.remove(writer);
161         }
162       }
163     }
164 
165     /*** 
166 	Continually accepts client connections.  Client connections
167         are refused when MAX_CONNECTIONS is reached. 
168     */
169     public void run() {
170       while(!serverSocket.isClosed()) {
171         try {
172           Socket newClient = serverSocket.accept();
173           PrintWriter pw = new PrintWriter(newClient.getOutputStream());
174           if(connections.size() < MAX_CONNECTIONS) {
175             connections.addElement(newClient);
176             writers.addElement(pw);
177             pw.print("TelnetAppender v1.0 (" + connections.size() 
178 		     + " active connections)\r\n\r\n");
179             pw.flush();
180           } else {
181             pw.print("Too many connections.\r\n");
182             pw.flush();
183             newClient.close();
184           }
185         } catch(Exception e) {
186           if (!serverSocket.isClosed()) {
187             LogLog.error("Encountered error while in SocketHandler loop.", e);
188           }
189           break;
190         }
191       }
192 
193       try {
194           serverSocket.close();
195       } catch(IOException ex) {
196       }
197     }
198 
199     public SocketHandler(int port) throws IOException {
200       serverSocket = new ServerSocket(port);
201       setName("TelnetAppender-" + getName() + "-" + port);
202     }
203 
204   }
205 }