X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=srcjar%2Forg%2Fapache%2Flog4j%2Fnet%2FSyslogAppender.java;fp=srcjar%2Forg%2Fapache%2Flog4j%2Fnet%2FSyslogAppender.java;h=b0246453c98bb2e3fdf23df07dc494ceb9be8544;hb=2d6292c0377bc6b773c6844a45d3f2c5fac352c7;hp=0000000000000000000000000000000000000000;hpb=954af328a2a6a0055572cd1a09ee035301222574;p=jalview.git diff --git a/srcjar/org/apache/log4j/net/SyslogAppender.java b/srcjar/org/apache/log4j/net/SyslogAppender.java new file mode 100644 index 0000000..b024645 --- /dev/null +++ b/srcjar/org/apache/log4j/net/SyslogAppender.java @@ -0,0 +1,602 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.log4j.net; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.regex.Pattern; + +import org.apache.log4j.AppenderSkeleton; +import org.apache.log4j.Layout; +import org.apache.log4j.helpers.SyslogQuietWriter; +import org.apache.log4j.helpers.SyslogWriter; +import org.apache.log4j.spi.LoggingEvent; + +// Contributors: Yves Bossel +// Christopher Taylor + +/** + Use SyslogAppender to send log messages to a remote syslog daemon. + + @author Ceki Gülcü + @author Anders Kristensen + */ +public class SyslogAppender extends AppenderSkeleton { + // The following constants are extracted from a syslog.h file + // copyrighted by the Regents of the University of California + // I hope nobody at Berkley gets offended. + + /** + * Maximum length of a TAG string. + */ + private static final int MAX_TAG_LEN = 32; + + /** Kernel messages */ + final static public int LOG_KERN = 0; + /** Random user-level messages */ + final static public int LOG_USER = 1<<3; + /** Mail system */ + final static public int LOG_MAIL = 2<<3; + /** System daemons */ + final static public int LOG_DAEMON = 3<<3; + /** security/authorization messages */ + final static public int LOG_AUTH = 4<<3; + /** messages generated internally by syslogd */ + final static public int LOG_SYSLOG = 5<<3; + + /** line printer subsystem */ + final static public int LOG_LPR = 6<<3; + /** network news subsystem */ + final static public int LOG_NEWS = 7<<3; + /** UUCP subsystem */ + final static public int LOG_UUCP = 8<<3; + /** clock daemon */ + final static public int LOG_CRON = 9<<3; + /** security/authorization messages (private) */ + final static public int LOG_AUTHPRIV = 10<<3; + /** ftp daemon */ + final static public int LOG_FTP = 11<<3; + + // other codes through 15 reserved for system use + /** reserved for local use */ + final static public int LOG_LOCAL0 = 16<<3; + /** reserved for local use */ + final static public int LOG_LOCAL1 = 17<<3; + /** reserved for local use */ + final static public int LOG_LOCAL2 = 18<<3; + /** reserved for local use */ + final static public int LOG_LOCAL3 = 19<<3; + /** reserved for local use */ + final static public int LOG_LOCAL4 = 20<<3; + /** reserved for local use */ + final static public int LOG_LOCAL5 = 21<<3; + /** reserved for local use */ + final static public int LOG_LOCAL6 = 22<<3; + /** reserved for local use*/ + final static public int LOG_LOCAL7 = 23<<3; + + protected static final int SYSLOG_HOST_OI = 0; + protected static final int FACILITY_OI = 1; + + static final String TAB = " "; + + static final Pattern NOT_ALPHANUM = Pattern.compile("[^\\p{Alnum}]"); + + // Have LOG_USER as default + int syslogFacility = LOG_USER; + String facilityStr; + boolean facilityPrinting = false; + + //SyslogTracerPrintWriter stp; + SyslogQuietWriter sqw; + String syslogHost; + + /** + * If true, the appender will generate the HEADER (timestamp and host name) + * part of the syslog packet. + * @since 1.2.15 + */ + private boolean header = false; + + /** + * The TAG part of the syslog message. + * + * @since 1.2.18 + */ + private String tag = null; + + /** + * Date format used if header = true. + * @since 1.2.15 + */ + private final SimpleDateFormat dateFormat = new SimpleDateFormat("MMM dd HH:mm:ss ", Locale.ENGLISH); + + /** + * Host name used to identify messages from this appender. + * @since 1.2.15 + */ + private String localHostname; + + /** + * Set to true after the header of the layout has been sent or if it has none. + */ + private boolean layoutHeaderChecked = false; + + public + SyslogAppender() { + this.initSyslogFacilityStr(); + } + + public + SyslogAppender(Layout layout, int syslogFacility) { + this.layout = layout; + this.syslogFacility = syslogFacility; + this.initSyslogFacilityStr(); + } + + public + SyslogAppender(Layout layout, String syslogHost, int syslogFacility) { + this(layout, syslogFacility); + setSyslogHost(syslogHost); + } + + /** + Release any resources held by this SyslogAppender. + + @since 0.8.4 + */ + synchronized + public + void close() { + closed = true; + if (sqw != null) { + try { + if (layoutHeaderChecked && layout != null && layout.getFooter() != null) { + sendLayoutMessage(layout.getFooter()); + } + sqw.close(); + sqw = null; + } catch(java.io.InterruptedIOException e) { + Thread.currentThread().interrupt(); + sqw = null; + } catch(IOException e) { + sqw = null; + } + } + } + + private + void initSyslogFacilityStr() { + facilityStr = getFacilityString(this.syslogFacility); + + if (facilityStr == null) { + System.err.println("\"" + syslogFacility + + "\" is an unknown syslog facility. Defaulting to \"USER\"."); + this.syslogFacility = LOG_USER; + facilityStr = "user:"; + } else { + facilityStr += ":"; + } + } + + /** + Returns the specified syslog facility as a lower-case String, + e.g. "kern", "user", etc. + */ + public + static + String getFacilityString(int syslogFacility) { + switch(syslogFacility) { + case LOG_KERN: return "kern"; + case LOG_USER: return "user"; + case LOG_MAIL: return "mail"; + case LOG_DAEMON: return "daemon"; + case LOG_AUTH: return "auth"; + case LOG_SYSLOG: return "syslog"; + case LOG_LPR: return "lpr"; + case LOG_NEWS: return "news"; + case LOG_UUCP: return "uucp"; + case LOG_CRON: return "cron"; + case LOG_AUTHPRIV: return "authpriv"; + case LOG_FTP: return "ftp"; + case LOG_LOCAL0: return "local0"; + case LOG_LOCAL1: return "local1"; + case LOG_LOCAL2: return "local2"; + case LOG_LOCAL3: return "local3"; + case LOG_LOCAL4: return "local4"; + case LOG_LOCAL5: return "local5"; + case LOG_LOCAL6: return "local6"; + case LOG_LOCAL7: return "local7"; + default: return null; + } + } + + /** + Returns the integer value corresponding to the named syslog + facility, or -1 if it couldn't be recognized. + + @param facilityName one of the strings KERN, USER, MAIL, DAEMON, + AUTH, SYSLOG, LPR, NEWS, UUCP, CRON, AUTHPRIV, FTP, LOCAL0, + LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. + The matching is case-insensitive. + + @since 1.1 + */ + public + static + int getFacility(String facilityName) { + if(facilityName != null) { + facilityName = facilityName.trim(); + } + if("KERN".equalsIgnoreCase(facilityName)) { + return LOG_KERN; + } else if("USER".equalsIgnoreCase(facilityName)) { + return LOG_USER; + } else if("MAIL".equalsIgnoreCase(facilityName)) { + return LOG_MAIL; + } else if("DAEMON".equalsIgnoreCase(facilityName)) { + return LOG_DAEMON; + } else if("AUTH".equalsIgnoreCase(facilityName)) { + return LOG_AUTH; + } else if("SYSLOG".equalsIgnoreCase(facilityName)) { + return LOG_SYSLOG; + } else if("LPR".equalsIgnoreCase(facilityName)) { + return LOG_LPR; + } else if("NEWS".equalsIgnoreCase(facilityName)) { + return LOG_NEWS; + } else if("UUCP".equalsIgnoreCase(facilityName)) { + return LOG_UUCP; + } else if("CRON".equalsIgnoreCase(facilityName)) { + return LOG_CRON; + } else if("AUTHPRIV".equalsIgnoreCase(facilityName)) { + return LOG_AUTHPRIV; + } else if("FTP".equalsIgnoreCase(facilityName)) { + return LOG_FTP; + } else if("LOCAL0".equalsIgnoreCase(facilityName)) { + return LOG_LOCAL0; + } else if("LOCAL1".equalsIgnoreCase(facilityName)) { + return LOG_LOCAL1; + } else if("LOCAL2".equalsIgnoreCase(facilityName)) { + return LOG_LOCAL2; + } else if("LOCAL3".equalsIgnoreCase(facilityName)) { + return LOG_LOCAL3; + } else if("LOCAL4".equalsIgnoreCase(facilityName)) { + return LOG_LOCAL4; + } else if("LOCAL5".equalsIgnoreCase(facilityName)) { + return LOG_LOCAL5; + } else if("LOCAL6".equalsIgnoreCase(facilityName)) { + return LOG_LOCAL6; + } else if("LOCAL7".equalsIgnoreCase(facilityName)) { + return LOG_LOCAL7; + } else { + return -1; + } + } + + + private void splitPacket(final String header, final String packet) { + int byteCount = packet.getBytes().length; + // + // if packet is less than RFC 3164 limit + // of 1024 bytes, then write it + // (must allow for up 5to 5 characters in the PRI section + // added by SyslogQuietWriter) + if (byteCount <= 1019) { + sqw.write(packet); + } else { + int split = header.length() + (packet.length() - header.length())/2; + splitPacket(header, packet.substring(0, split) + "..."); + splitPacket(header, header + "..." + packet.substring(split)); + } + } + + public + void append(LoggingEvent event) { + + if(!isAsSevereAsThreshold(event.getLevel())) { + return; + } + + // We must not attempt to append if sqw is null. + if(sqw == null) { + errorHandler.error("No syslog host is set for SyslogAppedender named \""+ + this.name+"\"."); + return; + } + + if (!layoutHeaderChecked) { + if (layout != null && layout.getHeader() != null) { + sendLayoutMessage(layout.getHeader()); + } + layoutHeaderChecked = true; + } + + String hdr = getPacketHeader(event.timeStamp); + String packet; + if (layout == null) { + packet = String.valueOf(event.getMessage()); + } else { + packet = layout.format(event); + } + if(facilityPrinting || hdr.length() > 0) { + StringBuffer buf = new StringBuffer(hdr); + if(facilityPrinting) { + buf.append(facilityStr); + } + buf.append(packet); + packet = buf.toString(); + } + + sqw.setLevel(event.getLevel().getSyslogEquivalent()); + // + // if message has a remote likelihood of exceeding 1024 bytes + // when encoded, consider splitting message into multiple packets + if (packet.length() > 256) { + splitPacket(hdr, packet); + } else { + sqw.write(packet); + } + + if (layout == null || layout.ignoresThrowable()) { + String[] s = event.getThrowableStrRep(); + if (s != null) { + for(int i = 0; i < s.length; i++) { + if (s[i].startsWith("\t")) { + sqw.write(hdr+TAB+s[i].substring(1)); + } else { + sqw.write(hdr+s[i]); + } + } + } + } + } + + /** + This method returns immediately as options are activated when they + are set. + */ + public + void activateOptions() { + if (header) { + getLocalHostname(); + } + if (layout != null && layout.getHeader() != null) { + sendLayoutMessage(layout.getHeader()); + } + layoutHeaderChecked = true; + } + + /** + The SyslogAppender requires a layout. Hence, this method returns + true. + + @since 0.8.4 */ + public + boolean requiresLayout() { + return true; + } + + /** + The SyslogHost option is the name of the the syslog host + where log output should go. A non-default port can be specified by + appending a colon and port number to a host name, + an IPv4 address or an IPv6 address enclosed in square brackets. + + WARNING If the SyslogHost is not set, then this appender + will fail. + */ + public + void setSyslogHost(final String syslogHost) { + this.sqw = new SyslogQuietWriter(new SyslogWriter(syslogHost), + syslogFacility, errorHandler); + //this.stp = new SyslogTracerPrintWriter(sqw); + this.syslogHost = syslogHost; + } + + /** + Returns the value of the SyslogHost option. + */ + public + String getSyslogHost() { + return syslogHost; + } + + /** + Set the syslog facility. This is the Facility option. + +

