fix(multiprocess): avoid double-building child metric names (#1035) by hazel-shen · Pull Request #1146 · prometheus/client_python
Description
Fixes #1035
Child metrics created via labels() could rebuild their full names when using metric subclasses with default namespace/subsystem/unit, especially in multiprocess mode, producing double-prefixed names.
Problem
With a subclass like:
class CustomCounter(Counter): def __init__(self, name, documentation, labelnames=(), namespace='mydefaultnamespace', subsystem='mydefaultsubsystem', unit='defaultunit', **kwargs): super().__init__(name, documentation, labelnames=labelnames, namespace=namespace, subsystem=subsystem, unit=unit, **kwargs)
and an instance:
c = CustomCounter( name='m', documentation='help', labelnames=('status', 'method'), namespace='mynamespace', subsystem='mysubsystem', unit='seconds', )
multiprocess mode could export child metrics with double-prefixed names like:
mydefaultnamespace_mydefaultsubsystem_mynamespace_mysubsystem_m_seconds_total
instead of the correct:
mynamespace_mysubsystem_m_seconds_total
This happened because the already-built full name was passed to the child metric constructor, which then applied namespace/subsystem/unit transformations a second time.
Solution
-
In
MetricWrapperBase.__init__, store original constructor arguments:self._original_name = name self._namespace = namespace self._subsystem = subsystem self._unit = unit
-
In
MetricWrapperBase.labels(), use stored values when creating child metrics:original_name = getattr(self, '_original_name', self._name) namespace = getattr(self, '_namespace', '') subsystem = getattr(self, '_subsystem', '') unit = getattr(self, '_unit', '') self._metrics[labelvalues] = self.__class__( original_name, namespace=namespace, subsystem=subsystem, unit=unit, ... )
This ensures the full name is built exactly once, making single-process and multiprocess exports consistent.
Testing
- All existing tests pass
- Verified correct name construction in multiprocess mode
- Verified subclass compatibility (child metrics preserve parent context)
- Backward compatible via
getattr()fallbacks
Breaking Changes
None (internal-only change to MetricWrapperBase)