/**
* Licensed to Cloudera, Inc. under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Cloudera, Inc. 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 com.cloudera.flume.handlers.syslog;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.cloudera.flume.conf.SourceFactory.SourceBuilder;
import com.cloudera.flume.core.Event;
import com.cloudera.flume.core.EventSource;
import com.cloudera.flume.handlers.text.EventExtractException;
import com.cloudera.util.ByteBufferInputStream;
/**
* This handles syslog wire data from udp. Each packet is one log event and is
* formated like so (rfc 5426):
*
* '<' PRI '>' VERSION ' ' stuff from a line in a file
*
* VERSION Doesn't seem to appear in my logs, so I will just append it to "stuff".
*
* Where PRI is a decimal number from 0-199
*
*/
public class SyslogUdpSource extends EventSource.Base {
static final Logger LOG = LoggerFactory.getLogger(SyslogUdpSource.class);
final public static int SYSLOG_UDP_PORT = 514;
int port = SYSLOG_UDP_PORT; // default udp syslog port
int maxsize = 1 << 16; // 64k is max allowable in RFC 5426
long rejects = 0;
DatagramSocket sock;
public SyslogUdpSource() {
}
public SyslogUdpSource(int port) {
this.port = port;
}
@Override
public void close() throws IOException {
LOG.info("closing SyslogUdpSource on port " + port);
if (sock == null) {
LOG.warn("double close of SyslogUdpSocket on udp:" + port
+ " , (this is ok but odd)");
return;
}
sock.close();
}
@Override
public Event next() throws IOException {
byte[] buf = new byte[maxsize];
DatagramPacket pkt = new DatagramPacket(buf, maxsize);
Event e = null;
do { // loop until we get a valid packet, drop bad ones.
sock.receive(pkt);
ByteBuffer bb = ByteBuffer.wrap(buf, 0, pkt.getLength());
ByteBufferInputStream bbis = new ByteBufferInputStream(bb);
DataInputStream in = new DataInputStream(bbis);
try {
e = SyslogWireExtractor.extractEvent(in);
} catch (EventExtractException ex) {
rejects++;
LOG.warn(rejects + " rejected packets. packet: " + pkt, ex);
LOG.debug("raw bytes " + Arrays.toString(pkt.getData()));
// TODO (jon) maybe have a hook here to do something with rejects
}
// need a sane way to fall out of his loop.
} while (e == null);
updateEventProcessingStats(e);
return e;
}
@Override
public void open() throws IOException {
sock = new DatagramSocket(port);
}
public static SourceBuilder builder() {
return new SourceBuilder() {
@Override
public EventSource build(String... argv) {
int port = SYSLOG_UDP_PORT; // default udp port, need root permissions
// for this.
if (argv.length > 1) {
throw new IllegalArgumentException("usage: syslogUdp([port no]) ");
}
if (argv.length == 1) {
port = Integer.parseInt(argv[0]);
}
return new SyslogUdpSource(port);
}
};
}
}