9

在我的应用程序中,我python.logging用于记录。

现在我想以交互方式控制日志级别,所以我创建了一个组合框帽子让用户选择“错误”、“警告”、“信息”...

我不太喜欢的是目前组合框中的值是硬编码的。相反,我希望有一个所有“命名”日志级别的列表(例如,系统默认值,以及通过添加logging.addLevelName的日志级别;但不是像“ 42 级”这样的假生成日志级别)

到目前为止,我想出的最好的方法是使用logging._levelNames字典。

但是,这似乎是一个私人成员,我不知何故有一种不好的感觉直接访问它。

所以我的问题是:在 Python 中列出所有当前定义的“命名”日志级别的正确方法是什么。

4

7 回答 7

6

由于您只是在读取值,因此logging._levelNames对我来说是一个合适的解决方案。继续logging.addLevelName设置新值。

于 2013-09-17T09:38:22.533 回答
3

没有特定的功能可以做您想做的事,但您拥有所需的一切logging._levelNames

看一下addLevelName定义,例如:

def addLevelName(level, levelName):
    """
    Associate 'levelName' with 'level'.

    This is used when converting levels to text during message formatting.
    """
    _acquireLock()
    try:    #unlikely to cause an exception, but you never know...
        _levelNames[level] = levelName
        _levelNames[levelName] = level
    finally:
        _releaseLock()

所以 agetLevelNames()可以这样实现:

import logging
def getLevelNames():
    for k, v in sorted(logging._levelNames.iteritems()):
        if isinstance(v, basestring):
            yield v, k

import pprint
pprint.pprint(list(getLevelNames()))

示例输出:

[('NOTSET', 0),
 ('DEBUG', 10),
 ('INFO', 20),
 ('WARNING', 30),
 ('ERROR', 40),
 ('CRITICAL', 50)]
于 2013-09-17T09:35:56.637 回答
2

我在编写程序的帮助字符串时遇到了同样的问题,该程序在命令行上接受日志级别的名称。作为在 Python 3.4_levelNames中重命名为的私有成员,_levelToName因此不能/不应该使用,我想出了这个解决方案(简化为 SO):

print "Levelnames: {}".format(", ".join(
    logging.getLevelName(x)
    for x in xrange(1, 101)
    if not logging.getLevelName(x).startswith('Level')))

或者,对于 Python 3.x:

print("Levelnames: {}".format(", ".join(
    logging.getLevelName(x)
    for x in range(1, 101)
    if not logging.getLevelName(x).startswith('Level'))))

它并不完全漂亮,但它似乎可以在所有 Python 版本中移植,并且它按升序打印出关卡名称:

Levelnames: DEBUG, INFO, WARNING, ERROR, CRITICAL
于 2014-10-30T10:39:53.760 回答
2

你应该对使用内部变量有一种不好的感觉,因为它们可以而且将会改变。例如,您无法再logging._levelNames在 python 3 中访问。

根据文档,级别或多或少都表示为常量,因此您可以假设这些级别始终可用,然后您自己添加的任何内容都在您的控制之下。

也就是说,他们没有一个简单的方法logging.listLevels()来列出当前加载的所有命名级别,这有点愚蠢。你应该给他们写一封措辞强硬的信。或功能请求取决于您真正关心的程度:)

并且应该措辞强硬,因为更改非常简单:

文件:/usr/local/lib/python3.9/logging/__init__.py

def listLevelNames():
    return sorted(_nameToLevel.keys())

如果您想变得真正花哨,您甚至可以按级别编号对名称进行排序。

编辑:我在comp.lang.python做了一个话题。别担心,我很有礼貌。

于 2021-09-02T16:41:34.750 回答
0

我也很失望该模块没有为此提供公共属性或方法。这是一个适用于 Python 2 和 3 的简洁解决方案:

import logging
LOG_LEVEL_NAMES = [logging.getLevelName(v) for v in
                   sorted(getattr(logging, '_levelToName', None)
                          or logging._levelNames)
                   if getattr(v, "real", 0)]

故障:logging模块名称try/exceptImportErrorgetattr(logging, '_levelToName', None)

在 Python 2 中,我们得到logging._levelNames,它略有不同——它同时具有 int->string 和 string->int 映射,因此我使用它来确保过滤器将所有字符串值视为 0。我们也以这种方式getattr(v, "real", 0)抛出。logging.NOTSET

然后我们对值进行排序并映射logging.getLevelName以返回到有序的名称列表。

于 2018-04-09T01:00:51.053 回答
0

What about something like this. Note Python 3.4 produces a slightly different dictionary than Python 2, but you can modify the function or the resultant dictionary to work around that if required.

import logging
import copy

def get_logging_level_names():

    #! find logging's internal dictionary for level names.
    #! the internal dict name changed in python 3.4.
    try:
        level_names = logging._levelToName
    except AttributeError:
        level_names = logging._levelNames

    #! return a copy to prevent modification logging's local dict.
    return copy.copy(level_names)

level_names = get_logging_level_names()
print('level_names = {!r}'.format(level_names))
于 2018-03-15T00:01:03.370 回答
0

实际上,这可能比我之前的答案更好。它返回级别名称的排序列表。如果愿意,您可以修改以返回字典对 {val:name} 或 {name:val} 的排序列表(按值)。

import logging

def get_logging_level_names():

    #! find logging's internal dictionary for level names.
    #! the internal dict name changed in python 3.4.
    try:
        level_to_name = logging._levelToName
        level_vals = level_to_name.keys()
    except AttributeError:
        level_to_name = logging._levelNames
        level_vals = [ key for key in level_to_name.keys() if isinstance(key,int) ]

    level_vals = sorted(level_vals)
    level_names = [ level_to_name[val] for val in level_vals ]
    return level_names

level_names = get_logging_level_names()
print('level_names = {!r}'.format(level_names))
于 2018-03-15T00:39:50.637 回答