Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/opentelemetry/metrics/_internal/__init__.py: 38%

181 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:35 +0000

1# Copyright The OpenTelemetry Authors 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14 

15# pylint: disable=too-many-ancestors 

16 

17""" 

18The OpenTelemetry metrics API describes the classes used to generate 

19metrics. 

20 

21The :class:`.MeterProvider` provides users access to the :class:`.Meter` which in 

22turn is used to create :class:`.Instrument` objects. The :class:`.Instrument` objects are 

23used to record measurements. 

24 

25This module provides abstract (i.e. unimplemented) classes required for 

26metrics, and a concrete no-op implementation :class:`.NoOpMeter` that allows applications 

27to use the API package alone without a supporting implementation. 

28 

29To get a meter, you need to provide the package name from which you are 

30calling the meter APIs to OpenTelemetry by calling `MeterProvider.get_meter` 

31with the calling instrumentation name and the version of your package. 

32 

33The following code shows how to obtain a meter using the global :class:`.MeterProvider`:: 

34 

35 from opentelemetry.metrics import get_meter 

36 

37 meter = get_meter("example-meter") 

38 counter = meter.create_counter("example-counter") 

39 

40.. versionadded:: 1.10.0 

41""" 

42 

43 

44from abc import ABC, abstractmethod 

45from logging import getLogger 

46from os import environ 

47from threading import Lock 

48from typing import List, Optional, Sequence, Set, Tuple, Union, cast 

49 

50from opentelemetry.environment_variables import OTEL_PYTHON_METER_PROVIDER 

51from opentelemetry.metrics._internal.instrument import ( 

52 CallbackT, 

53 Counter, 

54 Histogram, 

55 NoOpCounter, 

56 NoOpHistogram, 

57 NoOpObservableCounter, 

58 NoOpObservableGauge, 

59 NoOpObservableUpDownCounter, 

60 NoOpUpDownCounter, 

61 ObservableCounter, 

62 ObservableGauge, 

63 ObservableUpDownCounter, 

64 UpDownCounter, 

65 _ProxyCounter, 

66 _ProxyHistogram, 

67 _ProxyObservableCounter, 

68 _ProxyObservableGauge, 

69 _ProxyObservableUpDownCounter, 

70 _ProxyUpDownCounter, 

71) 

72from opentelemetry.util._once import Once 

73from opentelemetry.util._providers import _load_provider 

74 

75_logger = getLogger(__name__) 

76 

77 

78_ProxyInstrumentT = Union[ 

79 _ProxyCounter, 

80 _ProxyHistogram, 

81 _ProxyObservableCounter, 

82 _ProxyObservableGauge, 

83 _ProxyObservableUpDownCounter, 

84 _ProxyUpDownCounter, 

85] 

86 

87 

88class MeterProvider(ABC): 

89 """ 

90 MeterProvider is the entry point of the API. It provides access to `Meter` instances. 

91 """ 

92 

93 @abstractmethod 

94 def get_meter( 

95 self, 

96 name: str, 

97 version: Optional[str] = None, 

98 schema_url: Optional[str] = None, 

99 ) -> "Meter": 

100 """Returns a `Meter` for use by the given instrumentation library. 

101 

102 For any two calls it is undefined whether the same or different 

103 `Meter` instances are returned, even for different library names. 

104 

105 This function may return different `Meter` types (e.g. a no-op meter 

106 vs. a functional meter). 

107 

108 Args: 

109 name: The name of the instrumenting module. 

110 ``__name__`` may not be used as this can result in 

111 different meter names if the meters are in different files. 

112 It is better to use a fixed string that can be imported where 

113 needed and used consistently as the name of the meter. 

114 

115 This should *not* be the name of the module that is 

116 instrumented but the name of the module doing the instrumentation. 

117 E.g., instead of ``"requests"``, use 

118 ``"opentelemetry.instrumentation.requests"``. 

119 

120 version: Optional. The version string of the 

121 instrumenting library. Usually this should be the same as 

122 ``pkg_resources.get_distribution(instrumenting_library_name).version``. 

123 

124 schema_url: Optional. Specifies the Schema URL of the emitted telemetry. 

125 """ 

126 

127 

128class NoOpMeterProvider(MeterProvider): 

129 """The default MeterProvider used when no MeterProvider implementation is available.""" 

130 

131 def get_meter( 

132 self, 

133 name: str, 

134 version: Optional[str] = None, 

135 schema_url: Optional[str] = None, 

136 ) -> "Meter": 

