如何在Python中记录源文件名和行号

发布于 2024-07-13 20:29:28 字数 65 浏览 11 评论 0原文

是否可以装饰/扩展Python标准日志记录系统,以便在调用日志记录方法时它还记录文件和调用它的行号或者调用它的方法?

Is it possible to decorate/extend the python standard logging system, so that when a logging method is invoked it also logs the file and the line number where it was invoked or maybe the method that invoked it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

扬花落满肩 2024-07-20 20:29:28

当然,请检查日志文档中的格式化程序。 特别是 lineno 和 pathname 变量。

%(pathname)s 发出日志记录调用的源文件的完整路径名(如果可用)。

%(filename)s 路径名的文件名部分。

%(module)s 模块(文件名的名称部分)。

%(funcName)s 包含日志记录调用的函数名称。

%(lineno)d 发出日志记录调用的源行号(如果有)。

看起来像这样:

formatter = logging.Formatter('[%(asctime)s] p%(process)s {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s','%m-%d %H:%M:%S')

Sure, check formatters in logging docs. Specifically the lineno and pathname variables.

%(pathname)s Full pathname of the source file where the logging call was issued(if available).

%(filename)s Filename portion of pathname.

%(module)s Module (name portion of filename).

%(funcName)s Name of function containing the logging call.

%(lineno)d Source line number where the logging call was issued (if available).

Looks something like this:

formatter = logging.Formatter('[%(asctime)s] p%(process)s {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s','%m-%d %H:%M:%S')
明媚如初 2024-07-20 20:29:28

除了 Seb 非常有用的答案之外,这里有一个方便的代码片段,它以合理的格式演示了记录器的用法

#!/usr/bin/env python
import logging

logging.basicConfig(format='%(asctime)s,%(msecs)03d %(levelname)-8s [%(filename)s:%(lineno)d] %(message)s',
    datefmt='%Y-%m-%d:%H:%M:%S',
    level=logging.DEBUG)

logger = logging.getLogger(__name__)
logger.debug("This is a debug log")
logger.info("This is an info log")
logger.critical("This is critical")
logger.error("An error occurred")

:输出:

2017-06-06:17:07:02,158 DEBUG    [log.py:11] This is a debug log
2017-06-06:17:07:02,158 INFO     [log.py:12] This is an info log
2017-06-06:17:07:02,158 CRITICAL [log.py:13] This is critical
2017-06-06:17:07:02,158 ERROR    [log.py:14] An error occurred

On top of Seb's very useful answer, here is a handy code snippet that demonstrates the logger usage with a reasonable format:

#!/usr/bin/env python
import logging

logging.basicConfig(format='%(asctime)s,%(msecs)03d %(levelname)-8s [%(filename)s:%(lineno)d] %(message)s',
    datefmt='%Y-%m-%d:%H:%M:%S',
    level=logging.DEBUG)

logger = logging.getLogger(__name__)
logger.debug("This is a debug log")
logger.info("This is an info log")
logger.critical("This is critical")
logger.error("An error occurred")

Generates this output:

2017-06-06:17:07:02,158 DEBUG    [log.py:11] This is a debug log
2017-06-06:17:07:02,158 INFO     [log.py:12] This is an info log
2017-06-06:17:07:02,158 CRITICAL [log.py:13] This is critical
2017-06-06:17:07:02,158 ERROR    [log.py:14] An error occurred
樱桃奶球 2024-07-20 20:29:28
import logging

# your imports above ...


logging.basicConfig(
    format='%(asctime)s,%(msecs)d %(levelname)-8s [%(pathname)s:%(lineno)d in ' \
           'function %(funcName)s] %(message)s',
    datefmt='%Y-%m-%d:%H:%M:%S',
    level=logging.DEBUG
)

logger = logging.getLogger(__name__)

# your classes and methods below ...
# A very naive sample of usage:
try:
    logger.info('Sample of info log')
    # your code here
except Exception as e:
    logger.error(e)

与其他答案不同,这将记录文件的完整路径和可能发生错误的函数名称。 如果您的项目包含多个模块并且多个同名文件分布在这些模块中,则这一点很有用。

输出示例:

2022-12-02:10:00:00,000 INFO     [<stdin>:2 in function <module>] Sample of info log
2022-12-02:10:00:00,000 INFO     [<full path>/logging_test_file.py:15 in function <module>] Sample of info log
import logging

# your imports above ...


logging.basicConfig(
    format='%(asctime)s,%(msecs)d %(levelname)-8s [%(pathname)s:%(lineno)d in ' \
           'function %(funcName)s] %(message)s',
    datefmt='%Y-%m-%d:%H:%M:%S',
    level=logging.DEBUG
)

logger = logging.getLogger(__name__)

# your classes and methods below ...
# A very naive sample of usage:
try:
    logger.info('Sample of info log')
    # your code here
except Exception as e:
    logger.error(e)

Different from the other answers, this will log the full path of file and the function name that might have occurred an error. This is useful if you have a project with more than one module and several files with the same name distributed in these modules.

Example output:

2022-12-02:10:00:00,000 INFO     [<stdin>:2 in function <module>] Sample of info log
2022-12-02:10:00:00,000 INFO     [<full path>/logging_test_file.py:15 in function <module>] Sample of info log
大海や 2024-07-20 20:29:28

要以将调试日志记录发送到标准输出的方式构建上述内容:

import logging
import sys

root = logging.getLogger()
root.setLevel(logging.DEBUG)

ch = logging.StreamHandler(sys.stdout)
ch.setLevel(logging.DEBUG)
FORMAT = "[%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s"
formatter = logging.Formatter(FORMAT)
ch.setFormatter(formatter)
root.addHandler(ch)

