2017-11-11 109 views
0

我的python3程序有一些子模塊,我希望它們每個發送帶有不同系統日誌值的系統日誌消息。例如,其中一個可能會發送到myprogram/submod0,另一個可能會發送到myprogram/submod1。我使用syslog-ng將這些消息路由到不同的日誌文件。python3/syslog:多個系統日誌流?

我希望做的是這樣的事情,這是我所知道的是目前不可能在我這裏寫的方式:

syslog0 = syslog.openlog('myprogram/submod0', 
         syslog.LOG_PID, syslog.LOG_MAIL) 
syslog1 = syslog.openlog('myprogram/submod1', 
         syslog.LOG_PID, syslog.LOG_MAIL) 

...然後,內submod0,我想送系統日誌消息是這樣的...

syslog0.syslog('some sort of message') 

...並在submod1這樣...

syslog1.syslog('another message') 

但是,當然syslog.openlog不會返回任何類型的對象,我可以用這種方式作爲句柄。

有沒有什麼方法可以用python3的syslog工具完成我想要的功能?

我想我可以爲我想發送的每個系統日誌消息發出一個新的openlog。例如...

def mysyslog(ident, message): 
    syslog.openlog('myprogram/{}'.format(ident), 
        syslog.LOG_PID, syslog.LOG_MAIL) 
    syslog.syslog(message) 

...然後用我的submod0mysyslog('submod1', message)mysyslog('submod0', message)submod1內。這是我能完成自己想做什麼的唯一方式嗎?

預先感謝您。

回答

0

我找不到任何現有的python模塊做我想做的事,所以我決定編寫自己的syslog包裝器。它被寫爲打開基於host:port或套接字文件(如/dev/log)的系統日誌連接,然後接受所有其他參數,如facility,severity,program等,以在每次調用時發送系統日誌消息。

在所有參數處於各個日誌記錄方法調用級別時,可以將此類打包到更高級別,以便通過program提供句柄,例如,通過我在原始問題中指定的唯一連接這裏。

我只用python3.6和/dev/log的情況下測試了下面的代碼。它適用於我,但使用它需要您自擔風險。

#!/usr/bin/python3.6 

import os 
import sys 
import socket 
import datetime 

# Only imported for the syslog.LOG_INFO and syslog.LOG_USER constants. 
import syslog 

# Appends a newline in all cases. 
def _log_stderr(message): 
    if message: 
     sys.stderr.write(message) 
    sys.stderr.write('\n') 
    sys.stderr.flush() 

# XSyslog: a syslog wrapper class. 
# 
# This module allows the facility (such as LOG_USER), the 
# severity (such as LOG_INFO), and other syslog parameters 
# to be set on a message-by-message basis via one, single 
# syslog connection. 
# 
# Usage: 
# 
# slog = XSyslog([server=server], [port=port], [proto=proto], 
#     [clientname=clientname], [maxlen=maxlen]) 
# 
# This allows three cases: 
# (1) Connect to syslog via UDP to a host and port: 
#  Specify host, port, and proto='UDP'. 
# (2) Connect to syslog via TCP to a host and port: 
#  Specify host, port, and proto='TCP'. 
# (3) Connect to syslog via a socket file such as /dev/log. 
#  Specify proto=filename (e.g., proto='/dev/log'). 
#  In this case, host and port are ignored. 
# 
# clientname is an optional field for the syslog message. 
# maxlen is the maximum message length. 
# 
# Once the XSyslog object is created, the message can be sent as follows: 
# 
# slog = XSyslog([... parameters ...]) 
# slog.log(message, [facility=facility], [severity=severity], 
#      [timestamp=timestamp], [hostame=hostname], 
#      [program=program], [pid=pid]) 
#  facility defaults to LOG_USER 
#  severity defaults to LOG_INFO 
#  timestamp defaults to now 
#  hostname if None, use clientname if it exists; if '', no hostname. 
#  program defaults to "logger" 
#  pid  defaults to os.getpid() 

