如何在Python中配置日志记录到syslog?

153

我无法理解Python的logging模块。我的需求非常简单:我只想将所有内容记录到syslog中。在阅读文档后,我编写了这个简单的测试脚本:

import logging
import logging.handlers

my_logger = logging.getLogger('MyLogger')
my_logger.setLevel(logging.DEBUG)

handler = logging.handlers.SysLogHandler()

my_logger.addHandler(handler)

my_logger.debug('this is debug')
my_logger.critical('this is critical')

但是这个脚本没有在系统日志中产生任何日志记录。出了什么问题吗?

3
你在哪里查看你的系统日志信息? SysLogHandler() 将这些消息发射到本地主机上端口 514 的 UDP 套接字。 - suzanshakya
你说得完全正确。我也看到文档中的“localhost-514”,但没有想到默认情况下应该使用/dev/log.. 唉.. - thor
12个回答

164

把这一行改成:

handler = SysLogHandler(address='/dev/log')

这对我有效

import logging
import logging.handlers

my_logger = logging.getLogger('MyLogger')
my_logger.setLevel(logging.DEBUG)

handler = logging.handlers.SysLogHandler(address = '/dev/log')

my_logger.addHandler(handler)

my_logger.debug('this is debug')
my_logger.critical('this is critical')

12
请注意,正如文档所说的那样,在OS X上,'/var/run/syslog'是正确的选择。 - offby1
3
我们如何在syslog中识别这些日志?例如,我们可以给出任何应用程序名称或任何标签,例如syslogtag = django吗? - Luv33preet
请记得配置 /etc/syslog.d/conf 文件,并重新启动syslog/rsyslog服务。 - linrongbin
7
我测试过了,使用类似于logging.Formatter(fmt='myscriptname[%(process)d]: %(levelname)s: %(message)s', ...)这样的格式化程序,一个像 $programname == 'myscriptname' 这样的 rsyslog 条件是可行的。请注意,没有格式化程序是不行的。 - Peter
@Peter 谢谢伙计,很高兴你能在这么长时间之后仍然关注这个问题。如果我遇到问题,我会再联系你的。像你这样的人让社区运转良好。 - Luv33preet
显示剩余3条评论

32

我发现syslog模块可以很容易地实现你所描述的基本日志记录行为:

import syslog
syslog.syslog("This is a test message")
syslog.syslog(syslog.LOG_INFO, "Test message at INFO priority")

还有其他一些事情可以做,但只需完成前两行,根据我的理解,即可得到您所要求的内容。


1
我保留日志记录模块,因为它允许更改记录器设置而不影响所有语句。此外,还可以更改行为,以便在需要同时具有不同类型的日志记录时进行更改。 - chachan

31

无论是通过/dev/log还是通过TCP堆栈,您都应该始终使用本地主机进行日志记录。这样可以使符合完全RFC标准并支持更多功能的系统日志守护程序来处理syslog。这消除了远程守护程序需运行的需要,并提供了诸如rsyslog和syslog-ng之类的syslog守护程序的增强功能。SMTP也是同样的理念。只需将其交给本地SMTP软件。在这种情况下,请使用“程序模式”,而不是守护进程,但其原理相同。让更有能力的软件来处理它。这样就可以实现重试、排队、本地缓冲区、使用TCP而不是UDP进行syslog等操作。您还可以单独重新[配置]这些守护程序,而不是在代码中进行。

将编码留给您的应用程序,让其他软件协同完成其工作。


4
您提出了一个合理的观点。您能否指出各种日志守护进程使用的常见地址和端口?是否有标准的发现机制来确定守护进程是否绑定到TCP套接字? - init_js
3
我是唯一一个不明白这个答案与问题有何关联的人吗? - Saleh
@Saleh,是的,你没错。我来这里想知道如何将我的程序日志传输到本地系统日志以进行适当处理。如果这是一个独立的微型文章,或者是问题的开头段落,那么这是有意义和正确的;但这个人实际上并没有告诉我们如何“让更有能力的软件处理”我的Python应用程序的关键日志事件。我不知道为什么会得到赞同。 - undefined

20

从这里和其他地方整理出来, 这是在Ubuntu 12.04和CentOS6上有效的方法:

/etc/rsyslog.d/中创建一个以.conf结尾的文件,并添加以下文本:

local6.*        /var/log/my-logfile

重新启动 rsyslog,似乎重新加载无效以使用新的日志文件。也许只有重新加载现有的配置文件才会生效?

sudo restart rsyslog

