Package ZenEvents :: Module zensyslog
[hide private]
[frames] | no frames]

Source Code for Module ZenEvents.zensyslog

  1  #! /usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3  # ########################################################################## 
  4  # 
  5  # This program is part of Zenoss Core, an open source monitoring platform. 
  6  # Copyright (C) 2008, Zenoss Inc. 
  7  # 
  8  # This program is free software; you can redistribute it and/or modify it 
  9  # under the terms of the GNU General Public License version 2 as published by 
 10  # the Free Software Foundation. 
 11  # 
 12  # For complete information please visit: http://www.zenoss.com/oss/ 
 13  # 
 14  # ########################################################################## 
 15   
 16  __doc__ = """zensyslog 
 17   
 18  Turn syslog messages into events. 
 19   
 20  """ 
 21   
 22  import time 
 23  import socket 
 24   
 25  from twisted.internet.protocol import DatagramProtocol 
 26  from twisted.internet import reactor 
 27  from twisted.python import failure 
 28  from twisted.internet import defer 
 29   
 30  import Globals 
 31  from Products.ZenEvents.EventServer import EventServer 
 32  from Products.ZenEvents.SyslogProcessing import SyslogProcessor 
 33   
 34  from Products.ZenUtils.Utils import zenPath 
 35  from Products.ZenUtils.IpUtil import asyncNameLookup 
 36  from Products.ZenUtils.Driver import drive 
 37   
 38  SYSLOG_PORT = 514 
 39  try: 
 40      SYSLOG_PORT = socket.getservbyname('syslog', 'udp') 
 41  except socket.error: 
 42      pass 
 43   
 44   
