Skip to content

Commit

Permalink
Add support for multi-instrument callbacks (#1202)
Browse files Browse the repository at this point in the history
* Add support for multi-instrument callbacks

https://opentelemetry.io/docs/specs/otel/metrics/api/#multiple-instrument-callbacks

* Fix undefined offset on detach of batch callback with repeated instrument

* Unify BatchObservableCallback and ObservableCallback

* Add missing typehint

* Do not bump API dependency

Not required, SDK will continue to work with lower API versions, only `::batchObserve()` won't be usable.
  • Loading branch information
Nevay authored Jan 10, 2024
1 parent 82c0f8e commit 596d4a2
Show file tree
Hide file tree
Showing 30 changed files with 614 additions and 140 deletions.
12 changes: 12 additions & 0 deletions src/API/Metrics/AsynchronousInstrument.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\API\Metrics;

/**
* Marker interface for asynchronous instruments.
*/
interface AsynchronousInstrument
{
}
2 changes: 1 addition & 1 deletion src/API/Metrics/CounterInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use OpenTelemetry\Context\ContextInterface;

interface CounterInterface
interface CounterInterface extends SynchronousInstrument
{

/**
Expand Down
2 changes: 1 addition & 1 deletion src/API/Metrics/HistogramInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use OpenTelemetry\Context\ContextInterface;

interface HistogramInterface
interface HistogramInterface extends SynchronousInstrument
{

/**
Expand Down
34 changes: 34 additions & 0 deletions src/API/Metrics/MeterInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,40 @@
interface MeterInterface
{

/**
* Reports measurements for multiple asynchronous instrument from a single callback.
*
* The callback receives an {@link ObserverInterface} for each instrument. All provided
* instruments have to be created by this meter.
*
* ```php
* $callback = $meter->batchObserve(
* function(
* ObserverInterface $usageObserver,
* ObserverInterface $pressureObserver,
* ): void {
* [$usage, $pressure] = expensive_system_call();
* $usageObserver->observe($usage);
* $pressureObserver->observe($pressure);
* },
* $meter->createObservableCounter('usage', description: 'count of items used'),
* $meter->createObservableGauge('pressure', description: 'force per unit area'),
* );
* ```
*
* @param callable $callback function responsible for reporting the measurements
* @param AsynchronousInstrument $instrument first instrument to report measurements for
* @param AsynchronousInstrument ...$instruments additional instruments to report measurements for
* @return ObservableCallbackInterface token to detach callback
*
* @see https://opentelemetry.io/docs/specs/otel/metrics/api/#multiple-instrument-callbacks
*/
public function batchObserve(
callable $callback,
AsynchronousInstrument $instrument,
AsynchronousInstrument ...$instruments
): ObservableCallbackInterface;

/**
* Creates a `Counter`.
*
Expand Down
7 changes: 7 additions & 0 deletions src/API/Metrics/Noop/NoopMeter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,23 @@

namespace OpenTelemetry\API\Metrics\Noop;

use OpenTelemetry\API\Metrics\AsynchronousInstrument;
use OpenTelemetry\API\Metrics\CounterInterface;
use OpenTelemetry\API\Metrics\HistogramInterface;
use OpenTelemetry\API\Metrics\MeterInterface;
use OpenTelemetry\API\Metrics\ObservableCallbackInterface;
use OpenTelemetry\API\Metrics\ObservableCounterInterface;
use OpenTelemetry\API\Metrics\ObservableGaugeInterface;
use OpenTelemetry\API\Metrics\ObservableUpDownCounterInterface;
use OpenTelemetry\API\Metrics\UpDownCounterInterface;

final class NoopMeter implements MeterInterface
{
public function batchObserve(callable $callback, AsynchronousInstrument $instrument, AsynchronousInstrument ...$instruments): ObservableCallbackInterface
{
return new NoopObservableCallback();
}

public function createCounter(string $name, ?string $unit = null, ?string $description = null, array $advisory = []): CounterInterface
{
return new NoopCounter();
Expand Down
2 changes: 1 addition & 1 deletion src/API/Metrics/ObservableCounterInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace OpenTelemetry\API\Metrics;

interface ObservableCounterInterface
interface ObservableCounterInterface extends AsynchronousInstrument
{

/**
Expand Down
2 changes: 1 addition & 1 deletion src/API/Metrics/ObservableGaugeInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace OpenTelemetry\API\Metrics;

interface ObservableGaugeInterface
interface ObservableGaugeInterface extends AsynchronousInstrument
{

/**
Expand Down
2 changes: 1 addition & 1 deletion src/API/Metrics/ObservableUpDownCounterInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace OpenTelemetry\API\Metrics;

interface ObservableUpDownCounterInterface
interface ObservableUpDownCounterInterface extends AsynchronousInstrument
{

/**
Expand Down
12 changes: 12 additions & 0 deletions src/API/Metrics/SynchronousInstrument.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\API\Metrics;

/**
* Marker interface for synchronous instruments.
*/
interface SynchronousInstrument
{
}
2 changes: 1 addition & 1 deletion src/API/Metrics/UpDownCounterInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use OpenTelemetry\Context\ContextInterface;

interface UpDownCounterInterface
interface UpDownCounterInterface extends SynchronousInstrument
{

/**
Expand Down
2 changes: 1 addition & 1 deletion src/API/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"symfony/polyfill-php82": "^1.26"
},
"conflict": {
"open-telemetry/sdk": "<=1.0.1"
"open-telemetry/sdk": "<=1.0.4"
},
"autoload": {
"psr-4": {
Expand Down
43 changes: 43 additions & 0 deletions src/SDK/Metrics/AsynchronousInstruments.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\SDK\Metrics;

use ArrayAccess;
use OpenTelemetry\API\Metrics\ObservableCallbackInterface;
use function OpenTelemetry\SDK\Common\Util\closure;
use function OpenTelemetry\SDK\Common\Util\weaken;
use OpenTelemetry\SDK\Metrics\MetricRegistry\MetricWriterInterface;

/**
* @internal
*/
final class AsynchronousInstruments
{
/**
* @param ArrayAccess<object, ObservableCallbackDestructor> $destructors
* @param non-empty-list<Instrument> $instruments
*/
public static function observe(
MetricWriterInterface $writer,
ArrayAccess $destructors,
callable $callback,
array $instruments,
ReferenceCounterInterface $referenceCounter
): ObservableCallbackInterface {
$target = null;
$callback = weaken(closure($callback), $target);

$callbackId = $writer->registerCallback($callback, ...$instruments);
$referenceCounter->acquire();

$destructor = null;
if ($target) {
$destructor = $destructors[$target] ??= new ObservableCallbackDestructor($destructors, $writer);
$destructor->callbackIds[$callbackId] = $referenceCounter;
}

return new ObservableCallback($writer, $referenceCounter, $callbackId, $destructor, $target);
}
}
26 changes: 2 additions & 24 deletions src/SDK/Metrics/Counter.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,11 @@
namespace OpenTelemetry\SDK\Metrics;

use OpenTelemetry\API\Metrics\CounterInterface;
use OpenTelemetry\SDK\Metrics\MetricRegistry\MetricWriterInterface;

/**
* @internal
*/
final class Counter implements CounterInterface
final class Counter implements CounterInterface, InstrumentHandle
{
private MetricWriterInterface $writer;
private Instrument $instrument;
private ReferenceCounterInterface $referenceCounter;

public function __construct(MetricWriterInterface $writer, Instrument $instrument, ReferenceCounterInterface $referenceCounter)
{
$this->writer = $writer;
$this->instrument = $instrument;
$this->referenceCounter = $referenceCounter;

$this->referenceCounter->acquire();
}

public function __destruct()
{
$this->referenceCounter->release();
}

public function add($amount, iterable $attributes = [], $context = null): void
{
$this->writer->record($this->instrument, $amount, $attributes, $context);
}
use SynchronousInstrumentTrait { write as add; }
}
26 changes: 2 additions & 24 deletions src/SDK/Metrics/Histogram.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,11 @@
namespace OpenTelemetry\SDK\Metrics;

use OpenTelemetry\API\Metrics\HistogramInterface;
use OpenTelemetry\SDK\Metrics\MetricRegistry\MetricWriterInterface;

/**
* @internal
*/
final class Histogram implements HistogramInterface
final class Histogram implements HistogramInterface, InstrumentHandle
{
private MetricWriterInterface $writer;
private Instrument $instrument;
private ReferenceCounterInterface $referenceCounter;

public function __construct(MetricWriterInterface $writer, Instrument $instrument, ReferenceCounterInterface $referenceCounter)
{
$this->writer = $writer;
$this->instrument = $instrument;
$this->referenceCounter = $referenceCounter;

$this->referenceCounter->acquire();
}

public function __destruct()
{
$this->referenceCounter->release();
}

public function record($amount, iterable $attributes = [], $context = null): void
{
$this->writer->record($this->instrument, $amount, $attributes, $context);
}
use SynchronousInstrumentTrait { write as record; }
}
13 changes: 13 additions & 0 deletions src/SDK/Metrics/InstrumentHandle.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\SDK\Metrics;

/**
* @internal
*/
interface InstrumentHandle
{
public function getHandle(): Instrument;
}
Loading

0 comments on commit 596d4a2

Please sign in to comment.