The facilityName parameter must be one of the + strings KERN, USER, MAIL, DAEMON, AUTH, SYSLOG, LPR, NEWS, UUCP, + CRON, AUTHPRIV, FTP, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, + LOCAL5, LOCAL6, LOCAL7. Case is unimportant. + + @since 0.8.1 */ + public + void setFacility(String facilityName) { + if(facilityName == null) { + return; + } + + syslogFacility = getFacility(facilityName); + if (syslogFacility == -1) { + System.err.println("["+facilityName + + "] is an unknown syslog facility. Defaulting to [USER]."); + syslogFacility = LOG_USER; + } + + this.initSyslogFacilityStr(); + + // If there is already a sqw, make it use the new facility. + if(sqw != null) { + sqw.setSyslogFacility(this.syslogFacility); + } + } + + /** + Returns the value of the Facility option. + */ + public + String getFacility() { + return getFacilityString(syslogFacility); + } + + /** + If the FacilityPrinting option is set to true, the printed + message will include the facility name of the application. It is + false by default. + */ + public + void setFacilityPrinting(boolean on) { + facilityPrinting = on; + } + + /** + Returns the value of the FacilityPrinting option. + */ + public + boolean getFacilityPrinting() { + return facilityPrinting; + } + + /** + * If true, the appender will generate the HEADER part (that is, timestamp and host name) + * of the syslog packet. Default value is false for compatibility with existing behavior, + * however should be true unless there is a specific justification. + * @since 1.2.15 + */ + public final boolean getHeader() { + return header; + } + + /** + * Returns whether the appender produces the HEADER part (that is, timestamp and host name) + * of the syslog packet. + * @since 1.2.15 + */ + public final void setHeader(final boolean val) { + header = val; + } + + /** + * Sets the Tag option. + * + *

+ * If non-{@code null}, the printed HEADER will include the specified tag followed by a colon. If {@code null}, then no tag is printed. + *

+ *

+ * The default value is {@code null}. + *

+ * + * @param tag + * the TAG to be printed out with the header + * @see #getTag() + * @since 1.2.18 + */ + public void setTag(final String tag) { + String newTag = tag; + if (newTag != null) { + if (newTag.length() > MAX_TAG_LEN) { + newTag = newTag.substring(0, MAX_TAG_LEN); + } + if (NOT_ALPHANUM.matcher(newTag).find()) { + throw new IllegalArgumentException("tag contains non-alphanumeric characters"); + } + } + + this.tag = newTag; + } + + /** + * Gets the TAG to be printed with the HEADER portion of the log message. This will return {@code null} if no TAG is to be printed. + *

+ * The default value is {@code null}. + *

+ * + * @return the TAG, max length 32. + * @see #setTag(String) + * @since 1.2.18 + */ + public String getTag() { + return this.tag; + } + + /** + * Get the host name used to identify this appender. + * @return local host name + * @since 1.2.15 + */ + private String getLocalHostname() { + if (localHostname == null) { + try { + InetAddress addr = InetAddress.getLocalHost(); + localHostname = addr.getHostName(); + } catch (UnknownHostException uhe) { + localHostname = "UNKNOWN_HOST"; + } + } + return localHostname; + } + + /** + * Gets HEADER portion of packet. + * @param timeStamp number of milliseconds after the standard base time. + * @return HEADER portion of packet, will be zero-length string if header is false. + * @since 1.2.15 + */ + private String getPacketHeader(final long timeStamp) { + if (header) { + StringBuffer buf = new StringBuffer(dateFormat.format(new Date(timeStamp))); + // RFC 3164 says leading space, not leading zero on days 1-9 + if (buf.charAt(4) == '0') { + buf.setCharAt(4, ' '); + } + buf.append(getLocalHostname()); + buf.append(' '); + if(this.tag != null) { + buf.append(this.tag); + buf.append(": "); + } + return buf.toString(); + } + return ""; + } + + /** + * Set header or footer of layout. + * @param msg message body, may not be null. + */ + private void sendLayoutMessage(final String msg) { + if (sqw != null) { + String packet = msg; + String hdr = getPacketHeader(new Date().getTime()); + if(facilityPrinting || hdr.length() > 0) { + StringBuffer buf = new StringBuffer(hdr); + if(facilityPrinting) { + buf.append(facilityStr); + } + buf.append(msg); + packet = buf.toString(); + } + sqw.setLevel(6); + sqw.write(packet); + } + } +}