137 """Returns a NoOpMeter.""" 

138 super().get_meter(name, version=version, schema_url=schema_url) 

139 return NoOpMeter(name, version=version, schema_url=schema_url) 

140 

141 

142class _ProxyMeterProvider(MeterProvider): 

143 def __init__(self) -> None: 

144 self._lock = Lock() 

145 self._meters: List[_ProxyMeter] = [] 

146 self._real_meter_provider: Optional[MeterProvider] = None 

147 

148 def get_meter( 

149 self, 

150 name: str, 

151 version: Optional[str] = None, 

152 schema_url: Optional[str] = None, 

153 ) -> "Meter": 

154 with self._lock: 

155 if self._real_meter_provider is not None: 

156 return self._real_meter_provider.get_meter( 

157 name, version, schema_url 

158 ) 

159 

160 meter = _ProxyMeter(name, version=version, schema_url=schema_url) 

161 self._meters.append(meter) 

162 return meter 

163 

164 def on_set_meter_provider(self, meter_provider: MeterProvider) -> None: 

165 with self._lock: 

166 self._real_meter_provider = meter_provider 

167 for meter in self._meters: 

168 meter.on_set_meter_provider(meter_provider) 

169 

170 

171class Meter(ABC): 

172 """Handles instrument creation. 

173 

174 This class provides methods for creating instruments which are then 

175 used to produce measurements. 

176 """ 

177 

178 def __init__( 

179 self, 

180 name: str, 

181 version: Optional[str] = None, 

182 schema_url: Optional[str] = None, 

183 ) -> None: 

184 super().__init__() 

185 self._name = name 

186 self._version = version 

187 self._schema_url = schema_url 

188 self._instrument_ids: Set[str] = set() 

189 self._instrument_ids_lock = Lock() 

190 

191 @property 

192 def name(self) -> str: 

193 """ 

194 The name of the instrumenting module. 

195 """ 

196 return self._name 

197 

198 @property 

199 def version(self) -> Optional[str]: 

200 """ 

201 The version string of the instrumenting library. 

202 """ 

203 return self._version 

204 

205 @property 

206 def schema_url(self) -> Optional[str]: 

207 """ 

208 Specifies the Schema URL of the emitted telemetry 

209 """ 

210 return self._schema_url 

211 

212 def _is_instrument_registered( 

213 self, name: str, type_: type, unit: str, description: str 

214 ) -> Tuple[bool, str]: 

215 """ 

216 Check if an instrument with the same name, type, unit and description 

217 has been registered already. 

218 

219 Returns a tuple. The first value is `True` if the instrument has been 

220 registered already, `False` otherwise. The second value is the 

221 instrument id. 

222 """ 

223 

224 instrument_id = ",".join( 

225 [name.strip().lower(), type_.__name__, unit, description] 

226 ) 

227 

228 result = False 

229 

230 with self._instrument_ids_lock: 

231 if instrument_id in self._instrument_ids: 

232 result = True 

233 else: 

234 self._instrument_ids.add(instrument_id) 

235 

236 return (result, instrument_id) 

237 

238 @abstractmethod 

239 def create_counter( 

240 self, 

241 name: str, 

242 unit: str = "", 

243 description: str = "", 

244 ) -> Counter: 

245 """Creates a `Counter` instrument 

246 

247 Args: 

248 name: The name of the instrument to be created 

249 unit: The unit for observations this instrument reports. For 

250 example, ``By`` for bytes. UCUM units are recommended. 

251 description: A description for this instrument and what it measures. 

252 """ 

253 

254 @abstractmethod 

255 def create_up_down_counter( 

256 self, 

257 name: str, 

258 unit: str = "", 

259 description: str = "", 

260 ) -> UpDownCounter: 

261 """Creates an `UpDownCounter` instrument 

262 

263 Args: 

264 name: The name of the instrument to be created 

265 unit: The unit for observations this instrument reports. For 

266 example, ``By`` for bytes. UCUM units are recommended. 

267 description: A description for this instrument and what it measures. 

268 """ 

269 

270 @abstractmethod 

271 def create_observable_counter( 

272 self, 

273 name: str, 

274 callbacks: Optional[Sequence[CallbackT]] = None, 

275 unit: str = "", 

276 description: str = "", 

277 ) -> ObservableCounter: 