class XSyslog(object): 

    def __init__(self, server=None, port=None, proto='udp', clientname=None, maxlen=1024): 
     self.server  = server 
     self.port   = port 
     self.proto  = socket.SOCK_DGRAM 
     self.clientname = None 
     self.maxlen  = maxlen 
     self._protoname = '' 
     self._socket  = None 
     self._sockfile = None 
     self._connectargs =() 
     self._me   = os.path.splitext(self.__class__.__name__)[1][1:] 

     if proto: 
      if proto.lower() == 'udp': 
       self._protoname = proto.lower() 
       self.proto  = socket.SOCK_DGRAM 
       self._socketargs = (self.server, self.port, socket.AF_UNSPEC, self.proto) 
      elif proto.lower() == 'tcp': 
       self._protoname = proto.lower() 
       self.proto  = socket.SOCK_STREAM 
       self._socketargs = (self.server, self.port, socket.AF_UNSPEC, self.proto) 
      elif len(proto) > 0: 
       self._sockfile = proto 
       self._protoname = self._sockfile 
       self.proto  = socket.SOCK_DGRAM 
       self._socketargs = (socket.AF_UNIX, self.proto) 

     badargs = False 
     if self._sockfile: 
      pass 
     elif self.server and self.port: 
      pass 
     else: 
      badargs = True 
     if not self.proto: 
      badargs = True 
     if badargs: 
      raise ValueError("'proto' must be 'udp' or 'tcp' with 'server' and 'port', or else a socket filename like '/dev/log'") 

     if not self.clientname: 
      try: 
       self.clientname = socket.getfqdn() 
       if not self.clientname: 
        self.clientname = socket.gethostname() 
      except: 
       self.clientname = None 

    def _connect(self): 
     if self._socket is None: 
      if self._sockfile: 
       self._socket = socket.socket(*self._socketargs) 
       if not self._socket: 
        _log_stderr(':::::::: {}: unable to open socket file {}'.format(self._me, self._sockfile)) 
        return False 
       try: 
        self._socket.connect(self._sockfile) 
        return True 
       except socket.timeout as e: 
        _log_stderr(':::::::: {}: sockfile timeout e={}'.format(self._me, e)) 
        # Force-close the socket and its contained fd, to avoid fd leaks. 
        self.close() 
       except socket.error as e: 
        _log_stderr(':::::::: {}: sockfile error f={}, e={}'.format(self._me, self._sockfile, e)) 
        # Force-close the socket and its contained fd, to avoid fd leaks. 
        self.close() 
       except Exception as e: 
        # Any other exception which might occur ... 
        _log_stderr(':::::::: {}: sockfile exception f={}, e={}'.format(self._me, self._sockfile, e)) 
        # Force-close the socket and its contained fd, to avoid fd leaks. 
        self.close() 
       return False 
      else: 
       addrinfo = socket.getaddrinfo(*self._socketargs) 
       if addrinfo is None: 
        return False 
       # Check each socket family member until we find one we can connect to. 
       for (addr_fam, sock_kind, proto, ca_name, sock_addr) in addrinfo: 
        self._socket = socket.socket(addr_fam, self.proto) 
        if not self._socket: 
         _log_stderr(':::::::: {}: unable to get a {} socket'.format(self._me, self._protoname)) 
         return False 
        try: 
         self._socket.connect(sock_addr) 
         return True 
        except socket.timeout as e: 
         _log_stderr(':::::::: {}: {} timeout e={}'.format(self.me, self._protoname, e)) 
         # Force-close the socket and its contained fd, to avoid fd leaks. 
         self.close() 
         continue 
        except socket.error as e: 
         _log_stderr(':::::::: {}: {} error e={}'.format(self._me, self._protoname, e)) 
         # Force-close the socket and its contained fd, to avoid fd leaks. 
         self.close() 
         continue 
        except Exception as e: 
         # Any other exception which might occur ... 
         _log_stderr(':::::::: {}: {} exception e={}'.format(self._me, self._protoname, e)) 
         # Force-close the socket and its contained fd, to avoid fd leaks. 
         self.close() 
         continue 
       # Force-close the socket and its contained fd, to avoid fd leaks. 
       self.close() 
       return False 
     else: 
      return True 

    def close(self): 
     try: 
      self._socket.close() 
     except: 
      pass 
     self._socket = None 

    def log(self, message, facility=None, severity=None, timestamp=None, hostname=None, program=None, pid=None): 

     if message is None: 
      return 

     if not facility: 
      facility = syslog.LOG_USER 

     if not severity: 
      severity = syslog.LOG_INFO 

     pri = facility + severity 

     data = '<{}>'.format(pri) 

     if timestamp: 
      t = timestamp 
     else: 
      t = datetime.datetime.now() 
     data = '{}{}'.format(data, t.strftime('%Y-%m-%dT%H:%M:%S.%f')) 

     if hostname is None: 
      if self.clientname: 
       data = '{} {}'.format(data, self.clientname) 
     elif not hostname: 
      # For hostname == '', leave out the hostname, altogether. 
      pass 
     else: 
      data = '{} {}'.format(data, hostname) 

     if program: 
      data = '{} {}'.format(data, program) 
     else: 
      data = '{} logger'.format(data) 

     if not pid: 
      pid = os.getpid() 

     data = '{}[{}]: {}'.format(data, pid, message).encode('ascii', 'ignore') 

     if not self._socket: 
      self._connect() 

     if not self._socket: 
      raise Exception('{}: unable to connect to {} syslog via {}'.format(self._me, self._protoname, self._socketargs)) 
     try: 
      if self.maxlen: 
       self._socket.sendall(data[:self.maxlen]) 
      else: 
       self._socket.sendall(data) 
     except IOError as e: 
      _log_stderr(':::::::: {}: {} syslog io error {} via {}'.format(self._me, self._protoname, e, self._socketargs)) 
      self.close() 
      raise 
     except Exception as e: 
      # Any other exception which might occur ... 
      _log_stderr(':::::::: {}: {} syslog exception {} via {}'.format(self._me, self._protoname, e, self._socketargs)) 
      self.close() 
      raise