Skip to content

Commit d9cb442

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

File tree

4 files changed

+188
-10
lines changed

4 files changed

+188
-10
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: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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+
* </pre>
30+
*/
31+
public class ConcurrentRfc3164DateFormat {
32+
private final BlockingQueue<SimpleDateFormat> monthDateFormats;
33+
private final BlockingQueue<SimpleDateFormat> timeDateFormats;
34+
private final Locale locale;
35+
private final TimeZone timeZone;
36+
37+
/**
38+
* @param locale the locale whose date pattern symbols should be used
39+
* @param timeZone the timezone used by the underlying calendar
40+
* @param maxCacheSize
41+
* @throws NullPointerException if the given pattern or locale is null
42+
* @throws IllegalArgumentException if the given pattern is invalid
43+
*/
44+
public ConcurrentRfc3164DateFormat(Locale locale, TimeZone timeZone, int maxCacheSize) {
45+
this.monthDateFormats = new LinkedBlockingDeque<SimpleDateFormat>(maxCacheSize);
46+
this.timeDateFormats = new LinkedBlockingDeque<SimpleDateFormat>(maxCacheSize);
47+
this.locale = locale;
48+
this.timeZone = timeZone;
49+
}
50+
51+
/**
52+
* Formats a Date into a date/time string.
53+
*
54+
* @param date the time value to be formatted into a time string.
55+
* @return the formatted time string.
56+
*/
57+
@Nonnull
58+
@SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_BAD_PRACTICE")
59+
public String format(@Nonnull Date date) {
60+
61+
62+
// MONTH
63+
SimpleDateFormat monthDateFormat = monthDateFormats.poll();
64+
if (monthDateFormat == null) {
65+
monthDateFormat = new SimpleDateFormat("MMM", locale);
66+
monthDateFormat.setTimeZone(timeZone);
67+
}
68+
String month;
69+
try {
70+
month = monthDateFormat.format(date);
71+
} finally {
72+
monthDateFormats.offer(monthDateFormat);
73+
}
74+
75+
// DAY OF MONTH
76+
GregorianCalendar calendar = new GregorianCalendar();
77+
calendar.setTime(date);
78+
79+
int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
80+
String dayofMonthPadding = dayOfMonth < 10 ? " " : "";
81+
82+
// TIME
83+
SimpleDateFormat timeDateFormat = timeDateFormats.poll();
84+
if (timeDateFormat == null) {
85+
timeDateFormat = new SimpleDateFormat("HH:mm:ss", locale);
86+
timeDateFormat.setTimeZone(timeZone);
87+
}
88+
String time;
89+
try {
90+
time = timeDateFormat.format(date);
91+
} finally {
92+
timeDateFormats.offer(timeDateFormat);
93+
}
94+
95+
String result = month + ' ' + dayofMonthPadding + dayOfMonth + ' ' + time;
96+
97+
return result;
98+
}
99+
100+
@Override
101+
public String toString() {
102+
return "ConcurrentRfc3164DateFormat";
103+
}
104+
}

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
}
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)