然后,您可以使用此测试程序确保其实际运行。
import logging, sys
from logging import config

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(module)s P%(process)d T%(thread)d %(message)s'
            },
        },
    'handlers': {
        'stdout': {
            'class': 'logging.StreamHandler',
            'stream': sys.stdout,
            'formatter': 'verbose',
            },
        'sys-logger6': {
            'class': 'logging.handlers.SysLogHandler',
            'address': '/dev/log',
            'facility': "local6",
            'formatter': 'verbose',
            },
        },
    'loggers': {
        'my-logger': {
            'handlers': ['sys-logger6','stdout'],
            'level': logging.DEBUG,
            'propagate': True,
            },
        }
    }

config.dictConfig(LOGGING)


logger = logging.getLogger("my-logger")

logger.debug("Debug")
logger.info("Info")
logger.warn("Warn")
logger.error("Error")
logger.critical("Critical")

2
在CentOS7上重新启动rsyslog,使用命令sudo service rsyslog restart - radtek

12

我添加了一些额外的注释,以防有人需要帮助,因为我发现这个交换很有用,但需要这一点额外的信息才能使它全部工作。

如果要使用SysLogHandler记录到特定的日志设施,您需要指定设施值。 例如说你已经定义了:

local3.* /var/log/mylog

在syslog中,那么你需要使用:

handler = logging.handlers.SysLogHandler(address = ('localhost',514), facility=19)

并且您还需要在UDP上启动syslog以使用localhost而不是/dev/log。


3
没有必要让syslog监听UDP。您提供的示例也可以完美运行,只需使用address='/dev/log'即可。 - thor
6
好的,但使用地址=('localhost', 514) ,当您拥有一个日志服务器时,只需将localhost替换为服务器的地址,即可实现远程日志记录;-) - Oliver Henriot
6
设施=19来自哪里?为什么不是facility="local3"? - boatcoder
4
@Mark0978 19是RFC3146(以及随后的RFC5424)定义的local3的数字表示。 - Andrew Sledge
4
我也曾经想过这个问题,并发现设施代码在Python的SysLogHandler源代码中。 - clebio

11

你的syslog.conf是否设置了处理facility=user?

你可以使用facility参数来设置python日志器使用的facility,类似于这样:

handler = logging.handlers.SysLogHandler(facility=SysLogHandler.LOG_DAEMON)

你需要指定你提供给 facility 参数的 LOG_DAEMON 是什么。 - tzot
5
那将是 SysLogHandler.LOG_DAEMON - Craig Trader

8
import syslog
syslog.openlog(ident="LOG_IDENTIFIER",logoption=syslog.LOG_PID, facility=syslog.LOG_LOCAL0)
syslog.syslog('Log processing initiated...')

上述脚本将使用我们自定义的“LOG_IDENTIFIER”记录到LOCAL0设施中... 您可以使用LOCAL[0-7]来进行本地记录。

1
你的评论与原始请求无关。 - thor
@thor 我同意这是相关的。我猜syslog包比纯Python实现更有效率?(虽然不太灵活) - Daniel Santos

7
https://github.com/luismartingil/per.scripts/tree/master/python_syslog
#!/usr/bin/python
# -*- coding: utf-8 -*-

'''
Implements a new handler for the logging module which uses the pure syslog python module.

@author:  Luis Martin Gil
@year: 2013
'''
import logging
import syslog

class SysLogLibHandler(logging.Handler):
    """A logging handler that emits messages to syslog.syslog."""
    FACILITY = [syslog.LOG_LOCAL0,
                syslog.LOG_LOCAL1,
                syslog.LOG_LOCAL2,
                syslog.LOG_LOCAL3,
                syslog.LOG_LOCAL4,
                syslog.LOG_LOCAL5,
                syslog.LOG_LOCAL6,
                syslog.LOG_LOCAL7]
    def __init__(self, n):
        """ Pre. (0 <= n <= 7) """
        try:
            syslog.openlog(logoption=syslog.LOG_PID, facility=self.FACILITY[n])
        except Exception , err:
            try:
                syslog.openlog(syslog.LOG_PID, self.FACILITY[n])
            except Exception, err:
                try:
                    syslog.openlog('my_ident', syslog.LOG_PID, self.FACILITY[n])
                except:
                    raise
        # We got it
        logging.Handler.__init__(self)

    def emit(self, record):
        syslog.syslog(self.format(record))

