【问题标题】:How to mix logging Handlers (File & Timed) and compress log in the same config file?如何混合日志处理程序(文件和定时)并在同一个配置文件中压缩日志?
【发布时间】:2015-06-18 14:06:43
【问题描述】:

我需要在 json 中准备一个日志记录配置文件,该文件按时间、大小旋转它并压缩为我的应用程序中的所有模块旋转的 de 文件(我现在卡住了)。我想用一个单独的 json 配置文件来做,这是我当前的文件,但是这个配置只按时间轮换:

{
    "version": 1,
    "disable_existing_loggers": false,
    "formatters": {
        "simple": {
            "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
            "datefmt": "%Y-%m-%d %H:%M:%S"
        }
    },

    "handlers": {
        "my_rotate": {
            "level": "DEBUG",
            "class": "logging.handlers.TimedRotatingFileHandler",
            "formatter": "simple",
            "when": "D",
            "interval": 1,
            "backupCount": 5,
            "filename": "/var/log/test.log",
            "encoding": "utf8"
        }
    },

    "loggers": {
        "my_module": {
            "level": "DEBUG",
            "handlers": ["my_rotate"]
        }
    },

    "root": {
        "level": "DEBUG",
        "handlers": ["my_rotate"],
        "propagate": false
    }
}

如果我在处理程序部分 (logging.handlers.RotateFileHandler) 添加其他处理程序以按大小旋转结果日志文件重复所有消息。

我的主程序使用以下方法获取记录器属性:

config_json_file = open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "logging.json"))
logging_data = json.load(config_json_file)
logging.config.dictConfig(logging_data)

我的模块像这样调用记录器:

class StoreData(object):
    def __init__(self):
        self.logger = logging.getLogger(__name__)

如何在这个文件中混合我的三个要求(按大小和时间旋转,并压缩旋转的文件)?

非常感谢!

更新 1

正如@Bakuriu 建议的那样,我创建了一个新类来覆盖能够压缩旋转文件所需的方法:

class MyRotatingFileHandler(logging.handlers.TimedRotatingFileHandler):
    """ My rotating file hander to compress rotated file """

    def __init__(self, **kwargs):
        logging.handlers.TimedRotatingFileHandler.__init__(self, **kwargs)

    def rotate(self, source, dest):
        """ Compress rotated log file """
        os.rename(source, dest)
        f_in = open(dest, 'rb')
        f_out = gzip.open("%s.gz" % dest, 'wb')
        f_out.writelines(f_in)
        f_out.close()
        f_in.close()
        os.remove(dest)

所以“新”类被称为来自日志记录配置文件,我在其中添加了一个新的处理程序:

