Source code for duct.outputs.riemann
"""
.. module:: riemann
:synopsis: Riemann output
.. moduleauthor:: Colin Alston <colin@tamvera.com>
"""
import asyncio
import logging
import ssl
import time
import random
from duct.protocol import riemann
from duct.objects import Output
log = logging.getLogger(__name__)
[docs]
class RiemannTCP(Output):
"""Riemann TCP output
:param server: Riemann server hostname (default: localhost)
:type server: str.
:param port: Riemann server port (default: 5555)
:type port: int.
:param failover: Enable server failover; `server` may be a list
:type failover: bool.
:param maxrate: Maximum de-queue rate (0 is no limit)
:type maxrate: int.
:param maxsize: Maximum queue size (default: 250000)
:type maxsize: int.
:param interval: De-queue interval in seconds (default: 1.0)
:type interval: float.
:param pressure: Maximum backpressure (-1 is no limit)
:type pressure: int.
:param tls: Use TLS (default: false)
:type tls: bool.
:param cert: Client certificate path
:type cert: str.
:param key: Client private key path
:type key: str.
:param allow_nan: Send events with None metric value (default: true)
:type allow_nan: bool.
"""
def __init__(self, *a):
Output.__init__(self, *a)
self._tick_task = None
self.inter = float(self.config.get('interval', 1.0))
self.pressure = int(self.config.get('pressure', -1))
self.maxsize = int(self.config.get('maxsize', 250000))
self.expire = self.config.get('expire', False)
self.allow_nan = self.config.get('allow_nan', True)
self.client = None
maxrate = int(self.config.get('maxrate', 0))
self.queueDepth = int(maxrate * self.inter) if maxrate > 0 else None
self.tls = self.config.get('tls', False)
if self.tls:
self.cert = self.config['cert']
self.key = self.config['key']
[docs]
async def createClient(self):
"""Create a TCP connection to Riemann and start the drain loop"""
server = self.config.get('server', 'localhost')
port = self.config.get('port', 5555)
failover = self.config.get('failover', False)
self.client = riemann.RiemannTCPClient(server, port, failover=failover)
if self.tls:
ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
ctx.load_cert_chain(certfile=self.cert, keyfile=self.key)
self.client._ssl = ctx
else:
self.client._ssl = None
initial = random.choice(self.client.hosts) if failover else server
log.info('Connecting to Riemann on %s:%s', initial, port)
try:
await self.client.connect()
except Exception:
asyncio.create_task(self.client.reconnect_loop())
self._tick_task = asyncio.create_task(self._drain_loop())
async def _drain_loop(self):
try:
while True:
await asyncio.sleep(self.inter)
await self._tick()
except asyncio.CancelledError:
pass
async def _tick(self):
if self.client and self.client.connected:
if (self.pressure < 0) or (self.client.pressure <= self.pressure):
await self.emptyQueue()
elif self.expire:
now = time.time()
self.events = [ev for ev in self.events
if (now - ev.time) <= ev.ttl]
[docs]
async def emptyQueue(self):
"""Drain the event queue to Riemann"""
if not self.events:
return
if self.queueDepth and len(self.events) > self.queueDepth:
events = self.events[:self.queueDepth]
self.events = self.events[self.queueDepth:]
else:
events = self.events
self.events = []
if not self.allow_nan:
events = [ev for ev in events if ev.metric is not None]
if events:
await self.client.sendEvents(events)
[docs]
async def stop(self):
if self._tick_task:
self._tick_task.cancel()
try:
await self._tick_task
except asyncio.CancelledError:
pass
if self.client:
await self.client.disconnect()
[docs]
class RiemannUDP(Output):
"""Riemann UDP output (spray-and-pray mode)
:param server: Riemann server IP address (default: 127.0.0.1)
:type server: str.
:param port: Riemann server port (default: 5555)
:type port: int.
"""
def __init__(self, *a):
Output.__init__(self, *a)
self.protocol = None
[docs]
async def createClient(self):
"""Create a UDP connection to Riemann"""
server = self.config.get('server', '127.0.0.1')
port = self.config.get('port', 5555)
self.protocol = await riemann.create_riemann_udp(server, port)
log.info('Riemann UDP ready for %s:%s', server, port)
[docs]
async def eventsReceived(self, events):
"""Receives a list of events and transmits them to Riemann"""
if self.protocol:
self.protocol.sendEvents(events)