logging.debug("I am sent to standard out.")

将上述内容放入名为 debug_logging_example.py 的文件中会生成输出:

[debug_logging_example.py:14 -             <module>() ] I am sent to standard out.

然后,如果您想关闭日志记录,请注释掉 root.setLevel(logging.DEBUG)

对于单个文件(例如类分配),我发现这是一种比使用 print() 语句更好的方法。 它允许您在提交之前在一个位置关闭调试输出。

To build on the above in a way that sends debug logging to standard out:

import logging
import sys

root = logging.getLogger()
root.setLevel(logging.DEBUG)

ch = logging.StreamHandler(sys.stdout)
ch.setLevel(logging.DEBUG)
FORMAT = "[%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s"
formatter = logging.Formatter(FORMAT)
ch.setFormatter(formatter)
root.addHandler(ch)

logging.debug("I am sent to standard out.")

Putting the above into a file called debug_logging_example.py produces the output:

[debug_logging_example.py:14 -             <module>() ] I am sent to standard out.

Then if you want to turn off logging comment out root.setLevel(logging.DEBUG).

For single files (e.g. class assignments) I've found this a far better way of doing this as opposed to using print() statements. Where it allows you to turn the debug output off in a single place before you submit it.

酒中人 2024-07-20 20:29:28

对于使用 PyCharm 或 Eclipse pydev 的开发人员,以下内容将在控制台日志输出中生成指向日志语句源的链接:

import logging, sys, os
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, format='%(message)s | \'%(name)s:%(lineno)s\'')
log = logging.getLogger(os.path.basename(__file__))


log.debug("hello logging linked to source")

请参阅 Eclipse 控制台中的 Pydev 源文件超链接,用于更长的讨论和历史记录。

For devs using PyCharm or Eclipse pydev, the following will produce a link to the source of the log statement in the console log output:

import logging, sys, os
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, format='%(message)s | \'%(name)s:%(lineno)s\'')
log = logging.getLogger(os.path.basename(__file__))


log.debug("hello logging linked to source")

See Pydev source file hyperlinks in Eclipse console for longer discussion and history.

原来是傀儡 2024-07-20 20:29:28

如果使用 GetLogger(name) 选项设置记录器,其中名称是您指定的名称,您还可以使用 %(name)s 格式化记录器。 您可以使用 GetLogger 函数在每个文件中指定不同的名称,当生成日志时,您将通过您设置的名称知道来自哪个文件。

示例:

import logging

logging.getLogger("main")
logging.basicConfig(#filename=log_fpath, 
                    level=log_level,
                    format='[%(asctime)s] src:%(name)s %(levelname)s:%(message)s',
                    handlers=[logging.FileHandler(log_fpath)])

If the logger is set with the GetLogger(name) option, where the name is a name that you specified you can also format the logger using %(name)s. You can specify a different name in every file with the GetLogger function, and when a log is produced you will know from which file comes through the name you set.

Example:

import logging

logging.getLogger("main")
logging.basicConfig(#filename=log_fpath, 
                    level=log_level,
                    format='[%(asctime)s] src:%(name)s %(levelname)s:%(message)s',
                    handlers=[logging.FileHandler(log_fpath)])
送你一个梦 2024-07-20 20:29:28

原来的问题提到“装饰/扩展”。 不知道是我想太多还是其他人都走错了方向。 如果我们要装饰 python 默认日志记录工具,同时让堆栈信息按预期工作,例如“filename”和“lineno”显示装饰外部所需的堆栈。 然后@Tony S Yu在另一篇文章中给出了答案: https://stackoverflow.com/a/55998744/8013269

关键是使用stacklevel属性,我用一段代码来演示:

logging.basicConfig(
    format=('%(asctime)s,%(msecs)03d %(levelname)-8s '
            '[%(filename)s:%(lineno)d] %(message)s'),
    level=logging.DEBUG,
    datefmt='%H:%M:%S')

def my_log(*args, **kwargs):
    indent = kwargs.pop('indent', 0)
    msg = ' ' * indent + ' '.join([str(a) for a in args])
    print(f'[MYLOG] {msg}')
    return logging.info(msg, stacklevel=2, **kwargs)

the original question mentioned "decorate/extend". not sure if it's just i think too much or everyone else goes a wrong direction. if we're going to decorate python default logging facility, while having stack info works as expected, such as "filename" and "lineno" showing desired stack outside decorate. then there is an answer from @Tony S Yu in another post: https://stackoverflow.com/a/55998744/8013269

the key is to use stacklevel attribute, i'll demonstrate with a piece of code:

logging.basicConfig(
    format=('%(asctime)s,%(msecs)03d %(levelname)-8s '
            '[%(filename)s:%(lineno)d] %(message)s'),
    level=logging.DEBUG,
    datefmt='%H:%M:%S')

def my_log(*args, **kwargs):
    indent = kwargs.pop('indent', 0)
    msg = ' ' * indent + ' '.join([str(a) for a in args])
    print(f'[MYLOG] {msg}')
    return logging.info(msg, stacklevel=2, **kwargs)
最舍不得你 2024-07-20 20:29:28

创建 pytest.ini 文件

使用 log_format pytest.ini

log_format = %(asctime)s %(levelname) %(message)s (%(pathname)s:%(lineno)s)
log_cli_level = DEBUG

Create pytest.ini file with log_format

pytest.ini

log_format = %(asctime)s %(levelname) %(message)s (%(pathname)s:%(lineno)s)
log_cli_level = DEBUG
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文