278 """Creates an `ObservableCounter` instrument 

279 

280 An observable counter observes a monotonically increasing count by calling provided 

281 callbacks which accept a :class:`~opentelemetry.metrics.CallbackOptions` and return 

282 multiple :class:`~opentelemetry.metrics.Observation`. 

283 

284 For example, an observable counter could be used to report system CPU 

285 time periodically. Here is a basic implementation:: 

286 

287 def cpu_time_callback(options: CallbackOptions) -> Iterable[Observation]: 

288 observations = [] 

289 with open("/proc/stat") as procstat: 

290 procstat.readline() # skip the first line 

291 for line in procstat: 

292 if not line.startswith("cpu"): break 

293 cpu, *states = line.split() 

294 observations.append(Observation(int(states[0]) // 100, {"cpu": cpu, "state": "user"})) 

295 observations.append(Observation(int(states[1]) // 100, {"cpu": cpu, "state": "nice"})) 

296 observations.append(Observation(int(states[2]) // 100, {"cpu": cpu, "state": "system"})) 

297 # ... other states 

298 return observations 

299 

300 meter.create_observable_counter( 

301 "system.cpu.time", 

302 callbacks=[cpu_time_callback], 

303 unit="s", 

304 description="CPU time" 

305 ) 

306 

307 To reduce memory usage, you can use generator callbacks instead of 

308 building the full list:: 

309 

310 def cpu_time_callback(options: CallbackOptions) -> Iterable[Observation]: 

311 with open("/proc/stat") as procstat: 

312 procstat.readline() # skip the first line 

313 for line in procstat: 

314 if not line.startswith("cpu"): break 

315 cpu, *states = line.split() 

316 yield Observation(int(states[0]) // 100, {"cpu": cpu, "state": "user"}) 

317 yield Observation(int(states[1]) // 100, {"cpu": cpu, "state": "nice"}) 

318 # ... other states 

319 

320 Alternatively, you can pass a sequence of generators directly instead of a sequence of 

321 callbacks, which each should return iterables of :class:`~opentelemetry.metrics.Observation`:: 

322 

323 def cpu_time_callback(states_to_include: set[str]) -> Iterable[Iterable[Observation]]: 

324 # accept options sent in from OpenTelemetry 

325 options = yield 

326 while True: 

327 observations = [] 

328 with open("/proc/stat") as procstat: 

329 procstat.readline() # skip the first line 

330 for line in procstat: 

331 if not line.startswith("cpu"): break 

332 cpu, *states = line.split() 

333 if "user" in states_to_include: 

334 observations.append(Observation(int(states[0]) // 100, {"cpu": cpu, "state": "user"})) 

335 if "nice" in states_to_include: 

336 observations.append(Observation(int(states[1]) // 100, {"cpu": cpu, "state": "nice"})) 

337 # ... other states 

338 # yield the observations and receive the options for next iteration 

339 options = yield observations 

340 

341 meter.create_observable_counter( 

342 "system.cpu.time", 

343 callbacks=[cpu_time_callback({"user", "system"})], 

344 unit="s", 

345 description="CPU time" 

346 ) 

347 

348 The :class:`~opentelemetry.metrics.CallbackOptions` contain a timeout which the 

349 callback should respect. For example if the callback does asynchronous work, like 

350 making HTTP requests, it should respect the timeout:: 

351 

352 def scrape_http_callback(options: CallbackOptions) -> Iterable[Observation]: 

353 r = requests.get('http://scrapethis.com', timeout=options.timeout_millis / 10**3) 

354 for value in r.json(): 

355 yield Observation(value) 

356 

357 Args: 

358 name: The name of the instrument to be created 

359 callbacks: A sequence of callbacks that return an iterable of 

360 :class:`~opentelemetry.metrics.Observation`. Alternatively, can be a sequence of generators that each 

361 yields iterables of :class:`~opentelemetry.metrics.Observation`. 

362 unit: The unit for observations this instrument reports. For 

363 example, ``By`` for bytes. UCUM units are recommended. 

364 description: A description for this instrument and what it measures. 

365 """ 

366 

367 @abstractmethod 

368 def create_histogram( 

369 self, 

370 name: str, 

371 unit: str = "", 

372 description: str = "", 

373 ) -> Histogram: 

374 """Creates a :class:`~opentelemetry.metrics.Histogram` instrument 

375 

376 Args: 

377 name: The name of the instrument to be created 

378 unit: The unit for observations this instrument reports. For 

379 example, ``By`` for bytes. UCUM units are recommended. 

380 description: A description for this instrument and what it measures. 

381 """ 