45 -class ZenSyslog(DatagramProtocol, EventServer):
46 """ 47 ZenSyslog 48 """ 49 50 name = 'zensyslog' 51 SYSLOG_DATE_FORMAT = '%b %d %H:%M:%S' 52 SAMPLE_DATE = 'Apr 10 15:19:22' 53
54 - def __init__(self):
55 EventServer.__init__(self) 56 if not self.options.useFileDescriptor\ 57 and self.options.syslogport < 1024: 58 self.openPrivilegedPort('--listen', '--proto=udp', 59 '--port=%s:%d' 60 % (self.options.listenip, 61 self.options.syslogport)) 62 self.changeUser() 63 self.minpriority = self.options.minpriority 64 self.processor = None 65 66 if self.options.logorig: 67 import logging 68 self.olog = logging.getLogger('origsyslog') 69 self.olog.setLevel(20) 70 self.olog.propagate = False 71 lname = zenPath('log/origsyslog.log') 72 hdlr = logging.FileHandler(lname) 73 hdlr.setFormatter(logging.Formatter('%(message)s')) 74 self.olog.addHandler(hdlr) 75 if self.options.useFileDescriptor is not None: 76 self.useUdpFileDescriptor(int(self.options.useFileDescriptor)) 77 else: 78 reactor.listenUDP(self.options.syslogport, self, 79 interface=self.options.listenip)
80 81
82 - def configure(self):
83 """ 84 Initialize the daemon 85 86 @return: Twisted deferred object 87 @rtype: Twisted deferred object 88 """ 89 def inner(driver): 90 """ 91 Generator function to gather zProperites and then initialize. 92 93 @param driver: driver 94 @type driver: string 95 @return: Twisted deferred object 96 @rtype: Twisted deferred object 97 """ 98 yield EventServer.configure(self) 99 driver.next() 100 self.log.info('Fetching the default syslog priority') 101 yield self.model().callRemote('getDefaultPriority') 102 self.processor = SyslogProcessor(self.sendEvent, 103 self.options.minpriority, self.options.parsehost, 104 self.options.monitor, driver.next()) 105 self.log.info('Configuration finished')
106 107 return drive(inner)
108 109
110 - def expand(self, msg, client_address):
111 """ 112 Expands a syslog message into a string format suitable for writing 113 to the filesystem such that it appears the same as it would 114 had the message been logged by the syslog daemon. 115 116 @param msg: syslog message 117 @type msg: string 118 @param client_address: IP info of the remote device (ipaddr, port) 119 @type client_address: tuple of (string, number) 120 @return: message 121 @rtype: string 122 """ 123 # pri := facility * severity 124 stop = msg.find('>') 125 126 # check for a datestamp. default to right now if date not present 127 start = stop + 1 128 stop = start + len(ZenSyslog.SAMPLE_DATE) 129 dateField = msg[start:stop] 130 try: 131 date = time.strptime(dateField, 132 ZenSyslog.SYSLOG_DATE_FORMAT) 133 year = time.localtime()[0] 134 date = (year, ) + date[1:] 135 start = stop + 1 136 except ValueError: 137 138 # date not present, so use today's date 139 date = time.localtime() 140 141 # check for a hostname. default to localhost if not present 142 stop = msg.find(' ', start) 143 if msg[stop - 1] == ':': 144 hostname = client_address[0] 145 else: 146 hostname = msg[start:stop] 147 start = stop + 1 148 149 # the message content 150 body = msg[start:] 151 152 # assemble the message 153 prettyTime = time.strftime(ZenSyslog.SYSLOG_DATE_FORMAT, date) 154 message = '%s %s %s' % (prettyTime, hostname, body) 155 return message
156 157
158 - def datagramReceived(self, msg, client_address):
159 """ 160 Consume the network packet 161 162 @param msg: syslog message 163 @type msg: string 164 @param client_address: IP info of the remote device (ipaddr, port) 165 @type client_address: tuple of (string, number) 166 """ 167 (ipaddr, port) = client_address 168 if self.options.logorig: 169 if self.options.logformat == 'human': 170 message = self.expand(msg, client_address) 171 else: 172 message = msg 173 self.olog.info(message) 174 175 if self.options.noreverseLookup: 176 d = defer.succeed(ipaddr) 177 else: 178 d = asyncNameLookup(ipaddr) 179 d.addBoth(self.gotHostname, (msg, ipaddr, time.time()))
180 181
182 - def gotHostname(self, response, data):
183 """ 184 Send the resolved address, if possible, and the event via the thread 185 186 @param response: Twisted response 187 @type response: Twisted response 188 @param data: (msg, ipaddr, rtime) 189 @type data: tuple of (string, string, datetime object) 190 """ 191 (msg, ipaddr, rtime) = data 192 if isinstance(response, failure.Failure): 193 host = ipaddr 194 else: 195 host = response 196 if self.processor: 197 self.processor.process(msg, ipaddr, host, rtime)
198 199
200 - def buildOptions(self):
201 """ 202 Command-line options 203 """ 204 EventServer.buildOptions(self) 205 self.parser.add_option('--dmdpath', dest='dmdpath', 206 default='/zport/dmd', 207 help='Zope path to our DMD /zport/dmd') 208 self.parser.add_option('--parsehost', dest='parsehost', 209 action='store_true', default=False, 210 help='Try to parse the hostname part of a syslog HEADER' 211 ) 212 self.parser.add_option('--stats', dest='stats', 213 action='store_true', default=False, 214 help='Print statistics to log every 2 secs') 215 self.parser.add_option('--logorig', dest='logorig', 216 action='store_true', default=False, 217 help='Log the original message') 218 self.parser.add_option('--logformat', dest='logformat', 219 default='human', 220 help='Human-readable (/var/log/messages) or raw (wire)' 221 ) 222 self.parser.add_option('--minpriority', dest='minpriority', 223 default=6, type='int', 224 help='Minimum priority message that zensyslog will accept' 225 ) 226 self.parser.add_option('--heartbeat', dest='heartbeat', 227 default=60, 228 help='Number of seconds between heartbeats' 229 ) 230 self.parser.add_option('--syslogport', dest='syslogport', 231 default=SYSLOG_PORT, type='int', 232 help='Port number to use for syslog events' 233 ) 234 self.parser.add_option('--listenip', dest='listenip', 235 default='0.0.0.0', 236 help='IP address to listen on. Default is 0.0.0.0' 237 ) 238 self.parser.add_option('--useFileDescriptor', 239 dest='useFileDescriptor', type='int', 240 help='Read from an existing connection rather opening a new port.' 241 , default=None) 242 self.parser.add_option('--noreverseLookup', dest='noreverseLookup', 243 action='store_true', default=False, 244 help="Don't convert the remote device's IP address to a hostname." 245 )
246 247 248 if __name__ == '__main__': 249 zsl = ZenSyslog() 250 zsl.run() 251 zsl.report() 252