2017-11-11 2 views
0

Mein Python3-Programm hat eine Reihe von Submodulen, und ich möchte, dass sie jeweils Syslog-Nachrichten mit einem anderen syslog Wert senden. Zum Beispiel könnte einer von ihnen an myprogram/submod0 senden, und ein anderer könnte an myprogram/submod1 senden. Ich verwende syslog-ng, um diese Nachrichten an verschiedene Protokolldateien weiterzuleiten.python3/syslog: mehrere Syslog-Streams?

Was Ich mag würde tun, so etwas, was ich weiß, ist nicht möglich, in der Art und Weise ist, dass ich es hier gerade schreibe:

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

... und dann innerhalb von submod0, ich möchte diese syslog-Meldungen wie senden ...

syslog0.syslog('some sort of message') 

... und auf diese Weise innerhalb submod1 ...

syslog1.syslog('another message') 

Aber natürlich gibt syslog.openlog keine Art von Objekt zurück, das ich auf diese Weise als Handle verwenden kann.

Gibt es eine Möglichkeit, die ich mit den syslog Einrichtungen von Python3 erreichen kann, was ich will?

Ich nehme an, dass ich eine neue openlog für jede Syslog-Nachricht ausgeben könnte, die ich senden möchte. Zum Beispiel ...

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

... und dann verwenden mysyslog('submod0', message) in meinem submod0 und mysyslog('submod1', message) in meinem submod1. Ist das die einzige Möglichkeit, was ich erreichen kann?

Vielen Dank im Voraus.

Antwort

0

Ich konnte keine vorhandenen Python-Module finden, die das tun, was ich möchte, also habe ich beschlossen, meinen eigenen Syslog-Wrapper zu schreiben. Es wird geschrieben, um eine Syslog-Verbindung basierend auf entweder host:port oder einer Socket-Datei wie /dev/log zu öffnen und alle anderen Parameter wie facility, severity, program usw. bei jedem Aufruf zu akzeptieren, um eine Syslog-Nachricht zu senden.

Mit allen Parametern auf der Ebene des einzelnen Aufrufs der Protokollierungsmethode könnte diese Klasse auf einer höheren Ebene eingebunden werden, um z. B. eindeutige Verbindungen über program zu behandeln, was ich in meiner ursprünglichen Frage angegeben habe Hier.

Ich habe nur den folgenden Code mit python3.6 und im /dev/log Fall getestet. Es funktioniert für mich, aber benutze es auf eigene Gefahr.

#!/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 
Verwandte Themen