Skip to content

gh-150818: Speed up logging.getLogger() for existing loggers (GH-150825)#150825

Merged
vsajip merged 2 commits into
python:mainfrom
gaborbernat:opt/logging-getlogger-fastpath
Jun 4, 2026
Merged

gh-150818: Speed up logging.getLogger() for existing loggers (GH-150825)#150825
vsajip merged 2 commits into
python:mainfrom
gaborbernat:opt/logging-getlogger-fastpath

Conversation

@gaborbernat
Copy link
Copy Markdown
Contributor

@gaborbernat gaborbernat commented Jun 2, 2026

logging.getLogger(name) returns the singleton logger for a name, creating it on first use. Every call takes the logging lock, even the overwhelmingly common case where the logger already exists and is returned unchanged. Libraries fetch their logger by name throughout their code, at module import and again inside functions, so the same handful of names are looked up over and over for the life of a process. Those repeat lookups are pure overhead: the logger is already there.

This returns an existing, fully-initialised logger through a lock-free fast path before falling back to the locked section. The fast path reads loggerDict with a single dict.get(), which is atomic under both the GIL and free threading, and a Logger is inserted into that dict only after it is fully constructed under the lock, so the fast path never observes a half-built object or a placeholder. First-time creation, placeholder resolution and the parent/child wiring still run under the lock exactly as before.

Resolving logger names collected from the top-1000 corpus improves from 6.68 µs to 5.02 µs, 33% faster.

Benchmark base patched
getLogger, existing loggers 6.68 µs 5.02 µs: 33% faster
Benchmark (pyperf)

Run base vs patched by swapping Lib/logging/__init__.py on the same interpreter. The names are real getLogger() string arguments mined from the top-1000 corpus.

import logging, pyperf

names = ["elastic_transport.transport", "peewee.pool", "LiteLLM", "httpx",
    "uvicorn.error", "uvicorn.access", "fsspec", "boto3", "posthog", "amqp",
    "nox", "tldextract.cache", "dulwich.lfs", "urllib3.connectionpool",
    "asyncio", "sqlalchemy.engine", "werkzeug", "django.request", "celery.worker",
    "kafka.conn", "redis.client", "paramiko.transport", "matplotlib.font_manager"]
for n in names:
    logging.getLogger(n)   # pre-create, as modules do at import

runner = pyperf.Runner()
runner.bench_func("getLogger %d existing loggers" % len(names),
                  lambda: [logging.getLogger(n) for n in names])

Resolves #150818.

getLogger() took the logging lock on every call, including the common case of
an already-registered logger. Return that logger through a lock-free fast path
backed by an atomic dict lookup. First-time creation, placeholder resolution
and parent/child wiring still run under the lock, and the fast path is safe
under both the GIL and free threading.
@gaborbernat gaborbernat force-pushed the opt/logging-getlogger-fastpath branch from defd276 to 9c0df96 Compare June 2, 2026 23:45
Comment thread Lib/logging/__init__.py Outdated
Both branches inside the lock assign rv before it is read, so the
reset is dead code.
@gaborbernat gaborbernat requested a review from sobolevn June 3, 2026 14:42
@vsajip vsajip added the 🔨 test-with-buildbots Test PR w/ buildbots; report in status section label Jun 3, 2026
@bedevere-bot
Copy link
Copy Markdown

🤖 New build scheduled with the buildbot fleet by @vsajip for commit efad324 🤖

Results will be shown at:

https://buildbot.python.org/all/#/grid?branch=refs%2Fpull%2F150825%2Fmerge

If you want to schedule another build, you need to add the 🔨 test-with-buildbots label again.

@bedevere-bot bedevere-bot removed the 🔨 test-with-buildbots Test PR w/ buildbots; report in status section label Jun 3, 2026
@vsajip vsajip changed the title gh-150818: Speed up logging.getLogger() for existing loggers gh-150818: Speed up logging.getLogger() for existing loggers (GH-150825) Jun 4, 2026
@vsajip vsajip merged commit e833f57 into python:main Jun 4, 2026
138 of 152 checks passed
@vsajip vsajip added needs backport to 3.13 bugs and security fixes needs backport to 3.14 bugs and security fixes needs backport to 3.15 pre-release feature fixes, bugs and security fixes labels Jun 4, 2026
@miss-islington-app
Copy link
Copy Markdown

Thanks @gaborbernat for the PR, and @vsajip for merging it 🌮🎉.. I'm working now to backport this PR to: 3.13.
🐍🍒⛏🤖 I'm not a witch! I'm not a witch!

@miss-islington-app
Copy link
Copy Markdown

Thanks @gaborbernat for the PR, and @vsajip for merging it 🌮🎉.. I'm working now to backport this PR to: 3.14.
🐍🍒⛏🤖

@miss-islington-app
Copy link
Copy Markdown

Thanks @gaborbernat for the PR, and @vsajip for merging it 🌮🎉.. I'm working now to backport this PR to: 3.15.
🐍🍒⛏🤖

@bedevere-app
Copy link
Copy Markdown

bedevere-app Bot commented Jun 4, 2026

GH-150927 is a backport of this pull request to the 3.13 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.13 bugs and security fixes label Jun 4, 2026
@bedevere-app
Copy link
Copy Markdown

bedevere-app Bot commented Jun 4, 2026

GH-150928 is a backport of this pull request to the 3.14 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.14 bugs and security fixes label Jun 4, 2026
@bedevere-app
Copy link
Copy Markdown

bedevere-app Bot commented Jun 4, 2026

GH-150929 is a backport of this pull request to the 3.15 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.15 pre-release feature fixes, bugs and security fixes label Jun 4, 2026
@StanFromIreland
Copy link
Copy Markdown
Member

We don't usually backport features?

@gaborbernat
Copy link
Copy Markdown
Contributor Author

Is performance improvements a feature? 🤔

@vsajip
Copy link
Copy Markdown
Member

vsajip commented Jun 4, 2026

@StanFromIreland As there is no API change, I've assumed it would be OK to back-port so that earlier versions get the benefit too. Is there some precedent for not doing that?

@vsajip
Copy link
Copy Markdown
Member

vsajip commented Jun 4, 2026

I see that the issue was marked as a feature, by which I suppose it meant that it wasn't a bug. But it's not a feature in the sense of an API enhancement or fix.

@StanFromIreland
Copy link
Copy Markdown
Member

As documented in the devguide:

The only changes allowed to occur in a maintenance branch without debate are bug fixes, test improvements, and edits to the documentation.

And also:

The only branch that receives new features is main

Neither of those places elaborate on the reasoning for this, but I think it is largely to avoid introducing any possible regressions, i.e., to preserve backwards compatibility.

@vsajip
Copy link
Copy Markdown
Member

vsajip commented Jun 4, 2026

I've raised a discussion topic as that same para in the devguide says

only rare exceptions are accepted and must be discussed first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Speed up logging.getLogger() for already-registered loggers

5 participants