diff --git a/src/main/java/org/graylog2/syslog4j/SyslogConstants.java b/src/main/java/org/graylog2/syslog4j/SyslogConstants.java index ca8fc5e..3ded474 100644 --- a/src/main/java/org/graylog2/syslog4j/SyslogConstants.java +++ b/src/main/java/org/graylog2/syslog4j/SyslogConstants.java @@ -24,6 +24,8 @@ public interface SyslogConstants extends Serializable { public static final String STRUCTURED_DATA_NILVALUE = "-"; public static final String STRUCTURED_DATA_EMPTY_VALUE = "[0@0]"; + public static final String SYSLOG_DATEFORMAT_RFC5424 = "yyyy-MM-dd'T'HH:mm:ss'Z'"; + public static final int MAX_MESSAGE_LENGTH_RFC5424 = 2048; public static final String CHAR_SET_DEFAULT = "UTF-8"; diff --git a/src/main/java/org/graylog2/syslog4j/SyslogIF.java b/src/main/java/org/graylog2/syslog4j/SyslogIF.java index 35bcf7f..83f5d55 100644 --- a/src/main/java/org/graylog2/syslog4j/SyslogIF.java +++ b/src/main/java/org/graylog2/syslog4j/SyslogIF.java @@ -1,5 +1,7 @@ package org.graylog2.syslog4j; +import java.util.Date; + /** * SyslogIF provides a common interface for all Syslog4j client implementations. *
@@ -20,9 +22,11 @@ public interface SyslogIF extends SyslogConstants { public void backLog(int level, String message, Throwable reasonThrowable); public void backLog(int level, String message, String reason); - + public void log(int level, String message); + public void log(int level, String message, Date datetime); + public void debug(String message); public void info(String message); diff --git a/src/main/java/org/graylog2/syslog4j/SyslogMessageProcessorIF.java b/src/main/java/org/graylog2/syslog4j/SyslogMessageProcessorIF.java index 0122bf2..6852b89 100644 --- a/src/main/java/org/graylog2/syslog4j/SyslogMessageProcessorIF.java +++ b/src/main/java/org/graylog2/syslog4j/SyslogMessageProcessorIF.java @@ -1,6 +1,7 @@ package org.graylog2.syslog4j; import java.io.Serializable; +import java.util.Date; /** * SyslogMessageProcessorIF provides an extensible interface for writing custom @@ -19,4 +20,6 @@ public interface SyslogMessageProcessorIF extends Serializable { public byte[] createPacketData(byte[] header, byte[] message, int start, int length); public byte[] createPacketData(byte[] header, byte[] message, int start, int length, byte[] splitBeginText, byte[] splitEndText); + + public String createSyslogHeader(int facility, int level, String localName, boolean sendLocalName, Date datetime); } diff --git a/src/main/java/org/graylog2/syslog4j/impl/AbstractSyslog.java b/src/main/java/org/graylog2/syslog4j/impl/AbstractSyslog.java index 97364f5..b7bdc37 100644 --- a/src/main/java/org/graylog2/syslog4j/impl/AbstractSyslog.java +++ b/src/main/java/org/graylog2/syslog4j/impl/AbstractSyslog.java @@ -14,6 +14,7 @@ import org.graylog2.syslog4j.impl.message.structured.StructuredSyslogMessageIF; import org.graylog2.syslog4j.util.SyslogUtility; import java.util.ArrayList; +import java.util.Date; import java.util.List; /** @@ -123,6 +124,17 @@ public abstract class AbstractSyslog implements SyslogIF { log(getMessageProcessor(), level, message); } } + + public void log(int level, String message, Date datetime) { + if (this.syslogConfig.isUseStructuredData()) { + StructuredSyslogMessageIF structuredMessage = new StructuredSyslogMessage(null, null, null, message); + + log(getStructuredMessageProcessor(), level, structuredMessage.createMessage(), datetime); + + } else { + log(getMessageProcessor(), level, message, datetime); + } + } public void log(int level, SyslogMessageIF message) { if (message instanceof StructuredSyslogMessageIF) { @@ -223,7 +235,8 @@ public abstract class AbstractSyslog implements SyslogIF { } try { - write(messageProcessor, level, _message); + String header = messageProcessor.createSyslogHeader(this.syslogConfig.getFacility(), level, this.syslogConfig.getLocalName(), this.syslogConfig.isSendLocalTimestamp(), this.syslogConfig.isSendLocalName()); + write(messageProcessor, level, _message, header); } catch (SyslogRuntimeException sre) { if (sre.getCause() != null) { @@ -239,9 +252,38 @@ public abstract class AbstractSyslog implements SyslogIF { } } - protected void write(SyslogMessageProcessorIF messageProcessor, int level, String message) throws SyslogRuntimeException { - String header = messageProcessor.createSyslogHeader(this.syslogConfig.getFacility(), level, this.syslogConfig.getLocalName(), this.syslogConfig.isSendLocalTimestamp(), this.syslogConfig.isSendLocalName()); + public void log(SyslogMessageProcessorIF messageProcessor, int level, String message, Date datetime) { + String _message = null; + if (this.syslogConfig.isIncludeIdentInMessageModifier()) { + _message = prefixMessage(message, IDENT_SUFFIX_DEFAULT); + _message = modifyMessage(level, _message); + + } else { + _message = modifyMessage(level, message); + _message = prefixMessage(_message, IDENT_SUFFIX_DEFAULT); + } + + try { + String header = messageProcessor.createSyslogHeader(this.syslogConfig.getFacility(), level, this.syslogConfig.getLocalName(), this.syslogConfig.isSendLocalName(), datetime); + write(messageProcessor, level, _message, header); + + } catch (SyslogRuntimeException sre) { + if (sre.getCause() != null) { + backLog(level, _message, sre.getCause()); + + } else { + backLog(level, _message, sre); + } + + if (this.syslogConfig.isThrowExceptionOnWrite()) { + throw sre; + } + } + } + + protected void write(SyslogMessageProcessorIF messageProcessor, int level, String message, String header) throws SyslogRuntimeException { + byte[] h = SyslogUtility.getBytes(this.syslogConfig, header); byte[] m = SyslogUtility.getBytes(this.syslogConfig, message); diff --git a/src/main/java/org/graylog2/syslog4j/impl/AbstractSyslogConfig.java b/src/main/java/org/graylog2/syslog4j/impl/AbstractSyslogConfig.java index c4d4628..5704c54 100644 --- a/src/main/java/org/graylog2/syslog4j/impl/AbstractSyslogConfig.java +++ b/src/main/java/org/graylog2/syslog4j/impl/AbstractSyslogConfig.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; import org.graylog2.syslog4j.SyslogBackLogHandlerIF; +import org.graylog2.syslog4j.SyslogConstants; import org.graylog2.syslog4j.SyslogMessageModifierIF; import org.graylog2.syslog4j.SyslogRuntimeException; import org.graylog2.syslog4j.impl.backlog.printstream.SystemErrSyslogBackLogHandler; @@ -370,6 +371,7 @@ public abstract class AbstractSyslogConfig implements AbstractSyslogConfigIF { public void setUseStructuredData(boolean useStructuredData) { this.useStructuredData = useStructuredData; + setMaxMessageLength(SyslogConstants.MAX_MESSAGE_LENGTH_RFC5424); } public Class getSyslogWriterClass() { diff --git a/src/main/java/org/graylog2/syslog4j/impl/message/processor/AbstractSyslogMessageProcessor.java b/src/main/java/org/graylog2/syslog4j/impl/message/processor/AbstractSyslogMessageProcessor.java index c4cf1e9..c10bc22 100644 --- a/src/main/java/org/graylog2/syslog4j/impl/message/processor/AbstractSyslogMessageProcessor.java +++ b/src/main/java/org/graylog2/syslog4j/impl/message/processor/AbstractSyslogMessageProcessor.java @@ -1,8 +1,6 @@ package org.graylog2.syslog4j.impl.message.processor; -import java.text.SimpleDateFormat; import java.util.Date; -import java.util.Locale; import org.graylog2.syslog4j.SyslogConstants; import org.graylog2.syslog4j.SyslogMessageProcessorIF; @@ -74,21 +72,6 @@ public abstract class AbstractSyslogMessageProcessor implements SyslogMessagePro buffer.append(">"); } - protected void appendLocalTimestamp(StringBuffer buffer) { - SimpleDateFormat dateFormat = new SimpleDateFormat(SYSLOG_DATEFORMAT, Locale.ENGLISH); - - String datePrefix = dateFormat.format(new Date()); - - int pos = buffer.length() + 4; - - buffer.append(datePrefix); - - // RFC 3164 requires leading space for days 1-9 - if (buffer.charAt(pos) == '0') { - buffer.setCharAt(pos, ' '); - } - } - protected void appendLocalName(StringBuffer buffer, String localName) { if (localName != null) { buffer.append(localName); @@ -99,20 +82,7 @@ public abstract class AbstractSyslogMessageProcessor implements SyslogMessagePro buffer.append(' '); } - - public String createSyslogHeader(int facility, int level, String localName, boolean sendLocalTimestamp, boolean sendLocalName) { - StringBuffer buffer = new StringBuffer(); - - appendPriority(buffer, facility, level); - - if (sendLocalTimestamp) { - appendLocalTimestamp(buffer); - } - - if (sendLocalName) { - appendLocalName(buffer, localName); - } - - return buffer.toString(); - } + + protected abstract void appendTimestamp(StringBuffer buffer, Date datetime); + } diff --git a/src/main/java/org/graylog2/syslog4j/impl/message/processor/SyslogMessageProcessor.java b/src/main/java/org/graylog2/syslog4j/impl/message/processor/SyslogMessageProcessor.java index 8f56de3..b75b0a2 100644 --- a/src/main/java/org/graylog2/syslog4j/impl/message/processor/SyslogMessageProcessor.java +++ b/src/main/java/org/graylog2/syslog4j/impl/message/processor/SyslogMessageProcessor.java @@ -1,5 +1,9 @@ package org.graylog2.syslog4j.impl.message.processor; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + /** * SyslogMessageProcessor wraps AbstractSyslogMessageProcessor. @@ -32,4 +36,67 @@ public class SyslogMessageProcessor extends AbstractSyslogMessageProcessor { public static SyslogMessageProcessor getDefault() { return defaultInstance; } + + /* (non-Javadoc) + * @see org.graylog2.syslog4j.impl.message.processor.AbstractSyslogMessageProcessor#appendTimestamp(java.lang.StringBuffer, java.util.Date) + * + * This is compatible with BSD protocol + */ + @Override + public void appendTimestamp(StringBuffer buffer, Date datetime) { + SimpleDateFormat dateFormat = new SimpleDateFormat(SYSLOG_DATEFORMAT, Locale.ENGLISH); + + String datePrefix = dateFormat.format(datetime); + + int pos = buffer.length() + 4; + + buffer.append(datePrefix); + + // RFC 3164 requires leading space for days 1-9 + if (buffer.charAt(pos) == '0') { + buffer.setCharAt(pos, ' '); + } + + } + + /* (non-Javadoc) + * @see org.graylog2.syslog4j.SyslogMessageProcessorIF#createSyslogHeader(int, int, java.lang.String, boolean, boolean) + * + * This is compatible with BSD protocol + */ + public String createSyslogHeader(int facility, int level, String localName, boolean sendLocalTimestamp, boolean sendLocalName) { + StringBuffer buffer = new StringBuffer(); + + appendPriority(buffer, facility, level); + + if (sendLocalTimestamp) { + appendTimestamp(buffer, new Date()); + } + + if (sendLocalName) { + appendLocalName(buffer, localName); + } + + return buffer.toString(); + } + + /* (non-Javadoc) + * @see org.graylog2.syslog4j.SyslogMessageProcessorIF#createSyslogHeader(int, int, java.lang.String, boolean, java.util.Date) + * + * This is compatible with BSD protocol + */ + public String createSyslogHeader(int facility, int level, String localName, boolean sendLocalName, Date datetime) { + StringBuffer buffer = new StringBuffer(); + + appendPriority(buffer, facility, level); + + appendTimestamp(buffer, datetime); + + if (sendLocalName) { + appendLocalName(buffer, localName); + } + + return buffer.toString(); + } + } diff --git a/src/main/java/org/graylog2/syslog4j/impl/message/processor/structured/StructuredSyslogMessageProcessor.java b/src/main/java/org/graylog2/syslog4j/impl/message/processor/structured/StructuredSyslogMessageProcessor.java index bb83ab6..75186de 100644 --- a/src/main/java/org/graylog2/syslog4j/impl/message/processor/structured/StructuredSyslogMessageProcessor.java +++ b/src/main/java/org/graylog2/syslog4j/impl/message/processor/structured/StructuredSyslogMessageProcessor.java @@ -1,7 +1,12 @@ package org.graylog2.syslog4j.impl.message.processor.structured; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.ISODateTimeFormat; +import org.graylog2.syslog4j.SyslogConstants; import org.graylog2.syslog4j.impl.message.processor.AbstractSyslogMessageProcessor; import org.graylog2.syslog4j.impl.message.structured.StructuredSyslogMessage; @@ -29,16 +34,16 @@ import org.graylog2.syslog4j.impl.message.structured.StructuredSyslogMessage; * @version $Id: StructuredSyslogMessageProcessor.java,v 1.4 2011/01/11 05:11:13 cvs Exp $ */ public class StructuredSyslogMessageProcessor extends AbstractSyslogMessageProcessor { - private static final long serialVersionUID = -1563777226913475257L; - + private static final long serialVersionUID = -1563777226913475257L; + public static String VERSION = "1"; + + private static final StructuredSyslogMessageProcessor INSTANCE = new StructuredSyslogMessageProcessor(); - private static final StructuredSyslogMessageProcessor INSTANCE = new StructuredSyslogMessageProcessor(); protected static StructuredSyslogMessageProcessor defaultInstance = INSTANCE; - private String applicationName = STRUCTURED_DATA_APP_NAME_DEFAULT_VALUE; - private String processId = STRUCTURED_DATA_PROCESS_ID_DEFAULT_VALUE; - + private String applicationName = STRUCTURED_DATA_APP_NAME_DEFAULT_VALUE; + private String processId = STRUCTURED_DATA_PROCESS_ID_DEFAULT_VALUE; private DateTimeFormatter dateTimeFormatter = ISODateTimeFormat.dateTime(); public static void setDefault(StructuredSyslogMessageProcessor messageProcessor) { @@ -84,23 +89,53 @@ public class StructuredSyslogMessageProcessor extends AbstractSyslogMessageProce this.processId = processId; } - public String createSyslogHeader(final int facility, final int level, String localName, final boolean sendLocalTimestamp, final boolean sendLocalName) { - final StringBuffer buffer = new StringBuffer(); + /* (non-Javadoc) + * @see org.graylog2.syslog4j.impl.message.processor.AbstractSyslogMessageProcessor#appendTimestamp(java.lang.StringBuffer, java.util.Date) + * + * This is compatible with RFC5424 protocol. + */ + @Override + public void appendTimestamp(StringBuffer buffer, Date datetime) { + SimpleDateFormat formatter = new SimpleDateFormat(SyslogConstants.SYSLOG_DATEFORMAT_RFC5424); + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(datetime.getTime()); + String formatedTimestamp = formatter.format(calendar.getTime()); + buffer.append(formatedTimestamp); + buffer.append(' '); + } + /* (non-Javadoc) + * @see org.graylog2.syslog4j.SyslogMessageProcessorIF#createSyslogHeader(int, int, java.lang.String, boolean, boolean) + * + * This is compatible with RFC5424 protocol. + * + * RFC5424 does not allow flags of sendLocalTimestamp and sendLocalName be off and therefore the incoming flags will not be used in this method. + * + */ + public String createSyslogHeader(int facility, int level, String localName, boolean sendLocalTimestamp, boolean sendLocalName) { + return createSyslogHeaderInner(facility, level, localName, new Date()); + } + + /* (non-Javadoc) + * @see org.graylog2.syslog4j.SyslogMessageProcessorIF#createSyslogHeader(int, int, java.lang.String, boolean, java.util.Date) + * + * This is compatible with RFC5424 protocol. + * + * RFC5424 does not allow sendLocalName flag to be off and therefore sendLocalName will not be used in this method. + */ + public String createSyslogHeader(int facility, int level, String localName, boolean sendLocalName, Date datetime) { + return createSyslogHeaderInner(facility, level, localName, datetime); + } + + private String createSyslogHeaderInner(int facility, int level, String localName, Date datetime) { + StringBuffer buffer = new StringBuffer(); appendPriority(buffer, facility, level); buffer.append(VERSION); buffer.append(' '); - - getDateTimeFormatter().printTo(buffer, System.currentTimeMillis()); - buffer.append(' '); - + appendTimestamp(buffer, datetime); appendLocalName(buffer, localName); - - buffer.append(StructuredSyslogMessage.nilProtect(this.applicationName)) - .append(' '); - + buffer.append(StructuredSyslogMessage.nilProtect(this.applicationName)).append(' '); buffer.append(StructuredSyslogMessage.nilProtect(this.processId)).append(' '); - return buffer.toString(); } } diff --git a/src/main/java/org/graylog2/syslog4j/impl/multiple/MultipleSyslog.java b/src/main/java/org/graylog2/syslog4j/impl/multiple/MultipleSyslog.java index 839a58e..6cfad8f 100644 --- a/src/main/java/org/graylog2/syslog4j/impl/multiple/MultipleSyslog.java +++ b/src/main/java/org/graylog2/syslog4j/impl/multiple/MultipleSyslog.java @@ -1,5 +1,7 @@ package org.graylog2.syslog4j.impl.multiple; +import java.util.Date; + import org.graylog2.syslog4j.Syslog; import org.graylog2.syslog4j.SyslogConfigIF; import org.graylog2.syslog4j.SyslogConstants; @@ -171,4 +173,9 @@ public class MultipleSyslog implements SyslogIF { public String getProtocol() { return this.syslogProtocol; } + + public void log(int level, String message, Date datetime) { + + } + } diff --git a/src/main/java/org/graylog2/syslog4j/impl/net/tcp/TCPNetSyslogConfig.java b/src/main/java/org/graylog2/syslog4j/impl/net/tcp/TCPNetSyslogConfig.java index f80dee9..5a63cb6 100644 --- a/src/main/java/org/graylog2/syslog4j/impl/net/tcp/TCPNetSyslogConfig.java +++ b/src/main/java/org/graylog2/syslog4j/impl/net/tcp/TCPNetSyslogConfig.java @@ -45,6 +45,17 @@ public class TCPNetSyslogConfig extends AbstractNetSyslogConfig implements TCPNe protected int freshConnectionInterval = TCP_FRESH_CONNECTION_INTERVAL_DEFAULT; + /** + * useFrameHeader flag enables frame header. + * + * Frame header is sometimes used when writing the syslog message (for example in syslog-ng server syslog() driver). + * It does not allow delimiterSequence; its structure is : digit(size of message byte array) + space + syslog message + * e.g. where the size of message (byte array) is 89: + * 89 <165>1 2003-10-11T22:14:15.003Z mymachine.example.com evntslog - ID47 [exampleSDID@32473] + * + */ + private boolean useFrameHeader; + public TCPNetSyslogConfig() { initialize(); } @@ -149,6 +160,14 @@ public class TCPNetSyslogConfig extends AbstractNetSyslogConfig implements TCPNe public void setFreshConnectionInterval(int freshConnectionInterval) { this.freshConnectionInterval = freshConnectionInterval; } + + public void setUseFrameHeader(boolean useFrameHeader) { + this.useFrameHeader = useFrameHeader; + } + + public boolean isUseFrameHeader() { + return this.useFrameHeader; + } public Class getSyslogWriterClass() { return TCPNetSyslogWriter.class; diff --git a/src/main/java/org/graylog2/syslog4j/impl/net/tcp/TCPNetSyslogConfigIF.java b/src/main/java/org/graylog2/syslog4j/impl/net/tcp/TCPNetSyslogConfigIF.java index 66b00c9..5e5ea65 100644 --- a/src/main/java/org/graylog2/syslog4j/impl/net/tcp/TCPNetSyslogConfigIF.java +++ b/src/main/java/org/graylog2/syslog4j/impl/net/tcp/TCPNetSyslogConfigIF.java @@ -45,4 +45,8 @@ public interface TCPNetSyslogConfigIF extends AbstractNetSyslogConfigIF { public int getFreshConnectionInterval(); public void setFreshConnectionInterval(int interval); + + public void setUseFrameHeader(boolean useFrameHeader); + + public boolean isUseFrameHeader(); } diff --git a/src/main/java/org/graylog2/syslog4j/impl/net/tcp/TCPNetSyslogWriter.java b/src/main/java/org/graylog2/syslog4j/impl/net/tcp/TCPNetSyslogWriter.java index aa7e84a..0401e7c 100644 --- a/src/main/java/org/graylog2/syslog4j/impl/net/tcp/TCPNetSyslogWriter.java +++ b/src/main/java/org/graylog2/syslog4j/impl/net/tcp/TCPNetSyslogWriter.java @@ -129,7 +129,6 @@ public class TCPNetSyslogWriter extends AbstractSyslogWriter { public void write(byte[] message) throws SyslogRuntimeException { Socket currentSocket = null; - int attempts = 0; while (attempts != -1 && attempts < (this.tcpNetSyslogConfig.getWriteRetries() + 1)) { try { @@ -140,17 +139,25 @@ public class TCPNetSyslogWriter extends AbstractSyslogWriter { } OutputStream os = currentSocket.getOutputStream(); - - if (this.tcpNetSyslogConfig.isSetBufferSize()) { - currentSocket.setSendBufferSize(message.length); + + String frameHeader = ""; + if(this.tcpNetSyslogConfig.isUseFrameHeader()){ + frameHeader = message.length + " "; } - + + if (this.tcpNetSyslogConfig.isSetBufferSize()) { + currentSocket.setSendBufferSize(message.length + frameHeader.length()); + } + + os.write(frameHeader.getBytes()); os.write(message); - byte[] delimiterSequence = this.tcpNetSyslogConfig.getDelimiterSequence(); - if (delimiterSequence != null && delimiterSequence.length > 0) { - os.write(delimiterSequence); - } + if(!this.tcpNetSyslogConfig.isUseFrameHeader()) { + byte[] delimiterSequence = this.tcpNetSyslogConfig.getDelimiterSequence(); + if (delimiterSequence != null && delimiterSequence.length > 0) { + os.write(delimiterSequence); + } + } this.syslog.setBackLogStatus(false); diff --git a/src/main/java/org/graylog2/syslog4j/impl/unix/socket/UnixSocketSyslog.java b/src/main/java/org/graylog2/syslog4j/impl/unix/socket/UnixSocketSyslog.java index c8f43b0..93551ab 100644 --- a/src/main/java/org/graylog2/syslog4j/impl/unix/socket/UnixSocketSyslog.java +++ b/src/main/java/org/graylog2/syslog4j/impl/unix/socket/UnixSocketSyslog.java @@ -1,6 +1,7 @@ package org.graylog2.syslog4j.impl.unix.socket; import java.nio.ByteBuffer; +import java.util.List; import org.graylog2.syslog4j.SyslogRuntimeException; import org.graylog2.syslog4j.impl.AbstractSyslog; @@ -39,6 +40,11 @@ public class UnixSocketSyslog extends AbstractSyslog { System.arraycopy(sunPath.getBytes(), 0, this.sun_path, 0, sunPath.length()); System.arraycopy(ZERO_BYTE, 0, this.sun_path, sunPath.length(), 1); } + + @Override + protected List getFieldOrder() { + return Collections.emptyList(); + } } protected interface CLibrary extends Library {