Skip to content

Commit

Permalink
Add ServerTiming response propagator
Browse files Browse the repository at this point in the history
  • Loading branch information
cedricziel committed Nov 23, 2023
1 parent f3e21f4 commit 2ddf2a5
Show file tree
Hide file tree
Showing 22 changed files with 762 additions and 2 deletions.
12 changes: 12 additions & 0 deletions src/Propagation/ResponsePropagator/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
* text=auto

*.md diff=markdown
*.php diff=php

/.gitattributes export-ignore
/.gitignore export-ignore
/.php-cs-fixer.php export-ignore
/phpstan.neon.dist export-ignore
/phpunit.xml.dist export-ignore
/psalm.xml.dist export-ignore
/tests export-ignore
1 change: 1 addition & 0 deletions src/Propagation/ResponsePropagator/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/vendor/
43 changes: 43 additions & 0 deletions src/Propagation/ResponsePropagator/.php-cs-fixer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php
$finder = PhpCsFixer\Finder::create()
->exclude('vendor')
->exclude('var/cache')
->in(__DIR__);

$config = new PhpCsFixer\Config();
return $config->setRules([
'concat_space' => ['spacing' => 'one'],
'declare_equal_normalize' => ['space' => 'none'],
'is_null' => true,
'modernize_types_casting' => true,
'ordered_imports' => true,
'php_unit_construct' => true,
'single_line_comment_style' => true,
'yoda_style' => false,
'@PSR2' => true,
'array_syntax' => ['syntax' => 'short'],
'blank_line_after_opening_tag' => true,
'blank_line_before_statement' => true,
'cast_spaces' => true,
'declare_strict_types' => true,
'function_typehint_space' => true,
'include' => true,
'lowercase_cast' => true,
'new_with_braces' => true,
'no_extra_blank_lines' => true,
'no_leading_import_slash' => true,
'echo_tag_syntax' => true,
'no_unused_imports' => true,
'no_useless_else' => true,
'no_useless_return' => true,
'phpdoc_order' => true,
'phpdoc_scalar' => true,
'phpdoc_types' => true,
'short_scalar_cast' => true,
'single_blank_line_before_namespace' => true,
'single_quote' => true,
'trailing_comma_in_multiline' => true,
])
->setRiskyAllowed(true)
->setFinder($finder);

