Skip to content

Commit fb0a208

Browse files
Fix #37 - Fix RFC 3164 date padding: use for padding day of month instead of using 0
1 parent 4c16653 commit fb0a208

File tree

5 files changed

+203
-18
lines changed

5 files changed

+203
-18
lines changed

src/main/java/com/cloudbees/syslog/SyslogMessage.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
import com.cloudbees.syslog.util.CachingReference;
3636
import com.cloudbees.syslog.util.ConcurrentDateFormat;
37+
import com.cloudbees.syslog.util.ConcurrentRfc3164DateFormat;
3738

3839
/**
3940
* Syslog message as defined in <a href="https://tools.ietf.org/html/rfc5424">RFC 5424 - The Syslog Protocol</a>.
@@ -48,7 +49,7 @@ public class SyslogMessage {
4849

4950
private final static int DEFAULT_CONCURRENCY = 50;
5051
protected final static ConcurrentDateFormat rfc3339DateFormat;
51-
protected final static ConcurrentDateFormat rfc3164DateFormat;
52+
protected final static ConcurrentRfc3164DateFormat rfc3164DateFormat;
5253
private static CachingReference<String> localhostNameReference = new CachingReference<String>(10, TimeUnit.SECONDS) {
5354
@Override
5455
protected String newObject() {
@@ -81,9 +82,8 @@ protected String newObject() {
8182
* The TIMESTAMP field is the local time and is in the format of "Mmm dd hh:mm:ss" (without the quote marks)
8283
* </quote>
8384
*/
84-
rfc3164DateFormat = new ConcurrentDateFormat(
85-
"MMM dd HH:mm:ss",
86-
Locale.US,
85+
rfc3164DateFormat = new ConcurrentRfc3164DateFormat(
86+
Locale.getDefault(),
8787
TimeZone.getDefault(),
8888
concurrency);
8989
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package com.cloudbees.syslog.util;
2+
3+
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
4+
5+
import javax.annotation.Nonnull;
6+
import java.text.SimpleDateFormat;
7+
import java.util.*;
8+
import java.util.concurrent.BlockingQueue;
9+
import java.util.concurrent.LinkedBlockingDeque;
10+
11+
/**
12+
* https://tools.ietf.org/html/rfc3164#section-4.1.2
13+
* <pre>
14+
* The TIMESTAMP field is the local time and is in the format of "Mmm dd
15+
* hh:mm:ss" (without the quote marks) where:
16+
*
17+
* Mmm is the English language abbreviation for the month of the
18+
* year with the first character in uppercase and the other two
19+
* characters in lowercase. The following are the only acceptable
20+
* values:
21+
*
22+
* Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
23+
*
24+
* dd is the day of the month. If the day of the month is less
25+
* than 10, then it MUST be represented as a space and then the
26+
* number. For example, the 7th day of August would be
27+
* represented as "Aug 7", with two spaces between the "g" and
28+
* the "7".
29+
*
30+
* hh:mm:ss is the local time. The hour (hh) is represented in a
31+
* 24-hour format. Valid entries are between 00 and 23,
32+
* inclusive. The minute (mm) and second (ss) entries are between
33+
* 00 and 59 inclusive.
34+
* </pre>
35+
*/
36+
public class ConcurrentRfc3164DateFormat {
37+
private final BlockingQueue<SimpleDateFormat> monthDateFormats;
38+
private final BlockingQueue<SimpleDateFormat> timeDateFormats;
39+
private final Locale locale;
40+
private final TimeZone timeZone;
41+
42+
/**
43+
* @param locale the locale whose date pattern symbols should be used
44+
* @param timeZone the timezone used by the underlying calendar
45+
* @param maxCacheSize
46+
* @throws NullPointerException if the given pattern or locale is null
47+
* @throws IllegalArgumentException if the given pattern is invalid
48+
*/
49+
public ConcurrentRfc3164DateFormat(Locale locale, TimeZone timeZone, int maxCacheSize) {
50+
this.monthDateFormats = new LinkedBlockingDeque<>(maxCacheSize);
51+
this.timeDateFormats = new LinkedBlockingDeque<>(maxCacheSize);
52+
this.locale = locale;
53+
this.timeZone = timeZone;
54+
}
55+
56+
/**
57+
* Formats a Date into a date/time string.
58+
*
59+
* @param date the time value to be formatted into a time string.
60+
* @return the formatted time string.
61+
*/
62+
@Nonnull
63+
@SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_BAD_PRACTICE")
64+
public String format(@Nonnull Date date) {
65+
66+
67+
// MONTH
68+
69+
String month;
70+
71+
72+
// DAY OF MONTH
73+
GregorianCalendar calendar = new GregorianCalendar();
74+
calendar.setTime(date);
75+
76+
int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
77+
String dayofMonthPadding = dayOfMonth < 10 ? " " : "";
78+
79+
// TIME
80+
SimpleDateFormat timeDateFormat = timeDateFormats.poll();
81+
if (timeDateFormat == null) {
82+
timeDateFormat = new SimpleDateFormat("HH:mm:ss", locale);
83+
timeDateFormat.setTimeZone(timeZone);
84+
}
85+
String time;
86+
try {
87+
time = timeDateFormat.format(date);
88+
} finally {
89+
timeDateFormats.offer(timeDateFormat);
90+
}
91+
92+
String result = month + ' ' + dayofMonthPadding + dayOfMonth + ' ' + time;
93+
94+
return result;
95+
}
96+
97+
@Override
98+
public String toString() {
99+
return "ConcurrentRfc3164DateFormat";
100+
}
101+
}

src/test/java/com/cloudbees/syslog/SyslogMessageTest.java

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ public void testRfc5424FormatWithStructuredData() throws Exception {
9595
System.out.println(SyslogMessage.rfc3339DateFormat.format(cal.getTime()));
9696
System.out.println(cal.getTimeInMillis());
9797

98-
9998
SyslogMessage message = new SyslogMessage()
10099
.withTimestamp(cal.getTimeInMillis())
101100
.withAppName("my_app")
@@ -119,15 +118,15 @@ public void testRfc5424FormatWithStructuredData() throws Exception {
119118
}
120119

121120
@Test
122-
public void testRfc3164Format() throws Exception {
121+
public void testRfc3164Format_withTwoDigitsDay() throws Exception {
123122

124123
Calendar cal = Calendar.getInstance();
125124
cal.setTimeZone(TimeZone.getDefault());
126-
cal.set(2013, Calendar.DECEMBER, 5, 10, 30, 5);
125+
cal.set(2013, Calendar.DECEMBER, 15, 10, 30, 5);
127126
cal.set(Calendar.MILLISECOND, 0);
128127

129-
System.out.println(SyslogMessage.rfc3339DateFormat.format(cal.getTime()));
130-
System.out.println(cal.getTimeInMillis());
128+
// System.out.println(SyslogMessage.rfc3339DateFormat.format(cal.getTime()));
129+
// System.out.println(cal.getTimeInMillis());
131130

132131

133132
SyslogMessage message = new SyslogMessage()
@@ -140,9 +139,37 @@ public void testRfc3164Format() throws Exception {
140139
.withMsg("a syslog message");
141140

142141
String actual = message.toRfc3164SyslogMessage();
143-
String expected = "<14>Dec 05 10:30:05 myserver.example.com my_app: a syslog message";
142+
String expected = "<14>Dec 15 10:30:05 myserver.example.com my_app: a syslog message";
144143

145144
assertThat(actual, is(expected));
145+
}
146+
147+
/**
148+
* https://tools.ietf.org/html/rfc3164#section-4.1.2
149+
*/
150+
@Test
151+
public void testRfc3164Format_withSingleDigitDay() throws Exception {
152+
153+
Calendar cal = Calendar.getInstance();
154+
cal.setTimeZone(TimeZone.getDefault());
155+
cal.set(2013, Calendar.AUGUST, 7, 10, 30, 5);
156+
cal.set(Calendar.MILLISECOND, 0);
157+
158+
// System.out.println(SyslogMessage.rfc3339DateFormat.format(cal.getTime()));
159+
// System.out.println(cal.getTimeInMillis());
146160

161+
SyslogMessage message = new SyslogMessage()
162+
.withTimestamp(cal.getTimeInMillis())
163+
.withAppName("my_app")
164+
.withHostname("myserver.example.com")
165+
.withFacility(Facility.USER)
166+
.withSeverity(Severity.INFORMATIONAL)
167+
.withTimestamp(cal.getTimeInMillis())
168+
.withMsg("a syslog message");
169+
170+
String actual = message.toRfc3164SyslogMessage();
171+
String expected = "<14>Aug 7 10:30:05 myserver.example.com my_app: a syslog message";
172+
173+
assertThat(actual, is(expected));
147174
}
148175
}

src/test/java/com/cloudbees/syslog/sender/TcpSyslogMessageSenderTest.java

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,23 @@ public class TcpSyslogMessageSenderTest {
3333
@Test
3434
public void send() throws Exception {
3535
TcpSyslogMessageSender messageSender = new TcpSyslogMessageSender();
36-
messageSender.setDefaultMessageHostname("mysecretkey");
36+
// messageSender.setDefaultMessageHostname("mysecretkey");
3737
messageSender.setDefaultAppName("myapp");
3838
messageSender.setDefaultFacility(Facility.USER);
3939
messageSender.setDefaultSeverity(Severity.INFORMATIONAL);
40-
messageSender.setSyslogServerHostname("logs2.papertrailapp.com");
41-
messageSender.setSyslogServerPort(46022);
40+
41+
// messageSender.setSyslogServerHostname("logs2.papertrailapp.com");
42+
// messageSender.setSyslogServerPort(46022);
43+
// messageSender.setSsl(true);
44+
45+
messageSender.setSyslogServerHostname("localhost");
46+
messageSender.setSyslogServerPort(9000);
47+
4248
messageSender.setMessageFormat(MessageFormat.RFC_3164);
43-
messageSender.setSsl(true);
4449
messageSender.sendMessage("unit test message over tcp éèà " + getClass() + " - " + new Timestamp(System.currentTimeMillis()));
4550
}
4651

47-
@Ignore
52+
// @Ignore
4853
@Test
4954
public void send2() throws Exception {
5055

@@ -57,10 +62,15 @@ public void send2() throws Exception {
5762
.withTimestamp(System.currentTimeMillis());
5863

5964
TcpSyslogMessageSender messageSender = new TcpSyslogMessageSender();
60-
messageSender.setSyslogServerHostname("logs2.papertrailapp.com");
61-
messageSender.setSyslogServerPort(46022);
65+
66+
// messageSender.setSyslogServerHostname("logs2.papertrailapp.com");
67+
// messageSender.setSyslogServerPort(46022);
68+
// messageSender.setSsl(true);
69+
70+
messageSender.setSyslogServerHostname("localhost");
71+
messageSender.setSyslogServerPort(9000);
72+
6273
messageSender.setMessageFormat(MessageFormat.RFC_3164);
63-
messageSender.setSsl(true);
6474

6575
System.out.println(msg.toSyslogMessage(messageSender.getMessageFormat()));
6676

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.cloudbees.syslog.util;
2+
3+
import org.hamcrest.Matchers;
4+
import org.junit.Assert;
5+
import org.junit.Test;
6+
7+
import java.util.Calendar;
8+
import java.util.Date;
9+
import java.util.Locale;
10+
import java.util.TimeZone;
11+
12+
public class ConcurrentRfc3164DateFormatTest {
13+
14+
@Test
15+
public void test_format_date_with_single_digit_day_of_month(){
16+
TimeZone timeZone = TimeZone.getTimeZone("UTC");
17+
Calendar cal = Calendar.getInstance();
18+
cal.setTimeZone(timeZone);
19+
cal.set(2013, Calendar.AUGUST, 7, 10, 30, 5);
20+
cal.set(Calendar.MILLISECOND, 0);
21+
22+
Date singleDigitDayOfMonthDate = cal.getTime();
23+
24+
ConcurrentRfc3164DateFormat rfc3164DateFormat = new ConcurrentRfc3164DateFormat(Locale.US, timeZone, 50);
25+
26+
String actual = rfc3164DateFormat.format(singleDigitDayOfMonthDate);
27+
28+
Assert.assertThat(actual, Matchers.is("Aug 7 10:30:05"));
29+
}
30+
31+
@Test
32+
public void test_format_date_with_double_digit_day_of_month(){
33+
TimeZone timeZone = TimeZone.getTimeZone("UTC");
34+
Calendar cal = Calendar.getInstance();
35+
cal.setTimeZone(timeZone);
36+
cal.set(2013, Calendar.AUGUST, 17, 10, 30, 5);
37+
cal.set(Calendar.MILLISECOND, 0);
38+
39+
Date singleDigitDayOfMonthDate = cal.getTime();
40+
41+
ConcurrentRfc3164DateFormat rfc3164DateFormat = new ConcurrentRfc3164DateFormat(Locale.US, timeZone, 50);
42+
43+
String actual = rfc3164DateFormat.format(singleDigitDayOfMonthDate);
44+
45+
Assert.assertThat(actual, Matchers.is("Aug 17 10:30:05"));
46+
}
47+
}

0 commit comments

Comments
 (0)