可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
In my application, I'm using python.logging
for logging.
Now I want to control the loglevel interactively, so i created a combobox hat lets the user select "ERROR", "WARN", "INFO",...
What I don't really like is that currently the values in the combobox are hardcoded.
Instead,Ii would like to have a list of all "named" loglevels (e.g. both the system defaults, but also those added via logging.addLevelName
; but not the fake generated loglevels like "Level 42")
The best I have come up with so far is to use the logging._levelNames
dictionary.
But then this seems to be a private member, and I somehow have a bad feeling accessing it directly.
So my question is: what's the proper way to list all currently defined "named" loglevels in Python.
回答1:
As you are only reading values, logging._levelNames
looks an appropriate solution to me. Keep going with logging.addLevelName
for setting new values though.
回答2:
There is no specific function to do what you want, but you have everything you need with logging._levelNames
.
Take a look at the addLevelName
definition for example:
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()
So a getLevelNames()
could be implemented like this:
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()))
Example output:
[('NOTSET', 0),
('DEBUG', 10),
('INFO', 20),
('WARNING', 30),
('ERROR', 40),
('CRITICAL', 50)]
回答3:
I just had the same problem while writing the help string of a program that accepts the name of the log level on the command line. As _levelNames
is a private member that was renamed to _levelToName
in Python 3.4 and thus cannot/should not get used, I came up with this solution (simplified for SO):
print "Levelnames: {}".format(", ".join(
logging.getLevelName(x)
for x in xrange(1, 101)
if not logging.getLevelName(x).startswith('Level')))
or, for Python 3.x:
print("Levelnames: {}".format(", ".join(
logging.getLevelName(x)
for x in range(1, 101)
if not logging.getLevelName(x).startswith('Level'))))
It's not entirely pretty, but it seems portable across all Python versions, and it prints out the level names in ascending order:
Levelnames: DEBUG, INFO, WARNING, ERROR, CRITICAL
回答4:
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))
回答5:
Actually, this might be better than my previous answer. It returns a sorted list of level names. You can modify to return a sorted list (by value) of dict pairs {val:name} or {name:val} if you prefer.
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))
回答6:
I too am dismayed that the module doesn't offer a public attr or method for this. Here's a terse solution that works in Python 2 and 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)]
Breakdown: The module name (logging
) did not change, so we can avoid a try/except
around an ImportError
for py2/3 compatibility by using getattr(logging, '_levelToName', None)
to get the Python 3 value, if possible.
In Python 2 we get logging._levelNames
, which is slightly different -- it has both int->string and string->int mappings, so I use getattr(v, "real", 0)
to ensure that the filter treats all the string values as 0. We also throw out logging.NOTSET
this way.
Then we sort the values and map logging.getLevelName
to get back to an ordered list of names.