Adopt uvicorn's loggers so the format holds either way - #65
Merged
Merged
Conversation
Started with the `uvicorn` CLI, uvicorn applies its LOGGING_CONFIG via dictConfig before importing the app: the `uvicorn` logger gets its own handler and propagate: False. setup_logging replaces the *root* handler, which never reaches it, so uvicorn's startup and shutdown lines stayed plain text in the middle of an otherwise ECS stream -- and a shipper parsing one JSON object per line drops exactly the lines you want during an incident. Measured in a consuming service: 8 of 19 production lines were not ECS. run_uvicorn already passes log_config=None and was unaffected; nothing stopped a consumer from using the CLI, as that service first did. uvicorn.error needs no entry of its own -- uvicorn gives it no handler, so it propagates to `uvicorn` and the same fix carries it. Listed anyway so a future change to that config cannot quietly reintroduce the problem. uvicorn.access is deliberately NOT adopted, and this is where the recorded proposal would have caused a new bug: it said to clear the handlers of all three. RequestContextMiddleware already emits the access log, so uvicorn's version as well would put two access lines on every request -- which is why setup_logging has been silencing it by level all along. One of the new tests exists purely to fail if somebody "finishes the job". The restore-and-recheck step of the mutation check misled me for several rounds and is worth recording: the mutation happened to be size-neutral (adding `, "uvicorn.access"` and removing `"uvicorn.access", ` -- 18 characters each way) and the restore landed in the same second, so (mtime, size) matched and Python kept running the mutated bytecode from __pycache__ while inspect.getsource showed the corrected file. Tracing Logger.setLevel was what separated "the code is wrong" from "the code you are reading is not the code running". Mutation checks want __pycache__ cleared.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Candidate addition #6.
Started with the
uvicornCLI, uvicorn applies itsLOGGING_CONFIGviadictConfigbefore importing the app: theuvicornlogger gets its own handler andpropagate: False.setup_loggingreplaces the root handler, which never reaches it — so uvicorn's startup and shutdown lines stayed plain text in the middle of an otherwise ECS stream, and a shipper parsing one JSON object per line drops exactly the lines you want during an incident.Measured in the consuming service: 8 of 19 production lines were not ECS.
run_uvicornalready passeslog_config=Noneand was unaffected. Nothing stopped a consumer from using the CLI, as that service first did.Where the recorded proposal would have caused a new bug
The note said to clear the handlers of
uvicorn,uvicorn.erroranduvicorn.access. That third one is wrong:RequestContextMiddlewarealready emits the access log (request_completed, with status, duration, route and request ID —request_context.py:217). Adopting uvicorn's as well puts two access lines on every request, which is precisely whysetup_logginghas been silencing it by level all along. It stays silenced, with a comment saying why it is an exception rather than an oversight, and one of the new tests exists purely to fail if somebody "finishes the job".uvicorn.errorneeds no entry of its own — uvicorn gives it no handler, so it propagates touvicornand the same fix carries it. Verified againstuvicorn.config.LOGGING_CONFIG. Listed anyway so a future change to that config cannot quietly reintroduce the problem.A diagnostic detour worth recording
The restore-and-recheck step of the mutation check misled me for several rounds, and the cause is nasty:
The mutation was size-neutral — adding
, "uvicorn.access"and removing"uvicorn.access",, 18 characters each way — and the restore landed in the same second. Python's bytecode cache validates on(mtime, size), so both matched and it kept running the mutated bytecode from__pycache__whileinspect.getsourceshowed the corrected file on disk.The symptom was absurd:
httpxgot its level set butuvicorn.accessdid not, from the same loop. TracingLogger.setLevelwas what separated "the code is wrong" from "the code you are reading is not the code running". Both mutations were then re-run with__pycache__cleared and behave correctly.Takeaway: a mutation check should clear
__pycache__between steps.Verification
405 passed (from 403), coverage 92.24%,
make extras-checkgreen.Mutation-checked both directions:
test_uvicorn_loggers_are_adoptedfailsuvicorn.accesstest_uvicorn_access_is_not_adoptedand the pre-existingtest_noisy_libraries_are_quietedfailThe autouse
restore_loggingfixture was extended to save and restore the handlers andpropagateofuvicorn/uvicorn.error, sincesetup_loggingnow mutates process-global state that it previously left alone.Next
Four candidates left: #2 (
request.state.userfor the access log), #4 (token provider transport), #1 (Repository.get_or_raise), #7 (log.logger, breaking). Then release 0.3.0.