0
0
Fork 0
mirror of https://github.com/alerta/alerta.git synced 2025-01-30 03:33:59 +00:00
alerta_alerta/alerta/syslog/daemon.py
2014-01-31 11:00:44 +00:00

241 lines
8.4 KiB
Python

import sys
import socket
import select
import re
from alerta.common import config, syslog
from alerta.common import log as logging
from alerta.common.daemon import Daemon
from alerta.common.alert import Alert
from alerta.common.heartbeat import Heartbeat
from alerta.common.dedup import DeDup
from alerta.common.mq import Messaging, MessageHandler
from alerta.common.graphite import StatsD
Version = '2.0.12'
LOG = logging.getLogger(__name__)
CONF = config.CONF
class SyslogMessage(MessageHandler):
def __init__(self, mq):
self.mq = mq
MessageHandler.__init__(self)
def on_disconnected(self):
self.mq.reconnect()
class SyslogDaemon(Daemon):
syslog_opts = {
'syslog_udp_port': 514,
'syslog_tcp_port': 514,
}
def __init__(self, prog, **kwargs):
config.register_opts(SyslogDaemon.syslog_opts)
Daemon.__init__(self, prog, kwargs)
def run(self):
self.running = True
LOG.info('Starting UDP listener...')
# Set up syslog UDP listener
try:
udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp.bind(('', CONF.syslog_udp_port))
except socket.error, e:
LOG.error('Syslog UDP error: %s', e)
sys.exit(2)
LOG.info('Listening on syslog port %s/udp' % CONF.syslog_udp_port)
LOG.info('Starting TCP listener...')
# Set up syslog TCP listener
try:
tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
tcp.bind(('', CONF.syslog_tcp_port))
tcp.listen(5)
except socket.error, e:
LOG.error('Syslog TCP error: %s', e)
sys.exit(2)
LOG.info('Listening on syslog port %s/tcp' % CONF.syslog_tcp_port)
self.statsd = StatsD() # graphite metrics
# Connect to message queue
self.mq = Messaging()
self.mq.connect(callback=SyslogMessage(self.mq))
self.dedup = DeDup(by_value=True)
count = 0
while not self.shuttingdown:
try:
LOG.debug('Waiting for syslog messages...')
ip, op, rdy = select.select([udp, tcp], [], [], CONF.loop_every)
if ip:
for i in ip:
if i == udp:
data, addr = udp.recvfrom(4096)
data = unicode(data, 'utf-8', errors='ignore')
LOG.debug('Syslog UDP data received from %s: %s', addr, data)
if i == tcp:
client, addr = tcp.accept()
data = client.recv(4096)
data = unicode(data, 'utf-8', errors='ignore')
client.close()
LOG.debug('Syslog TCP data received from %s: %s', addr, data)
syslogAlerts = self.parse_syslog(addr[0], data)
for syslogAlert in syslogAlerts:
if self.dedup.is_send(syslogAlert):
self.mq.send(syslogAlert)
self.statsd.metric_send('alert.syslog.alerts.total', 1)
count += 1
if not ip or count % 5 == 0:
LOG.debug('Send heartbeat...')
heartbeat = Heartbeat(version=Version)
self.mq.send(heartbeat)
except (KeyboardInterrupt, SystemExit):
self.shuttingdown = True
LOG.info('Shutdown request received...')
self.running = False
LOG.info('Disconnecting from message broker...')
self.mq.disconnect()
def parse_syslog(self, addr, data):
LOG.debug('Parsing syslog message...')
syslogAlerts = list()
event = None
resource = None
for msg in data.split('\n'):
# NOTE: if syslog msgs aren't being split on newlines and #012 appears instead then
# try adding "$EscapeControlCharactersOnReceive off" to rsyslog.conf
if not msg or 'last message repeated' in msg:
continue
if re.match('<\d+>1', msg):
# Parse RFC 5424 compliant message
m = re.match(r'<(\d+)>1 (\S+) (\S+) (\S+) (\S+) (\S+) (.*)', msg)
if m:
PRI = int(m.group(1))
ISOTIMESTAMP = m.group(2)
HOSTNAME = m.group(3)
APPNAME = m.group(4)
PROCID = m.group(5)
MSGID = m.group(6)
TAG = '%s[%s] %s' % (APPNAME, PROCID, MSGID)
MSG = m.group(7)
LOG.info("Parsed RFC 5424 message OK")
else:
LOG.error("Could not parse RFC 5424 syslog message: %s", msg)
continue
elif re.match(r'<(\d{1,3})>\S{3}\s', msg):
# Parse RFC 3164 compliant message
m = re.match(r'<(\d{1,3})>\S{3}\s{1,2}\d?\d \d{2}:\d{2}:\d{2} (\S+)( (\S+):)? (.*)', msg)
if m:
PRI = int(m.group(1))
HOSTNAME = m.group(2)
TAG = m.group(4)
MSG = m.group(5)
LOG.info("Parsed RFC 3164 message OK")
else:
LOG.error("Could not parse RFC 3164 syslog message: %s", msg)
continue
elif re.match('<\d+>.*%[A-Z0-9_-]+', msg):
# Parse Cisco Syslog message
m = re.match('<(\d+)>.*(%([A-Z0-9_-]+)):? (.*)', msg)
if m:
LOG.debug(m.groups())
PRI = int(m.group(1))
CISCO_SYSLOG = m.group(2)
try:
CISCO_FACILITY, CISCO_SEVERITY, CISCO_MNEMONIC = m.group(3).split('-')
except ValueError, e:
LOG.error('Could not parse Cisco syslog - %s: %s', e, m.group(3))
CISCO_FACILITY = CISCO_SEVERITY = CISCO_MNEMONIC = 'na'
TAG = CISCO_MNEMONIC
MSG = m.group(4)
event = CISCO_SYSLOG
# replace IP address with a hostname, if necessary
try:
socket.inet_aton(addr)
(resource, _, _) = socket.gethostbyaddr(addr)
except (socket.error, socket.herror):
resource = addr
resource = '%s:%s' % (resource, CISCO_FACILITY)
else:
LOG.error("Could not parse Cisco syslog message: %s", msg)
continue
facility, level = syslog.decode_priority(PRI)
# Defaults
event = event or '%s%s' % (facility.capitalize(), level.capitalize())
resource = resource or '%s%s' % (HOSTNAME, ':' + TAG if TAG else '')
severity = syslog.priority_to_code(level)
group = 'Syslog'
value = level
text = MSG
environment = ['INFRA']
service = ['Platform']
tags = {'Priority': '%s.%s' % (facility, level)}
correlate = list()
timeout = None
threshold_info = None
summary = None
raw_data = msg
syslogAlert = Alert(
resource=resource,
event=event,
correlate=correlate,
group=group,
value=value,
severity=severity,
environment=environment,
service=service,
text=text,
event_type='syslogAlert',
tags=tags,
timeout=timeout,
threshold_info=threshold_info,
summary=summary,
raw_data=raw_data,
)
suppress = syslogAlert.transform_alert(facility=facility, level=level)
if suppress:
LOG.info('Suppressing %s.%s alert', facility, level)
LOG.debug('%s', syslogAlert)
continue
if syslogAlert.get_type() == 'Heartbeat':
syslogAlert = Heartbeat(origin=syslogAlert.origin, version='n/a', timeout=syslogAlert.timeout)
syslogAlerts.append(syslogAlert)
return syslogAlerts