Skip to content

Commit 516c8ab

Browse files
authored
Fix kwargs in proxy metric instruments, preventing warning about duplicate histograms (#1149)
1 parent af73c06 commit 516c8ab

File tree

2 files changed

+51
-97
lines changed

2 files changed

+51
-97
lines changed

logfire/_internal/metrics.py

Lines changed: 39 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def shutdown(self, timeout_millis: float = 30_000) -> None:
7979
if isinstance(self.provider, SDKMeterProvider):
8080
self.provider.shutdown(timeout_millis)
8181

82-
def force_flush(self, timeout_millis: float = 30_000) -> None: # pragma: no cover
82+
def force_flush(self, timeout_millis: float = 30_000) -> None:
8383
with self.lock:
8484
if isinstance(self.provider, SDKMeterProvider): # pragma: no branch
8585
self.provider.force_flush(timeout_millis)
@@ -113,29 +113,27 @@ def set_meter(self, meter_provider: MeterProvider) -> None:
113113
for instrument in self._instruments:
114114
instrument.on_meter_set(real_meter)
115115

116+
def _add_proxy_instrument(self, instrument_type: type[_ProxyInstrument[InstrumentT]], **kwargs: Any) -> InstrumentT:
117+
with self._lock:
118+
proxy = instrument_type(self._meter, **kwargs)
119+
self._instruments.add(proxy)
120+
return proxy # type: ignore
121+
116122
def create_counter(
117123
self,
118124
name: str,
119125
unit: str = '',
120126
description: str = '',
121127
) -> Counter:
122-
with self._lock:
123-
proxy = _ProxyCounter(self._meter.create_counter(name, unit, description), name, unit, description)
124-
self._instruments.add(proxy)
125-
return proxy
128+
return self._add_proxy_instrument(_ProxyCounter, name=name, unit=unit, description=description)
126129

127130
def create_up_down_counter(
128131
self,
129132
name: str,
130133
unit: str = '',
131134
description: str = '',
132135
) -> UpDownCounter:
133-
with self._lock:
134-
proxy = _ProxyUpDownCounter(
135-
self._meter.create_up_down_counter(name, unit, description), name, unit, description
136-
)
137-
self._instruments.add(proxy)
138-
return proxy
136+
return self._add_proxy_instrument(_ProxyUpDownCounter, name=name, unit=unit, description=description)
139137

140138
def create_observable_counter(
141139
self,
@@ -144,16 +142,9 @@ def create_observable_counter(
144142
unit: str = '',
145143
description: str = '',
146144
) -> ObservableCounter:
147-
with self._lock:
148-
proxy = _ProxyObservableCounter(
149-
self._meter.create_observable_counter(name, callbacks, unit, description),
150-
name,
151-
callbacks,
152-
unit,
153-
description,
154-
)
155-
self._instruments.add(proxy)
156-
return proxy
145+
return self._add_proxy_instrument(
146+
_ProxyObservableCounter, name=name, unit=unit, description=description, callbacks=callbacks
147+
)
157148

158149
def create_histogram(
159150
self,
@@ -162,12 +153,7 @@ def create_histogram(
162153
description: str = '',
163154
**kwargs: Any,
164155
) -> Histogram:
165-
with self._lock:
166-
proxy = _ProxyHistogram(
167-
self._meter.create_histogram(name, unit, description, **kwargs), name, unit, description
168-
)
169-
self._instruments.add(proxy)
170-
return proxy
156+
return self._add_proxy_instrument(_ProxyHistogram, name=name, unit=unit, description=description, **kwargs)
171157

172158
def create_gauge(
173159
self,
@@ -182,10 +168,7 @@ def create_gauge(
182168
'You should upgrade to 1.23.0 or newer:\n'
183169
' pip install opentelemetry-sdk>=1.23.0'
184170
)
185-
with self._lock:
186-
proxy = _ProxyGauge(self._meter.create_gauge(name, unit, description), name, unit, description)
187-
self._instruments.add(proxy)
188-
return proxy
171+
return self._add_proxy_instrument(_ProxyGauge, name=name, unit=unit, description=description)
189172

190173
def create_observable_gauge(
191174
self,
@@ -194,16 +177,9 @@ def create_observable_gauge(
194177
unit: str = '',
195178
description: str = '',
196179
) -> ObservableGauge:
197-
with self._lock:
198-
proxy = _ProxyObservableGauge(
199-
self._meter.create_observable_gauge(name, callbacks, unit, description),
200-
name,
201-
callbacks,
202-
unit,
203-
description,
204-
)
205-
self._instruments.add(proxy)
206-
return proxy
180+
return self._add_proxy_instrument(
181+
_ProxyObservableGauge, name=name, unit=unit, description=description, callbacks=callbacks
182+
)
207183

208184
def create_observable_up_down_counter(
209185
self,
@@ -212,33 +188,18 @@ def create_observable_up_down_counter(
212188
unit: str = '',
213189
description: str = '',
214190
) -> ObservableUpDownCounter:
215-
with self._lock:
216-
proxy = _ProxyObservableUpDownCounter(
217-
self._meter.create_observable_up_down_counter(name, callbacks, unit, description),
218-
name,
219-
callbacks,
220-
unit,
221-
description,
222-
)
223-
self._instruments.add(proxy)
224-
return proxy
191+
return self._add_proxy_instrument(
192+
_ProxyObservableUpDownCounter, name=name, unit=unit, description=description, callbacks=callbacks
193+
)
225194

226195

227196
InstrumentT = TypeVar('InstrumentT', bound=Instrument)
228197

229198

230199
class _ProxyInstrument(ABC, Generic[InstrumentT]):
231-
def __init__(
232-
self,
233-
instrument: InstrumentT,
234-
name: str,
235-
unit: str,
236-
description: str,
237-
) -> None:
238-
self._name = name
239-
self._unit = unit
240-
self._description = description
241-
self._instrument = instrument
200+
def __init__(self, meter: Meter, **kwargs: Any) -> None:
201+
self._kwargs = kwargs
202+
self._instrument = self._create_real_instrument(meter)
242203

243204
def on_meter_set(self, meter: Meter) -> None:
244205
"""Called when a real meter is set on the creating _ProxyMeter."""
@@ -255,20 +216,7 @@ def _create_real_instrument(self, meter: Meter) -> InstrumentT:
255216
def _increment_span_metric(self, amount: float, attributes: Attributes | None = None):
256217
span = get_current_span()
257218
if isinstance(span, _LogfireWrappedSpan):
258-
span.increment_metric(self._name, attributes or {}, amount)
259-
260-
261-
class _ProxyAsynchronousInstrument(_ProxyInstrument[InstrumentT], ABC):
262-
def __init__(
263-
self,
264-
instrument: InstrumentT,
265-
name: str,
266-
callbacks: Sequence[CallbackT] | None,
267-
unit: str,
268-
description: str,
269-
) -> None:
270-
super().__init__(instrument, name, unit, description)
271-
self._callbacks = callbacks
219+
span.increment_metric(self._kwargs['name'], attributes or {}, amount)
272220

273221

274222
class _ProxyCounter(_ProxyInstrument[Counter], Counter):
@@ -285,7 +233,7 @@ def add(
285233
self._instrument.add(amount, attributes, *args, **kwargs)
286234

287235
def _create_real_instrument(self, meter: Meter) -> Counter:
288-
return meter.create_counter(self._name, self._unit, self._description)
236+
return meter.create_counter(**self._kwargs)
289237

290238

291239
class _ProxyHistogram(_ProxyInstrument[Histogram], Histogram):
@@ -300,28 +248,22 @@ def record(
300248
self._instrument.record(amount, attributes, *args, **kwargs)
301249

302250
def _create_real_instrument(self, meter: Meter) -> Histogram:
303-
return meter.create_histogram(self._name, self._unit, self._description)
251+
return meter.create_histogram(**self._kwargs)
304252

305253

306-
class _ProxyObservableCounter(_ProxyAsynchronousInstrument[ObservableCounter], ObservableCounter):
307-
def _create_real_instrument(self, meter: Meter) -> ObservableCounter: # pragma: no cover
308-
return meter.create_observable_counter(self._name, self._callbacks, self._unit, self._description)
254+
class _ProxyObservableCounter(_ProxyInstrument[ObservableCounter], ObservableCounter):
255+
def _create_real_instrument(self, meter: Meter) -> ObservableCounter:
256+
return meter.create_observable_counter(**self._kwargs)
309257

310258

311-
class _ProxyObservableGauge(
312-
_ProxyAsynchronousInstrument[ObservableGauge],
313-
ObservableGauge,
314-
):
315-
def _create_real_instrument(self, meter: Meter) -> ObservableGauge: # pragma: no cover
316-
return meter.create_observable_gauge(self._name, self._callbacks, self._unit, self._description)
259+
class _ProxyObservableGauge(_ProxyInstrument[ObservableGauge], ObservableGauge):
260+
def _create_real_instrument(self, meter: Meter) -> ObservableGauge:
261+
return meter.create_observable_gauge(**self._kwargs)
317262

318263

319-
class _ProxyObservableUpDownCounter(
320-
_ProxyAsynchronousInstrument[ObservableUpDownCounter],
321-
ObservableUpDownCounter,
322-
):
323-
def _create_real_instrument(self, meter: Meter) -> ObservableUpDownCounter: # pragma: no cover
324-
return meter.create_observable_up_down_counter(self._name, self._callbacks, self._unit, self._description)
264+
class _ProxyObservableUpDownCounter(_ProxyInstrument[ObservableUpDownCounter], ObservableUpDownCounter):
265+
def _create_real_instrument(self, meter: Meter) -> ObservableUpDownCounter:
266+
return meter.create_observable_up_down_counter(**self._kwargs)
325267

326268

327269
class _ProxyUpDownCounter(_ProxyInstrument[UpDownCounter], UpDownCounter):
@@ -335,7 +277,7 @@ def add(
335277
self._instrument.add(amount, attributes, *args, **kwargs)
336278

337279
def _create_real_instrument(self, meter: Meter) -> UpDownCounter:
338-
return meter.create_up_down_counter(self._name, self._unit, self._description)
280+
return meter.create_up_down_counter(**self._kwargs)
339281

340282

341283
if Gauge is not None: # pragma: no branch
@@ -347,10 +289,10 @@ def set(
347289
attributes: Attributes | None = None,
348290
*args: Any,
349291
**kwargs: Any,
350-
) -> None: # pragma: no cover
292+
) -> None:
351293
self._instrument.set(amount, attributes, *args, **kwargs)
352294

353-
def _create_real_instrument(self, meter: Meter): # pragma: no cover
354-
return meter.create_gauge(self._name, self._unit, self._description)
295+
def _create_real_instrument(self, meter: Meter):
296+
return meter.create_gauge(**self._kwargs)
355297
else: # pragma: no cover
356298
_ProxyGauge = None # type: ignore

tests/test_metrics.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,3 +488,15 @@ def test_metrics_in_non_recording_spans(exporter: TestExporter, config_kwargs: d
488488
}
489489
]
490490
)
491+
492+
493+
def test_reconfigure(caplog: pytest.LogCaptureFixture):
494+
for _ in range(3):
495+
logfire.configure(send_to_logfire=False, console=False)
496+
meter.create_histogram('foo', unit='x', description='bar', explicit_bucket_boundaries_advisory=[1, 2, 3])
497+
# Previously a bug caused a warning to be logged when reconfiguring the metrics
498+
assert not caplog.messages
499+
500+
# For comparison, this logs a warning because the advisory is different (unset)
501+
meter.create_histogram('foo', unit='x', description='bar')
502+
assert caplog.messages

0 commit comments

Comments
 (0)