Skip to content

fix(multiprocess): avoid double-building child metric names (#1035) - #1146

Merged
csmarchbanks merged 2 commits into
prometheus:masterfrom
hazel-shen:fix/multiprocess-child-name-double-build-1035
Nov 17, 2025
Merged

fix(multiprocess): avoid double-building child metric names (#1035)#1146
csmarchbanks merged 2 commits into
prometheus:masterfrom
hazel-shen:fix/multiprocess-child-name-double-build-1035

Conversation

@hazel-shen

@hazel-shen hazel-shen commented Nov 14, 2025

Copy link
Copy Markdown
Contributor

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)

@hazel-shen
hazel-shen force-pushed the fix/multiprocess-child-name-double-build-1035 branch from 7bef223 to 11f2793 Compare November 14, 2025 11:23
@hazel-shen
hazel-shen force-pushed the fix/multiprocess-child-name-double-build-1035 branch 2 times, most recently from 2c01fe2 to e21061a Compare November 14, 2025 11:30
Signed-off-by: hazel-shen <mail@hazel.style>
@hazel-shen
hazel-shen force-pushed the fix/multiprocess-child-name-double-build-1035 branch from e21061a to d0cf1fc Compare November 14, 2025 11:40

@csmarchbanks csmarchbanks left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@csmarchbanks
csmarchbanks merged commit e8f8bae into prometheus:master Nov 17, 2025
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants