package cf.dropsonde.metron; import io.netty.channel.ChannelInboundHandlerAdapter; import org.cloudfoundry.dropsonde.events.CounterEvent; import org.cloudfoundry.dropsonde.events.Error; import org.cloudfoundry.dropsonde.events.LogMessage; import org.cloudfoundry.dropsonde.events.ValueMetric; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.nio.NioDatagramChannel; import okio.ByteString; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.time.Instant; /** * @author Mike Heath */ public class MetronClientBuilder { public static final String DEFAULT_METRON_AGENT_HOST = "localhost"; public static final int DEFAULT_METRON_AGENT_PORT = 3457; private final String origin; private SocketAddress metronAgent = new InetSocketAddress(DEFAULT_METRON_AGENT_HOST, DEFAULT_METRON_AGENT_PORT); private EventLoopGroup eventLoopGroup; private Class<? extends DatagramChannel> channelClass; private MetronClientBuilder(String origin) { this.origin = origin; } public MetronClientBuilder metronAgent(InetSocketAddress metronAgent) { this.metronAgent = metronAgent; return this; } public MetronClientBuilder eventLoopGroup(EventLoopGroup eventLoopGroup, Class<? extends DatagramChannel> channelClass) { this.eventLoopGroup = eventLoopGroup; this.channelClass = channelClass; return this; } public static MetronClientBuilder create(String origin) { return new MetronClientBuilder(origin); } public MetronClient build() { return new MetronClient() { private final EventLoopGroup eventLoopGroup; private final Channel channel; { // we are not handling ControlMessages / HeartBeats anymore so a simple // ChannelInboundHandlerAdapter to forward to next handler. final Bootstrap bootstrap = new Bootstrap().handler(new ChannelInboundHandlerAdapter()); if (MetronClientBuilder.this.eventLoopGroup == null) { eventLoopGroup = new NioEventLoopGroup(1); bootstrap.group(eventLoopGroup).channel(NioDatagramChannel.class); } else { eventLoopGroup = null; bootstrap.group(MetronClientBuilder.this.eventLoopGroup).channel(channelClass); } channel = bootstrap.connect(metronAgent).syncUninterruptibly().channel(); channel.pipeline().addLast(new EnvelopeEncoder()); channel.pipeline().addLast(new EventWrapperEncoder(origin)); } @Override public LogEmitter createLogEmitter(final String sourceType, final String sourceInstance) { return new LogEmitter() { @Override public void emit(Instant timestamp, String applicationGuid, String message) { emitLog(timestamp, applicationGuid, message, LogMessage.MessageType.OUT); } @Override public void emitError(Instant timestamp, String applicationGuid, String message) { emitLog(timestamp, applicationGuid, message, LogMessage.MessageType.ERR); } private void emitLog(Instant timestamp, String applicationGuid, String message, LogMessage.MessageType type) { final LogMessage logMessage = new LogMessage.Builder() .app_id(applicationGuid) .message(ByteString.encodeUtf8(message)) .source_type(sourceType) .source_instance(sourceInstance) .timestamp(Time.timestamp(timestamp)) .message_type(type) .build(); channel.writeAndFlush(logMessage); } }; } @Override public void emitCounterEvent(String name, long delta) { channel.writeAndFlush(new CounterEvent.Builder().name(name).delta(delta).build()); } @Override public void emitError(String source, int code, String message) { channel.writeAndFlush(new Error(source, code, message)); } @Override public void emitValueMetric(String name, double value, String unit) { channel.writeAndFlush(new ValueMetric(name, value, unit)); } @Override public HttpStartStopEmitter createHttpStartStopEmitter() { return new HttpStartStopEmitter() { @Override public void emit() { channel.writeAndFlush(builder.build()); } }; } @Override public void close() { channel.close(); if (eventLoopGroup != null) { eventLoopGroup.shutdownGracefully(); } } }; } }