Skip to content

Commit

Permalink
Span Refactor (#422)
Browse files Browse the repository at this point in the history
* First pass at updating Span API

* First pass at updated Span implementation

* Update SDK Span return types to `ReadWriteSpan`

Drop support for PHP 7.3

* Finish initial implementation

* Implement `SpanData`

* Implement `StatusData`

* Re-implement and rename `NoopSpan` to `NonRecordingSpan`

* Updates to support `SpanData` in exporters/span processors

* Add timestamp conversion helper methods

Update Exporters to use `SpanData`
Start updating Exporter tests
Implement non immutable `SpanData` test helper class

* Finish updating exporters/converters

* Fix next batch of tests

* First pass at `Tracer` refactor

Leverage a `SpanBuilder` to create the span
Use a dedicated object to share state

* Just use `NonRecordingSpan` constructor versus `create`

Start of adding tests for new Tracer implementation

* Remove some unneeded `max`

Make `Sampler` the 2nd arg to `TraceProvider`
Fix Integration tests

* Add `NoopSpanProcessor`

Continue updating/adding tests

* Install mockery

Ensure span processor is called with expected contexts/spans

* Update `SpanData` to directly return dropped amounts

Make `SpanProcessor#onStart` take a `ReadWriteSpan`
Update empty span fallback name

* Revamp `Clock` interface/implementation

* Add some additional doc comments

Continued work on updating test coverage

* Add more `Span` tests

* Get tests to a passing state

* Fix type errors

* Make CI happy

* Apply limits within `Span` constructor and `SpanBuilder` logic

Add test coverage for these cases
  • Loading branch information
Blacksmoke16 authored Sep 29, 2021
1 parent 0be91c2 commit 6cffcbe
Show file tree
Hide file tree
Showing 112 changed files with 4,159 additions and 5,136 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
strategy:
matrix:
operating-system: [ubuntu-latest]
php-versions: ['7.3', '7.4', '8.0']
php-versions: ['7.4', '8.0']

steps:
- uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion .phan/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
//
// Note that the **only** effect of choosing `'5.6'` is to infer that functions removed in php 7.0 exist.
// (See `backward_compatibility_checks` for additional options)
'target_php_version' => '7.3',
'target_php_version' => '7.4',

// If enabled, missing properties will be created when
// they are first seen. If false, we'll report an
Expand Down
10 changes: 3 additions & 7 deletions api/Trace/Attributes.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,12 @@

interface Attributes extends \IteratorAggregate, \Countable
{
/**
* Setting event should not invalidate nor change any existing iterators.
* @param string $name
* @param mixed $value
* @return Attributes
*/
public function setAttribute(string $name, $value): Attributes;
public function getAttribute(string $name): ?Attribute;
public function get(string $name);

public function count(): int;
public function getIterator(): AttributesIterator;

public function getTotalAddedValues(): int;
public function getDroppedAttributesCount(): int;
}
15 changes: 10 additions & 5 deletions api/Trace/Clock.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@

interface Clock
{
public const NANOS_PER_SECOND = 1000000000;

/**
* A combination of the Monotonic and Realtime Clocks
* Monotonic time value in the first slot, as it'll get accessed more frequently in duration calculations.
*
* @return array{int, int}
* Returns the current epoch wall-clock timestamp in nanoseconds.
* This timestamp should _ONLY_ be used to compute a current time.
* Use {@see Clock::nanoTime} for calculating durations.
*/
public function moment(): array;
public function now(): int;

/**
* Returns the current epoch monotonic timestamp in nanoseconds that can only be used to calculate elapsed time.
*/
public function nanoTime(): int;
}
1 change: 0 additions & 1 deletion api/Trace/Events.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,5 @@ interface Events extends \IteratorAggregate, \Countable
*/
public function addEvent(string $name, ?Attributes $attributes = null, ?int $timestamp = null): Events;

public function count(): int;
public function getIterator(): EventsIterator;
}
1 change: 0 additions & 1 deletion api/Trace/Links.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

