Python:如何使用logging模块每天创建日志文件?

39
我对Python的logging模块还不熟悉。在我的应用程序运行时,我想每天创建一个新的日志文件。
log file name - my_app_20170622.log
log file entries within time - 00:00:01 to 23:59:59

第二天我想创建一个带有明天日期的新日志文件 -

log file name - my_app_20170623.log
log file entries within time - 00:00:01 to 23:59:59

我正在使用Python的日志记录模块。

我使用的方式如下 -

log_level = int(log_level)
logger = logging.getLogger('simple')
logger.setLevel(log_level)
fh = logging.FileHandler(log_file_name)
fh.setLevel(log_level)
formatter = logging.Formatter(log_format)
fh.setFormatter(formatter)
logger.addHandler(fh)

Python的日志模块中是否有任何配置可以每天创建一个日志?


1
可能是使用Python需要每天进行日志轮换(0utc)的重复问题。 - Alastair McCormack
6个回答

60

你需要创建一个TimedRotatingFileHandler

from logging.handlers import TimedRotatingFileHandler
logname = "my_app.log"
handler = TimedRotatingFileHandler(logname, when="midnight", backupCount=30)
handler.suffix = "%Y%m%d"
logger.addHandler(handler)

这段代码将创建一个my_app.log文件,但是当当前日期结束时,在午夜时会将日志移动到名为my_app.log.20170623的新日志文件中。

希望对您有所帮助。


这个"%Y%m%d"格式不会创建20170623这样的文件,它会创建类似于my_app.log.2017-06-23的文件。为什么会这样呢? - ketan
你是对的 @kit,我犯了个错误。请尝试修复后的解决方案。后缀修改必须在处理程序中完成,而不是在日志记录器中。 - Adrian Antunez
是的,我已经发布了正确的解决方案作为回答这个问题,以获取适当的文件名。 - ketan
2
嗨@kit,为什么你把正确的解决方案发布为答案?我的回答已经满足了你的要求,不是吗? - Adrian Antunez

14

RotatingFileHandler

class RotatingFileHandler(  filename[, mode[, maxBytes[, backupCount]]])

返回RotatingFileHandler类的一个新实例。指定的文件将被打开并用作日志记录的流。如果未指定模式,则使用a。默认情况下,文件会无限增长。

RotatingFileHandler允许我们在当前日志文件达到一定大小时将日志语句旋转到新文件中。

在此示例中,当它达到500字节时,我们将将其旋转到一个新文件中,最多备份2个文件。

import logging
import logging.handlers as handlers
import time

logger = logging.getLogger('my_app')
logger.setLevel(logging.INFO)

logHandler = handlers.RotatingFileHandler('app.log', maxBytes=500, backupCount=2)
logHandler.setLevel(logging.INFO)
logger.addHandler(logHandler)

def main():
    while True:
        time.sleep(1)
        logger.info("A Sample Log Statement")

main()
执行此操作后,您应该注意到每当app.log超过500个字节时,它将被关闭并重命名为app.log.x,其中x的值递增,直到达到我们设置的backupCount为止。

TimedRotatingFileHandler

class TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])
返回TimedRotatingFileHandler类的新实例。指定的文件被打开并用作记录的流。在旋转时,它还设置文件名后缀。旋转基于when和interval的乘积而发生。
您可以使用when指定间隔类型。可能的值列表如下,注意它们不区分大小写:
|   Value  |    Type of interval   |
|:--------:|:---------------------:|
|     S    |        Seconds        |
|     M    |        Minutes        |
|     H    |         Hours         |
|     D    |          Days         |
|     W    |  Week day (0=Monday)  |
| midnight | Roll over at midnight |

TimedRotatingFileHandler允许我们按时间切片来捕获日志文件。

import logging
import logging.handlers as handlers
import time

logger = logging.getLogger('my_app')
logger.setLevel(logging.INFO)

logHandler = handlers.TimedRotatingFileHandler('timed_app.log', when='M', interval=1)
logHandler.setLevel(logging.INFO)
logger.addHandler(logHandler)

def main():
    while True:
        time.sleep(1)
        logger.info("A Sample Log Statement")

main()

运行此代码将无限期地每分钟创建新的日志文件。我们可以在logHandler实例上设置backupCount参数,它将限制我们创建的日志文件数量。

