在python日志教程中,有一个包含两个python脚本的示例:myapp.py
和mylib.py
代码是:
# myapp.py
import logging
import mylib
def main():
logging.basicConfig(filename='myapp.log', level=logging.INFO)
logging.info('Started')
mylib.do_something()
logging.info('Finished')
if __name__ == '__main__':
main()
和
# mylib.py
import logging
def do_something():
logging.info('Doing something')
我不知道在basicConfig
中使用myapp.py
如何更改mylib.py
中的日志记录行为。
我认为在Python中,当您在两个不同的脚本中导入相同的模块时,它们会完全断开连接,因为第一个模块变为myapp.logging
而第二个变为mylib.logging
。
编辑:
我将mylib.py代码更改为
# mylib.py
import logging
def do_something():
logging.warning('Doing something')
do_something()
当我运行myapp.py时,日志现在会在控制台中打印出来,不再出现在日志文件中。怎么可能?
答案 0 :(得分:6)
在理解python的模块时有一个错误(如果我可以这么称呼它):
我认为在Python中,当您在两个不同的脚本中导入相同的模块时,它们会完全断开连接,因为第一个模块变为
myapp.logging
而第二个变为mylib.logging
。
事实并非如此。
import
语句在遇到时被解释,当你在主程序(python myapp.py
或等价物)上运行python时,会执行这些行:
import logging
(导入logging
模块),
import mylib
(导入您的库mylib.py
),
def main():
(将名称main
绑定到函数的已编译字节码),并且:
if __name__ == '__main__':
main()
(运行main
,因为本地名称__name__
实际上绑定到一个比较字符串__main__
的字符串。)
到目前为止,这可能不是很令人惊讶(除了def main()
可能在导入myapp.py
时遇到的情况下运行。
可能 令人惊讶的部分是在执行两个import
语句期间发生的事情。
导入机制已经发展了一些(在Python3中与Python2略有不同),但实质上它会做出各种各样的事情:
sys.path
)sys.modules
(见下文)<type 'module'>
或其中的名称)绑定到您提供的名称(隐式使用常规import
,明确地使用from ... import ... as name
)。这里的关键项目之一是上面的斜体部分。该模块实际上是在第一个import
上运行的。导入成功的结果是一个模块实例,它被添加到sys.modules
字典:
$ python2
...
>>> import sys
>>> x = sys.modules['copy_reg']
>>> print x
<module 'copy_reg' from '/usr/local/lib/python2.7/copy_reg.pyc'>
如果此时你要求python 重新导入该模块,它将悄然几乎不做任何事情:
>>> import copy_reg
>>>
这里发生的事情是Python注意到模块已经加载并且在sys.modules
中,因此它只是提取已经加载的模块实体(我们也绑定到上面的符号x
)。然后它将名称copy_reg
绑定到此已存在的模块。如果你import copy_reg as y
:
>>> import copy_reg as y
该名称另外绑定到符号y
:
>>> print x, y, copy_reg
<module 'copy_reg' from '/usr/local/lib/python2.7/copy_reg.pyc'> <module 'copy_reg' from '/usr/local/lib/python2.7/copy_reg.pyc'> <module 'copy_reg' from '/usr/local/lib/python2.7/copy_reg.pyc'>
要看到这些实际上是相同的模块,我们可以使用id
函数,它打印底层对象的内部地址:
>>> print id(x), id(y), id(copy_reg)
34367067648 34367067648 34367067648
(不同版本的Python,或者不同的版本,可能会在这里产生不同的3个地址值,但所有这三个都匹配,因为这些都是指同一个模块)。
在任何情况下,在main
myapp
中,符号logging
与{{1}中的符号logging
引用相同的logging
模块}}
在原始代码中,您致电mylib
do_something
mylib
来logging.warning
main
myapp
mylib
1}}已经调用了日志配置代码。因此,日志消息按指示进行。
在您的修改中,do_something
语句加载logging.warning
后,您已将import mylib
更改为无条件调用myilb
myapp
函数创建模块。这种情况发生在main
,之前绑定main
到代码和之前调用basicConfig
之前。所以 消息在控制台上出现。
由日志记录代码决定是否遵守从main
发出的(稍后)"touchstart"
来电。正如您从自己的示例中看到的那样,它不支持在打印消息后尝试重定向根配置(这是因为我记得当时设置其内部日志处理程序)。
答案 1 :(得分:1)
如果您使用logging.basicConfig
设置日志级别,我相信日志级别在Python中全局工作。
最简单的答案可能是(as jake77 suggested already):
logger = logging.getLogger(__name__)
然后专门设置该记录器的级别:
logger.setLevel(logging.DEBUG) # or whichever
有关于记录here的最佳做法的好文章。它可能与您的案例相关或不相关,但它给出的最佳建议是
[...]库没有配置日志记录级别,格式化程序或处理程序的业务。
这也是一个很好的答案here。
答案 2 :(得分:-1)
根据reference日志记录模块提供了一个用于创建和存储记录器以供将来使用的界面,
对具有相同名称的getLogger()的多次调用将始终返回对同一Logger对象的引用。
此外,直接使用logging.info
等于获取默认命名的logger并使用它。您可以查看源代码以获取有关此机制实现的更多详细信息。
答案 3 :(得分:-1)
我认为您应该在模块中创建一个记录器实例
logger = logging.getLogger(__name__)
然后使用
logger.info('message')
记录内容。
根据对其他答案的评论进行编辑。 设置级别不是必需的,除非你想过滤一些消息,比如只记录警告。 您肯定希望在更改代码后启动新控制台。