1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
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
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 }