我正在使用 Python 2.6 作为批处理脚本的替代品。它将通过双击启动,因此用户将丢失/忽略所有到标准输出的输出。所以,我决定添加日志记录,为了简单起见,我为此编写了一个类。我的想法是,我可以在我的代码中的任何地方使用 Logging.Logger
,记录器将准备就绪。
我希望一个目录中的旧日志文件不超过 10 个,所以我手动清除旧的。我还没有通过 API 找到类似的功能,而且我很偏执,想记录所有内容,事实上日志目录中存在具有意外名称的文件。
所以,下面是我对此类的尝试,但是当我尝试测试(运行)它时出现错误:
>>> ================================ RESTART ================================
>>>
Traceback (most recent call last):
File "C:/AutomationScripts/build scripts/Deleteme.py", line 6, in <module>
class Logging:
File "C:/AutomationScripts/build scripts/Deleteme.py", line 42, in Logging
__clearOldLogs(dummySetting)
File "C:/AutomationScripts/build scripts/Deleteme.py", line 38, in __clearOldLogs
_assert(Logger, 'Logger does not exist yet! Why?')
NameError: global name '_assert' is not defined
>>>
是的,我来自 Java/C# 背景。我可能没有以“Pythonic”方式做事。请帮助我做正确的事情,并给出一个完整的可行答案,而不是简单地指出我知识中的漏洞。我相信我已经提供了足够多的代码示例。抱歉,如果没有 Settings 类它就无法运行,但希望你明白了。
# This file has been tested on Python 2.6.*. For Windows only.
import logging # For actual logging
import tkMessageBox # To display an error (if logging is unavailable)
class Logging:
"""
Logging class provides simplified interface to logging
as well as provides some handy functions.
"""
# To be set when the logger is properly configured.
Logger = None
@staticmethod
def _assert(condition, message):
""" Like a regular assert, except that it should be visible to the user. """
if condition: return
# Else log and fail
if Logger:
Logger.debug(traceback.format_stack())
Logger.error('Assert failed: ' + message)
else:
tkMessageBox.showinfo('Assert failed: ' + message, traceback.format_stack())
assert(condition)
@staticmethod
def _removeFromEnd(string, endString):
_assert(string.endswith(endString),
"String '{0}' does not end in '{1}'.".format(string, endString))
return string[:-len(endString)]
def __clearOldLogs(logSettings):
"""
We want to clear old (by date) files if we get too many.
We should call this method only after variable 'Logger' has been created.
"""
# The following check should not be necessary from outside of
# Logging class, when it is fully defined
_assert(Logger, 'Logger does not exist yet! Why?')
# Do more work here
def __createLogger(logSettings):
logFileName = logSettings.LogFileNameFunc()
#_assert(False, 'Test')
logName = _removeFromEnd(logFileName, logSettings.FileExtension)
logFileName = os.path.join(logSettings.BaseDir, logFileName)
# If someone tried to log something before basicConfig is called,
# Python creates a default handler that goes to the console and will
# ignore further basicConfig calls. Remove the handler if there is one.
root = logging.getLogger()
if root.handlers:
for handler in root.handlers:
root.removeHandler(handler)
logging.basicConfig(filename = logFileName, name = logName, level = logging.DEBUG, format = "%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger(logName)
return logger
# Settings is a separate class (not dependent on this one).
Logger = __createLogger(Settings.LOGGING)
__clearOldLogs(Settings.LOGGING)
if __name__ == '__main__':
# This code section will not run when the class is imported.
# If it is run directly, then we will print debugging info.
logger = Logging.Logger
logger.debug('Test debug message.')
logger.info('Test info message.')
logger.warning('Test warning message.')
logger.error('Test error message.')
logger.critical('Test critical message.')
欢迎提出相关问题、风格建议和完整答案。谢谢!
最佳答案
您收到该异常是因为您调用的是 _assert()
而不是 Logging._assert()
。错误消息告诉您它正在模块的全局命名空间而不是类命名空间中寻找 _assert()
;要让它在后者中显示,您必须明确指定它。
当然,在这种情况下,您要尝试在类仍在定义时执行此操作,并且在类完成之前名称不可用,因此很难做到这一点。
一个解决方案是取消缩进以下两行(我已将其编辑为使用完全限定名称),以便它们在类定义之外;他们将在之后立即被处决。
Logger = Logging.__createLogger(Settings.LOGGING)
Logging.__clearOldLogs(Settings.LOGGING)
一个有帮助的风格建议:与其用一堆静态方法创建一个类,不如考虑让它们成为模块中的顶级函数。您模块的用户(包括您自己)会发现更容易获得他们想要的功能。没有理由仅仅将类用作容器;模块本身就是这样一个容器。
一个模块基本上是一个单一的*.py
文件(虽然你可以创建有多个文件的模块,但现在就可以了)。当您执行 import
时,您导入的是一个模块。在您的示例中,tkMessageBox
和 logging
都是模块。所以只需创建一个单独的文件(确保其名称不与现有的 Python 模块名称冲突),将其保存在与主脚本相同的目录中,然后将其导入主脚本。如果您将其命名为 mylogging.py
那么您将 import mylogging
并以 mylogging.clearOldLogs()
或其他方式访问其中的函数(类似于你现在可以将他们作为一个类(class)来解决)。
Python 中的“全局”名称并不是真正的全局名称,它们只是定义它们的模块的全局名称。因此,模块是划分功能的好方法,尤其是部分 (例如日志记录),您希望在未来的许多脚本中重复使用。
关于Python 不允许我在类定义尚未完成时使用类内部的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4556289/