The Wayback Machine - https://web.archive.org/web/20201029072848/https://github.com/hynek/structlog/issues/233
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AttributeError on 'PrintLogger' when setting up logger with its own isolated config #233

Open
j19sch opened this issue Dec 1, 2019 · 3 comments

Comments

@j19sch
Copy link

@j19sch j19sch commented Dec 1, 2019

For a pytest plugin I'm working on, I want to set up a logger with structlog that's completely isolated from whatever structlog logger the code under test might be using. So I figured I need to use structlog.wrap_logger(), but when I do so, I run into AttributeError: 'PrintLogger' object has no attribute 'setLevel'.

Analyzing the issue I have created two similar code examples, one works and one doesn't.

This one works:

import logging
import structlog

structlog.configure(logger_factory=structlog.stdlib.LoggerFactory())

my_logger = structlog.wrap_logger(None, wrapper_class=structlog.stdlib.BoundLogger)

my_logger.setLevel("INFO")
my_logger.addHandler(logging.StreamHandler())

my_logger.info("this log record is displayed")
my_logger.debug("this log record isn't")

And this one doesn't:

import logging
import structlog

my_logger = structlog.wrap_logger(None, logger_factory=structlog.stdlib.LoggerFactory(),
                                  wrapper_class=structlog.stdlib.BoundLogger)

my_logger.setLevel("INFO")
my_logger.addHandler(logging.StreamHandler())

my_logger.info("this log record is displayed")
my_logger.debug("this log record isn't")
Traceback (most recent call last):
  File "/home/j19sch/workspace/struclog-playground/this_does_not_work.py", line 7, in <module>
    my_logger.setLevel("INFO")
  File "/home/j19sch/workspace/struclog-playground/venv/lib/python3.7/site-packages/structlog/stdlib.py", line 176, in setLevel
    self._logger.setLevel(level)
AttributeError: 'PrintLogger' object has no attribute 'setLevel'

Could someone explain the difference in behavior between the two? Or, if I'm trying to set up this isolated logger in the wrong way, give me pointers for the proper way?

@j19sch j19sch changed the title Trouble setting up logger with its own isolated config AttributeError on 'PrintLogger' when setting up logger with its own isolated config Dec 1, 2019
@j19sch
Copy link
Author

@j19sch j19sch commented Dec 1, 2019

To add some context, the repository for my plugin can be found here: https://github.com/j19sch/pytest-instrument. And the relevant code here: https://github.com/j19sch/pytest-instrument/blob/add-logger-fixture/pytest_instrument/plugin.py

What I'm trying to achieve:

  • exposing a structlog logger to be used by tests
  • this structlog logger wraps the stdlib logger, so pytest captures the log records and can sent them to stdout
  • these log records are also being written to file as json objects
  • additional json objects about pytest's setup/test call/teardown are written to the same file, but they need to bypass stdlib logging, so they don't get captured by pytest

As mentioned in the issue description, I got this to work by using structlog.configure(), but my concern is that this may affect any structlog configuration in the code under test, which I want to avoid.

@hynek
Copy link
Owner

@hynek hynek commented Dec 3, 2019

wrap_logger() wraps a logger, but you're passing None as a logger.

If you pass in a logger, it works:

In [2]: import logging
   ...: import structlog
   ...:
   ...: my_logger = structlog.wrap_logger(logging.getLogger(), logger_factory=structlog.stdlib.LoggerFactory(),
   ...:                                   wrapper_class=structlog.stdlib.BoundLogger)
   ...:
   ...: my_logger.setLevel("INFO")
   ...: my_logger.addHandler(logging.StreamHandler())
   ...:
   ...: my_logger.info("this log record is displayed")
   ...: my_logger.debug("this log record isn't")
2019-12-03 15:46.00 this log record is displayed   logger_factory=<structlog.stdlib.LoggerFactory object at 0x7f97415645e0>

If you go the configure() route and call structlog.get_logger(), structlog will call logging.getLogger() for you.

@j19sch
Copy link
Author

@j19sch j19sch commented Dec 7, 2019

Thank you! That does indeed work for setting the level, etc.

Unfortunately, it also results in the log records containing filename _base.py, funcName _proxy_to_logger, and lineno 192. If I take the configure() route, this is not the case and the values match the file, function and line number of where I create the log record.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
2 participants
You can’t perform that action at this time.