fixes and tests for MetricExporterFactory (#926)

Fixing grpc path and allowing compression from MetricExporerFactory
This commit is contained in:
Brett McBride 2023-02-16 10:00:14 +11:00 committed by GitHub
parent 19b9326751
commit 2a0a9e33e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 211 additions and 24 deletions

View File

@ -6,16 +6,26 @@ namespace OpenTelemetry\Contrib\Otlp;
use OpenTelemetry\API\Common\Signal\Signals;
use OpenTelemetry\SDK\Common\Configuration\Configuration;
use OpenTelemetry\SDK\Common\Configuration\KnownValues;
use OpenTelemetry\SDK\Common\Configuration\Defaults;
use OpenTelemetry\SDK\Common\Configuration\Variables;
use OpenTelemetry\SDK\Common\Export\TransportFactoryInterface;
use OpenTelemetry\SDK\Common\Export\TransportInterface;
use OpenTelemetry\SDK\Common\Otlp\HttpEndpointResolver;
use OpenTelemetry\SDK\Metrics\MetricExporterFactoryInterface;
use OpenTelemetry\SDK\Metrics\MetricExporterInterface;
use OpenTelemetry\SDK\Registry;
use UnexpectedValueException;
class MetricExporterFactory implements MetricExporterFactoryInterface
{
private const DEFAULT_COMPRESSION = 'none';
private ?TransportFactoryInterface $transportFactory;
public function __construct(?TransportFactoryInterface $transportFactory = null)
{
$this->transportFactory = $transportFactory;
}
/**
* @psalm-suppress ArgumentTypeCoercion
*/
@ -28,6 +38,9 @@ class MetricExporterFactory implements MetricExporterFactoryInterface
return new MetricExporter($this->buildTransport($protocol));
}
/**
* @psalm-suppress UndefinedClass
*/
private function buildTransport(string $protocol): TransportInterface
{
/**
@ -35,32 +48,44 @@ class MetricExporterFactory implements MetricExporterFactoryInterface
* - OTEL_METRIC_EXPORT_INTERVAL
* - OTEL_METRIC_EXPORT_TIMEOUT
*/
$endpoint = Configuration::has(Variables::OTEL_EXPORTER_OTLP_METRICS_ENDPOINT)
? Configuration::getString(Variables::OTEL_EXPORTER_OTLP_METRICS_ENDPOINT)
: Configuration::getString(Variables::OTEL_EXPORTER_OTLP_ENDPOINT);
$endpoint = $this->getEndpoint($protocol);
$headers = Configuration::has(Variables::OTEL_EXPORTER_OTLP_METRICS_HEADERS)
? Configuration::getMap(Variables::OTEL_EXPORTER_OTLP_METRICS_HEADERS)
: Configuration::getMap(Variables::OTEL_EXPORTER_OTLP_HEADERS);
$headers += OtlpUtil::getUserAgentHeader();
$compression = $this->getCompression();
$factory = Registry::transportFactory($protocol);
switch ($protocol) {
case KnownValues::VALUE_GRPC:
return $factory->create(
$endpoint . OtlpUtil::method(Signals::METRICS),
ContentTypes::PROTOBUF,
$headers
);
case KnownValues::VALUE_HTTP_PROTOBUF:
case KnownValues::VALUE_HTTP_JSON:
return $factory->create(
$endpoint,
Protocols::contentType($protocol),
$headers
);
default:
throw new UnexpectedValueException('Unknown otlp protocol: ' . $protocol);
$factoryClass = Registry::transportFactory($protocol);
$factory = $this->transportFactory ?: new $factoryClass();
return $factory->create(
$endpoint,
Protocols::contentType($protocol),
$headers,
$compression,
);
}
private function getCompression(): string
{
return Configuration::has(Variables::OTEL_EXPORTER_OTLP_METRICS_COMPRESSION) ?
Configuration::getEnum(Variables::OTEL_EXPORTER_OTLP_METRICS_COMPRESSION) :
Configuration::getEnum(Variables::OTEL_EXPORTER_OTLP_COMPRESSION, self::DEFAULT_COMPRESSION);
}
private function getEndpoint(string $protocol): string
{
if (Configuration::has(Variables::OTEL_EXPORTER_OTLP_METRICS_ENDPOINT)) {
return Configuration::getString(Variables::OTEL_EXPORTER_OTLP_METRICS_ENDPOINT);
}
$endpoint = Configuration::has(Variables::OTEL_EXPORTER_OTLP_ENDPOINT)
? Configuration::getString(Variables::OTEL_EXPORTER_OTLP_ENDPOINT)
: Defaults::OTEL_EXPORTER_OTLP_ENDPOINT;
if ($protocol === Protocols::GRPC) {
return $endpoint . OtlpUtil::method(Signals::METRICS);
}
return HttpEndpointResolver::create()->resolveToString($endpoint, Signals::METRICS);
}
}

View File

@ -0,0 +1,162 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\Tests\Unit\Contrib\Otlp;
use AssertWell\PHPUnitGlobalState\EnvironmentVariables;
use OpenTelemetry\Contrib\Otlp\MetricExporterFactory;
use OpenTelemetry\SDK\Common\Configuration\KnownValues;
use OpenTelemetry\SDK\Common\Configuration\Variables;
use OpenTelemetry\SDK\Common\Export\TransportFactoryInterface;
use OpenTelemetry\SDK\Common\Export\TransportInterface;
use PHPUnit\Framework\TestCase;
/**
* @covers \OpenTelemetry\Contrib\Otlp\MetricExporterFactory
* @psalm-suppress UndefinedInterfaceMethod
*/
class MetricExporterFactoryTest extends TestCase
{
use EnvironmentVariables;
private TransportFactoryInterface $transportFactory;
private TransportInterface $transport;
public function setUp(): void
{
$this->transportFactory = $this->createMock(TransportFactoryInterface::class);
$this->transport = $this->createMock(TransportInterface::class);
}
public function tearDown(): void
{
$this->restoreEnvironmentVariables();
}
public function test_unknown_protocol_exception(): void
{
$this->expectException(\RuntimeException::class);
$this->setEnvironmentVariable(Variables::OTEL_EXPORTER_OTLP_PROTOCOL, 'foo');
$factory = new MetricExporterFactory();
$factory->create();
}
/**
* @dataProvider configProvider
*/
public function test_create(array $env, string $endpoint, string $protocol, string $compression, array $headerKeys = []): void
{
foreach ($env as $k => $v) {
$this->setEnvironmentVariable($k, $v);
}
$factory = new MetricExporterFactory($this->transportFactory);
// @phpstan-ignore-next-line
$this->transportFactory
->expects($this->once())
->method('create')
->with(
$this->equalTo($endpoint),
$this->equalTo($protocol),
$this->callback(function ($headers) use ($headerKeys) {
$this->assertEqualsCanonicalizing($headerKeys, array_keys($headers));
return true;
}),
$this->equalTo($compression)
)
->willReturn($this->transport);
// @phpstan-ignore-next-line
$this->transport->method('contentType')->willReturn($protocol);
$factory->create();
}
public function configProvider(): array
{
$defaultHeaderKeys = ['User-Agent'];
return [
'signal-specific endpoint unchanged' => [
'env' => [
Variables::OTEL_EXPORTER_OTLP_METRICS_PROTOCOL => KnownValues::VALUE_GRPC,
Variables::OTEL_EXPORTER_OTLP_METRICS_ENDPOINT => 'http://collector:4317/foo/bar', //should not be changed, per spec
],
'endpoint' => 'http://collector:4317/foo/bar',
'protocol' => 'application/x-protobuf',
'compression' => 'none',
'headerKeys' => $defaultHeaderKeys,
],
'endpoint has path appended' => [
'env' => [
Variables::OTEL_EXPORTER_OTLP_METRICS_PROTOCOL => KnownValues::VALUE_GRPC,
Variables::OTEL_EXPORTER_OTLP_ENDPOINT => 'http://collector:4317',
],
'endpoint' => 'http://collector:4317/opentelemetry.proto.collector.metrics.v1.MetricsService/Export',
'protocol' => 'application/x-protobuf',
'compression' => 'none',
'headerKeys' => $defaultHeaderKeys,
],
'protocol' => [
'env' => [
Variables::OTEL_EXPORTER_OTLP_PROTOCOL => KnownValues::VALUE_HTTP_NDJSON,
],
'endpoint' => 'http://localhost:4318/v1/metrics',
'protocol' => 'application/x-ndjson',
'compression' => 'none',
'headerKeys' => $defaultHeaderKeys,
],
'signal-specific protocol' => [
'env' => [
Variables::OTEL_EXPORTER_OTLP_PROTOCOL => KnownValues::VALUE_HTTP_JSON,
],
'endpoint' => 'http://localhost:4318/v1/metrics',
'protocol' => 'application/json',
'compression' => 'none',
'headerKeys' => $defaultHeaderKeys,
],
'defaults' => [
'env' => [],
'endpoint' => 'http://localhost:4318/v1/metrics',
'protocol' => 'application/x-protobuf',
'compression' => 'none',
'headerKeys' => $defaultHeaderKeys,
],
'compression' => [
'env' => [
Variables::OTEL_EXPORTER_OTLP_COMPRESSION => 'gzip',
],
'endpoint' => 'http://localhost:4318/v1/metrics',
'protocol' => 'application/x-protobuf',
'compression' => 'gzip',
'headerKeys' => $defaultHeaderKeys,
],
'signal-specific compression' => [
'env' => [
Variables::OTEL_EXPORTER_OTLP_METRICS_COMPRESSION => 'gzip',
],
'endpoint' => 'http://localhost:4318/v1/metrics',
'protocol' => 'application/x-protobuf',
'compression' => 'gzip',
'headerKeys' => $defaultHeaderKeys,
],
'headers' => [
'env' => [
Variables::OTEL_EXPORTER_OTLP_HEADERS => 'key1=foo,key2=bar',
],
'endpoint' => 'http://localhost:4318/v1/metrics',
'protocol' => 'application/x-protobuf',
'compression' => 'none',
'headerKeys' => array_merge($defaultHeaderKeys, ['key1', 'key2']),
],
'signal-specific headers' => [
'env' => [
Variables::OTEL_EXPORTER_OTLP_METRICS_HEADERS => 'key3=foo,key4=bar',
],
'endpoint' => 'http://localhost:4318/v1/metrics',
'protocol' => 'application/x-protobuf',
'compression' => 'none',
'headerKeys' => array_merge($defaultHeaderKeys, ['key3', 'key4']),
],
];
}
}

View File

@ -80,9 +80,9 @@ class SpanExporterFactoryTest extends TestCase
'signal-specific endpoint unchanged' => [
'env' => [
Variables::OTEL_EXPORTER_OTLP_TRACES_PROTOCOL => KnownValues::VALUE_GRPC,
Variables::OTEL_EXPORTER_OTLP_TRACES_ENDPOINT => 'http://collector:4317', //should not be changed, per spec
Variables::OTEL_EXPORTER_OTLP_TRACES_ENDPOINT => 'http://collector:4317/foo/bar', //should not be changed, per spec
],
'endpoint' => 'http://collector:4317',
'endpoint' => 'http://collector:4317/foo/bar',
'protocol' => 'application/x-protobuf',
'compression' => 'none',
'headerKeys' => $defaultHeaderKeys,