-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
logging.py: Don't change log level of the root logger to bigger numeric value #3307
Conversation
USE CASE Our use case is the following:
This constraint is quite important for us, because DEBUG logs are quite a lot, and if we allow logging plugin to capture DEBUG logs we get memory issues in Jenkins, where the tests run. |
in order to see if we could get by with just correct handler setup i ventured into the python logging package can you please add a test and a change-log entry? i'm also pondering if we should target master or features, since its a slight behaviour change to get this right |
@@ -153,7 +153,7 @@ def catching_logs(handler, formatter=None, level=None): | |||
root_logger.addHandler(handler) | |||
if level is not None: | |||
orig_level = root_logger.level | |||
root_logger.setLevel(level) | |||
root_logger.setLevel(min(orig_level, level)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i believe we need to ensure the level of the handler is saved/restored as well, but set to the exact level given,
so the handling will happen at the expected level for the designated handler, even if other handlers work with the original level
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe setting the log capturing handlers level here
Line 146 in b2b629f
handler.setLevel(level) |
is enough, or correct me if I am wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
correct, that's more than sufficient, thanks for looking that up 👍
@RonnyPfannschmidt thanks for quick reply, I 'll add tests and entry in changelog. |
doc/en/logging.rst
Outdated
@@ -224,6 +224,8 @@ made in ``3.4`` after community feedback: | |||
|
|||
* Log levels are no longer changed unless explicitly requested by the :confval:`log_level` configuration | |||
or ``--log-level`` command-line options. This allows users to configure logger objects themselves. | |||
When this parameter is used and user requests log level of smaller numeric value than the log level of the root | |||
logger then the root logger will be adjusted to have level equal to the requested logging level. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In case this doesn't make sense please suggest rephrase!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we don't need to document it in logging.rst. I think having a reasonably descriptive msg in the changelog is enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed.
a5435ef
to
62afe33
Compare
Thanks @KKoukiou for the PR!
Your use case makes sense, but isn't a little confusing that @Thisch couldyou please weigh on this? |
For those of you, who don't know what this PR is fixing: Imagine you want to log to a file with a certain log-file-level (e.g. INFO) but don't want to pollute the live-log output (e.g. log-level ERROR) with too many log-messages. Then you can do the following
However, with pytest 3.4 only log messages with log-level>=ERROR are written to the log-file I'll take a closer look at the implementation in the following days. |
@Thisch I see, thanks for the explanation! 👍 |
@nicoddemus thanks for raising this up and @Thisch thanks for the explanation. On the other hand, what I didn't like from first place is that when we use What I consider as more normal behavior, is that all these "--log-level-*" options in pytest will actually only affect the levels of the handlers they are internally creating and nothing else. If the root logger that they are actually applied on, has Wouldn't it be more clear if all these options where clearly creating only relevant handlers for the root logger and nothing else? I bet there was some reason behind this decision, but I really don't see it now. |
@Thisch gentle ping, we'd like to include this in 3.5 which is due next week |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i believe its fine, but i'D like the additional input of @Thisch
Thx @KKoukiou for your suggestion
Why do you think that this should be more normal behavior?
This is clear.
Why should the users modify the configuration of the root logger at all in Can you come up with an example, which exposes the weaknesses of the
Unfortunately, I can't comment on that. |
testing/logging/test_reporting.py
Outdated
assert all(x in contents for x in ["warning text going to logger", | ||
"error text going to logger"]) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've extended your test a bit:
def test_root_logger_affected(testdir):
testdir.makepyfile('''
import logging
logger = logging.getLogger()
logger.addHandler(logging.FileHandler('pytest.log'))
def test_foo():
logger.info('info text ' + 'going to logger')
logger.warning('warning text ' + 'going to logger')
logger.error('error text ' + 'going to logger')
assert 0
''')
log_file = testdir.tmpdir.join('pytest.log').strpath
result = testdir.runpytest('--log-level=ERROR')
assert result.ret == 1
# the capture log calls in the stdout section only contain the
# logger.error msg, because --log-level=ERROR
result.stdout.fnmatch_lines(['*error text going to logger*',])
with pytest.raises(pytest.fail.Exception):
result.stdout.fnmatch_lines(['*warning text going to logger*',])
with pytest.raises(pytest.fail.Exception):
result.stdout.fnmatch_lines(['*info text going to logger*',])
# the log file should contain the warning and the error log messages and
# not the info one, because the default level of the root logger is
# WARNING.
assert os.path.isfile(log_file)
with open(log_file) as rfh:
contents = rfh.read()
assert "info text going to logger" not in contents
assert "warning text going to logger" in contents
assert "error text going to logger" in contents
Can you use that instead, if your a satisfied with it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. Thanks for extending it.
changelog/3307.feature.rst
Outdated
@@ -0,0 +1,3 @@ | |||
pytest should not change the log level of the root logger | |||
when the ``log-level`` parameter is set with value | |||
``LOG_LEVEL>root_logger_level``. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-> when the log-level
parameter has greater numeric value than that of the level of the root logger.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
doc/en/logging.rst
Outdated
@@ -224,6 +224,8 @@ made in ``3.4`` after community feedback: | |||
|
|||
* Log levels are no longer changed unless explicitly requested by the :confval:`log_level` configuration | |||
or ``--log-level`` command-line options. This allows users to configure logger objects themselves. | |||
When this parameter is used and user requests log level of smaller numeric value than the log level of the root | |||
logger then the root logger will be adjusted to have level equal to the requested logging level. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we don't need to document it in logging.rst. I think having a reasonably descriptive msg in the changelog is enough.
Here is another example which is fixed by this PR: import logging
logger = logging.getLogger()
def test_foo():
logger.info('info text ' + 'going to logger')
logger.warning('warning text ' + 'going to logger')
logger.error('error text ' + 'going to logger')
assert 0 Without this fix the following testrun does not show the info and the warning log message in the live log output
@KKoukiou please add this test also to the unittests |
testing/logging/test_reporting.py
Outdated
import logging | ||
|
||
logger = logging.getLogger() | ||
logger.addHandler(logging.FileHandler('pytest.log')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer to not add the FileHandler manually but use the --log-file cmd line option instead. I'm fine with testing both cases
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replaced with --log-file.
@Thisch thanks for being open to this discussion. See answer inline.
See answer bellow.
Overwritting the root logger configuration from pytest, according to some Additionally, when we in pytest overwrite the logger values and invalidate the
Lets say your tests use the following logging configuration file. handlers:
file:
class : logging.FileHandler
level: DEBUG
filename: logconfig.log
loggers:
randomlogger:
level: DEBUG
handlers:
- file
propagate: 0
root:
level: INFO
handlers:
- file logconfig.log file will have DEBUG logs from randomlogger If you 'll pass --log-level=DEBUG because someone decided that This is example might not be the best, but my point is that I believe we should PS: I know that we can use here log-file option and avoid this, but imagine root logger
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good now, except the few code style issues.
testing/logging/test_reporting.py
Outdated
|
||
def test_log_cli_level_log_level_interaction(testdir): | ||
testdir.makepyfile(''' | ||
import logging |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This and the following lines should be indented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
testing/logging/test_reporting.py
Outdated
@@ -49,6 +49,66 @@ def test_foo(): | |||
'text going to stderr']) | |||
|
|||
|
|||
def test_root_logger_affected(testdir): | |||
testdir.makepyfile(''' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use tiple-double-quotes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
testing/logging/test_reporting.py
Outdated
logger.error('error text ' + 'going to logger') | ||
|
||
assert 0 | ||
''') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unindent this line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
testing/logging/test_reporting.py
Outdated
'*WARNING*warning text going to logger', | ||
'*ERROR*error text going to logger', | ||
'=* 1 failed in *=', | ||
]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lines 103 to 108 should be unindented by one level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Thanks @KKoukiou and everyone involved! |
This PR updates [pytest](https://pypi.org/project/pytest) from **3.5.1** to **3.6.3**. <details> <summary>Changelog</summary> ### 3.6.2 ``` ========================= Bug Fixes --------- - Fix regression in ``Node.add_marker`` by extracting the mark object of a ``MarkDecorator``. (`3555 <https://github.com/pytest-dev/pytest/issues/3555>`_) - Warnings without ``location`` were reported as ``None``. This is corrected to now report ``<undetermined location>``. (`3563 <https://github.com/pytest-dev/pytest/issues/3563>`_) - Continue to call finalizers in the stack when a finalizer in a former scope raises an exception. (`3569 <https://github.com/pytest-dev/pytest/issues/3569>`_) - Fix encoding error with `print` statements in doctests (`3583 <https://github.com/pytest-dev/pytest/issues/3583>`_) Improved Documentation ---------------------- - Add documentation for the ``--strict`` flag. (`3549 <https://github.com/pytest-dev/pytest/issues/3549>`_) Trivial/Internal Changes ------------------------ - Update old quotation style to parens in fixture.rst documentation. (`3525 <https://github.com/pytest-dev/pytest/issues/3525>`_) - Improve display of hint about ``--fulltrace`` with ``KeyboardInterrupt``. (`3545 <https://github.com/pytest-dev/pytest/issues/3545>`_) - pytest's testsuite is no longer runnable through ``python setup.py test`` -- instead invoke ``pytest`` or ``tox`` directly. (`3552 <https://github.com/pytest-dev/pytest/issues/3552>`_) - Fix typo in documentation (`3567 <https://github.com/pytest-dev/pytest/issues/3567>`_) ``` ### 3.6.1 ``` ========================= Bug Fixes --------- - Fixed a bug where stdout and stderr were logged twice by junitxml when a test was marked xfail. (`3491 <https://github.com/pytest-dev/pytest/issues/3491>`_) - Fix ``usefixtures`` mark applyed to unittest tests by correctly instantiating ``FixtureInfo``. (`3498 <https://github.com/pytest-dev/pytest/issues/3498>`_) - Fix assertion rewriter compatibility with libraries that monkey patch ``file`` objects. (`3503 <https://github.com/pytest-dev/pytest/issues/3503>`_) Improved Documentation ---------------------- - Added a section on how to use fixtures as factories to the fixture documentation. (`3461 <https://github.com/pytest-dev/pytest/issues/3461>`_) Trivial/Internal Changes ------------------------ - Enable caching for pip/pre-commit in order to reduce build time on travis/appveyor. (`3502 <https://github.com/pytest-dev/pytest/issues/3502>`_) - Switch pytest to the src/ layout as we already suggested it for good practice - now we implement it as well. (`3513 <https://github.com/pytest-dev/pytest/issues/3513>`_) - Fix if in tests to support 3.7.0b5, where a docstring handling in AST got reverted. (`3530 <https://github.com/pytest-dev/pytest/issues/3530>`_) - Remove some python2.5 compatibility code. (`3529 <https://github.com/pytest-dev/pytest/issues/3529>`_) ``` ### 3.6.0 ``` ========================= Features -------- - Revamp the internals of the ``pytest.mark`` implementation with correct per node handling which fixes a number of long standing bugs caused by the old design. This introduces new ``Node.iter_markers(name)`` and ``Node.get_closest_mark(name)`` APIs. Users are **strongly encouraged** to read the `reasons for the revamp in the docs <https://docs.pytest.org/en/latest/mark.htmlmarker-revamp-and-iteration>`_, or jump over to details about `updating existing code to use the new APIs <https://docs.pytest.org/en/latest/mark.htmlupdating-code>`_. (`3317 <https://github.com/pytest-dev/pytest/issues/3317>`_) - Now when ``pytest.fixture`` is applied more than once to the same function a ``ValueError`` is raised. This buggy behavior would cause surprising problems and if was working for a test suite it was mostly by accident. (`2334 <https://github.com/pytest-dev/pytest/issues/2334>`_) - Support for Python 3.7's builtin ``breakpoint()`` method, see `Using the builtin breakpoint function <https://docs.pytest.org/en/latest/usage.htmlbreakpoint-builtin>`_ for details. (`3180 <https://github.com/pytest-dev/pytest/issues/3180>`_) - ``monkeypatch`` now supports a ``context()`` function which acts as a context manager which undoes all patching done within the ``with`` block. (`3290 <https://github.com/pytest-dev/pytest/issues/3290>`_) - The ``--pdb`` option now causes KeyboardInterrupt to enter the debugger, instead of stopping the test session. On python 2.7, hitting CTRL+C again exits the debugger. On python 3.2 and higher, use CTRL+D. (`3299 <https://github.com/pytest-dev/pytest/issues/3299>`_) - pytest not longer changes the log level of the root logger when the ``log-level`` parameter has greater numeric value than that of the level of the root logger, which makes it play better with custom logging configuration in user code. (`3307 <https://github.com/pytest-dev/pytest/issues/3307>`_) Bug Fixes --------- - A rare race-condition which might result in corrupted ``.pyc`` files on Windows has been hopefully solved. (`3008 <https://github.com/pytest-dev/pytest/issues/3008>`_) - Also use iter_marker for discovering the marks applying for marker expressions from the cli to avoid the bad data from the legacy mark storage. (`3441 <https://github.com/pytest-dev/pytest/issues/3441>`_) - When showing diffs of failed assertions where the contents contain only whitespace, escape them using ``repr()`` first to make it easy to spot the differences. (`3443 <https://github.com/pytest-dev/pytest/issues/3443>`_) Improved Documentation ---------------------- - Change documentation copyright year to a range which auto-updates itself each time it is published. (`3303 <https://github.com/pytest-dev/pytest/issues/3303>`_) Trivial/Internal Changes ------------------------ - ``pytest`` now depends on the `python-atomicwrites <https://github.com/untitaker/python-atomicwrites>`_ library. (`3008 <https://github.com/pytest-dev/pytest/issues/3008>`_) - Update all pypi.python.org URLs to pypi.org. (`3431 <https://github.com/pytest-dev/pytest/issues/3431>`_) - Detect `pytest_` prefixed hooks using the internal plugin manager since ``pluggy`` is deprecating the ``implprefix`` argument to ``PluginManager``. (`3487 <https://github.com/pytest-dev/pytest/issues/3487>`_) - Import ``Mapping`` and ``Sequence`` from ``_pytest.compat`` instead of directly from ``collections`` in ``python_api.py::approx``. Add ``Mapping`` to ``_pytest.compat``, import it from ``collections`` on python 2, but from ``collections.abc`` on Python 3 to avoid a ``DeprecationWarning`` on Python 3.7 or newer. (`3497 <https://github.com/pytest-dev/pytest/issues/3497>`_) ``` </details> <details> <summary>Links</summary> - PyPI: https://pypi.org/project/pytest - Changelog: https://pyup.io/changelogs/pytest/ - Repo: https://github.com/pytest-dev/pytest/issues - Homepage: http://pytest.org </details>
@KKoukiou I'm currently having a look at removing the root-logger-level code from the logging plugin, due to a discussion in #7139 (comment). |
When doing log capturing we need to temporary change the level of the root logger to be at least
of the log level that the handler used for log capturing has (log_level), so that we 'll be able to capture such logs.
If root logger level has smaller numeric value than
log_level
parameter we should not enforce root logger level tolog_level
because we might miss important logs from root logger gathered by other handlers.