python程序日志_如何在python应用程序中实现日志记录

python程序日志

A practical guide to implementing logging

实施日志的实用指南

Recently, I set about building a Python application to query a PostgreSQL database, process the data and push subsequent trigger events to a Kafka queue. However, before tackling the interesting aspects, I knew I needed to get the basics right. And one very important basic for any application is logging!

最近,我着手构建一个Python应用程序来查询PostgreSQL数据库,处理数据并将后续触发事件推送到Kafka队列。 但是,在解决有趣的方面之前,我知道我需要正确了解基础知识。 对于任何应用程序来说,一个非常重要的基础是日志记录!

In this article I’ll first explain some of the key features of logging in Python and, more importantly, demonstrate how I implemented a logger in my application.

在本文中,我将首先解释Python日志记录的一些关键功能,更重要的是,演示如何在应用程序中实现记录器。

Image for post
An example of an error log when connecting to PostgreSQL — Image from author
连接到PostgreSQL时的错误日志的示例—照片作者作者

打印(“为什么不呢?”) (print(‘Why not?’))

The first question to tackle: Why not just use print()? It’s tempting to take the easy route and add print statements all over your code. We’ve all written code thats looks a bit like this:

要解决的第一个问题:为什么不只使用print() ? 采取简单的方法并在代码中添加打印语句很诱人。 我们都编写了如下代码:

print("Getting some docs...")
docs = getDocs()
print("Doc count %s", len(docs))
print("Finished")

Generally this is fine for small scripts, or if you need something quick and dirty while developing or debugging. However, print() is just not a viable logging solution for larger applications. Especially if you are planning on promoting your code into production environments.

通常,这适用于小型脚本,或者在开发或调试时需要快速又肮脏的东西。 但是,对于大型应用程序, print()并不是可行的日志记录解决方案。 特别是如果您打算将代码推广到生产环境中。

To touch on a few of the reasons why print should be avoided. Firstly, It only gives you the option of logging to stdout. This is going to be problematic if you are logging lots of data. Really, you want to be writing your logs somewhere that can be easily persisted, backed up and queried at a later date. Another reason is that print statements are not configurable at run time. To switch on/off specific logs you will need to modify your code every single time. This will mean redeploying your code to production every time you need to turn on debug logging! On top of this, it’s also much more difficult to include valuable information and context, such as the line number and the time the log message was generated.

谈谈应避免打印的一些原因。 首先,它仅使您可以选择登录到stdout 。 如果您要记录大量数据,这将是有问题的。 确实,您希望将日志写入可以轻松保存,备份和稍后查询的地方。 另一个原因是在运行时无法配置打印语句。 要打开/关闭特定的日志,您需要每次都修改代码。 这意味着您每次需要打开调试日志记录时,都将代码重新部署到生产环境中! 最重要的是,包含有价值的信息和上下文(例如行号和生成日志消息的时间)也要困难得多。

I could go on, but hopefully you are already convinced!

我可以继续,但是希望您已经说服了!

输入Python的日志记录模块 (Enter Python’s Logging Module)

Fortunately, the importance of logging is not a new phenomenon. Python ships with a ready made logging solution as part of the Python standard library. It solves all the afore mentioned problems with using print. For example:

幸运的是,测井的重要性并不是一个新现象。 Python随附了现成的日志记录解决方案,作为Python标准库的一部分。 它解决了使用打印的所有上述问题。 例如:

  • Automatically add context, such as line number & timestamps to logs

    自动将上下文(例如行号和时间戳)添加到日志中
  • It’s possible to update our logger at runtime by passing a configuration file to the app

    通过将配置文件传递给应用程序,可以在运行时更新记录器
  • It is easy to customise the log severity and configure different logging levels for different environments

    自定义日志严重性并针对不同环境配置不同的日志记录级别很容易

Lets try it out and set up a very basic logger:

让我们尝试一下并设置一个非常基本的记录器:

Running this gives:

运行此命令可获得:

INFO:__main__:Getting some docs...
INFO:__main__:Doc count 2
INFO:__main__:Finished

Easy peasy! ✅ 👏

十分简单! 👏

Here, we have imported the logging module from the Python standard library. We then updated the default basic log level to log INFO messages. Next, logger = logging.getLogger(__name__) instantiates our logging instance. Finally, we passed an event to the logger with a log level of INFO by callinglogger.info("").

