diff --git a/superset/utils/date_parser.py b/superset/utils/date_parser.py index 4d202a03ca8..7405cba27a5 100644 --- a/superset/utils/date_parser.py +++ b/superset/utils/date_parser.py @@ -51,6 +51,13 @@ from superset.constants import InstantTimeComparison, LRU_CACHE_MAX_SIZE, NO_TIM ParserElement.enable_packrat() +# parsedatetime emits a noisy DEBUG record ("eval now with context - False, False") +# on every relative-date evaluation. Superset has no actionable use for that +# internal trace, and it floods production logs whenever the root logger is at +# DEBUG. Suppress the library's own logger to WARNING; real failures still +# surface, just not the per-call chatter. +logging.getLogger("parsedatetime").setLevel(logging.WARNING) + logger = logging.getLogger(__name__) # Mapping of ordinal words to their numeric values for date expressions diff --git a/tests/unit_tests/utils/date_parser_tests.py b/tests/unit_tests/utils/date_parser_tests.py index d5cb387f897..5d275f2f271 100644 --- a/tests/unit_tests/utils/date_parser_tests.py +++ b/tests/unit_tests/utils/date_parser_tests.py @@ -722,3 +722,41 @@ def test_time_range_bounded_whitespace_regex_invalid(time_range: str) -> None: """Reject expressions with 0 or 6+ spaces (fall back to DATETIME wrapping).""" result = get_since_until(time_range) assert result[0] is None, f"Expected '{time_range}' to NOT match bounded regex" + + +def test_datetime_eval_does_not_emit_parsedatetime_debug_logs( + caplog: pytest.LogCaptureFixture, +) -> None: + """ + Regression for #33365: ``parsedatetime`` emits a noisy DEBUG record + (``parsedatetime:eval now with context - False, False``) every time + ``datetime_eval`` runs against a relative expression. In production + deployments running at DEBUG level this floods the logs — the user + report notes "this appears frequently" and a search of the issue + tracker turns up "dozens of places where people have posted a log + with that in it." + + The fix is to silence the ``parsedatetime`` logger at module load + in ``superset/utils/date_parser.py`` (e.g. + ``logging.getLogger("parsedatetime").setLevel(logging.WARNING)``). + Their own DEBUG output is internal library chatter that Superset + does not surface to operators in any actionable way. + + This test captures all log records at DEBUG level during a single + ``datetime_eval`` call and asserts that none of them come from the + ``parsedatetime`` logger. If the suppression is removed or bypassed, + the test fails immediately. + """ + import logging + + with caplog.at_level(logging.DEBUG): + datetime_eval("datetime('now')") + + parsedatetime_records = [ + r for r in caplog.records if r.name.startswith("parsedatetime") + ] + assert not parsedatetime_records, ( + "parsedatetime emitted DEBUG records during datetime_eval — these " + "flood production logs. Records: " + + repr([(r.levelname, r.getMessage()) for r in parsedatetime_records]) + )