interface Links extends \IteratorAggregate, \Countable
{
public function count(): int;
public function getIterator(): LinksIterator;

/**
Expand Down
28 changes: 0 additions & 28 deletions api/Trace/MonotonicClock.php

This file was deleted.

28 changes: 0 additions & 28 deletions api/Trace/RealtimeClock.php

This file was deleted.

95 changes: 64 additions & 31 deletions api/Trace/Span.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,62 +4,95 @@

namespace OpenTelemetry\Trace;

use OpenTelemetry\Context\Context;
use OpenTelemetry\Context\ImplicitContextKeyed;
use Throwable;

/**
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#span-operations
*/
interface Span extends SpanStatus, SpanKind, ImplicitContextKeyed
interface Span extends ImplicitContextKeyed
{
/**
* Attributes SHOULD preserve the order in which they're set. Setting an attribute with the same key as an existing
* attribute SHOULD overwrite the existing attribute's value.
* @param string $key
* @param bool|int|float|string|array $value Note: the array MUST be homogeneous, i.e. it MUST NOT contain values
* of different types.
* @return Span Must return $this to allow setting multiple attributes at once in a chain
* Returns the {@see Span} from the provided *$context*,
* falling back on {@see Span::getInvalid()} if there is no span in the provided context.
*
* @todo Implement this in the API layer
*/
public function setAttribute(string $key, $value): Span;
public static function fromContext(Context $context): Span;

/**
* @param string $name
* @param int $timestamp
* @param Attributes|null $attributes
* @return Span Must return $this to allow setting multiple attributes at once in a chain.
* Returns the current {@see Span} from the current {@see Context},
* falling back on {@see Span::getEmpty()} if there is no span in the current context.
*
* @todo Implement this in the API layer
*/
public function addEvent(string $name, int $timestamp, ?Attributes $attributes = null): Span;
public static function getCurrent(): Span;

/**
* Returns an invalid {@see Span} that is used when tracing is disabled, such s when there is no available SDK.
*/
public static function getInvalid(): Span;

/**
* Returns a non-recording {@see Span} that hold the provided *$spanContext* but has no functionality.
* It will not be exported and al tracing operations are no-op, but can be used to propagate a valid {@see SpanContext} downstream.
*
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#wrapping-a-spancontext-in-a-span
*
* @param Throwable $exception
* @return Span Must return $this to allow setting multiple attributes at once in a chain.
* @todo Implement this in the API layer
*/
public function recordException(Throwable $exception, ?Attributes $attributes = null): Span;
public static function wrap(SpanContext $spanContext): Span;

/**
* Calling this method is highly discouraged; the name should be set on creation and left alone.
* @param string $name
* @return Span Must return $this
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#get-context
*/
public function updateName(string $name): Span;
public function getContext(): SpanContext;

/**
* Sets the Status of the Span. If used, this will override the default Span status, which is OK.
* Only the value of the last call will be recorded, and implementations are free to ignore previous calls.
* @param string $code
* @param string|null $description
* @return Span Must return $this
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#isrecording
*/
public function setSpanStatus(string $code, ?string $description = null): Span;
public function isRecording(): bool;

/**
* @param int|null $timestamp
* @return Span Must return $this
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#set-attributes
*
* @param non-empty-string $key
* @param bool|int|float|string|array|null $value Note: the array MUST be homogeneous, i.e. it MUST NOT contain values of different types.
*/
public function end(int $timestamp = null): Span;
public function setAttribute(string $key, $value): Span;

public function getContext(): SpanContext;
/**
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#set-attributes
*/
public function setAttributes(Attributes $attributes): Span;

public function isRecording(): bool;
/**
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#add-events
*/
public function addEvent(string $name, ?Attributes $attributes = null, int $timestamp = null): Span;

/**
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#record-exception
*/
public function recordException(Throwable $exception, Attributes $attributes = null): Span;

/**
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#updatename
*
* @param non-empty-string $name
*/
public function updateName(string $name): Span;

/**
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#set-status
*
* @psalm-param StatusCode::STATUS_* $code
*/
public function setStatus(string $code, string $description = null): Span;

/**
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#end
*/
public function end(int $endEpochNanos = null): void;
}
57 changes: 57 additions & 0 deletions api/Trace/SpanBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\Trace;

use OpenTelemetry\Context\Context;

/**
* Obtained from a {@see Tracer} and used to construct a {@see Span}.
*
* NOTE: A span builder may only be used to construct a single span.
* Calling {@see SpanBuilder::startSpan} multiple times will lead to undefined behavior.
*/
interface SpanBuilder
{
/**
* Sets the parent {@see Context} to use.
*
* If no {@see Span} is available in the provided context, the resulting span will become a root span,
* as if {@see SpanBuilder::setNoParent} was called.
*
* Defaults to {@see Context::getCurrent} when {@see SpanBuilder::startSpan} was called if not explicitly set.
*/
public function setParent(Context $parentContext): SpanBuilder;

/**
* Makes the to be created {@see Span} a root span of a new trace.
*/
public function setNoParent(): SpanBuilder;
public function addLink(SpanContext $context, Attributes $attributes = null): SpanBuilder;
public function setAttribute(string $key, $value): SpanBuilder;
public function setAttributes(Attributes $attributes): SpanBuilder;

/**
* Sets an explicit start timestamp for the newly created {@see Span}.
* The provided *$timestamp* is assumed to be in nanoseconds.
*
* Defaults to the timestamp when {@see SpanBuilder::startSpan} was called if not explicitly set.
*/
public function setStartTimestamp(int $timestamp): SpanBuilder;

/**
* @psalm-param SpanKind::KIND_* $spanKind
*/
public function setSpanKind(int $spanKind): SpanBuilder;

/**
* Starts and returns a new {@see Span}.
*
* The user _MUST_ manually end the span by calling {@see Span::end}.
*
* This method does _NOT_ automatically install the span into the current context.
* The user is responsible for calling {@see Span::activate} when they wish to do so.
*/
public function startSpan(): Span;
}
18 changes: 17 additions & 1 deletion api/Trace/SpanContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,27 @@

namespace OpenTelemetry\Trace;

/**
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#spancontext
*/
interface SpanContext
{
const TRACE_FLAG_SAMPLED = 1;
public const TRACE_FLAG_SAMPLED = 0x01;
public const TRACE_FLAG_DEFAULT = 0x00;

/** @todo Implement this in the API layer */
public static function createFromRemoteParent(string $traceId, string $spanId, int $traceFlags = self::TRACE_FLAG_DEFAULT, ?TraceState $traceState = null): SpanContext;

/** @todo Implement this in the API layer */
public static function getInvalid(): SpanContext;

/** @todo Implement this in the API layer */
public static function create(string $traceId, string $spanId, int $traceFlags = self::TRACE_FLAG_DEFAULT, ?TraceState $traceState = null): SpanContext;

/** @psalm-mutation-free */
public function getTraceId(): string;

/** @psalm-mutation-free */
public function getSpanId(): string;
public function getTraceFlags(): int;
public function getTraceState(): ?TraceState;
Expand Down
23 changes: 14 additions & 9 deletions api/Trace/SpanKind.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,26 @@
/**
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#spankind
*/
interface SpanKind
final class SpanKind
{
public const KIND_INTERNAL = 0;
public const KIND_CLIENT = 1;
public const KIND_SERVER = 2;
public const KIND_PRODUCER = 3;
public const KIND_CONSUMER = 4;

public const TYPES = [
self::KIND_INTERNAL,
self::KIND_CLIENT,
self::KIND_SERVER,
self::KIND_PRODUCER,
self::KIND_CONSUMER,
];
public static function getChoices(): array
{
return [
self::KIND_INTERNAL,
self::KIND_CLIENT,
self::KIND_SERVER,
self::KIND_PRODUCER,
self::KIND_CONSUMER,
];
}

public function getSpanKind(): int;
private function __construct()
{
}
}
Loading

0 comments on commit 6cffcbe

Please sign in to comment.