在这里,我们从Python标准库中导入了日志记录模块。 然后,我们更新了默认的基本日志级别以记录INFO消息。 接下来, logger = logging.getLogger(__name__)实例化我们的日志记录实例。 最后,我们通过调用logger.info("")将事件传递给日志级别为INFO的记录器。

At first glance, this output might appear suspiciously similar to using print(). Next, we’ll expand our example logger to demonstrate some of the more powerful features that the Python standard logging modules provides.

乍一看,此输出看起来可疑地类似于使用print() 。 接下来,我们将扩展示例记录器,以演示Python标准记录模块提供的一些更强大的功能。

日志级别 (Log Levels)

We can configure the severity of the logs being output and filter out unimportant ones. The module defines five constants throughout the spectrum, making it easy to differentiate between messages. The numeric values of logging levels are given in the following table.

我们可以配置输出日志的严重性,并过滤掉不重要的日志。 该模块在整个频谱中定义了五个常数,从而可以轻松区分消息。 下表中给出了日志记录级别的数值。

Image for post
https://docs.python.org/3/library/logging.html#logging-levels https://docs.python.org/3/library/logging.html#logging-levels的日志记录级别

It’s important not to flood your logs with lots of messages. To achieve concise logs, we should be careful to define the correct log level for each event.

重要的是不要在日志中充斥大量消息。 为了获得简洁的日志,我们应该为每个事件定义正确的日志级别。

