python3/syslog:多个系统日志流?
python3/syslog: multiple syslog streams?
我的 python3 程序有许多子模块,我希望它们每个都发送具有不同 syslog ident
值的 syslog 消息。例如,其中一个可能会发送到 myprogram/submod0
,而另一个可能会发送到 myprogram/submod1
。我使用 syslog-ng
将这些消息路由到不同的日志文件。
我想做的是这样的事情,我知道目前我在这里写的方式是不可能的:
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
return 任何类型的对象我都可以用这种方式用作句柄。
有什么方法可以使用 python3 的 syslog
工具来完成我想要的事情吗?
我想我可以为我要发送的每条系统日志消息发出一个新的 openlog
。例如...
def mysyslog(ident, message):
syslog.openlog('myprogram/{}'.format(ident),
syslog.LOG_PID, syslog.LOG_MAIL)
syslog.syslog(message)
...然后在我的 submod0
中使用 mysyslog('submod0', message)
并在我的 submod1
中使用 mysyslog('submod1', message)
。这是我完成我想做的事情的唯一方法吗?
提前谢谢你。
好的。我看到我可以通过 logging.handlers.SysLogHandler
...
来做到这一点
https://docs.python.org/3/library/logging.handlers.html#sysloghandler
这是我的问题的答案,但并不理想,因为我尽量避免使用 logging
模块。
我将继续寻找另一种方法。
我找不到任何现有的 python 模块来执行我想要的操作,所以我决定编写自己的系统日志包装器。它被编写为基于 host:port
或套接字文件(例如 /dev/log
)打开系统日志连接,然后接受所有其他参数,例如 facility
、severity
、 program
,等每次调用发送系统日志消息。
由于所有参数都处于单个日志记录方法调用的级别,因此 class 可以在更高级别进行包装,以提供句柄,例如,通过 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
这对于 Python 的 syslog
是不可能的。
唯一的选择是
- 使用
syslog
,调用openlog(ident)
切换标识符
CPython 的 syslog
包装了 unix 库 syslog
(#include <syslog.h>
)。那里存在相同的限制...您可以将 ident
设置为 openlog(ident)
,但在使用 syslog()
发送消息时则不能。也许 unix 库允许不同标识符的多个“记录器”,我不知道......但绝对不是 Python 的 syslog
.
你可以在这里看到 CPython 对这一切的实现:https://github.com/python/cpython/blob/92a98ed/Modules/syslogmodule.c
- 使用
logging.handlers.SysLogHandler
SysLogHandler
挺好的。您可以使用自己的 ident
值创建不同的处理程序。它处理格式化。它会设置您想要通过(UDP、TCP 或 unix 域)发送消息的任何套接字。
我的 python3 程序有许多子模块,我希望它们每个都发送具有不同 syslog ident
值的 syslog 消息。例如,其中一个可能会发送到 myprogram/submod0
,而另一个可能会发送到 myprogram/submod1
。我使用 syslog-ng
将这些消息路由到不同的日志文件。
我想做的是这样的事情,我知道目前我在这里写的方式是不可能的:
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
return 任何类型的对象我都可以用这种方式用作句柄。
有什么方法可以使用 python3 的 syslog
工具来完成我想要的事情吗?
我想我可以为我要发送的每条系统日志消息发出一个新的 openlog
。例如...
def mysyslog(ident, message):
syslog.openlog('myprogram/{}'.format(ident),
syslog.LOG_PID, syslog.LOG_MAIL)
syslog.syslog(message)
...然后在我的 submod0
中使用 mysyslog('submod0', message)
并在我的 submod1
中使用 mysyslog('submod1', message)
。这是我完成我想做的事情的唯一方法吗?
提前谢谢你。
好的。我看到我可以通过 logging.handlers.SysLogHandler
...
https://docs.python.org/3/library/logging.handlers.html#sysloghandler
这是我的问题的答案,但并不理想,因为我尽量避免使用 logging
模块。
我将继续寻找另一种方法。
我找不到任何现有的 python 模块来执行我想要的操作,所以我决定编写自己的系统日志包装器。它被编写为基于 host:port
或套接字文件(例如 /dev/log
)打开系统日志连接,然后接受所有其他参数,例如 facility
、severity
、 program
,等每次调用发送系统日志消息。
由于所有参数都处于单个日志记录方法调用的级别,因此 class 可以在更高级别进行包装,以提供句柄,例如,通过 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
这对于 Python 的 syslog
是不可能的。
唯一的选择是
- 使用
syslog
,调用openlog(ident)
切换标识符
CPython 的 syslog
包装了 unix 库 syslog
(#include <syslog.h>
)。那里存在相同的限制...您可以将 ident
设置为 openlog(ident)
,但在使用 syslog()
发送消息时则不能。也许 unix 库允许不同标识符的多个“记录器”,我不知道......但绝对不是 Python 的 syslog
.
你可以在这里看到 CPython 对这一切的实现:https://github.com/python/cpython/blob/92a98ed/Modules/syslogmodule.c
- 使用
logging.handlers.SysLogHandler
SysLogHandler
挺好的。您可以使用自己的 ident
值创建不同的处理程序。它处理格式化。它会设置您想要通过(UDP、TCP 或 unix 域)发送消息的任何套接字。