KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > protomatter > syslog > UNIXSyslogLog


1 package com.protomatter.syslog;
2
3 /**
4  * {{{ The Protomatter Software License, Version 1.0
5  * derived from The Apache Software License, Version 1.1
6  *
7  * Copyright (c) 1998-2002 Nate Sammons. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution,
22  * if any, must include the following acknowledgment:
23  * "This product includes software developed for the
24  * Protomatter Software Project
25  * (http://protomatter.sourceforge.net/)."
26  * Alternately, this acknowledgment may appear in the software itself,
27  * if and wherever such third-party acknowledgments normally appear.
28  *
29  * 4. The names "Protomatter" and "Protomatter Software Project" must
30  * not be used to endorse or promote products derived from this
31  * software without prior written permission. For written
32  * permission, please contact support@protomatter.com.
33  *
34  * 5. Products derived from this software may not be called "Protomatter",
35  * nor may "Protomatter" appear in their name, without prior written
36  * permission of the Protomatter Software Project
37  * (support@protomatter.com).
38  *
39  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
40  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
41  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
42  * DISCLAIMED. IN NO EVENT SHALL THE PROTOMATTER SOFTWARE PROJECT OR
43  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
45  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
46  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
47  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
48  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
49  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50  * SUCH DAMAGE. }}}
51  */

52
53 import java.io.*;
54 import java.net.*;
55 import java.text.*;
56 import java.util.*;
57
58 import com.protomatter.util.*;
59
60 /**
61  * A logger that sends UDP packets to a UNIX syslog server.
62  * This logger sends UDP packets to UNIX servers running a BSD-style
63  * <tt>syslogd</tt> daemon. Refer to
64  * <a HREF="http://www.ietf.org/rfc/rfc3164.txt">RFC 3164</A> for
65  * information about the protocol.<P>
66  *
67  * With the default formatting options, a call to <TT>Syslog.info(this, "Hello there")</TT>
68  * will result in the following to be written to <tt>/var/log/messages</tt> (or its
69  * equivalent) on the UNIX box:<P>
70  *
71  * <ul><table border=1 cellpadding=4 cellspacing=0>
72  * <tr><td><pre>
73  *
74  * Feb 10 16:02:21 sillysailor ProtomatterSyslog: [INFO] MyClass: Hello there &nbsp;
75  * </pre></td></tr>
76  * </table></ul><P>
77  *
78  * This assumes you logged from a class called <tt>MyClass</tt>, that you
79  * havn't set the tag to some more meaningful value, and that your hostname
80  * is "<tt>sillysailor</tt>".
81  *
82  * @see com.protomatter.syslog.xml.UNIXSyslogLog_Helper XML configuration class
83  */

