I am trying to setup structlog and set log level. My code looks like this:
import structlog
import logging
filepath=open("out.log",'a')
logging.basicConfig(
level=logging.INFO
)
structlog.configure(
processors=[structlog.stdlib.filter_by_level],
wrapper_class=structlog.BoundLogger,
context_class=dict,
logger_factory=structlog.PrintLoggerFactory(filepath),
)
logger = structlog.getLogger()
logger.info('test')
This fails:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.8/site-packages/structlog/_base.py", line 189, in _proxy_to_logger
args, kw = self._process_event(method_name, event, event_kw)
File "/usr/local/lib/python3.8/site-packages/structlog/_base.py", line 149, in _process_event
event_dict = proc(self._logger, method_name, event_dict)
File "/usr/local/lib/python3.8/site-packages/structlog/stdlib.py", line 381, in filter_by_level
if logger.isEnabledFor(_NAME_TO_LEVEL[name]):
AttributeError: 'PrintLogger' object has no attribute 'isEnabledFor'
Okay, sure. I am not supposed to use PrintLogger with stdlib processors. But I want to filter by log level (because that's how logging usually works, eh?) So how do I do that? I assume I need to use some other logger factory, but which one? Of course structlog.stdlib.LoggerFactory works, but it doesn't redirect to a file.
So I said: okay I will create my own filter:
def my_filter_by_level(logger, name, event_dict):
if True:
return event_dict
else:
raise DropEvent
...
processors=[my_filter_by_level],
And when I try to use the logger I get:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.8/site-packages/structlog/_base.py", line 190, in _proxy_to_logger
return getattr(self._logger, method_name)(*args, **kw)
TypeError: msg() got an unexpected keyword argument 'organization'
This is coming from
logger = logger.bind(**{"organization": "blah"})
but... why? What is wrong with my processor?
Well, the reason my filter didn't work is this: https://www.structlog.org/en/stable/processors.html#adapting
Last filter in chain is special and can't just return a dictionary.
And the following filter did the trick:
def my_filter_by_level(logger, name, event_dict):
this_level = structlog.stdlib._NAME_TO_LEVEL[name]
set_level = structlog.stdlib._NAME_TO_LEVEL[loglevel]
if this_level >= set_level:
return event_dict
else:
raise structlog.DropEvent