382 

383 @abstractmethod 

384 def create_observable_gauge( 

385 self, 

386 name: str, 

387 callbacks: Optional[Sequence[CallbackT]] = None, 

388 unit: str = "", 

389 description: str = "", 

390 ) -> ObservableGauge: 

391 """Creates an `ObservableGauge` instrument 

392 

393 Args: 

394 name: The name of the instrument to be created 

395 callbacks: A sequence of callbacks that return an iterable of 

396 :class:`~opentelemetry.metrics.Observation`. Alternatively, can be a generator that yields iterables 

397 of :class:`~opentelemetry.metrics.Observation`. 

398 unit: The unit for observations this instrument reports. For 

399 example, ``By`` for bytes. UCUM units are recommended. 

400 description: A description for this instrument and what it measures. 

401 """ 

402 

403 @abstractmethod 

404 def create_observable_up_down_counter( 

405 self, 

406 name: str, 

407 callbacks: Optional[Sequence[CallbackT]] = None, 

408 unit: str = "", 

409 description: str = "", 

410 ) -> ObservableUpDownCounter: 

411 """Creates an `ObservableUpDownCounter` instrument 

412 

413 Args: 

414 name: The name of the instrument to be created 

415 callbacks: A sequence of callbacks that return an iterable of 

416 :class:`~opentelemetry.metrics.Observation`. Alternatively, can be a generator that yields iterables 

417 of :class:`~opentelemetry.metrics.Observation`. 

418 unit: The unit for observations this instrument reports. For 

419 example, ``By`` for bytes. UCUM units are recommended. 

420 description: A description for this instrument and what it measures. 

421 """ 

422 

423 

424class _ProxyMeter(Meter): 

425 def __init__( 

426 self, 

427 name: str, 

428 version: Optional[str] = None, 

429 schema_url: Optional[str] = None, 

430 ) -> None: 

431 super().__init__(name, version=version, schema_url=schema_url) 

432 self._lock = Lock() 

433 self._instruments: List[_ProxyInstrumentT] = [] 

434 self._real_meter: Optional[Meter] = None 

435 

436 def on_set_meter_provider(self, meter_provider: MeterProvider) -> None: 

437 """Called when a real meter provider is set on the creating _ProxyMeterProvider 

438 

439 Creates a real backing meter for this instance and notifies all created 

440 instruments so they can create real backing instruments. 

441 """ 

442 real_meter = meter_provider.get_meter( 

443 self._name, self._version, self._schema_url 

444 ) 

445 

446 with self._lock: 

447 self._real_meter = real_meter 

448 # notify all proxy instruments of the new meter so they can create 

449 # real instruments to back themselves 

450 for instrument in self._instruments: 

451 instrument.on_meter_set(real_meter) 

452 

453 def create_counter( 

454 self, 

455 name: str, 

456 unit: str = "", 

457 description: str = "", 

458 ) -> Counter: 

459 with self._lock: 

460 if self._real_meter: 

461 return self._real_meter.create_counter(name, unit, description) 

462 proxy = _ProxyCounter(name, unit, description) 

463 self._instruments.append(proxy) 

464 return proxy 

465 

466 def create_up_down_counter( 

467 self, 

468 name: str, 

469 unit: str = "", 

470 description: str = "", 

471 ) -> UpDownCounter: 

472 with self._lock: 

473 if self._real_meter: 

474 return self._real_meter.create_up_down_counter( 

475 name, unit, description 

476 ) 

477 proxy = _ProxyUpDownCounter(name, unit, description) 

478 self._instruments.append(proxy) 

479 return proxy 

480 

481 def create_observable_counter( 

482 self, 

483 name: str, 

484 callbacks: Optional[Sequence[CallbackT]] = None, 

485 unit: str = "", 

486 description: str = "", 

487 ) -> ObservableCounter: 

488 with self._lock: 

489 if self._real_meter: 

490 return self._real_meter.create_observable_counter( 

491 name, callbacks, unit, description 

492 ) 

493 proxy = _ProxyObservableCounter( 

494 name, callbacks, unit=unit, description=description 

495 ) 

496 self._instruments.append(proxy) 

497 return proxy 

498 

499 def create_histogram( 

500 self, 

501 name: str, 

502 unit: str = "", 

503 description: str = "", 

504 ) -> Histogram: 

