在 Python 中,是否可以使用 fileConfig 更改日志级别而无需重新启动应用程序?如果不能通过 fileConfig 实现,是否有其他方法可以获得相同的结果?
更新:这是针对运行在服务器上的应用程序,我希望系统管理员可以更改配置文件,该文件将在运行时由应用程序选择并动态更改日志级别。当时我正在使用 gevent,因此我已将我的代码添加为其中一个答案,该代码使用 inotify 来选择更改配置文件。
在 Python 中,是否可以使用 fileConfig 更改日志级别而无需重新启动应用程序?如果不能通过 fileConfig 实现,是否有其他方法可以获得相同的结果?
更新:这是针对运行在服务器上的应用程序,我希望系统管理员可以更改配置文件,该文件将在运行时由应用程序选择并动态更改日志级别。当时我正在使用 gevent,因此我已将我的代码添加为其中一个答案,该代码使用 inotify 来选择更改配置文件。
fileConfig
是一个根据文件配置日志级别的机制;您可以在程序中随时动态更改它。
在要更改日志级别的记录对象上调用 .setLevel()
。通常情况下,您会在根记录器上执行此操作:
logging.getLogger().setLevel(logging.DEBUG)
除了被接受的答案之外:根据您初始化记录器的方式,您可能还需要更新记录器的处理程序:
import logging
level = logging.DEBUG
logger = logging.getLogger()
logger.setLevel(level)
for handler in logger.handlers:
handler.setLevel(level)
在 sfinken 的答案 和 Starman 后来的评论上进行进一步扩展,你还可以检查处理程序的类型以针对特定的输出器进行定位,例如:
import logging
logger = logging.getLogger()
for handler in logger.handlers:
if isinstance(handler, type(logging.StreamHandler())):
handler.setLevel(logging.DEBUG)
logger.debug('Debug logging enabled')
isinstance(handler, logging.StreamHandler)
- modulus0import logging
logging.getLogger().setLevel(logging.INFO)
getLogger()
会返回根记录器。fileConfig()
可以在程序运行时动态改变日志配置,不过对于简单的更改,采用像Martijn Pieters回答中建议的编程方法可能更适合。日志甚至提供了一个套接字服务器,使用listen()
/ stopListening()
API监听配置更改,如此处所述。要使日志监听特定端口,您可以使用
t = logging.config.listen(PORT_NUMBER)
t.start()
停止监听,请拨打
logging.config.stopListening()
要向服务器发送数据,您可以使用例如:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('localhost', PORT_NUMBER))
with open(CONFIG_FILE) as f:
data_to_send = f.read()
s.send(struct.pack('>L', len(data_to_send)))
s.send(data_to_send)
s.close()
fileConfig()
调用的内部实现意味着您无法在调用中指定 disable_existing_loggers=False
,这在某些情况下使此功能不太有用。您可以使用相同的 API 使用 dictConfig 模式发送 JSON 文件,这将允许更好地控制重新配置。这需要 Python 2.7/3.2 或以上版本(其中添加了 dictConfig()
)。或者,您可以使用 stdlib 代码实现自己的侦听器,其工作方式相同,但会根据您的特定需求进行定制。def my_fileConfig(fname, defaults=None, disable_existing_loggers=False):
"""
这个函数仅仅是将 fileConfig 函数的 disable_existing_loggers 参数设置为 False 而不是 True。
它旨在用于 monkey patch,以便可以在不禁用所有非根记录器的情况下使用配置监听器。
"""
orig_fileConfig(fname, defaults, disable_existing_loggers)
orig_fileConfig = logging.config.fileConfig
# HACK: 用我的版本替换 fileConfig。
logging.config.fileConfig = my_fileConfig
logging.config.fileConfig(config_path)
- Gdogg最终我决定使用inotify和gevent来检查文件写入操作,一旦我知道文件已经被更改,然后我根据配置为每个记录器设置级别。
import gevent
import gevent_inotifyx as inotify
from gevent.queue import Queue
class FileChangeEventProducer(gevent.Greenlet):
def __init__(self, fd, queue):
gevent.Greenlet.__init__(self)
self.fd = fd
self.queue = queue
def _run(self):
while True:
events = inotify.get_events(self.fd)
for event in events:
self.queue.put(event)
gevent.sleep(0)
class FileChangeEventConsumer(gevent.Greenlet):
def __init__(self, queue, callBack):
gevent.Greenlet.__init__(self)
self.queue = queue
self.callback = callBack
def _run(self):
while True:
_ = self.queue.get()
self.callback()
gevent.sleep(0)
class GeventManagedFileChangeNotifier:
def __init__(self, fileLocation, callBack):
self.fileLocation = fileLocation
self.callBack = callBack
self.queue = Queue()
self.fd = inotify.init()
self.wd = inotify.add_watch(self.fd, self.fileLocation, inotify.IN_CLOSE_WRITE)
def start(self):
producer = FileChangeEventProducer(self.fd, self.queue)
producer.start()
consumer = FileChangeEventConsumer(self.queue, self.callBack)
consumer.start()
return (producer, consumer)
def _setUpLoggingConfigFileChangeNotifier(self):
loggingFileNameWithFullPath = self._getFullPathForLoggingConfig()
self.gFsNotifier = GeventManagedFileChangeNotifier(loggingFileNameWithFullPath, self._onLogConfigChanged)
self.fsEventProducer, self.fsEventConsumer = self.gFsNotifier.start()
def _onLogConfigChanged(self):
self.rootLogger.info('Log file config has changed - examining the changes')
newLoggingConfig = Config(self.resourcesDirectory, [self.loggingConfigFileName]).config.get('LOG')
self.logHandler.onLoggingConfigChanged(newLoggingConfig)
一旦我有了新的日志文件配置,我就可以从配置中为每个记录器连接正确的记录级别。我只是想分享这个答案,如果有人尝试在Gevent中使用它,这可能会有所帮助。
logging.basicConfig(level=logging.DEBUG)
logger.info('debug')
logger.info('info')
logger.warning('warning')
print()
logging.basicConfig(level=logging.WARNING, force=True)
logger.info('debug')
logger.info('info')
logger.warning('warning')
exit()
和输出
INFO:root:debug
INFO:root:info
WARNING:root:warning
WARNING:root:warning
import threading
import time
def reset_level():
# you can reload your own config file or use logging.config.fileConfig here
print 'Something else'
pass
t = threading.Timer(10, reset_level)
t.start()
while True:
# your app code
print 'Test'
time.sleep(2)
输出:
Test
Test
Test
Test
Test
Something else
Test
Test
更新: 请查看Martijn Pieters提出的解决方案。
open
方法。 - hafiz031import logging
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
logger.debug('This message should appear on the console')
logger.warning('So should this')
logger.setLevel(logging.WARNING)
logger.debug('This message should NOT appear on the console')
logger.warning('This one should')
你甚至可以设置不同的记录器
SIGUSR1
,SIGUSR2
仅适用于类Unix系统。您可以使用logging.config.listen()
来监听新配置(https://docs.python.org/3/library/logging.config.html#logging.config.listen) - yoonghm