Skip to content

Commit 82be09b

Browse files
committed
feat(sdk): add custom sampler registry support
Add ability to register custom sampler factories via the Registry, allowing users to extend the SDK with custom sampling strategies. Implementation: - Add SamplerFactoryInterface for sampler factory contracts - Add factory classes for all built-in samplers (AlwaysOn, AlwaysOff, TraceIdRatioBased, ParentBased variants) - Add Registry::registerSamplerFactory() and Registry::samplerFactory() - Refactor SamplerFactory to use the registry instead of hardcoded logic - Add _register.php to auto-register built-in sampler factories Tests: - Add unit tests for all sampler factory classes - Add registry tests for sampler factory registration - Add tests for custom sampler registration with clobber behavior
1 parent 6957241 commit 82be09b

21 files changed

Lines changed: 420 additions & 23 deletions

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
"src/SDK/Metrics/MetricExporter/_register.php",
7575
"src/SDK/Propagation/_register.php",
7676
"src/SDK/Trace/SpanExporter/_register.php",
77+
"src/SDK/Trace/Sampler/_register.php",
7778
"src/SDK/Common/Dev/Compatibility/_load.php",
7879
"src/SDK/Common/Util/functions.php",
7980
"src/SDK/_autoload.php"

phpstan.neon.dist

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,11 @@ parameters:
6262
message: "#^Caught class .* not found\\.#"
6363
paths:
6464
- src/Config/SDK/Configuration/Environment/Adapter/
65+
-
66+
message: "#Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeParentInterface::.+\\(\\)#"
67+
paths:
68+
- src/Config/SDK
69+
-
70+
message: "#PHPDoc tag @extends contains generic type Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\.*NodeDefinition<.*> but class .* is not generic#"
71+
paths:
72+
- src/Config/SDK

src/SDK/Registry.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use OpenTelemetry\SDK\Logs\LogRecordExporterFactoryInterface;
1111
use OpenTelemetry\SDK\Metrics\MetricExporterFactoryInterface;
1212
use OpenTelemetry\SDK\Resource\ResourceDetectorInterface;
13+
use OpenTelemetry\SDK\Trace\Sampler\SamplerFactoryInterface;
1314
use OpenTelemetry\SDK\Trace\SpanExporter\SpanExporterFactoryInterface;
1415
use RuntimeException;
1516
use TypeError;
@@ -28,6 +29,7 @@ class Registry
2829
private static array $logRecordExporterFactories = [];
2930
private static array $resourceDetectors = [];
3031
private static array $responsePropagators = [];
32+
private static array $samplerFactories = [];
3133

3234
/**
3335
* @param TransportFactoryInterface|class-string<TransportFactoryInterface> $factory
@@ -134,6 +136,27 @@ public static function registerResponsePropagator(string $name, ResponsePropagat
134136
self::$responsePropagators[$name] = $responsePropagator;
135137
}
136138

139+
/**
140+
* @param SamplerFactoryInterface|class-string<SamplerFactoryInterface> $factory
141+
* @throws TypeError
142+
*/
143+
public static function registerSamplerFactory(string $sampler, SamplerFactoryInterface|string $factory, bool $clobber = false): void
144+
{
145+
if (!$clobber && array_key_exists($sampler, self::$samplerFactories)) {
146+
return;
147+
}
148+
if (!is_subclass_of($factory, SamplerFactoryInterface::class)) {
149+
throw new TypeError(
150+
sprintf(
151+
'Cannot register sampler factory: %s must exist and implement %s',
152+
is_string($factory) ? $factory : $factory::class,
153+
SamplerFactoryInterface::class
154+
)
155+
);
156+
}
157+
self::$samplerFactories[$sampler] = $factory;
158+
}
159+
137160
public static function spanExporterFactory(string $exporter): SpanExporterFactoryInterface
138161
{
139162
if (!array_key_exists($exporter, self::$spanExporterFactories)) {
@@ -221,4 +244,16 @@ public static function resourceDetectors(): array
221244
{
222245
return array_values(self::$resourceDetectors);
223246
}
247+
248+
public static function samplerFactory(string $sampler): SamplerFactoryInterface
249+
{
250+
if (!array_key_exists($sampler, self::$samplerFactories)) {
251+
throw new RuntimeException('Sampler factory not registered for: ' . $sampler);
252+
}
253+
$class = self::$samplerFactories[$sampler];
254+
$factory = (is_callable($class)) ? $class : new $class();
255+
assert($factory instanceof SamplerFactoryInterface);
256+
257+
return $factory;
258+
}
224259
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\SDK\Trace\Sampler;
6+
7+
use OpenTelemetry\SDK\Trace\SamplerInterface;
8+
9+
class AlwaysOffSamplerFactory implements SamplerFactoryInterface
10+
{
11+
#[\Override]
12+
public function create(): SamplerInterface
13+
{
14+
return new AlwaysOffSampler();
15+
}
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\SDK\Trace\Sampler;
6+
7+
use OpenTelemetry\SDK\Trace\SamplerInterface;
8+
9+
class AlwaysOnSamplerFactory implements SamplerFactoryInterface
10+
{
11+
#[\Override]
12+
public function create(): SamplerInterface
13+
{
14+
return new AlwaysOnSampler();
15+
}
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\SDK\Trace\Sampler;
6+
7+
use OpenTelemetry\SDK\Trace\SamplerInterface;
8+
9+
class ParentBasedAlwaysOffSamplerFactory implements SamplerFactoryInterface
10+
{
11+
#[\Override]
12+
public function create(): SamplerInterface
13+
{
14+
return new ParentBased(new AlwaysOffSampler());
15+
}
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\SDK\Trace\Sampler;
6+
7+
use OpenTelemetry\SDK\Trace\SamplerInterface;
8+
9+
class ParentBasedAlwaysOnSamplerFactory implements SamplerFactoryInterface
10+
{
11+
#[\Override]
12+
public function create(): SamplerInterface
13+
{
14+
return new ParentBased(new AlwaysOnSampler());
15+
}
16+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\SDK\Trace\Sampler;
6+
7+
use OpenTelemetry\SDK\Common\Configuration\Configuration;
8+
use OpenTelemetry\SDK\Common\Configuration\Variables;
9+
use OpenTelemetry\SDK\Trace\SamplerInterface;
10+
11+
class ParentBasedTraceIdRatioSamplerFactory implements SamplerFactoryInterface
12+
{
13+
#[\Override]
14+
public function create(): SamplerInterface
15+
{
16+
$ratio = Configuration::getRatio(Variables::OTEL_TRACES_SAMPLER_ARG);
17+
18+
return new ParentBased(new TraceIdRatioBasedSampler($ratio));
19+
}
20+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\SDK\Trace\Sampler;
6+
7+
use OpenTelemetry\SDK\Trace\SamplerInterface;
8+
9+
interface SamplerFactoryInterface
10+
{
11+
public function create(): SamplerInterface;
12+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\SDK\Trace\Sampler;
6+
7+
use OpenTelemetry\SDK\Common\Configuration\Configuration;
8+
use OpenTelemetry\SDK\Common\Configuration\Variables;
9+
use OpenTelemetry\SDK\Trace\SamplerInterface;
10+
11+
class TraceIdRatioBasedSamplerFactory implements SamplerFactoryInterface
12+
{
13+
#[\Override]
14+
public function create(): SamplerInterface
15+
{
16+
$ratio = Configuration::getRatio(Variables::OTEL_TRACES_SAMPLER_ARG);
17+
18+
return new TraceIdRatioBasedSampler($ratio);
19+
}
20+
}

0 commit comments

Comments
 (0)