505 with self._lock: 

506 if self._real_meter: 

507 return self._real_meter.create_histogram( 

508 name, unit, description 

509 ) 

510 proxy = _ProxyHistogram(name, unit, description) 

511 self._instruments.append(proxy) 

512 return proxy 

513 

514 def create_observable_gauge( 

515 self, 

516 name: str, 

517 callbacks: Optional[Sequence[CallbackT]] = None, 

518 unit: str = "", 

519 description: str = "", 

520 ) -> ObservableGauge: 

521 with self._lock: 

522 if self._real_meter: 

523 return self._real_meter.create_observable_gauge( 

524 name, callbacks, unit, description 

525 ) 

526 proxy = _ProxyObservableGauge( 

527 name, callbacks, unit=unit, description=description 

528 ) 

529 self._instruments.append(proxy) 

530 return proxy 

531 

532 def create_observable_up_down_counter( 

533 self, 

534 name: str, 

535 callbacks: Optional[Sequence[CallbackT]] = None, 

536 unit: str = "", 

537 description: str = "", 

538 ) -> ObservableUpDownCounter: 

539 with self._lock: 

540 if self._real_meter: 

541 return self._real_meter.create_observable_up_down_counter( 

542 name, 

543 callbacks, 

544 unit, 

545 description, 

546 ) 

547 proxy = _ProxyObservableUpDownCounter( 

548 name, callbacks, unit=unit, description=description 

549 ) 

550 self._instruments.append(proxy) 

551 return proxy 

552 

553 

554class NoOpMeter(Meter): 

555 """The default Meter used when no Meter implementation is available. 

556 

557 All operations are no-op. 

558 """ 

559 

560 def create_counter( 

561 self, 

562 name: str, 

563 unit: str = "", 

564 description: str = "", 

565 ) -> Counter: 

566 """Returns a no-op Counter.""" 

567 super().create_counter(name, unit=unit, description=description) 

568 if self._is_instrument_registered( 

569 name, NoOpCounter, unit, description 

570 )[0]: 

571 _logger.warning( 

572 "An instrument with name %s, type %s, unit %s and " 

573 "description %s has been created already.", 

574 name, 

575 Counter.__name__, 

576 unit, 

577 description, 

578 ) 

579 return NoOpCounter(name, unit=unit, description=description) 

580 

581 def create_up_down_counter( 

582 self, 

583 name: str, 

584 unit: str = "", 

585 description: str = "", 

586 ) -> UpDownCounter: 

587 """Returns a no-op UpDownCounter.""" 

588 super().create_up_down_counter( 

589 name, unit=unit, description=description 

590 ) 

591 if self._is_instrument_registered( 

592 name, NoOpUpDownCounter, unit, description 

593 )[0]: 

594 _logger.warning( 

595 "An instrument with name %s, type %s, unit %s and " 

596 "description %s has been created already.", 

597 name, 

598 UpDownCounter.__name__, 

599 unit, 

600 description, 

601 ) 

602 return NoOpUpDownCounter(name, unit=unit, description=description) 

603 

604 def create_observable_counter( 

605 self, 

606 name: str, 

607 callbacks: Optional[Sequence[CallbackT]] = None, 

608 unit: str = "", 

609 description: str = "", 

610 ) -> ObservableCounter: 

611 """Returns a no-op ObservableCounter.""" 

612 super().create_observable_counter( 

613 name, callbacks, unit=unit, description=description 

614 ) 

615 if self._is_instrument_registered( 

616 name, NoOpObservableCounter, unit, description 

617 )[0]: 

618 _logger.warning( 

619 "An instrument with name %s, type %s, unit %s and " 

620 "description %s has been created already.", 

621 name, 

622 ObservableCounter.__name__, 

623 unit, 

624 description, 

625 ) 

626 return NoOpObservableCounter( 

627 name, 

628 callbacks, 

629 unit=unit, 

630 description=description, 

631 ) 

632 

633 def create_histogram( 

634 self, 

635 name: str, 

636 unit: str = "", 

637 description: str = "", 

638 ) -> Histogram: 

639 """Returns a no-op Histogram.""" 

640 super().create_histogram(name, unit=unit, description=description) 

641 if self._is_instrument_registered( 

642 name, NoOpHistogram, unit, description 

643 )[0]: 

644 _logger.warning( 

645 "An instrument with name %s, type %s, unit %s and " 

646 "description %s has been created already.", 

647 name, 

648 Histogram.__name__, 

649 unit, 

650 description, 

651 ) 

