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.IOException;
21 import java.net.DatagramPacket;
22 import java.net.InetAddress;
23 import java.net.MulticastSocket;
24 import java.net.UnknownHostException;
25
26 import org.apache.log4j.AppenderSkeleton;
27 import org.apache.log4j.helpers.Constants;
28 import org.apache.log4j.spi.LoggingEvent;
29 import org.apache.log4j.helpers.LogLog;
30
31
32 /***
33 * Multicast-based Appender. Works in conjunction with the MulticastReceiver, which expects
34 * a LoggingEvent encoded using XMLLayout.
35 *
36 * Sends log information as a multicast datagrams.
37 *
38 * <p>Messages are not sent as LoggingEvent objects but as text after
39 * applying XMLLayout.
40 *
41 * <p>The port and remoteHost properties can be set in configuration properties.
42 * By setting the remoteHost to a broadcast address any number of clients can
43 * listen for log messages.
44 *
45 * <p>This was inspired and really extended/copied from {@link SocketAppender}. Please
46 * see the docs for the proper credit to the authors of that class.
47 *
48 * @author <a href="mailto:kbrown@versatilesolutions.com">Kevin Brown</a>
49 * @author Scott Deboy <sdeboy@apache.org>
50 *
51 */
52 public class MulticastAppender extends AppenderSkeleton implements PortBased {
53 /***
54 The default port number for the multicast packets. (9991).
55 */
56 static final int DEFAULT_PORT = 9991;
57
58 /***
59 We remember host name as String in addition to the resolved
60 InetAddress so that it can be returned via getOption().
61 */
62 String hostname;
63 String remoteHost;
64 String application;
65 int timeToLive;
66 InetAddress address;
67 int port = DEFAULT_PORT;
68 MulticastSocket outSocket;
69 private String encoding;
70
71 private boolean locationInfo = false;
72
73 public MulticastAppender() {
74 super(false);
75 }
76
77 /***
78 Open the multicast sender for the <b>RemoteHost</b> and <b>Port</b>.
79 */
80 public void activateOptions() {
81 try {
82 hostname = InetAddress.getLocalHost().getHostName();
83 } catch (UnknownHostException uhe) {
84 try {
85 hostname = InetAddress.getLocalHost().getHostAddress();
86 } catch (UnknownHostException uhe2) {
87 hostname = "unknown";
88 }
89 }
90
91
92 if (application == null) {
93 application = System.getProperty(Constants.APPLICATION_KEY);
94 } else {
95 if (System.getProperty(Constants.APPLICATION_KEY) != null) {
96 application = application + "-" + System.getProperty(Constants.APPLICATION_KEY);
97 }
98 }
99
100 if(remoteHost != null) {
101 address = getAddressByName(remoteHost);
102 } else {
103 String err = "The RemoteHost property is required for SocketAppender named "+ name;
104 LogLog.error(err);
105 throw new IllegalStateException(err);
106 }
107
108 connect();
109 super.activateOptions();
110 }
111
112 /***
113 Close this appender.
114 <p>This will mark the appender as closed and
115 call then {@link #cleanUp} method.
116 */
117 public synchronized void close() {
118 if (closed) {
119 return;
120 }
121
122 this.closed = true;
123 cleanUp();
124 }
125
126 /***
127 Close the Socket and release the underlying
128 connector thread if it has been created
129 */
130 public void cleanUp() {
131 if (outSocket != null) {
132 try {
133 outSocket.close();
134 } catch (Exception e) {
135 LogLog.error("Could not close outSocket.", e);
136 }
137
138 outSocket = null;
139 }
140 }
141
142 void connect() {
143 if (this.address == null) {
144 return;
145 }
146
147 try {
148
149 cleanUp();
150 outSocket = new MulticastSocket();
151 outSocket.setTimeToLive(timeToLive);
152 } catch (IOException e) {
153 LogLog.error("Error in connect method of MulticastAppender named "+name, e);
154 }
155 }
156
157 public void append(LoggingEvent event) {
158 if (event == null) {
159 return;
160 }
161
162 if(locationInfo) {
163 event.getLocationInformation();
164 }
165
166 if (outSocket != null) {
167 event.setProperty(Constants.HOSTNAME_KEY, hostname);
168
169 if (application != null) {
170 event.setProperty(Constants.APPLICATION_KEY, application);
171 }
172
173 if(locationInfo) {
174 event.getLocationInformation();
175 }
176
177
178 try {
179 StringBuffer buf = new StringBuffer(layout.format(event));
180
181 byte[] payload;
182 if(encoding == null) {
183 payload = buf.toString().getBytes();
184 } else {
185 payload = buf.toString().getBytes(encoding);
186 }
187
188 DatagramPacket dp =
189 new DatagramPacket(payload, payload.length, address, port);
190 outSocket.send(dp);
191 } catch (IOException e) {
192 outSocket = null;
193 LogLog.warn("Detected problem with Multicast connection: " + e);
194 }
195 }
196 }
197
198 InetAddress getAddressByName(String host) {
199 try {
200 return InetAddress.getByName(host);
201 } catch (Exception e) {
202 LogLog.error("Could not find address of [" + host + "].", e);
203 return null;
204 }
205 }
206
207 /***
208 The <b>RemoteHost</b> option takes a string value which should be
209 the host name or ipaddress to send the multicast packets.
210 */
211 public void setRemoteHost(String host) {
212 remoteHost = host;
213 }
214
215 /***
216 Returns value of the <b>RemoteHost</b> option.
217 */
218 public String getRemoteHost() {
219 return remoteHost;
220 }
221
222 /***
223 The <b>LocationInfo</b> option takes a boolean value. If true,
224 the information sent to the remote host will include location
225 information. By default no location information is sent to the server.
226 */
227 public void setLocationInfo(boolean locationInfo) {
228 this.locationInfo = locationInfo;
229 }
230
231 /***
232 * Returns value of the <b>LocationInfo</b> option.
233 */
234 public boolean getLocationInfo() {
235 return locationInfo;
236 }
237
238 /***
239 The <b>Encoding</b> option specifies how the bytes are encoded. If this option is not specified,
240 the System encoding is used.
241 */
242 public void setEncoding(String encoding) {
243 this.encoding = encoding;
244 }
245
246 /***
247 Returns value of the <b>Encoding</b> option.
248 */
249 public String getEncoding() {
250 return encoding;
251 }
252 /***
253 The <b>App</b> option takes a string value which should be the name of the application getting logged.
254 If property was already set (via system property), don't set here.
255 */
256 public void setApplication(String app) {
257 this.application = app;
258 }
259
260 /***
261 Returns value of the <b>App</b> option.
262 */
263 public String getApplication() {
264 return application;
265 }
266
267 /***
268 The <b>Time to live</b> option takes a positive integer representing
269 the time to live value.
270 */
271 public void setTimeToLive(int timeToLive) {
272 this.timeToLive = timeToLive;
273 }
274
275 /***
276 Returns value of the <b>Time to Live</b> option.
277 */
278 public int getTimeToLive() {
279 return timeToLive;
280 }
281
282 /***
283 The <b>Port</b> option takes a positive integer representing
284 the port where multicast packets will be sent.
285 */
286 public void setPort(int port) {
287 this.port = port;
288 }
289
290 /***
291 Returns value of the <b>Port</b> option.
292 */
293 public int getPort() {
294 return port;
295 }
296
297
298
299
300 public boolean isActive() {
301
302 return true;
303 }
304
305 /***
306 * Gets whether appender requires a layout.
307 * @return false
308 */
309 public boolean requiresLayout() {
310 return true;
311 }
312
313 }