logger.critical("Really bad event"
logger.error("An error")
logger.warning("An unexpected event")
logger.info("Used for tracking normal application flow")
logger.debug("Log data or variables for developing")

I tend to use the debug level to log the data being passed around the app. Here is an example of using 3 different log levels in the few lines of code responsible for sending events to Kafka.

我倾向于使用调试级别来记录在应用程序周围传递的数据。 这是在几行代码中使用3种不同日志级别的示例,这些代码负责将事件发送到Kafka。

格式化日志 (Formatting Logs)

The default formatter of the Python logging module doesn’t provide a great amount of detail. Fortunately, it is easy to configure the log format to add all the context we need to produce super useful log messages.

Python日志记录模块的默认格式化程序未提供大量详细信息。 幸运的是,很容易配置日志格式以添加生成超级有用的日志消息所需的所有上下文。

For example, here we add a timestamp and the log level to the log message.

例如,在这里,我们在日志消息中添加时间戳和日志级别。

formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')

It’s best practice to add as much context as possible to your logs. This can easily be achieved by adding structured data to the log message’s metadata. For example, you may have scaled your application to run with multiple workers. In this case it might be important to know which worker was logging each event when you’re debugging so lets add a worker id context to the log metadata.

最佳做法是在日志中添加尽可能多的上下文。 通过将结构化数据添加到日志消息的元数据中,可以轻松实现这一点。 例如,您可能已缩放应用程序以与多个工作程序一起运行。 在这种情况下,调试时知道哪个工作程序正在记录每个事件可能很重要,因此让我们将工作程序ID上下文添加到日志元数据中。

# Create the log formatterformatter = 
handler.setFormatter(formatter)logger.info('Querying database for docs...', extra={'worker':
'id_1'})

The output becomes:

输出变为:

2020-09-02 22:06:18,170 - id_1 - INFO - Querying database for docs...

日志处理程序 (Log Handlers)

Now we have perfectly formatted logs being fired at us from all over our application code, we need to consider where those logs are ending up. By default the logs are being written to stdout, but Python’s logging module provides us with the functionality to push logs to alternative locations. For example, to save logs to example.log file on disk.

现在,我们已经从所有应用程序代码中向我们发射了格式正确的日志,我们需要考虑这些日志在哪里结束。 默认情况下,日志会写入stdout,但是Python的日志记录模块为我们提供了将日志推送到其他位置的功能。 例如,将日志保存到磁盘上的example.log文件中。

# create a file handler
handler = logging.FileHandler('example.log')
handler.setLevel(logging.INFO)

There are several types of handlers that can be used. For the complete list, see the documentation for handlers. It is also possible to define custom logging handlers for different use cases. For example, here is a library that defines a log handler for pushing logs to Slack!

可以使用几种类型的处理程序。 有关完整列表,请参见处理程序文档 。 也可以为不同用例定义自定义日志记录处理程序。 例如, 是一个库,该库定义了用于将日志推送到Slack的日志处理程序!

To summarise. We’ve set up the Python standard logging module and configured it to log to different locations with custom log formats. You can find the final code for the example logger below:

总结一下。 我们已经设置了Python标准日志记录模块,并将其配置为使用自定义日志格式记录到其他位置。 您可以在下面找到示例记录器的最终代码:

实作 (Implementation)

Hopefully, by now you should have a good idea of some of the key features of the Python logging module. That’s all well and good, but how should you implement all this different configuration and manage importing logging modules in your application files? It’s a point often over looked by many guides. Here is how I decided to handle it.

希望到目前为止,您应该对Python日志记录模块的一些关键功能有所了解。 很好,但是您应该如何实现所有这些不同的配置并管理应用程序文件中的导入日志记录模块? 许多指南经常忽略这一点。 这是我决定处理的方式。

First up, I decided to extract all the logging configuration into a config file called logging.ini. You can find all the information you need on how to format the log file in the official docs, so I won’t go over it all here. Currently, my logging.ini is super basic. When the application is out of development and ready for deployment I’ll be extending the logging configuration to handle different environments, rotate logs on disk and sent alerts to a Slack channel.

首先,我决定将所有日志记录配置提取到一个名为logging.ini的配置文件中。 您可以在官方文档中找到有关如何格式化日志文件所需的所有信息,因此在此不再赘述。 目前,我的logging.ini超级基础。 当应用程序停止开发并准备部署时,我将扩展日志记录配置以处理不同的环境,旋转磁盘上的日志并将警报发送到Slack通道。

The great thing is, this really simplifies the implementation from a code point of view because you need much less boilerplate to set up log handlers and formatters. Above, we had 15 lines of code setting up just one formatter and one handler - it’s easy to imagine how this could become a lot bigger with more complex use cases. Now all that complexity has been abstracted away to the config file. The result…to implement your logger you just need two extra lines of code in your app!

伟大的事情是,这真的是从一个码点简化了实现,因为你需要少得多的样板设置日志处理程序和格式化。 上面,我们有15行代码仅设置了一个格式化程序和一个处理程序-可以想象,如果使用更复杂的用例,代码可能会变得更大。 现在,所有这些复杂性已被抽象到配置文件中。 结果……要实现记录器,您只需要在应用程序中多添加两行代码即可!

import logging.configlogging.config.fileConfig(fname='logger.ini')

Pretty neat! But where should this code live exactly?

漂亮整齐! 但是此代码应准确地存放在哪里?

My application is structured with a __main__.py as the entry point. I decided to implement my logger inside this file in order to not distract from the main functionality contained within app.py. The below __main__.py will handle reading the config file and applying the configuration to your logger.

我的应用程序的结构是一个__main__.py作为入口点。 我决定在此文件中实现我的记录器,以免分散app.py中包含的主要功能。 下面的__main__.py将处理读取配置文件并将配置应用于记录器的操作。

Finally, we need to make use of our creation, by firing off some logs! But these messages aren’t going to be generated by the__main__function, so how should we handle passing our logger to other files and functions within the application?

最后,我们需要通过释放一些日志来利用我们的创造! 但是这些消息不会由__main__函数生成,因此我们应该如何处理将记录器传递给应用程序中的其他文件和函数呢?

Again this becomes trivial. To use the logger in another file, we only need to import the logging module and instantiate it. It will automatically inherit the configuration that we passed to it in __main__. For example, to start logging messages in app.py:

再次,这变得微不足道。 要在另一个文件中使用记录器,我们只需要导入记录模块并将其实例化即可。 它将自动继承我们在__main__传递给它的配置。 例如,要开始在app.py记录消息:

The above debug message will be handled based on the logging configuration read fromlogging.ini. And the same will be true for every other python file in you application, as long as you import logging at the top. Yes, it is really that simple!

以上调试消息将根据从logging.ini读取的日志配置进行处理。 只要您在顶部import logging ,应用程序中的其他所有python文件都一样。 是的,真的就是这么简单!

To recap, not only have you seen some of the features that make the Python logging module an awesome tool to have in your armoury, you have also seen how easy it is to implement.

回顾一下,您不仅看到了使Python日志记录模块成为强大工具的某些功能,而且还看到了实现的难易程度。

Thank you for reading and I hope you found this guide useful. Happy logging!

感谢您的阅读,希望本指南对您有所帮助。 祝您登录愉快!

翻译自: https://medium.com/@leo.brack/how-to-implement-logging-in-your-python-application-1730315003c4

python程序日志

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值