在Python中编写自定义日志处理程序
#python #devops #microservices #logs

您是否曾经想过Logz .io或Loggly .com之类的伐木服务如何将日志运到他们的平台上?例如,logz .io有一个opensource记录处理程序,他们用来将日志从您的应用程序运输到其平台,在此对这些日志上进行进一步的分析,以使您能够获得有关应用程序性能的实质性见解。在这篇简短的文章中,我们将简要介绍使用Python standard logging软件包编写用户定义的日志处理程序的基础。

您可能想知道为什么我选择写这个相当“奇怪”的话题,但是良好的记录系统的重要性不能被夸大,无论您的应用程序是Web应用程序,移动设备,甚至是操作系统,登录都可以使您启用。获取有关您的申请表现的有用信息。一个良好的记录系统有助于在发生错误时捕获错误,而不是依靠用户报告这些错误,大多数人只是不知道该怎么做。

logging.streamhandler

Python标准记录程序包提供处理程序类,该类基本上定义了特定日志消息的处理方式,我们可以配置一个简单的处理程序,将日志消息打印到终端:

LOGGING = {
    "version": 1, 
    "disable_existing_loggers": False,
    "formatters": {
        "stdformatter": {"format": "DateTime=%(asctime)s loglevel=%(levelname)-6s  %(funcName)s() L%(lineno)-4d %(message)s call_trace=%(pathname)s L%(lineno)-4d"},
    },
    "handlers": {
        "stdhandler": {
            "class": "logging.StreamHandler",
            "formatter": "stdformatter",
            'stream': 'ext://sys.stdout'
        },
    },
    "loggers" : {
        "root": {
            "handlers": ["stdhandler"], 
            "level": "DEBUG",
            "propagate": True
            }
        }
}

我们可以在log.py 中初始化此配置,假设log.py文件是我们要log
的应用程序

import logging
from logging.config import dictConfig


dictConfig(LOGGING)
logger = logging.getLogger()
logger.debug("logger started")

def logToConsole():
    logger.info("Application started, succesfully")
    logger.info("Logging to the console")
    logger.info("Finished logging to console!")

# call the function
logToConsole()

我们运行log.py文件时python3 log.py;我们可以看到与端子上显示的相似的输出

Logging to the console with python
如您所见,我们已经能够配置直接登录控制台(STDOUT)的处理程序,请记住,您不需要创建任何连接即可登录到终端,因为StreamHandler是该端子的默认处理程序记录包。记录程序包还带有一系列已经制造的处理程序您可以使用Aside the StreamHandler,其中一些列出了:

  • fileHandler :将记录输出发送到磁盘文件
  • nullhandler :它本质上是图书馆开发人员使用的无用处理程序。我从来没有任何理由使用:)
  • BaserotatingHandler :这基本上是特殊用例的另一种fileHandler
  • sockethandler :将记录输出发送到网络插座。
  • sysloghandler :将记录消息发送到远程或本地unix syslog。
  • smtphandler :使用SMTP。
  • 将日志消息发送到电子邮件地址
  • httphandler :使用GET或发布语义来支持将记录消息发送到Web服务器。

记录包提供了几个处理程序,可以找到here,其中有些您可能永远不会使用用例,因为它们是为非常特定的用例构建的。

自定义处理程序

假设我们正在建立一个基于云的日志记录,作为像logz.io这样的服务软件,我们想在这里存储,检索和分析我们的日志以获得更好的见解; Python提供了一个低级的“ API”,以使用logging.Handler类轻松地从Python应用程序传输日志。在下面的示例中,我们将创建一个简单的自定义处理程序,该处理程序写入“ .txt”文件。显然,日志消息没有写入TXT文件,但这是为了说明如何以简单的方式编写自定义日志处理程序。

class SpecialHandler(logging.Handler):

    def __init__(self )-> None:
        self.sender = LogSender()
        logging.Handler.__init__(self=self)

    def emit(self, record) -> None:
        self.sender.writeLog(record)

在上面的代码中,我们声明了一个名为SpecialHandler的类,该类从logging.Handler基类继承,这基本上允许我们扩展基本处理程序,从而使其适合我们的使用。然后,我们声明__init__方法并启动在检索每个日志记录时处理每个日志记录的类。

 class LogSender:
    def __init__(self) -> None:
        pass

    def writeLog(self, msg: logging.LogRecord) -> None:
        with open("application.txt",'a',encoding = 'utf-8') as f:
            f.write(f"{msg} \n")

LogSender类充当日志记录的检索,因为它们是从处理程序类的emit方法发送的; logsender 类声明了writeLog方法,该方法接受msg: logging.LogRecord类型的任何参数。在接收记录时,它在附加模式下打开txt文件application.txt并将日志消息写入文件,在其末尾添加新行。
这基本上是创建自定义Handler所需的全部,我们可以配置我们的LOGGING Dictionary,以使用几行代码识别此新处理程序,例如:

LOGGING = {
    "version": 1, 
    "disable_existing_loggers": False,
    "formatters": {
        "stdformatter": {"format": "DateTime=%(asctime)s loglevel=%(levelname)-6s  %(funcName)s() L%(lineno)-4d %(message)s call_trace=%(pathname)s L%(lineno)-4d"},
    },
    "handlers": {
        "stdhandler": {
            "class": "logging.StreamHandler",
            "formatter": "stdformatter",
            'stream': 'ext://sys.stdout'
        },
        "specialhandler":{
            "class" : "writer.SpecialHandler",
            "formatter": "stdformatter",
        }
    },
    "loggers" : {
        "root": {
            "handlers": ["stdhandler", "specialhandler"], 
            "level": "DEBUG",
            "propagate": True
            }
        }
}

请记住,Writer.specialhandler是我们在Writer.py文件中写的自定义处理程序类。要初始化此更改,我们可以将Writer.py文件导入到我们的log.py文件中,然后再次将记录配置加载到DICTCONFIG中,但是这次使用将写入我们的应用程序的自定义处理程序.txt文件。

import logging
from logging.config import dictConfig
import writer #writer.py contains our SpecialHandler and LogSender class respectively

LOGGING = {
    "version": 1, 
    "disable_existing_loggers": False,
    "formatters": {
        "stdformatter": {"format": "DateTime=%(asctime)s loglevel=%(levelname)-6s  %(funcName)s() L%(lineno)-4d %(message)s call_trace=%(pathname)s L%(lineno)-4d"},
    },
    "handlers": {
        "stdhandler": {
            "class": "logging.StreamHandler",
            "formatter": "stdformatter",
            'stream': 'ext://sys.stdout'
        },
        "specialhandler":{
            "class" : "writer.SpecialHandler",
            "formatter": "stdformatter",
        }
    },
    "loggers" : {
        "root": {
            "handlers": ["stdhandler", "specialhandler"], 
            "level": "DEBUG",
            "propagate": True
            }
        }
}

dictConfig(LOGGING)
logger = logging.getLogger()
logger.debug("logger started")

def logToConsole():
    logger.info("Application started, succesfully")
    logger.info("Logging to the console")
    logger.info("Finished logging to console!")

# call the function
logToConsole()

运行python3 log.py时,我们可以在当前目录中创建一个application.txt文件,其中包含所有日志记录。

custom handler output

结论

记录应用程序可以节省我们在应用程序内调试隐藏错误的时间,并能够获得有关应用程序的性能,可操作性和需求而无需任何麻烦的有用见解。在这篇简短的文章中,我们看到了如何为应用程序编写基本的自定义日志处理程序,以适合我们的特定需求。

让我知道您对本文的看法,以及可以改善其用户体验的任何修正案,感谢您的阅读和度过愉快的时光!