Skip to content

Commit

Permalink
Add Resource and InstrumentationLibrary
Browse files Browse the repository at this point in the history
  • Loading branch information
lalex committed Jun 5, 2020
1 parent aa8e52a commit b245af1
Show file tree
Hide file tree
Showing 8 changed files with 259 additions and 9 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"psr-4": {
"OpenTelemetry\\Trace\\": "api/Trace",
"OpenTelemetry\\Sdk\\Internal\\": "sdk/Internal",
"OpenTelemetry\\Sdk\\Trace\\": "sdk/Trace"
"OpenTelemetry\\Sdk\\Trace\\": "sdk/Trace",
"OpenTelemetry\\Sdk\\Resource\\": "sdk/Resource"
}
},
"autoload-dev": {
Expand Down
73 changes: 73 additions & 0 deletions sdk/Resource/ResourceConstants.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\Sdk\Resource;

/**
* For certain attribute groups if any attribute from the group is present in the Resource then all attributes
* that are marked as Required MUST be also present in the Resource. However it is also valid if the entire attribute
* group is omitted (i.e. none of the attributes from the particular group are present even though some of them are
* marked as Required in this document).
*
* @link https://github.com/open-telemetry/opentelemetry-specification/tree/master/specification/resource/semantic_conventions
*/
class ResourceConstants
{
/**
* Service
*/
public const SERVICE_NAME = 'service.name'; // required
public const SERVICE_NAMESPACE = 'service.namespace';
public const SERVICE_INSTANCE_ID = 'service.instance.id'; // required
public const SERVICE_VERSION = 'service.version';

/**
* Telemetry SDK
*/
public const TELEMETRY_SDK_NAME = 'telemetry.sdk.name';
public const TELEMETRY_SDK_LANGUAGE = 'telemetry.sdk.language';
public const TELEMETRY_SDK_VERSION = 'telemetry.sdk.version';

/**
* Container
*/
public const CONTAINER_NAME = 'container.name';
public const CONTAINER_IMAGE_NAME = 'container.image.name';
public const CONTAINER_IMAGE_TAG = 'container.image.tag';

/**
* Function as a Service
*/
public const FAAS_NAME = 'faas.name'; // required
public const FAAS_ID = 'faas.id'; // required
public const FAAS_VERSION = 'faas.version';
public const FAAS_INSTANCE = 'faas.instance';

/**
* Kubernetes
*/
public const K8S_CLUSTER_NAME = 'k8s.cluster.name';
public const K8S_NAMESPACE_NAME = 'k8s.namespace.name';
public const K8S_POD_NAME = 'k8s.pod.name';
public const K8S_DEPLOYMENT_NAME = 'k8s.deployment.name';

/**
* Host
*/
public const HOST_HOSTNAME = 'host.hostname';
public const HOST_ID = 'host.id';
public const HOST_NAME = 'host.name';
public const HOST_TYPE = 'host.type';
public const HOST_IMAGE_NAME = 'host.image.name';
public const HOST_IMAGE_ID = 'host.image.id';
public const HOST_IMAGE_VERSION = 'host.image.version';

/**
* Cloud
*/
public const CLOUD_PROVIDER = 'cloud.provider';
public const CLOUD_ACCOUNT_ID = 'cloud.account.id';
public const CLOUD_REGION = 'cloud.region';
public const CLOUD_ZONE = 'cloud.zone';
}
64 changes: 64 additions & 0 deletions sdk/Resource/ResourceInfo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\Sdk\Resource;

use OpenTelemetry\Sdk\Trace\Attributes;

/**
* A Resource is an immutable representation of the entity producing telemetry. For example, a process producing telemetry
* that is running in a container on Kubernetes has a Pod name, it is in a namespace and possibly is part of a Deployment
* which also has a name. All three of these attributes can be included in the Resource.
*
* The class named as ResourceInfo due to `resource` is the soft reserved word in PHP.
*/
class ResourceInfo
{
private $attributes = [];

private function __construct(Attributes $attributes)
{
$this->attributes = $attributes;
}

public static function create(Attributes $attributes): self
{
return new ResourceInfo(clone $attributes);
}

/**
* Merges two resources into a new one.
* Conflicts (i.e. a key for which attributes exist on both the primary and secondary resource) are handled as follows:
* - If the value on the primary resource is an empty string, the result has the value of the secondary resource.
* - Otherwise, the value of the primary resource is used.
*
* @param ResourceInfo $primary
* @param ResourceInfo $secondary
* @return ResourceInfo
*/
public static function merge(ResourceInfo $primary, ResourceInfo $secondary): self
{
// clone attributes from the primary resource
$mergedAttributes = clone $primary->getAttributes();

// merge attributes from the secondary resource
foreach ($secondary->getAttributes() as $name => $attribute) {
if (null === $mergedAttributes->getAttribute($name) || $mergedAttributes->getAttribute($name)->getValue() === '') {
$mergedAttributes->setAttribute($name, $attribute->getValue());
}
}

return new ResourceInfo($mergedAttributes);
}

public static function emptyResource(): self
{
return new ResourceInfo(new Attributes());
}

public function getAttributes(): Attributes
{
return $this->attributes;
}
}
34 changes: 34 additions & 0 deletions sdk/Trace/InstrumentationLibrary.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\Sdk\Trace;

class InstrumentationLibrary
{
private $name;

private $version;

public function __construct(string $name, ?string $version = '')
{
$this->name = $name;
$this->version = $version;
}

/**
* @return string
*/
public function getName(): string
{
return $this->name;
}

/**
* @return string|null
*/
public function getVersion(): ?string
{
return $this->version;
}
}
14 changes: 11 additions & 3 deletions sdk/Trace/Tracer.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,18 @@ class Tracer implements API\Tracer
*/
private $provider;

public function __construct(TracerProvider $provider, API\SpanContext $context = null)
{
$this->provider = $provider;
/**
* @var InstrumentationLibrary
*/
private $instrumentationLibrary;

public function __construct(
TracerProvider $provider,
InstrumentationLibrary $instrumentationLibrary,
API\SpanContext $context = null
) {
$this->provider = $provider;
$this->instrumentationLibrary = $instrumentationLibrary;
$context = $context ?: SpanContext::generate();

// todo: hold up, why do we automatically make a root Span?
Expand Down
17 changes: 15 additions & 2 deletions sdk/Trace/TracerProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace OpenTelemetry\Sdk\Trace;

use OpenTelemetry\Sdk\Resource\ResourceInfo;
use OpenTelemetry\Trace as API;

final class TracerProvider implements API\TracerProvider
Expand All @@ -19,9 +20,15 @@ final class TracerProvider implements API\TracerProvider
*/
protected $spanProcessors;

public function __construct()
/**
* @var ResourceInfo
*/
private $resource;

public function __construct(?ResourceInfo $resource = null)
{
$this->spanProcessors = new SpanMultiProcessor();
$this->resource = $resource ?? ResourceInfo::emptyResource();
}

public function getTracer(string $name, ?string $version = ''): API\Tracer
Expand All @@ -31,8 +38,9 @@ public function getTracer(string $name, ?string $version = ''): API\Tracer
}

$spanContext = SpanContext::generate();
$instrumentationLibrary = new InstrumentationLibrary($name, $version);

return $this->tracers[$name] = new Tracer($this, $spanContext);
return $this->tracers[$name] = new Tracer($this, $instrumentationLibrary, $spanContext);
}

public function addSpanProcessor(SpanProcessor $processor): self
Expand All @@ -46,4 +54,9 @@ public function getSpanProcessor(): SpanMultiProcessor
{
return $this->spanProcessors;
}

public function getResource(): ResourceInfo
{
return $this->resource;
}
}
56 changes: 56 additions & 0 deletions tests/unit/Resource/ResourceTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\Sdk\Tests;

