001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.camel.component.syslog;
019
020 import java.net.InetAddress;
021 import java.net.UnknownHostException;
022 import java.nio.ByteBuffer;
023 import java.util.Calendar;
024 import java.util.Date;
025 import java.util.GregorianCalendar;
026 import java.util.HashMap;
027 import java.util.Map;
028
029 import org.apache.camel.Converter;
030 import org.slf4j.Logger;
031 import org.slf4j.LoggerFactory;
032
033 public final class Rfc3164SyslogConverter {
034
035 private static final transient Logger LOG = LoggerFactory.getLogger(Rfc3164SyslogConverter.class);
036
037 private static enum MONTHS {
038 jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec
039 }
040
041 private static Map<String, MONTHS> monthValueMap = new HashMap<String, MONTHS>() {
042 {
043 put("jan", MONTHS.jan);
044 put("feb", MONTHS.feb);
045 put("mar", MONTHS.mar);
046 put("apr", MONTHS.apr);
047 put("may", MONTHS.may);
048 put("jun", MONTHS.jun);
049 put("jul", MONTHS.jul);
050 put("aug", MONTHS.aug);
051 put("sep", MONTHS.sep);
052 put("oct", MONTHS.oct);
053 put("nov", MONTHS.nov);
054 put("dec", MONTHS.dec);
055 }
056 };
057
058 private Rfc3164SyslogConverter() {
059 //Utility class
060 }
061
062 @Converter
063 public static String toString(SyslogMessage message) {
064 StringBuilder sbr = new StringBuilder();
065 sbr.append("<");
066 if (message.getFacility() == null) {
067 message.setFacility(SyslogFacility.USER);
068 }
069 if (message.getSeverity() == null) {
070 message.setSeverity(SyslogSeverity.INFO);
071 }
072 if (message.getHostname() == null) {
073 //This is massively ugly..
074 try {
075 message.setHostname(InetAddress.getLocalHost().toString());
076 } catch (UnknownHostException e) {
077 message.setHostname("UNKNOWN_HOST");
078 }
079 }
080 sbr.append(message.getFacility().ordinal() * 8 + message.getSeverity().ordinal());
081 sbr.append(">");
082 if (message.getTimestamp() == null) {
083 message.setTimestamp(new Date());
084 }
085
086 //SDF isn't going to help much here.
087
088 Calendar cal = GregorianCalendar.getInstance();
089 cal.setTime(message.getTimestamp());
090
091 String firstLetter = MONTHS.values()[cal.get(Calendar.MONTH)].toString().substring(0, 1); // Get first letter
092 String remainder = MONTHS.values()[cal.get(Calendar.MONTH)].toString()
093 .substring(1); // Get remainder of word.
094 String capitalized = firstLetter.toUpperCase() + remainder.toLowerCase();
095
096 sbr.append(capitalized);
097 sbr.append(" ");
098
099 if (cal.get(Calendar.DAY_OF_MONTH) < 10) {
100 sbr.append(" ").append(cal.get(Calendar.DAY_OF_MONTH));
101 } else {
102 sbr.append(cal.get(Calendar.DAY_OF_MONTH));
103 }
104
105 sbr.append(" ");
106
107 if (cal.get(Calendar.HOUR_OF_DAY) < 10) {
108 sbr.append("0").append(cal.get(Calendar.HOUR_OF_DAY));
109 } else {
110 sbr.append(cal.get(Calendar.HOUR_OF_DAY));
111 }
112 sbr.append(":");
113
114 if (cal.get(Calendar.MINUTE) < 10) {
115 sbr.append("0").append(cal.get(Calendar.MINUTE));
116 } else {
117 sbr.append(cal.get(Calendar.MINUTE));
118 }
119 sbr.append(":");
120
121 if (cal.get(Calendar.SECOND) < 10) {
122 sbr.append("0").append(cal.get(Calendar.SECOND));
123 } else {
124 sbr.append(cal.get(Calendar.SECOND));
125 }
126 sbr.append(" ");
127
128 sbr.append(message.getHostname());
129 sbr.append(" ");
130 sbr.append(message.getLogMessage());
131 return sbr.toString();
132 }
133
134 @Converter
135 public static SyslogMessage toSyslogMessage(String body) {
136 return parseMessage(body.getBytes());
137 }
138
139 public static SyslogMessage parseMessage(byte[] bytes) {
140 ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
141 byteBuffer.put(bytes);
142 byteBuffer.rewind();
143
144 SyslogMessage syslogMessage = new SyslogMessage();
145 Character charFound = (char) byteBuffer.get();
146
147 while (charFound != '<') {
148 //Ignore noise in beginning of message.
149 charFound = (char) byteBuffer.get();
150 }
151 char priChar = 0;
152 if (charFound == '<') {
153 int facility = 0;
154
155 while (Character.isDigit(priChar = (char) (byteBuffer.get() & 0xff))) {
156 facility *= 10;
157 facility += Character.digit(priChar, 10);
158 }
159 syslogMessage.setFacility(SyslogFacility.values()[facility >> 3]);
160 syslogMessage.setSeverity(SyslogSeverity.values()[facility & 0x07]);
161 }
162
163 if (priChar != '>') {
164 //Invalid character - this is not a well defined syslog message.
165 LOG.error("Invalid syslog message, missing a > in the Facility/Priority part");
166 }
167
168 //Done parsing severity and facility
169 //<169>Oct 22 10:52:01 TZ-6 scapegoat.dmz.example.org 10.1.2.3 sched[0]: That's All Folks!
170 //Need to parse the date.
171
172 /**
173 The TIMESTAMP field is the local time and is in the format of "Mmm dd
174 hh:mm:ss" (without the quote marks) where:
175
176 Mmm is the English language abbreviation for the month of the
177 year with the first character in uppercase and the other two
178 characters in lowercase. The following are the only acceptable
179 values:
180
181 Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
182
183 dd is the day of the month. If the day of the month is less
184 than 10, then it MUST be represented as a space and then the
185 number. For example, the 7th day of August would be
186 represented as "Aug 7", with two spaces between the "g" and
187 the "7".
188
189 hh:mm:ss is the local time. The hour (hh) is represented in a
190 24-hour format. Valid entries are between 00 and 23,
191 inclusive. The minute (mm) and second (ss) entries are between
192 00 and 59 inclusive.
193
194
195 */
196
197 char[] month = new char[3];
198 for (int i = 0; i < 3; i++) {
199 month[i] = (char) (byteBuffer.get() & 0xff);
200 }
201 charFound = (char) byteBuffer.get();
202 if (charFound != ' ') {
203 //Invalid Message - missing mandatory space.
204 LOG.error("Invalid syslog message, missing a mandatory space after month");
205 }
206 charFound = (char) (byteBuffer.get() & 0xff);
207
208 int day = 0;
209 if (charFound == ' ') {
210 //Extra space for the day - this is okay.
211 //Just ignored per the spec.
212 } else {
213 day *= 10;
214 day += Character.digit(charFound, 10);
215 }
216
217 while (Character.isDigit(charFound = (char) (byteBuffer.get() & 0xff))) {
218 day *= 10;
219 day += Character.digit(charFound, 10);
220 }
221
222 int hour = 0;
223 while (Character.isDigit(charFound = (char) (byteBuffer.get() & 0xff))) {
224 hour *= 10;
225 hour += Character.digit(charFound, 10);
226 }
227
228 int minute = 0;
229 while (Character.isDigit(charFound = (char) (byteBuffer.get() & 0xff))) {
230 minute *= 10;
231 minute += Character.digit(charFound, 10);
232 }
233
234 int second = 0;
235 while (Character.isDigit(charFound = (char) (byteBuffer.get() & 0xff))) {
236 second *= 10;
237 second += Character.digit(charFound, 10);
238 }
239
240 //The host is the char sequence until the next ' '
241
242 StringBuilder host = new StringBuilder();
243 while ((charFound = (char) (byteBuffer.get() & 0xff)) != ' ') {
244 host.append(charFound);
245 }
246
247 syslogMessage.setHostname(host.toString());
248
249 StringBuilder msg = new StringBuilder();
250 while (byteBuffer.hasRemaining()) {
251 charFound = (char) (byteBuffer.get() & 0xff);
252 msg.append(charFound);
253 }
254
255 Calendar calendar = new GregorianCalendar();
256 calendar.set(Calendar.MONTH, monthValueMap.get(String.valueOf(month).toLowerCase()).ordinal());
257 calendar.set(Calendar.DAY_OF_MONTH, day);
258 calendar.set(Calendar.HOUR_OF_DAY, hour);
259 calendar.set(Calendar.MINUTE, minute);
260 calendar.set(Calendar.SECOND, second);
261
262 syslogMessage.setTimestamp(calendar.getTime());
263
264 syslogMessage.setLogMessage(msg.toString());
265 if (LOG.isTraceEnabled()) {
266 LOG.trace("Syslog message : " + syslogMessage.toString());
267 }
268
269 return syslogMessage;
270 }
271 }