if __name__ == '__main__':
    """ Lets play with the log class. """
    # Some variables we need
    _id = 'myproj_v2.0'
    logStr = 'debug'
    logFacilityLocalN = 1

    # Defines a logging level and logging format based on a given string key.
    LOG_ATTR = {'debug': (logging.DEBUG,
                          _id + ' %(levelname)-9s %(name)-15s %(threadName)-14s +%(lineno)-4d %(message)s'),
                'info': (logging.INFO,
                         _id + ' %(levelname)-9s %(message)s'),
                'warning': (logging.WARNING,
                            _id + ' %(levelname)-9s %(message)s'),
                'error': (logging.ERROR,
                          _id + ' %(levelname)-9s %(message)s'),
                'critical': (logging.CRITICAL,
                             _id + ' %(levelname)-9s %(message)s')}
    loglevel, logformat = LOG_ATTR[logStr]

    # Configuring the logger
    logger = logging.getLogger()
    logger.setLevel(loglevel)

    # Clearing previous logs
    logger.handlers = []

    # Setting formaters and adding handlers.
    formatter = logging.Formatter(logformat)
    handlers = []
    handlers.append(SysLogLibHandler(logFacilityLocalN))
    for h in handlers:
        h.setFormatter(formatter)
        logger.addHandler(h)

    # Yep!
    logging.debug('test debug')
    logging.info('test info')
    logging.warning('test warning')
    logging.error('test error')
    logging.critical('test critical')

这很有趣,但它在Python 2.6.6(RHEL 6.4)上无法运行:Traceback(最近的调用最先):文件“syslog_bridge.py”,第68行,在<module>中: handlers.append(SysLogLibHandler(logFacilityLocalN)) 文件“syslog_bridge.py”,第29行,在__init__中: syslog.openlog(syslog.LOG_PID,self.FACILITY [n])TypeError:标识字符串[,logoption [,facility]] - Steve Cohen
根据 https://github.com/luismartingil/scripts/commit/46c947233ca58530bd55336cdc810e9e51c84c90 进行编辑。 - luismartingil

4

以下是3.2及以上版本推荐使用的yaml dictConfig方式。

在日志cfg.yml中:

version: 1
disable_existing_loggers: true

formatters:
    default:
        format: "[%(process)d] %(name)s(%(funcName)s:%(lineno)s) - %(levelname)s: %(message)s"

handlers:
    syslog:
        class: logging.handlers.SysLogHandler
        level: DEBUG
        formatter: default
        address: /dev/log
        facility: local0

    rotating_file:
        class: logging.handlers.RotatingFileHandler
        level: DEBUG
        formatter: default
        filename: rotating.log
        maxBytes: 10485760 # 10MB
        backupCount: 20
        encoding: utf8

root:
    level: DEBUG
    handlers: [syslog, rotating_file]
    propogate: yes

loggers:
    main:
        level: DEBUG
        handlers: [syslog, rotating_file]
        propogate: yes

使用以下方法加载配置:

log_config = yaml.safe_load(open('cfg.yml'))
logging.config.dictConfig(log_config)

已配置了syslog和直接写入文件。注意,/dev/log是操作系统特定的。


1

我使用JSON日志记录,并希望使用UDP端口514的SysLogHandler。最终成功配置了JSON处理程序。在处理程序部分,我有:

{
    "syslog": {
        "class": "logging.handlers.SysLogHandler",
        "address": ["127.0.0.1", 514],
        "facility": "local6",
        "formatter": "syslog_fmt"
}

在其他地方找不到这个。

[编辑] 更清楚地说明这里发生了什么:这仅适用于 Python 代码,并使用 Python 的内置日志模块。该模块允许配置日志消息的格式和目标。配置日志格式和目标的一种方法是使用一个 JSON 文件来配置日志记录。

上面的示例允许我将日志消息发送到 syslog 守护程序。

下面包含了一个完整的示例文件。

{
    "version": 1,
    "disable_existing_loggers": "False",
    "formatters": {
        "verbose": {
            "format": "%(asctime)s:%(levelname)s:%(process)d:%(filename)s:%(funcName)s:L%(lineno)d:%(message)s"
        },
        "syslog": {
            "format": "%(levelname)s:%(process)d:%(filename)s:%(funcName)s:L%(lineno)d:%(message)s"
        }
    },
    "handlers": {
        "console": {
            "class":"logging.StreamHandler",
            "formatter": "standard"
        },
        "syslog": {
        "class": "logging.handlers.SysLogHandler",
        "address": ["127.0.0.1", 514],
        "facility": "local6",
        "formatter": "syslog_fmt"
        }
    },
    "loggers": {
        "": {
            "handlers": ["console","syslog"],
            "level": "DEBUG",
            "propagate": "True"
        }
    }
}

上面的示例将Python日志消息发送到syslog和控制台。不同目标的消息格式是不同的(syslog已经在每个消息前缀中添加了时间戳)。对于syslog目标,日志使用LOCAL6设施。

整个配置文件是什么样子的,我该如何从Python中读取它? - Andreas Schuldei
JSON日志记录是什么意思?你的syslog配置是以json格式吗?你不是以json格式记录日志吗?@ahnkle - Andreas Schuldei
JSON日志记录是使用JSON文件格式配置Python日志记录。可以使用logging.config.dictConfig(<JSON字符串>)读取JSON。 - ahnkle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接