use OpenTelemetry\Sdk\Resource\ResourceInfo;
use OpenTelemetry\Sdk\Trace\Attributes;
use PHPUnit\Framework\TestCase;

class ResourceTest extends TestCase
{
/**
* @test
*/
public function testEmptyResource()
{
$resource = ResourceInfo::emptyResource();
$this->assertEmpty($resource->getAttributes());
}

public function testGetAttributes()
{
$attributes = new Attributes();
$attributes->setAttribute('name', 'test');
$resource = ResourceInfo::create($attributes);

$this->assertEquals($attributes, $resource->getAttributes());
$this->assertEquals('test', $resource->getAttributes()->getAttribute('name')->getValue());
}

public function testMerge()
{
$primary = ResourceInfo::create(new Attributes(['name' => 'primary', 'empty' => '']));
$secondary = ResourceInfo::create(new Attributes(['version' => '1.0.0', 'empty' => 'value']));
$result = ResourceInfo::merge($primary, $secondary);

$this->assertCount(3, $result->getAttributes());
$this->assertEquals('primary', $result->getAttributes()->getAttribute('name')->getValue());
$this->assertEquals('1.0.0', $result->getAttributes()->getAttribute('version')->getValue());
$this->assertEquals('value', $result->getAttributes()->getAttribute('empty')->getValue());
}

public function testImmutableCreate()
{
$attributes = new Attributes();
$attributes->setAttribute('name', 'test');
$attributes->setAttribute('version', '1.0.0');

$resource = ResourceInfo::create($attributes);

$attributes->setAttribute('version', '2.0.0');

$this->assertEquals('1.0.0', $resource->getAttributes()->getAttribute('version')->getValue());
}
}
7 changes: 4 additions & 3 deletions tests/unit/Trace/TracingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@ public function testTracerSpanContextRestore()
{
// todo: stop making a new span when a trace is made and then use getTracer instead of new Tracer.
$tracerProvider = new SDK\TracerProvider();
$tracer = new Tracer($tracerProvider);
$instrumentationLibrary = new SDK\InstrumentationLibrary('OpenTelemetry.TracingTest.Test');
$tracer = new Tracer($tracerProvider, $instrumentationLibrary);
$spanContext = $tracer->getActiveSpan()->getContext();

$spanContext2 = SpanContext::restore($spanContext->getTraceId(), $spanContext->getSpanId());
$tracer2 = new Tracer($tracerProvider, $spanContext2);
$tracer2 = new Tracer($tracerProvider, $instrumentationLibrary, $spanContext2);

$this->assertSame($tracer->getActiveSpan()->getContext()->getTraceId(), $tracer2->getActiveSpan()->getContext()->getTraceId());
}
Expand All @@ -56,7 +57,7 @@ public function testSpanNameUpdate()
public function testNestedSpans()
{
$tracerProvider = new SDK\TracerProvider();
$tracer = new Tracer($tracerProvider);
$tracer = $tracerProvider->getTracer('OpenTelemetry.TracingTest');

$guard = $tracer->startAndActivateSpan('guard.validate');
$connection = $tracer->startAndActivateSpan('guard.validate.connection');
Expand Down

0 comments on commit b245af1

Please sign in to comment.