"handlers": {
    "my_rotate": {
        "level": "DEBUG",
        "class": "myMainPackage.MyRotatingFileHandler",
        "formatter": "simple",
        "when": "D",
        "interval": 1,
        "backupCount": 5,
        "filename": "/var/log/test.log",
        "encoding": "utf8"
}

在下一次更新中,我将添加一个方法来覆盖所需的方法,并能够将时间和文件大小轮换混合在一起。

更新 2

好吧,我已经覆盖了“shouldRollover”方法,因此我混合了旋转的时间和文件大小。

class MyRotatingFileHandler(logging.handlers.TimedRotatingFileHandler):
        """ My rotating file hander to compress rotated file """
        def __init__(self, filename, mode='a', maxBytes=0, backupCount=0, encoding=None,
                     delay=0, when='h', interval=1, utc=False):
            if maxBytes > 0:
                mode = 'a'
            logging.handlers.TimedRotatingFileHandler.__init__(
                self, filename, when, interval, backupCount, encoding, delay, utc)
            self.maxBytes = maxBytes
            self.backupCount = backupCount

        def shouldRollover(self, record):
            """ Determine if rollover should occur. """
            # Check rollover by size
            if self.stream is None:                 # delay was set...
                self.stream = self._open()
            if self.maxBytes > 0:                   # are we rolling over?
                msg = "%s\n" % self.format(record)
                self.stream.seek(0, 2)  #due to non-posix-compliant Windows feature
                if self.stream.tell() + len(msg) >= self.maxBytes:
                    return 1
            # Check rollover by time
            t = int(time.time())
            if t >= self.rolloverAt:
                return 1
            return 0

        def rotate(self, source, dest):
            """ Compress rotated log file """
            os.rename(source, dest)
            f_in = open(dest, 'rb')
            f_out = gzip.open("%s.gz" % dest, 'wb')
            f_out.writelines(f_in)
            f_out.close()
            f_in.close()
            os.remove(dest)

并修改我在 json 中的日志记录配置文件以按天轮换时间,但以秒为单位检查...这意味着 86400s(一天中的秒数)和 5Mb 的大小限制 em>:

"handlers": {
        "my_rotate_timed": {
            "level": "DEBUG",
            "class": "myMainPackage.MyRotatingFileHandler",
            "formatter": "simple",
            "when": "S",
            "interval": 86400,
            "backupCount": 5,
            "maxBytes": 5242880,
            "filename": "/var/log/test.log",
            "encoding": "utf8"
    }

这种方式重用了 TimedRotationFileHandler 中的其他方法,如果 maxBytes 调用了旋转,那么它使用从 init TimedRotationFileHandler 继承的以秒为单位的后缀方法,格式为: "%Y-%m-%d_%H-%M-%S" 。这就是我使用 "{when="S", interval=86400}" 而不是 "{when="D", interval=1}" 的原因

【问题讨论】:

  • 感谢分享,我去测试一下。顺便说一句,init 中的“mode”参数未使用
  • 我认为按大小进行的旋转工作不正常。在这种情况下,backupCount 始终为 1,即使它被另外配置。我会努力解决的

标签: python json logging


【解决方案1】:

这是意料之中的。如果你添加一个处理程序,这个处理程序要么不会产生消息(由于过滤),所以什么都不会改变,或者它会写入这些消息(从而复制它们)。

TimedRotatingFileHandlerRotatingFileHandler 分别仅支持按时间和按大小旋转。不能同时进行。

AFAIK没有内置的方法来实现你想要的,所以只使用配置文件你将无法实现你想要的,你必须编写代码来组合功能。

考虑阅读如何在Logging Cookbook 中创建新的旋转处理程序。如果将此处理程序类保存在文件mypackage.myrotatinghandler 中,则可以指定:

class: 'mypackage.myrotatinghandler.MyHandler'

在配置文件中。

请注意,要在保存文件时添加压缩,只需使用保存压缩文件的函数设置旋转处理程序的rotator 属性即可。摘自上面的链接:

def namer(name):
    return name + ".gz"

def rotator(source, dest):
    with open(source, "rb") as sf:
        data = sf.read()
        compressed = zlib.compress(data, 9)
        with open(dest, "wb") as df:
            df.write(compressed)
    os.remove(source)

rh = logging.handlers.RotatingFileHandler(...)
rh.rotator = rotator
rh.namer = namer

rh 处理程序的行为与普通的RotatingFileHandler 一样,但也会压缩日志。

但是,设置执行轮换的条件需要您重新编写处理程序的某些部分。您可以阅读logging.handlers 模块的源代码,了解内置处理程序是如何实现的。

【讨论】:

  • 那是完美的@Bakuriu。我已经创建了一个新类来覆盖特定方法以添加旋转日志压缩(更新问题)。当我有时间时,我将覆盖另一个类来混合时间和大小旋转。感谢您的回答
猜你喜欢
  • 1970-01-01
  • 2013-12-04
  • 2020-03-27
  • 2015-08-15
  • 2013-12-08
  • 1970-01-01
  • 1970-01-01
  • 2017-11-21
  • 1970-01-01
相关资源
最近更新 更多