Skip to content

Memory leak: Client.null_logger() adds a new NullHandler on every instantiation #144

@chyvak357

Description

@chyvak357

Describe the bug

Client.null_logger() calls addHandler(NullHandler()) on the shared "null_logger" logger every time a Client is instantiated without an explicit logger argument. The NullHandler instances accumulate indefinitely in logger.handlers (and in the global logging._handlerList weakref set), each bringing its own threading.RLock. In long-lived processes that create many Client instances (background workers, batch jobs, servers handling webhooks), this causes unbounded memory growth.

Steps to reproduce

import logging
from paddle_billing import Client

for _ in range(10_000):
    Client("fake_key")

print(len(logging.getLogger("null_logger").handlers))
# -> 10000

RSS grows ~5 MB per 10k Client instantiations, retained forever (GC cannot collect handlers because they are live-referenced from logger.handlers).

Expected behavior

Creating multiple Client instances should not permanently attach new handlers to a shared logger. The null logger should either:

be configured once at module import, or
guard addHandler with if not null_logger.hasHandlers().

Code snippets

Python version

Python 3.12

SDK version

paddle-python-sdk: 1.10.0

API version

Paddle Version 1

Additional context

Root cause
paddle_billing/Client.py:98-107:

@staticmethod
def null_logger() -> Logger:
null_logger = getLogger("null_logger")
null_logger.addHandler(NullHandler()) # runs on every call
return null_logger
Called unconditionally from Client.init:

self.log = logger if logger else Client.null_logger()

as a workaround I did this:
Pass a pre-configured logger explicitly to avoid hitting null_logger():

import logging
_PADDLE_LOGGER = logging.getLogger("paddle_billing_client")
client = Client(api_key, logger=_PADDLE_LOGGER)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions