Skip to content

Commit

Permalink
adding otlp/file exporter (#1481)
Browse files Browse the repository at this point in the history
* otlp_file config support
* handle otlp_file scheme://path format
* update kitchen-sink
  • Loading branch information
brettmc authored Jan 22, 2025
1 parent a4119f9 commit 7eb3abb
Show file tree
Hide file tree
Showing 9 changed files with 459 additions and 43 deletions.
3 changes: 3 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SamplerParentBased",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SamplerTraceIdRatioBased",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterConsole",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterOtlpFile",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterOtlpGrpc",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterOtlpHttp",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterZipkin",
Expand All @@ -147,11 +148,13 @@

"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\AggregationResolverDefault",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricExporterConsole",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricExporterOtlpFile",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricExporterOtlpGrpc",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricExporterOtlpHttp",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricReaderPeriodic",

"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterConsole",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterOtlpFile",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterOtlpGrpc",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterOtlpHttp",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordProcessorBatch",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\Config\SDK\ComponentProvider\Logs;

use Nevay\SPI\ServiceProviderDependency\PackageDependency;
use OpenTelemetry\Config\SDK\ComponentProvider\OutputStreamParser;
use OpenTelemetry\Config\SDK\Configuration\ComponentProvider;
use OpenTelemetry\Config\SDK\Configuration\ComponentProviderRegistry;
use OpenTelemetry\Config\SDK\Configuration\Context;
use OpenTelemetry\Config\SDK\Configuration\Validation;
use OpenTelemetry\Contrib\Otlp\ContentTypes;
use OpenTelemetry\Contrib\Otlp\LogsExporter;
use OpenTelemetry\SDK\Logs\LogRecordExporterInterface;
use OpenTelemetry\SDK\Registry;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\NodeBuilder;

/**
* @implements ComponentProvider<LogRecordExporterInterface>
*/
#[PackageDependency('open-telemetry/exporter-otlp', '^1.0.5')]
final class LogRecordExporterOtlpFile implements ComponentProvider
{
/**
* @param array{
* output_stream: string,
* } $properties
*/
public function createPlugin(array $properties, Context $context): LogRecordExporterInterface
{
$endpoint = OutputStreamParser::parse($properties['output_stream']);

return new LogsExporter(Registry::transportFactory('stream')->create(
endpoint: $endpoint,
contentType: ContentTypes::NDJSON,
));
}

public function getConfig(ComponentProviderRegistry $registry, NodeBuilder $builder): ArrayNodeDefinition
{
$node = $builder->arrayNode('otlp_file');
$node
->children()
->scalarNode('output_stream')->defaultValue('stdout')->validate()->always(Validation::ensureString())->end()->end()
->end()
;

return $node;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\Config\SDK\ComponentProvider\Metrics;

use Nevay\SPI\ServiceProviderDependency\PackageDependency;
use OpenTelemetry\Config\SDK\ComponentProvider\OutputStreamParser;
use OpenTelemetry\Config\SDK\Configuration\ComponentProvider;
use OpenTelemetry\Config\SDK\Configuration\ComponentProviderRegistry;
use OpenTelemetry\Config\SDK\Configuration\Context;
use OpenTelemetry\Config\SDK\Configuration\Validation;
use OpenTelemetry\Contrib\Otlp\ContentTypes;
use OpenTelemetry\Contrib\Otlp\MetricExporter;
use OpenTelemetry\SDK\Metrics\Data\Temporality;
use OpenTelemetry\SDK\Metrics\MetricExporterInterface;
use OpenTelemetry\SDK\Registry;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\NodeBuilder;

/**
* @implements ComponentProvider<MetricExporterInterface>
*/
#[PackageDependency('open-telemetry/exporter-otlp', '^1.0.5')]
final class MetricExporterOtlpFile implements ComponentProvider
{
/**
* @param array{
* output_stream: string,
* temporality_preference: 'cumulative'|'delta'|'lowmemory',
* default_histogram_aggregation: 'explicit_bucket_histogram|base2_exponential_bucket_histogram',
* } $properties
*/
public function createPlugin(array $properties, Context $context): MetricExporterInterface
{
$endpoint = OutputStreamParser::parse($properties['output_stream']);

$temporality = match ($properties['temporality_preference']) {
'cumulative' => Temporality::CUMULATIVE,
'delta' => Temporality::DELTA,
'lowmemory' => null,
};

return new MetricExporter(Registry::transportFactory('stream')->create(
endpoint: $endpoint,
contentType: ContentTypes::NDJSON,
), $temporality);
}

public function getConfig(ComponentProviderRegistry $registry, NodeBuilder $builder): ArrayNodeDefinition
{
$node = $builder->arrayNode('otlp_file');
$node
->children()
->scalarNode('output_stream')->defaultValue('stdout')->validate()->always(Validation::ensureString())->end()->end()
->enumNode('temporality_preference')
->values(['cumulative', 'delta', 'lowmemory'])
->defaultValue('cumulative')
->end()
->enumNode('default_histogram_aggregation')
->values(['explicit_bucket_histogram', 'base2_exponential_bucket_histogram'])
->defaultValue('explicit_bucket_histogram')
->end()
->end()
;

return $node;
}
}
40 changes: 40 additions & 0 deletions src/Config/SDK/ComponentProvider/OutputStreamParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\Config\SDK\ComponentProvider;

/**
* @internal
*/
class OutputStreamParser
{
private static ?string $root = null;

public static function setRoot(string $root): void
{
self::$root = $root;
}

public static function reset(): void
{
self::$root = null;
}

public static function parse(string $outputStream): string
{
if ($outputStream === 'stdout') {
return 'php://stdout';
}

$pattern = '/^(?P<scheme>[a-zA-Z][a-zA-Z0-9]*):\/\/(?P<path>.+)$/';
if (preg_match($pattern, $outputStream, $matches) !== 1) {
throw new \InvalidArgumentException('Invalid output_stream format: ' . $outputStream);
}

return match ($matches['scheme']) {
'file' => self::$root . $matches['path'],
default => throw new \InvalidArgumentException('Invalid endpoint scheme: ' . $matches['scheme']),
};
}
}
52 changes: 52 additions & 0 deletions src/Config/SDK/ComponentProvider/Trace/SpanExporterOtlpFile.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\Config\SDK\ComponentProvider\Trace;

use Nevay\SPI\ServiceProviderDependency\PackageDependency;
use OpenTelemetry\Config\SDK\ComponentProvider\OutputStreamParser;
use OpenTelemetry\Config\SDK\Configuration\ComponentProvider;
use OpenTelemetry\Config\SDK\Configuration\ComponentProviderRegistry;
use OpenTelemetry\Config\SDK\Configuration\Context;
use OpenTelemetry\Config\SDK\Configuration\Validation;
use OpenTelemetry\Contrib\Otlp\ContentTypes;
use OpenTelemetry\Contrib\Otlp\SpanExporter;
use OpenTelemetry\SDK\Registry;
use OpenTelemetry\SDK\Trace\SpanExporterInterface;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\NodeBuilder;

/**
* @implements ComponentProvider<SpanExporterInterface>
*/
#[PackageDependency('open-telemetry/exporter-otlp', '^1.0.5')]
final class SpanExporterOtlpFile implements ComponentProvider
{
/**
* @param array{
* output_stream: string,
* } $properties
*/
public function createPlugin(array $properties, Context $context): SpanExporterInterface
{
$endpoint = OutputStreamParser::parse($properties['output_stream']);

return new SpanExporter(Registry::transportFactory('stream')->create(
endpoint: $endpoint,
contentType: ContentTypes::NDJSON,
));
}

public function getConfig(ComponentProviderRegistry $registry, NodeBuilder $builder): ArrayNodeDefinition
{
$node = $builder->arrayNode('otlp_file');
$node
->children()
->scalarNode('output_stream')->defaultValue('stdout')->validate()->always(Validation::ensureString())->end()->end()
->end()
;

return $node;
}
}
3 changes: 3 additions & 0 deletions src/Config/SDK/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SamplerParentBased",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SamplerTraceIdRatioBased",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterConsole",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterOtlpFile",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterOtlpGrpc",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterOtlpHttp",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Trace\\SpanExporterZipkin",
Expand All @@ -59,11 +60,13 @@

"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\AggregationResolverDefault",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricExporterConsole",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricExporterOtlpFile",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricExporterOtlpGrpc",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricExporterOtlpHttp",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Metrics\\MetricReaderPeriodic",

"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterConsole",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterOtlpFile",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterOtlpGrpc",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterOtlpHttp",
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordProcessorBatch",
Expand Down
15 changes: 15 additions & 0 deletions tests/Integration/Config/ConfigurationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,29 @@

namespace OpenTelemetry\Tests\Integration\Config;

use OpenTelemetry\Config\SDK\ComponentProvider\OutputStreamParser;
use OpenTelemetry\Config\SDK\Configuration;
use org\bovigo\vfs\vfsStream;
use PHPUnit\Framework\Attributes\CoversNothing;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

#[CoversNothing]
final class ConfigurationTest extends TestCase
{
public function setUp(): void
{
// set up mock file system with /var/log directory, for otlp_file exporter.
$root = vfsStream::setup('/', null, ['var' => ['log' => []]])->url();

OutputStreamParser::setRoot($root);
}

public function tearDown(): void
{
OutputStreamParser::reset();
}

#[DataProvider('openTelemetryConfigurationDataProvider')]
public function test_open_telemetry_configuration(string $file): void
{
Expand Down
Loading

0 comments on commit 7eb3abb

Please sign in to comment.