From dbde46e51a394af87fced050449c7bc2b5ea7a79 Mon Sep 17 00:00:00 2001 From: aaronfriedman Date: Mon, 27 Apr 2026 15:55:03 -0400 Subject: [PATCH 1/3] Don't add log handlers if they already exist This prevents duplicate logging in pytest --- src/nypl_py_utils/functions/log_helper.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/nypl_py_utils/functions/log_helper.py b/src/nypl_py_utils/functions/log_helper.py index f5fc16e..5766be2 100644 --- a/src/nypl_py_utils/functions/log_helper.py +++ b/src/nypl_py_utils/functions/log_helper.py @@ -28,10 +28,13 @@ def get_structlog(module): without binding it to others, use `logger = logger.bind(contextvar=0)`. """ logger = logging.getLogger(module) - logger.addHandler(logging.StreamHandler(sys.stdout)) logger.setLevel(os.environ.get('LOG_LEVEL', 'INFO').upper()) logger.propagate = False # Prevents double logging + # No need to add handlers if there already are some (e.g. pytest) + if not logger.handlers: + logger.addHandler(logging.StreamHandler(sys.stdout)) + return structlog.wrap_logger( logger, processors=[ From 0f1ddce911a3c9405d52d18d22889a07322e29bc Mon Sep 17 00:00:00 2001 From: aaronfriedman Date: Mon, 27 Apr 2026 15:59:39 -0400 Subject: [PATCH 2/3] Update version --- .python-version | 2 +- CHANGELOG.md | 3 +++ pyproject.toml | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.python-version b/.python-version index c10780c..24ee5b1 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.13.1 +3.13 diff --git a/CHANGELOG.md b/CHANGELOG.md index ae38c6a..c3b60cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ # Changelog +## v1.10.2 4/27/26 +- Prevent double logging in pytest using structlog + ## v1.10.1 4/14/26 - Add optional timeout param for OAuth2Client diff --git a/pyproject.toml b/pyproject.toml index c7007e0..010f833 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "nypl_py_utils" -version = "1.10.1" +version = "1.10.2" authors = [ { name="Aaron Friedman", email="aaronfriedman@nypl.org" }, ] From c20a11ea1c631deab1c7b4d3d7e16c444871321b Mon Sep 17 00:00:00 2001 From: aaronfriedman Date: Tue, 28 Apr 2026 11:27:06 -0400 Subject: [PATCH 3/3] Propagate to existing loggers --- src/nypl_py_utils/functions/log_helper.py | 7 ++++--- tests/test_log_helper.py | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/nypl_py_utils/functions/log_helper.py b/src/nypl_py_utils/functions/log_helper.py index 5766be2..c298eb8 100644 --- a/src/nypl_py_utils/functions/log_helper.py +++ b/src/nypl_py_utils/functions/log_helper.py @@ -29,10 +29,11 @@ def get_structlog(module): """ logger = logging.getLogger(module) logger.setLevel(os.environ.get('LOG_LEVEL', 'INFO').upper()) - logger.propagate = False # Prevents double logging - # No need to add handlers if there already are some (e.g. pytest) - if not logger.handlers: + # We assume existing loggers will handle logging to stdout and that we + # don't need to do anything. Otherwise, manually add a stdout handler. + if not logger.hasHandlers(): + logger.propagate = False logger.addHandler(logging.StreamHandler(sys.stdout)) return structlog.wrap_logger( diff --git a/tests/test_log_helper.py b/tests/test_log_helper.py index 57d7bce..55f76bb 100644 --- a/tests/test_log_helper.py +++ b/tests/test_log_helper.py @@ -10,10 +10,10 @@ @freeze_time('2023-01-01 19:00:00') class TestLogHelper: - def test_json_logging(self, capsys): + def test_json_logging(self, caplog): logger = create_log('test_structlog', json=True) logger.info('test', some="json") - output = json.loads(capsys.readouterr().out) + output = json.loads(caplog.records[0].msg) assert output.get("message") == 'test' assert output.get("some") == 'json' assert output.get('level') == 'info'