65 changes: 65 additions & 0 deletions src/Propagation/ResponsePropagator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
[![Releases](https://img.shields.io/badge/releases-purple)](https://github.com/opentelemetry-php/contrib-propagator-traceresponse/releases)
[![Issues](https://img.shields.io/badge/issues-pink)](https://github.com/open-telemetry/opentelemetry-php/issues)
[![Source](https://img.shields.io/badge/source-contrib-green)](https://github.com/open-telemetry/opentelemetry-php-contrib/tree/main/src/Propagation/TraceResponse)
[![Mirror](https://img.shields.io/badge/mirror-opentelemetry--php--contrib-blue)](https://github.com/opentelemetry-php/contrib-propagator-traceresponse)
[![Latest Version](http://poser.pugx.org/open-telemetry/opentelemetry-propagation-traceresponse/v/unstable)](https://packagist.org/packages/open-telemetry/opentelemetry-propagation-traceresponse/)
[![Stable](http://poser.pugx.org/open-telemetry/opentelemetry-propagation-traceresponse/v/stable)](https://packagist.org/packages/open-telemetry/opentelemetry-propagation-traceresponse/)

This is a read-only subtree split of https://github.com/open-telemetry/opentelemetry-php-contrib.

# OpenTelemetry TraceResponse Propagator

**Note:** This package is experimental as `traceresponse` is currently an editors' draft.

This package provides a [Trace Context HTTP Response Headers Format](https://w3c.github.io/trace-context/#trace-context-http-response-headers-format)
propagator to inject the current span context into Response datastructures.

The main goal is to allow client-side technology (Real User Monitoring, HTTP Clients) to record
the server side context in order to allow referencing it.

## Requirements

* OpenTelemetry SDK and exporters (required to actually export traces)

Optional:
* OpenTelemetry extension (Some instrumentations can automatically use the `TraceResponsePropagator`)

## Usage

Assuming there is an active `SpanContext`, you can inject it into your response as follows:

```php
// your framework probably provides a datastructure to model HTTP responses
// and allows you to hook into the end of a request / listen to a matching event.
$response = new Response();

// get the current scope, bail out if none
$scope = Context::storage()->scope();
if (null === $scope) {
return;
}

// create a PropagationSetterInterface that knows how to inject response headers
$propagationSetter = new class implements OpenTelemetry\Context\Propagation\PropagationSetterInterface {
public function set(&$carrier, string $key, string $value) : void {
$carrier->headers->set($key, $value);
}
};
$propagator = new TraceResponseProgator();
$propagator->inject($response, $propagationSetter, $scope->context());
```

## Installation via composer

```bash
$ composer require open-telemetry/opentelemetry-propagation-traceresponse
```

## Installing dependencies and executing tests

From TraceResponse subdirectory:

```bash
$ composer install
$ ./vendor/bin/phpunit tests
```
39 changes: 39 additions & 0 deletions src/Propagation/ResponsePropagator/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "open-telemetry/opentelemetry-propagation-response",
"description": "OpenTelemetry response propagator api",
"keywords": ["opentelemetry", "otel", "open-telemetry", "propagator", "response"],
"type": "library",
"homepage": "https://opentelemetry.io/docs/php",
"readme": "./README.md",
"license": "Apache-2.0",
"minimum-stability": "dev",
"prefer-stable": true,
"require": {
"php": "^7.0|^8.0",
"open-telemetry/context": "^1.0"
},
"autoload": {
"psr-4": {
"OpenTelemetry\\Contrib\\Propagation\\Response\\": "src/"
}
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3",
"phan/phan": "^5.0",
"phpstan/phpstan": "^1.1",
"phpstan/phpstan-phpunit": "^1.0",
"psalm/plugin-phpunit": "^0.16",
"open-telemetry/sdk": "^1.0",
"phpunit/phpunit": "^9.5",
"vimeo/psalm": "^4.0",
"symfony/http-client": "^5.4|^6.0",
"guzzlehttp/promises": "^1.5",
"php-http/message-factory": "^1.0",
"nyholm/psr7": "^1.5"
},
"config": {
"allow-plugins": {
"php-http/discovery": true
}
}
}
9 changes: 9 additions & 0 deletions src/Propagation/ResponsePropagator/phpstan.neon.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
includes:
- vendor/phpstan/phpstan-phpunit/extension.neon

parameters:
tmpDir: var/cache/phpstan
level: 5
paths:
- src
- tests
44 changes: 44 additions & 0 deletions src/Propagation/ResponsePropagator/phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>

<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"
backupGlobals="false"
backupStaticAttributes="false"
cacheResult="false"
colors="false"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
forceCoversAnnotation="false"
processIsolation="false"
stopOnError="false"
stopOnFailure="false"
stopOnIncomplete="false"
stopOnSkipped="false"
stopOnRisky="false"
timeoutForSmallTests="1"
timeoutForMediumTests="10"
timeoutForLargeTests="60"
verbose="true">

<coverage processUncoveredFiles="true" disableCodeCoverageIgnore="false">
<include>
<directory>src</directory>
</include>
</coverage>

<php>
<ini name="date.timezone" value="UTC" />
<ini name="display_errors" value="On" />
<ini name="display_startup_errors" value="On" />
<ini name="error_reporting" value="E_ALL" />
</php>

<testsuites>
<testsuite name="unit">
<directory>tests/Unit</directory>
</testsuite>
</testsuites>

</phpunit>
15 changes: 15 additions & 0 deletions src/Propagation/ResponsePropagator/psalm.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0"?>
<psalm
errorLevel="3"
cacheDirectory="var/cache/psalm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd">
<projectFiles>
<directory name="src"/>
<directory name="tests"/>
</projectFiles>
<plugins>
<pluginClass class="Psalm\PhpUnitPlugin\Plugin"/>
</plugins>
</psalm>
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace OpenTelemetry\Contrib\Propagation\TraceResponse;
namespace OpenTelemetry\Contrib\Propagation\Response;

use OpenTelemetry\Context\ContextInterface;
use OpenTelemetry\Context\Propagation\PropagationSetterInterface;
Expand Down
125 changes: 125 additions & 0 deletions src/Propagation/ResponsePropagator/tests/Unit/PropagatorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\Tests\Propagation\Response\Unit;

use OpenTelemetry\API\Trace\SpanContext;
use OpenTelemetry\API\Trace\SpanContextInterface;
use OpenTelemetry\API\Trace\SpanContextValidator;
use OpenTelemetry\API\Trace\TraceFlags;
use OpenTelemetry\API\Trace\TraceState;
use OpenTelemetry\Context\Context;
use OpenTelemetry\Context\ContextInterface;
use OpenTelemetry\Contrib\Propagation\TraceResponse\TraceResponsePropagator as Propagator;
use OpenTelemetry\SDK\Trace\Span;
use PHPUnit\Framework\TestCase;

class PropagatorTest extends TestCase
{
private const TRACE_ID = '5759e988bd862e3fe1be46a994272793';
private const SPAN_ID = '53995c3f42cd8ad8';
private const TRACERESPONSE_HEADER_SAMPLED = '00-5759e988bd862e3fe1be46a994272793-53995c3f42cd8ad8-1';
private const TRACERESPONSE_HEADER_NOT_SAMPLED = '00-5759e988bd862e3fe1be46a994272793-53995c3f42cd8ad8-0';

/**
* @test
* fields() should return an array of fields with AWS X-Ray Trace ID Header
*/
public function test_fields()
{
$propagator = new Propagator();
$this->assertSame($propagator->fields(), [Propagator::TRACERESPONSE]);
}

/**
* @test
* Injects with a valid traceId, spanId, and is sampled
* restore(string $traceId, string $spanId, bool $sampled = false, bool $isRemote = false, ?API\TraceState $traceState = null): SpanContext
*/
public function test_inject_valid_sampled_trace_id()
{
$carrier = [];
(new Propagator())->inject(
$carrier,
null,
$this->withSpanContext(
SpanContext::create(self::TRACE_ID, self::SPAN_ID, TraceFlags::SAMPLED),
Context::getCurrent()
)
);

$this->assertSame(
[Propagator::TRACERESPONSE => self::TRACERESPONSE_HEADER_SAMPLED],
$carrier
);
}

/**
* @test
* Injects with a valid traceId, spanId, and is not sampled
*/
public function test_inject_valid_not_sampled_trace_id()
{
$carrier = [];
(new Propagator())->inject(
$carrier,
null,
$this->withSpanContext(
SpanContext::create(self::TRACE_ID, self::SPAN_ID),
Context::getCurrent()
)
);

$this->assertSame(
[Propagator::TRACERESPONSE => self::TRACERESPONSE_HEADER_NOT_SAMPLED],
$carrier
);
}

/**
* @test
* Test inject with tracestate - note: tracestate is not a part of traceresponse
*/
public function test_inject_trace_id_with_trace_state()
{
$carrier = [];
(new Propagator())->inject(
$carrier,
null,
$this->withSpanContext(
SpanContext::create(self::TRACE_ID, self::SPAN_ID, TraceFlags::SAMPLED, new TraceState('vendor1=opaqueValue1')),
Context::getCurrent()
)
);

$this->assertSame(
[Propagator::TRACERESPONSE => self::TRACERESPONSE_HEADER_SAMPLED],
$carrier
);
}

/**
* @test
* Test with an invalid spanContext, should return null
*/
public function test_inject_trace_id_with_invalid_span_context()
{
$carrier = [];
(new Propagator())->inject(
$carrier,
null,
$this->withSpanContext(
SpanContext::create(SpanContextValidator::INVALID_TRACE, SpanContextValidator::INVALID_SPAN, TraceFlags::SAMPLED, new TraceState('vendor1=opaqueValue1')),
Context::getCurrent()
)
);

$this->assertEmpty($carrier);
}

private function withSpanContext(SpanContextInterface $spanContext, ContextInterface $context): ContextInterface
{
return $context->withContextValue(Span::wrap($spanContext));
}
}
12 changes: 12 additions & 0 deletions src/Propagation/ServerTiming/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
* text=auto

*.md diff=markdown
*.php diff=php

/.gitattributes export-ignore
/.gitignore export-ignore
/.php-cs-fixer.php export-ignore
/phpstan.neon.dist export-ignore
/phpunit.xml.dist export-ignore
/psalm.xml.dist export-ignore
/tests export-ignore
1 change: 1 addition & 0 deletions src/Propagation/ServerTiming/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/vendor/
Loading

0 comments on commit 2ddf2a5

Please sign in to comment.