84 public class UNIXSyslogLog
85 extends BasicLogger
86 {
87    private static int DEFAULT_PORT = 514;
88
89    private DatagramSocket socket = null;
90    private InetAddress address = null;
91    private int port = DEFAULT_PORT;
92    private int facility = 16; // "Local Use 0" from RFC 3164
93
private String JavaDoc tag = Syslog.getResourceString(MessageConstants.UNIX_DEFAULT_TAG);
94    private byte[] tagBytes = tag.getBytes();
95    private boolean showHostname = false;
96    private String JavaDoc messageTemplate = "[{SEVERITY}] {CALLER}: {MESSAGE}";
97
98    public static final String JavaDoc CHANNEL_TOKEN = "{CHANNEL}";
99    public static final String JavaDoc FULLCALLER_TOKEN = "{CALLER-FULLNAME}";
100    public static final String JavaDoc CALLER_TOKEN = "{CALLER}";
101    public static final String JavaDoc MESSAGE_TOKEN = "{MESSAGE}";
102    public static final String JavaDoc THREAD_TOKEN = "{THREAD}";
103    public static final String JavaDoc SEVERITY_TOKEN = "{SEVERITY}";
104
105    private DateFormat dateFormat = new SimpleDateFormat("MMM dd HH:MM:ss");
106    private byte[] cacheDate = null;
107    private long cacheTime = 0;
108    private static long cacheTimeout = 1000;
109
110    private static int MAX_DATA_LENGTH = 1024; // max packet length from the spec
111

112    private static char SPACE = ' ';
113    private static char COLON = ':';
114    private static char L_ANGLE = '<';
115    private static char R_ANGLE = '>';
116
117    private static SimpleSyslogTextFormatter simpleFormat = null;
118
119    // See RFC 3164 for info on these values
120
/** Debug: debug-level messages (RFC 3164). */
121    public static final int UNIX_DEBUG = 7;
122
123    /** Informational: informational messages (RFC 3164). */
124    public static final int UNIX_INFO = 6;
125
126    /** Notice: normal but significant (RFC 3164). */
127    public static final int UNIX_NOTICE = 5;
128
129    /** Warning: warning conditions (RFC 3164). */
130    public static final int UNIX_WARNING = 4;
131
132    /** Error: error conditions (RFC 3164). */
133    public static final int UNIX_ERROR = 3;
134
135    /** Critical: critical conitions (RFC 3164). */
136    public static final int UNIX_CRITICAL = 2;
137
138    /** Alert: take action immediately (RFC 3164). */
139    public static final int UNIX_ALERT = 1;
140
141    /** Emergency: system is unusable (RFC 3164). */
142    public static final int UNIX_EMERGENCY = 0;
143
144    // map of syslog message severity -> UNIX syslog severity
145
private Map severityMap = null;
146
147    /**
148     * The default severity map.
149     */

150    public static Map DEFAULT_SEVERITY_MAP = null;
151
152    private int[] severityArray = new int[17]; // because Syslog.FATAL = 16
153

154    static
155    {
156        DEFAULT_SEVERITY_MAP = new HashMap();
157        DEFAULT_SEVERITY_MAP.put( new Integer JavaDoc(Syslog.DEBUG), new Integer JavaDoc(UNIX_DEBUG) );
158        DEFAULT_SEVERITY_MAP.put( new Integer JavaDoc(Syslog.INFO), new Integer JavaDoc(UNIX_INFO) );
159        DEFAULT_SEVERITY_MAP.put( new Integer JavaDoc(Syslog.WARNING), new Integer JavaDoc(UNIX_WARNING) );
160        DEFAULT_SEVERITY_MAP.put( new Integer JavaDoc(Syslog.ERROR), new Integer JavaDoc(UNIX_ERROR) );
161        DEFAULT_SEVERITY_MAP.put( new Integer JavaDoc(Syslog.FATAL), new Integer JavaDoc(UNIX_EMERGENCY) );
162    }
163
164    /**
165     * Create a new UNIX logger sending messages to the given host and port.
166     */

167    public UNIXSyslogLog(InetAddress address, int port)
168    throws SyslogInitException
169    {
170        this();
171        this.address = address;
172        this.port = port;
173    }
174
175    /**
176     * Create a new UNIX logger sending messages to the given host
177     * on the default syslog port (514).
178     */

179    public UNIXSyslogLog(InetAddress address)
180    throws SyslogInitException
181    {
182        this(address, DEFAULT_PORT);
183    }
184
185    /**
186     * Create a new UNIX logger with no settings. You must
187     * call <tt>setLogServer()</tt> at least before trying
188     * to use this.
189     */

190    public UNIXSyslogLog()
191    throws SyslogInitException
192    {
193        super();
194
195        this.simpleFormat = new SimpleSyslogTextFormatter();
196        this.simpleFormat.setClassWidth(0);
197
198        setSeverityMap(DEFAULT_SEVERITY_MAP);
199
200        try
201        {
202            socket = new DatagramSocket();
203        }
204        catch (SocketException x)
205        {
206            throw new SyslogInitException(Syslog.getResourceString(MessageConstants.UNIX_CANNOT_INIT_SOCKET), x);
207        }
208    }
209
210    /**
211     * Set the message body formatting template. This string is used
212     * to format the body of the UDP packet in conjunction with the
213     * <tt>java.text.MessageFormat</tt> class. The following tokens are
214     * replaced in the String during formatting:<P>
215     *
216     * <table border=1 cellpadding=3 cellspacing=0>
217     * <tr><td><tt>{CHANNEL}</tt></td><td>Channel name</td></tr>
218     * <tr><td><tt>{CALLER-FULLNAME}</tt></td><td>Full caller class name (with package)</td></tr>
219     * <tr><td><tt>{CALLER}</tt></td><td>Caller class name</td></tr>
220     * <tr><td><tt>{MESSAGE}</tt></td><td>Short message</td></tr>
221     * <tr><td><tt>{THREAD}</tt></td><td>Thread name</td></tr>
222     * <tr><td><tt>{SEVERITY}</tt></td><td>Severity ("<tt>DEBUG</tt>" ... "<tt>FATAL</tt>")</td></tr>
223     * </table><P>
224     *
225     * The default value is "<TT>[{SEVERITY}] {CALLER}: {MESSAGE}</TT>".
226     * You can set this to something like "<tt>{CHANNEL}: {CALLER}: {MESSAGE}</TT>" or
227     * "<tt>{CALLER}: [{CHANNEL}] {MESSAGE}</TT>" as examples to see what you can do
228     * with this.
229     */

230    public void setMessageTemplate(String JavaDoc template)
231    {
232        this.messageTemplate = template;
233    }
234
235    /**
236     * Get the text template used to format part of the packet body.
237     */

238    public String JavaDoc getMessageTemplate()
239    {
240        return this.messageTemplate;
241    }
242
243    /**
244     * Set the map to use when converting Syslog severities into UNIX severities.
245     * The key in the map should be a <tt>java.lang.Integer</tt> representing the
246     * Syslog severity (<TT>Syslog.DEBUG</TT> ... <tt>Syslog.FATAL</TT>), and
247     * the value should be a <tt>java.lang.Integer</TT> representing the UNIX
248     * severity to use for the given Syslog severity. The values for UNIX severities
249     * are described in RFC 3164, and are represented as static members of this
250     * class (<TT>UNIXSyslogLog.UNIX_DEBUG</TT> ... <TT>UNIXSyslogLog.UNIX_EMERGENCY</TT>).
251     */

252    public void setSeverityMap(Map map)
253    {
254        this.severityMap = map;
255
256        // move things into this array so we can do lookups faster
257
// and not create objects needlessly later.
258
try
259        {
260            severityArray[Syslog.DEBUG] = ((Integer JavaDoc)map.get(new Integer JavaDoc(Syslog.DEBUG))).intValue();
261            severityArray[Syslog.INFO] = ((Integer JavaDoc)map.get(new Integer JavaDoc(Syslog.INFO))).intValue();
262            severityArray[Syslog.WARNING] = ((Integer JavaDoc)map.get(new Integer JavaDoc(Syslog.WARNING))).intValue();
263            severityArray[Syslog.ERROR] = ((Integer JavaDoc)map.get(new Integer JavaDoc(Syslog.ERROR))).intValue();
264            severityArray[Syslog.FATAL] = ((Integer JavaDoc)map.get(new Integer JavaDoc(Syslog.FATAL))).intValue();
265        }
266        catch (NullPointerException JavaDoc x)
267        {
268            throw new IllegalArgumentException JavaDoc(Syslog.getResourceString(MessageConstants.UNIX_BAD_SEVERITY_MAP));
269        }
270    }
271
272    /**
273     * Get the map to use when converting Syslog severities into UNIX severities.
274     */

275    public Map getSeverityMap()
276    {
277        return this.severityMap;
278    }
279
280    /**
281     * Get the tag that messages appear as. Default is "<tt>ProtomatterSyslog</TT>".
282     */

283    public String JavaDoc getTag()
284    {
285        return this.tag;
286    }
287
288    /**
289     * Set the tag that messages appear as.
290     */

291    public void setTag(String JavaDoc tag)
292    {
293        this.tag = tag;
294        this.tagBytes = tag.getBytes();
295    }
296
297    /**
298     * Determine if we should show the hostname before the "tag". Default is "<TT>false</TT>"
299     * The spec says that you should do this, but when I tested under Red Hat
300     * Linux 7.2, it just repeated the hostname. Your mileage may vary. The default
301     * is <tt>false</tt>.
302     */

303    public boolean getShowHostname()
304    {
305        return this.showHostname;
306    }
307
308    /**
309     * Set if we should show the hostname before the "tag".
310     */

311    public void setShowHostname(boolean showHostname)
312    {
313        this.showHostname = showHostname;;
314    }
315
316    /**
317     * Set the facility ID that messages appear to be from.
318     * Refer to RFC 3164 for more information. Here is the
319     * list:<P>
320     * <TABLE cellpadding=4 cellspacing=0>
321     * <TR><TD>0</TD><TD>kernel messages</TD></TR>
322     * <TR><TD>1</TD><TD>user-level messages</TD><TD>
323     * <TR><TD>2</TD><TD>mail system</TD><TD>
324     * <TR><TD>3</TD><TD>system daemons</TD><TD>
325     * <TR><TD>4</TD><TD>security/authorization messages (note 1)</TD><TD>
326     * <TR><TD>5</TD><TD>messages generated internally by syslogd</TD><TD>
327     * <TR><TD>6</TD><TD>line printer subsystem</TD><TD>
328     * <TR><TD>7</TD><TD>network news subsystem</TD><TD>
329     * <TR><TD>8</TD><TD>UUCP subsystem</TD><TD>
330     * <TR><TD>9</TD><TD>clock daemon (note 2)</TD><TD>
331     * <TR><TD>10</TD><TD>security/authorization messages (note 1)</TD><TD>
332     * <TR><TD>11</TD><TD>FTP daemon</TD><TD>
333     * <TR><TD>12</TD><TD>NTP subsystem</TD><TD>
334     * <TR><TD>13</TD><TD>log audit (note 1)</TD><TD>
335     * <TR><TD>14</TD><TD>log alert (note 1)</TD><TD>
336     * <TR><TD>15</TD><TD>clock daemon (note 2)</TD><TD>
337     * <TR><TD>16</TD><TD>local use 0 (local0)</TD><TD>
338     * <TR><TD>17</TD><TD>local use 1 (local1)</TD><TD>
339     * <TR><TD>18</TD><TD>local use 2 (local2)</TD><TD>
340     * <TR><TD>19</TD><TD>local use 3 (local3)</TD><TD>
341     * <TR><TD>20</TD><TD>local use 4 (local4)</TD><TD>
342     * <TR><TD>21</TD><TD>local use 5 (local5)</TD><TD>
343     * <TR><TD>22</TD><TD>local use 6 (local6)</TD><TD>
344     * <TR><TD>23</TD><TD>local use 7 (local7)</TD><TD>
345     * </TABLE><P>
346     *
347     * <B>Note 1:</B> Various operating systems have been found to utilize
348     * Facilities 4, 10, 13 and 14 for security/authorization,
349     * audit, and alert messages which seem to be similar.<P>
350     * <B>Note 2:</B> Various operating systems have been found to utilize
351     * both Facilities 9 and 15 for clock (cron/at) messages.<P>
352     *
353     * The default is 16 (Local Use 0).
354     */

355    public void setFacility(int facility)
356    {
357        this.facility = facility;
358    }
359
360    /**
361     * Get the facility ID that messages appear to be from.
362     */

363    public int getFacility()
364    {
365        return this.facility;
366    }
367
368    /**
369     * Set the address of the log server.
370     */

371    public void setLogServer(InetAddress address)
372    {
373        this.address = address;
374    }
375
376    /**
377     * Get the address of the log server.
378     */

379    public InetAddress getLogServer()
380    {
381        return this.address;
382    }
383
384    /**
385     * Set the port number on the log server.
386     */

387    public void setPort(int port)
388    {
389        this.port = port;
390    }
391
392    /**
393     * Get the port number on the log server.
394     */

395    public int getPort()
396    {
397        return this.port;
398    }
399
400    private int translateMessageSeverity(int syslogSeverity)
401    {
402       return severityArray[syslogSeverity];
403    }
404
405    /**
406     * Log a message.
407     */

408    public final void log(SyslogMessage message)
409    {
410       try
411       {
412           // fast translation of severity
413
int severity = severityArray[message.level];
414
415           ByteArrayOutputStream os = new ByteArrayOutputStream(256);
416
417           // encode facility and severity -- "PRI" section
418
os.write(L_ANGLE);
419           os.write(String.valueOf(facility).getBytes());
420           os.write(String.valueOf((8*facility) + severity).getBytes());
421           os.write(R_ANGLE);
422
423           // next is the "HEADER" section -- TIMESTAMP HOSTNAME
424
byte timestamp[] = null;
425           long now = System.currentTimeMillis();
426           if ((now - cacheTime) > cacheTimeout)
427           {
428               timestamp = dateFormat.format(new Date(message.time)).getBytes();
429               cacheDate = timestamp;
430               cacheTime = now;
431           }
432           else
433           {
434               timestamp = cacheDate;
435           }
436           os.write(timestamp);
437           os.write(SPACE);
438
439           // For some reason, Linux syslog duplicates the hostname?
440
// Maybe I'm doing something wrong?
441
if (this.showHostname)
442           {
443               os.write(simpleFormat.getHostname(Syslog.getLocalHostName()).getBytes());
444               os.write(SPACE);
445           }
446
447           // Next is the MSG section -- TAG: CONTENT
448
// TAG is taken as the caller name,
449
// CONTENT is the short message
450
os.write(this.tagBytes);
451           os.write(COLON);
452           os.write(SPACE);
453
454           String JavaDoc body = messageTemplate;
455
456           body = StringUtil.replace(body, CHANNEL_TOKEN, message.channel);
457           body = StringUtil.replace(body, FULLCALLER_TOKEN, message.loggerClassname);
458           body = StringUtil.replace(body, CALLER_TOKEN, trimFromLastPeriod(message.loggerClassname));
459           body = StringUtil.replace(body, MESSAGE_TOKEN, String.valueOf(message.msg));
460           body = StringUtil.replace(body, THREAD_TOKEN, message.threadName);
461           body = StringUtil.replace(body, SEVERITY_TOKEN, getSeverityName(message.level));
462
463           os.write(body.getBytes());
464
465           // Be sure to truncate data according to the spec
466
byte[] data = os.toByteArray();
467           DatagramPacket packet = new DatagramPacket(data,
468             ((data.length > MAX_DATA_LENGTH) ? MAX_DATA_LENGTH : data.length),
469             address, port);
470
471           // Stuff the packet down the pipe
472
socket.send(packet);
473       }
474       catch (IOException x)
475       {
476         System.err.println(MessageFormat.format(
477           Syslog.getResourceString(MessageConstants.UNIX_CANNOT_WRITE_MESSAGE),
478           new Object JavaDoc[] { x.toString() }));
479         x.printStackTrace();
480       }
481    }
482
483    protected String JavaDoc getSeverityName(int syslogSeverity)
484    {
485        switch (syslogSeverity)
486        {
487            case Syslog.DEBUG: return "DEBUG";
488            case Syslog.INFO: return "INFO";
489            case Syslog.WARNING: return "WARNING";
490            case Syslog.ERROR: return "ERROR";
491            case Syslog.FATAL: return "FATAL";
492        }
493        return "UNKNOWN";
494    }
495
496    private static final String JavaDoc trimFromLastPeriod(String JavaDoc s)
497    {
498        if (s == null || s.length() == 0)
499            return "";
500        int index = s.lastIndexOf(".");
501        if (index == -1)
502            return s;
503        return s.substring(index +1);
504    }
505
506    public synchronized void shutdown()
507    {
508        // Don't do anything
509
}
510
511    public void flush()
512    {
513        // Don't do anything
514
}
515 }
516
Popular Tags