使用适当的日志级别

使用TimedRotatingFileHandlerRotatingFileHandler,可以做以下事情,例如将所有错误消息记录到旋转文件中,但将所有普通日志文件记录到TimedRotatingFileHandler中,因为我们希望预计会有比错误消息更多的普通日志文件。

两个级别的记录被拆分为两个不同的日志级别:INFOERROR分别放置到不同的位置。

import logging
import logging.handlers as handlers
import time

logger = logging.getLogger('my_app')
logger.setLevel(logging.INFO)

## Here we define our formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

logHandler = handlers.TimedRotatingFileHandler('normal.log', when='M', interval=1, backupCount=0)
logHandler.setLevel(logging.INFO)
logHandler.setFormatter(formatter)

errorLogHandler = handlers.RotatingFileHandler('error.log', maxBytes=5000, backupCount=0)
errorLogHandler.setLevel(logging.ERROR)
errorLogHandler.setFormatter(formatter)

logger.addHandler(logHandler)
logger.addHandler(errorLogHandler)

def main():
    while True:
        time.sleep(1)
        logger.info("A Sample Log Statement")
        logger.error("An error log statement")

main()

请注意,在运行此代码时会创建3个日志文件。 error.log 只包含级别为ERROR或更高级别的日志。 normal.log 将包含应用程序记录的所有日志消息的组合。

这些只是我认为在实现自己的日志系统时重要的几个方面。


7

最终,我得到了正确的答案并且想要分享这个答案。

基本上,需要创建一个像下面这样的TimedRotatingFileHandler -

log_format = "%(asctime)s - %(levelname)s - %(message)s"
log_level = 10
handler = TimedRotatingFileHandler("my_app.log", when="midnight", interval=1)
handler.setLevel(log_level)
formatter = logging.Formatter(log_format)
handler.setFormatter(formatter)

# add a suffix which you want
handler.suffix = "%Y%m%d"

#need to change the extMatch variable to match the suffix for it
handler.extMatch = re.compile(r"^\d{8}$") 

# finally add handler to logger    
logger.addHandler(handler)

以上代码将生成当前日志文件my_app.log以及前一天的日志文件my_app.log.20170704。

希望对您有所帮助。


为什么需要handler.extMatch这一行? - Adrian Antunez
1
我们需要根据自己的要求更改这个变量,例如如果我想要 20170704,那它的正则表达式应为 **^\d{8}$**。 - ketan
你试过不加那行代码吗?在我写的响应中,没有它也能正常工作。 - Adrian Antunez
是的,我尝试过了,如果没有那行代码就无法正常工作。 - ketan
1
handler.setLevel(log_level) 这一行让我困惑了几个小时,将其改为 logger.setLevel(log_level) - Bheid
显示剩余2条评论

3

TimedRotatingFileHandler 可以用于此目的。请参考以下代码。

from logging.config import dictConfig
import logging

dictConfig({
    'version': 1,
    'formatters': {
        'standard': {
            'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
        }
    },
    'handlers': {
        'myapp_handler': {
            'class': 'logging.handlers.TimedRotatingFileHandler',
            'filename': './my_app.log',
            'when': 'd',
            'interval': 1,
            'backupCount': 30,
            'level': 'DEBUG',
            "encoding": "utf8",
            'formatter': 'standard'
        },
    },
    'loggers': {
        'simple': {
            'level': 'DEBUG',
            'handlers': ['myapp_handler']
        }
    },
})


logger = logging.getLogger("simple")

logger.error("This is a test error")

在上面的例子中,我使用了dictConfig来配置记录器。请参考链接:https://docs.python.org/3/library/logging.config.html 以了解其他配置方式。
一旦日期改变,记录器模块将通过给当前文件添加日期后缀来创建一个新文件。

1
我建议您查看logging.handlers.TimedRotatingFileHandler。我认为这正是您要找的。

0

如果您在创建日志文件时只使用默认的追加选项,请在文件名中使用今天的日期YYYYMMDD。它将简单地附加到已创建的日志文件,否则它将创建该文件。当然,如果您希望日志从午夜开始和结束,那么这个方法就可以工作,但如果您希望在一天中的特定时间切换日志,则此方法将无法工作。


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