652 return NoOpHistogram(name, unit=unit, description=description) 

653 

654 def create_observable_gauge( 

655 self, 

656 name: str, 

657 callbacks: Optional[Sequence[CallbackT]] = None, 

658 unit: str = "", 

659 description: str = "", 

660 ) -> ObservableGauge: 

661 """Returns a no-op ObservableGauge.""" 

662 super().create_observable_gauge( 

663 name, callbacks, unit=unit, description=description 

664 ) 

665 if self._is_instrument_registered( 

666 name, NoOpObservableGauge, unit, description 

667 )[0]: 

668 _logger.warning( 

669 "An instrument with name %s, type %s, unit %s and " 

670 "description %s has been created already.", 

671 name, 

672 ObservableGauge.__name__, 

673 unit, 

674 description, 

675 ) 

676 return NoOpObservableGauge( 

677 name, 

678 callbacks, 

679 unit=unit, 

680 description=description, 

681 ) 

682 

683 def create_observable_up_down_counter( 

684 self, 

685 name: str, 

686 callbacks: Optional[Sequence[CallbackT]] = None, 

687 unit: str = "", 

688 description: str = "", 

689 ) -> ObservableUpDownCounter: 

690 """Returns a no-op ObservableUpDownCounter.""" 

691 super().create_observable_up_down_counter( 

692 name, callbacks, unit=unit, description=description 

693 ) 

694 if self._is_instrument_registered( 

695 name, NoOpObservableUpDownCounter, unit, description 

696 )[0]: 

697 _logger.warning( 

698 "An instrument with name %s, type %s, unit %s and " 

699 "description %s has been created already.", 

700 name, 

701 ObservableUpDownCounter.__name__, 

702 unit, 

703 description, 

704 ) 

705 return NoOpObservableUpDownCounter( 

706 name, 

707 callbacks, 

708 unit=unit, 

709 description=description, 

710 ) 

711 

712 

713_METER_PROVIDER_SET_ONCE = Once() 

714_METER_PROVIDER: Optional[MeterProvider] = None 

715_PROXY_METER_PROVIDER = _ProxyMeterProvider() 

716 

717 

718def get_meter( 

719 name: str, 

720 version: str = "", 

721 meter_provider: Optional[MeterProvider] = None, 

722) -> "Meter": 

723 """Returns a `Meter` for use by the given instrumentation library. 

724 

725 This function is a convenience wrapper for 

726 `opentelemetry.metrics.MeterProvider.get_meter`. 

727 

728 If meter_provider is omitted the current configured one is used. 

729 """ 

730 if meter_provider is None: 

731 meter_provider = get_meter_provider() 

732 return meter_provider.get_meter(name, version) 

733 

734 

735def _set_meter_provider(meter_provider: MeterProvider, log: bool) -> None: 

736 def set_mp() -> None: 

737 global _METER_PROVIDER # pylint: disable=global-statement 

738 _METER_PROVIDER = meter_provider 

739 

740 # gives all proxies real instruments off the newly set meter provider 

741 _PROXY_METER_PROVIDER.on_set_meter_provider(meter_provider) 

742 

743 did_set = _METER_PROVIDER_SET_ONCE.do_once(set_mp) 

744 

745 if log and not did_set: 

746 _logger.warning("Overriding of current MeterProvider is not allowed") 

747 

748 

749def set_meter_provider(meter_provider: MeterProvider) -> None: 

750 """Sets the current global :class:`~.MeterProvider` object. 

751 

752 This can only be done once, a warning will be logged if any further attempt 

753 is made. 

754 """ 

755 _set_meter_provider(meter_provider, log=True) 

756 

757 

758def get_meter_provider() -> MeterProvider: 

759 """Gets the current global :class:`~.MeterProvider` object.""" 

760 

761 if _METER_PROVIDER is None: 

762 if OTEL_PYTHON_METER_PROVIDER not in environ.keys(): 

763 return _PROXY_METER_PROVIDER 

764 

765 meter_provider: MeterProvider = _load_provider( # type: ignore 

766 OTEL_PYTHON_METER_PROVIDER, "meter_provider" 

767 ) 

768 _set_meter_provider(meter_provider, log=False) 

769 

770 # _METER_PROVIDER will have been set by one thread 

771 return cast("MeterProvider", _METER_PROVIDER)