From ae3ca2b7d75e84cb364e9a3ac11573afa51dcdad Mon Sep 17 00:00:00 2001 From: uditagrawal Date: Mon, 19 Oct 2026 20:46:04 -0700 Subject: [PATCH 1/2] chore(release): prepare json-logging 1.6.0 Bump the version to 1.6.0 and collect the changelog into a 1.6.0 section covering every change merged since 1.5.1: - Added: json_logging.EXCLUDED_FIELDS (#129, fixes #98) - Fixed: FastAPI request-context propagation (#123, fixes #124); outside-request correlation IDs in Flask, Quart and Connexion (#127, fixes #125); non-web get_correlation_id() (#128, fixes #126); Sanic 24+ logging config (#133) - Documentation: FastAPI/Uvicorn startup (#130, fixes #106); Gunicorn and Flask example (#131, fixes #25) - Compatibility: default output unchanged unless EXCLUDED_FIELDS is set; the framework behaviour changes; Python 3.9+ FastAPI context path; CI coverage The previous 'other integrations unchanged' and 'no change to the JSON schema' statements are replaced, since they do not hold for the whole release. The date is added when the release is cut. No library code change. --- CHANGELOG.md | 90 +++++++++++++++++++++++++++++++--------------------- setup.py | 2 +- 2 files changed, 55 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4243115..acf871b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,42 +6,60 @@ This project adheres to [Semantic Versioning](http://semver.org/). The format is based on [Keep a Changelog](http://keepachangelog.com/). ## Unreleased -- fix: `json_logging.get_correlation_id()` returns `-` after `init_non_web()` instead of raising - `AttributeError`. Fixes #126 - -- fix: Flask, Quart and Connexion no longer generate a new correlation ID for each log record emitted outside an - active HTTP request; those records now get `-`, as documented. Connexion 3 error handlers and ASGI middleware, - which run outside the Flask request context, still use the request's correlation header; without one they get - `-`, because a generated ID is only stored inside the Flask request context. Fixes #125 - -- fix: `init_sanic()` no longer raises `KeyError: 'format'` when Sanic 24.x and later omit - formatter `format` keys; older Sanic logging configuration remains supported (PR #133). - -- feat: `json_logging.EXCLUDED_FIELDS` omits the named fields from every JSON log record. Empty by default, so - output is unchanged. Fixes #98 - -- docs: FastAPI startup with Uvicorn, the double-initialisation error, and a tested example (`example/fastapi_startup`). - Fixes #106 - -- docs: tested Gunicorn and Flask example (`example/gunicorn_flask`) covering worker initialisation, request - instrumentation, JSON output, and the Gunicorn access log. Fixes #25 - -- fix: FastAPI application logs and `json_logging.get_correlation_id()` now use the request's correlation id - instead of `-` in synchronous (`def`) endpoints (fixes #124, PR #123). When request instrumentation is enabled with - `init_request_instrument(app)` (Python 3.9+): - - the current request is kept in a context variable for the duration of the request, including for URLs - excluded from request logging. It is visible to `def` and `async def` endpoints, sync and async - `BackgroundTasks`, `StreamingResponse` iterators, and work that inherits the request's context, such as - `asyncio.create_task()` (also after the response is sent), `asyncio.to_thread()`, - `contextvars.copy_context().run()` and AnyIO/Starlette threadpool workers; - - if no correlation id header is present, the id is now created once at the start of each request (also for - excluded URLs), so all threads and logs of one request share the same id. - - Threads that do not inherit the request's context (`threading.Thread`, `loop.run_in_executor()`), WebSocket - endpoints, applications without `init_request_instrument(app)`, and Python < 3.9 keep the previous behaviour. - There, the request is found only by inspecting the current thread's call stack, so a thread or handler that - does not hold the request object in one of its own frames (for example as a `request` argument) logs `-`. - Other framework integrations are not affected. +## 1.6.0 + +### Added + +- `json_logging.EXCLUDED_FIELDS`: an optional list, tuple, set or frozenset of field names to omit from every JSON + record (application, web and request logs, including `extra` and `props` entries). The default is `()`, so + existing applications need no change and their output fields stay the same. Set it before `init_*()`; formatters + read it when they are created. An invalid value (a bare string, or a non-string entry) raises `TypeError` from + `init_*()` or `init_request_instrument()` before any state changes, so it can be corrected and retried. This is + an output-formatting option, not a security redaction control. (#129, fixes #98) + +### Fixed + +- FastAPI: application logs and `json_logging.get_correlation_id()` use the request's correlation ID instead of `-` + in synchronous (`def`) endpoints. When request instrumentation is enabled with `init_request_instrument(app)`, the + current request is kept in a context variable for the duration of the request, including for URLs excluded from + request logging. It is visible to `def` and `async def` endpoints, sync and async `BackgroundTasks`, + `StreamingResponse` iterators, and work that inherits the request's context, such as `asyncio.create_task()` + (also after the response is sent), `asyncio.to_thread()`, `contextvars.copy_context().run()` and AnyIO/Starlette + threadpool workers. Without a correlation header, one ID is created at the start of each request, so all logs of + that request share it. (#123, fixes #124) +- Flask, Quart and Connexion: log records emitted outside an active HTTP request get `-`, as documented, instead of a + new random correlation ID per record. Connexion 3 error handlers and ASGI middleware, which run outside the Flask + request context, still use the request's correlation header; without one they get `-`, because a generated ID is + only stored inside the Flask request context. (#127, fixes #125) +- `json_logging.get_correlation_id()` returns `-` after `init_non_web()`, or before any `init_*()`, instead of + raising `AttributeError`. (#128, fixes #126) +- Sanic: `init_sanic()` no longer raises `KeyError: 'format'` with Sanic 24.x and later, whose default logging + configuration omits formatter `format` keys. Older Sanic configurations are still supported. (#133) + +### Documentation + +- FastAPI and Uvicorn startup: why `RuntimeError: Can not call init more than once` occurs when a module started as a + script also passes an import string to uvicorn, two working startup patterns, reload and multiple workers, and a + tested example in `example/fastapi_startup`. The duplicate-initialisation guard is unchanged. (#130, fixes #106) +- Gunicorn and Flask: a tested example in `example/gunicorn_flask` covering initialisation once per worker, request + instrumentation, correlation IDs, which output is JSON and on which stream, and Gunicorn's access and error logs; + with integration tests that run real Gunicorn workers. (#131, fixes #25) + +### Compatibility + +- No change is required from existing applications. With `EXCLUDED_FIELDS` left at its default, the JSON fields of + every record are the same as in 1.5.1; fields are only removed when an application lists them. +- Behaviour changes in several framework integrations, as listed under Fixed: outside-request records now get `-` + instead of a random ID (Flask, Quart, Connexion), non-web `get_correlation_id()` no longer raises, FastAPI + synchronous endpoints now get the request's ID, and Sanic 24+ initialises. +- The FastAPI request-context propagation is used on Python 3.9 and later. Python 3.8 and earlier keep the previous + behaviour: call-stack lookup only, and no correlation ID created at the start of the request. +- In FastAPI, threads that do not inherit the request's context (`threading.Thread`, `loop.run_in_executor()`), + WebSocket endpoints, and applications without `init_request_instrument(app)` keep the previous behaviour. There, a + thread or handler finds the request only if the request object is in one of its own frames, for example as a + `request` argument; otherwise it logs `-`. +- CI runs the unit tests on Python 3.9 to 3.14 and the Flask, Quart, FastAPI and Sanic smoke tests on Python 3.9 and + 3.14. Python 3.8 and earlier, including Python 2, are not tested. ## 1.5.1 - 2025-07-06 diff --git a/setup.py b/setup.py index 2b732b9..f34df1f 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name="json-logging", - version='1.5.1', + version='1.6.0', packages=find_packages(exclude=['contrib', 'docs', 'tests*', 'example', 'dist', 'build']), license='Apache License 2.0', description="JSON Python Logging", From 9266d34770c26d8d963bcb523e7fc9f6bb9b892f Mon Sep 17 00:00:00 2001 From: suppagoddo Date: Tue, 20 Oct 2026 20:56:10 -0700 Subject: [PATCH 2/2] chore(release): credit Bob T. and Udit Agrawal in 1.6.0 metadata --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index f34df1f..59bbf7c 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,8 @@ license='Apache License 2.0', description="JSON Python Logging", long_description=long_description, - author="Bob T.", + author="Bob T., Udit Agrawal", + maintainer="Udit Agrawal", keywords=["json", "elastic", "python", "python3", "python2", "logging", "logging-library", "json", "elasticsearch", "elk", "elk-stack", "logstash", "kibana"], platforms='any',