Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ jobs:
'Instrumentation/Psr15',
'Instrumentation/Psr16',
'Instrumentation/Psr18',
'Instrumentation/ReactHTTP',
'Instrumentation/Slim',
'Instrumentation/Symfony',
'Instrumentation/Yii',
Expand Down
2 changes: 2 additions & 0 deletions .gitsplit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ splits:
- prefix: "src/Instrumentation/Psr16"
target: "https://${GH_TOKEN}@github.com/opentelemetry-php/contrib-auto-psr16.git"
- prefix: "src/Instrumentation/Psr18"
target: "https://${GH_TOKEN}@github.com/opentelemetry-php/contrib-auto-react-http.git"
- prefix: "src/Instrumentation/ReactHTTP"
target: "https://${GH_TOKEN}@github.com/opentelemetry-php/contrib-auto-psr18.git"
- prefix: "src/Instrumentation/Slim"
target: "https://${GH_TOKEN}@github.com/opentelemetry-php/contrib-auto-slim.git"
Expand Down
14 changes: 14 additions & 0 deletions src/Instrumentation/ReactHTTP/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
* text=auto

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

/.gitattributes export-ignore
/.gitignore export-ignore
/.phan export-ignore
/.php-cs-fixer.php export-ignore
/examples 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/Instrumentation/ReactHTTP/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/vendor/
371 changes: 371 additions & 0 deletions src/Instrumentation/ReactHTTP/.phan/config.php

Large diffs are not rendered by default.

43 changes: 43 additions & 0 deletions src/Instrumentation/ReactHTTP/.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,
'type_declaration_spaces' => true,
'include' => true,
'lowercase_cast' => true,
'new_with_parentheses' => 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,
'blank_lines_before_namespace' => true,
'single_quote' => true,
'trailing_comma_in_multiline' => true,
])
->setRiskyAllowed(true)
->setFinder($finder);

26 changes: 26 additions & 0 deletions src/Instrumentation/ReactHTTP/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# OpenTelemetry ReactPHP HTTP Browser auto-instrumentation

Please read https://opentelemetry.io/docs/instrumentation/php/automatic/ for instructions on how to
install and configure the extension and SDK.

## Overview

Auto-instrumentation hooks are registered via composer, which will:

* create spans automatically for each React HTTP request that is sent
* add a `traceparent` header to the request to facilitate distributed tracing

## Configuration

The extension can be disabled via [runtime configuration](https://opentelemetry.io/docs/instrumentation/php/sdk/#configuration):

```shell
OTEL_PHP_DISABLED_INSTRUMENTATIONS=react-http
```

Request and/or response headers can be added as span attributes by adding to the `php.ini`:

```ini
otel.instrumentation.http.request_headers[]="Accept"
otel.instrumentation.http.response_headers[]="Content-Type"
```
18 changes: 18 additions & 0 deletions src/Instrumentation/ReactHTTP/_register.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

use OpenTelemetry\Contrib\Instrumentation\ReactHttp\ReactHttpInstrumentation;
use OpenTelemetry\SDK\Sdk;

if (class_exists(Sdk::class) && Sdk::isInstrumentationDisabled(ReactHttpInstrumentation::NAME) === true) {
return;
}

if (extension_loaded('opentelemetry') === false) {
trigger_error('The opentelemetry extension must be loaded in order to autoload the OpenTelemetry ReactPHP HTTP auto-instrumentation', E_USER_WARNING);

return;
}

ReactHttpInstrumentation::register();
48 changes: 48 additions & 0 deletions src/Instrumentation/ReactHTTP/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "open-telemetry/opentelemetry-auto-react-http",
"description": "OpenTelemetry auto-instrumentation for ReactPHP's HTTP Browser.",
"keywords": ["opentelemetry", "otel", "open-telemetry", "tracing", "reactphp", "async", "instrumentation"],
"type": "library",
"homepage": "https://opentelemetry.io/docs/php",
"readme": "./README.md",
"license": "Apache-2.0",
"minimum-stability": "dev",
"prefer-stable": true,
"require": {
"php": "^8.1",
"ext-opentelemetry": "*",
"open-telemetry/api": "^1.0",
"open-telemetry/sem-conv": "^1.32",
"react/http": "^1.11.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3",
"nyholm/psr7": "*",
"phan/phan": "^5.0",
"phpstan/phpstan": "^1.1",
"phpstan/phpstan-phpunit": "^1.0",
"psalm/plugin-phpunit": "^0.19.2",
"open-telemetry/sdk": "^1.0",
"phpunit/phpunit": "^9.5",
"react/async": "^4.3.0",
"vimeo/psalm": "6.4.0"
},
"autoload": {
"psr-4": {
"OpenTelemetry\\Contrib\\Instrumentation\\ReactHttp\\": "src/"
},
"files": [
"_register.php"
]
},
"autoload-dev": {
"psr-4": {
"OpenTelemetry\\Tests\\Instrumentation\\ReactHttp\\": "tests/"
}
},
"config": {
"allow-plugins": {
"php-http/discovery": false
}
}
}
8 changes: 8 additions & 0 deletions src/Instrumentation/ReactHTTP/examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# OpenTelemetry ReactPHP HTTP Browser Examples

These examples are all valid and working; they just show different ways of calling the ReactPHP HTTP Browser, following some of the examples in the [ReactPHP HTTP documentation](https://reactphp.org/http/).

- [http_only.php](http_only.php) - a “pure” example using only the ReactPHP HTTP library.
- [http_only_promises.php](http_only_promises.php) - same as above but with the [Promises](https://reactphp.org/http/#promises) handled in a separate loop.
- [http+async_blocking.php](http+async_blocking.php) - uses the [ReactPHP Async library](https://reactphp.org/async/) to achieve a more [traditional blocking](https://reactphp.org/http/#blocking) request.
- [http+async_nonblocking.php](http+async_nonblocking.php) - uses the [ReactPHP Async library](https://reactphp.org/async/) as intended, to make asynchronous HTTP requests while abstracting away the callables of Promises by taking advantage of PHP fibers.
60 changes: 60 additions & 0 deletions src/Instrumentation/ReactHTTP/examples/http+async_blocking.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

declare(strict_types=1);

use OpenTelemetry\API\Common\Time\Clock;
use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator;
use OpenTelemetry\SDK\Common\Export\Stream\StreamTransportFactory;
use OpenTelemetry\SDK\Resource\ResourceInfoFactory;
use OpenTelemetry\SDK\Sdk;
use OpenTelemetry\SDK\Trace\Sampler\AlwaysOnSampler;
use OpenTelemetry\SDK\Trace\SpanExporter\ConsoleSpanExporter;
use OpenTelemetry\SDK\Trace\SpanProcessor\BatchSpanProcessor;
use OpenTelemetry\SDK\Trace\TracerProvider;
use function React\Async\await;
use React\Http\Browser;
use React\Http\Message\Request;

require_once dirname(__DIR__) . '/vendor/autoload.php';

$transport = (new StreamTransportFactory())->create('php://output', 'application/json');
$exporter = new ConsoleSpanExporter($transport);

$tracerProvider = new TracerProvider(
new BatchSpanProcessor($exporter, Clock::getDefault()),
new AlwaysOnSampler(),
ResourceInfoFactory::emptyResource(),
);

Sdk::builder()
->setTracerProvider($tracerProvider)
->setPropagator(TraceContextPropagator::getInstance())
->setAutoShutdown(true)
->buildAndRegisterGlobal();

$root = $tracerProvider->getTracer('react-http-demo')->spanBuilder('root')->startSpan();
$rootScope = $root->activate();

try {
$browser = new Browser();

$requests = [
new Request('GET', 'https://postman-echo.com/get'),
new Request('POST', 'https://postman-echo.com/post'),
new Request('GET', 'https://httpbin.org/does-not-exist'),
new Request('GET', 'https://httpbin.org/get'),
new Request('PUT', 'localhost:2222/not-found'),
];

foreach ($requests as $request) {
try {
$response = await($browser->request($request->getMethod(), $request->getUri()));
echo sprintf('[%d] ', $response->getStatusCode()) . json_decode($response->getBody()->getContents())->url . PHP_EOL;
} catch (Throwable $t) {
var_dump($t->getMessage());
}
}
} finally {
$rootScope->detach();
$root->end();
}
69 changes: 69 additions & 0 deletions src/Instrumentation/ReactHTTP/examples/http+async_nonblocking.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

declare(strict_types=1);

use OpenTelemetry\API\Common\Time\Clock;
use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator;
use OpenTelemetry\Context\Context;
use OpenTelemetry\SDK\Common\Export\Stream\StreamTransportFactory;
use OpenTelemetry\SDK\Resource\ResourceInfoFactory;
use OpenTelemetry\SDK\Sdk;
use OpenTelemetry\SDK\Trace\Sampler\AlwaysOnSampler;
use OpenTelemetry\SDK\Trace\SpanExporter\ConsoleSpanExporter;
use OpenTelemetry\SDK\Trace\SpanProcessor\BatchSpanProcessor;
use OpenTelemetry\SDK\Trace\TracerProvider;
use function React\Async\async;
use function React\Async\await;
use React\EventLoop\Loop;
use React\Http\Browser;
use React\Http\Message\Request;

require_once dirname(__DIR__) . '/vendor/autoload.php';

$transport = (new StreamTransportFactory())->create('php://output', 'application/json');
$exporter = new ConsoleSpanExporter($transport);

$tracerProvider = new TracerProvider(
new BatchSpanProcessor($exporter, Clock::getDefault()),
new AlwaysOnSampler(),
ResourceInfoFactory::emptyResource(),
);

Sdk::builder()
->setTracerProvider($tracerProvider)
->setPropagator(TraceContextPropagator::getInstance())
->setAutoShutdown(true)
->buildAndRegisterGlobal();

$context = Context::getCurrent();

$root = $tracerProvider->getTracer('react-http-demo')->spanBuilder('root')->startSpan();

Loop::futureTick(async(static function () use ($context, $root) {
$contextScope = $context->activate();
$rootScope = $root->activate();
try {
$browser = new Browser();

$requests = [
new Request('GET', 'https://postman-echo.com/get'),
new Request('POST', 'https://postman-echo.com/post'),
new Request('GET', 'https://httpbin.org/does-not-exist'),
new Request('GET', 'https://httpbin.org/get'),
new Request('PUT', 'localhost:2222/not-found'),
];

foreach ($requests as $request) {
try {
$response = await($browser->request($request->getMethod(), $request->getUri()));
echo sprintf('[%d] ', $response->getStatusCode()) . json_decode($response->getBody()->getContents())->url . PHP_EOL;
} catch (Throwable $t) {
var_dump($t->getMessage());
}
}
} finally {
$rootScope->detach();
$root->end();
$contextScope->detach();
}
}));
64 changes: 64 additions & 0 deletions src/Instrumentation/ReactHTTP/examples/http_only.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

declare(strict_types=1);

use OpenTelemetry\API\Common\Time\Clock;
use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator;
use OpenTelemetry\SDK\Common\Export\Stream\StreamTransportFactory;
use OpenTelemetry\SDK\Resource\ResourceInfoFactory;
use OpenTelemetry\SDK\Sdk;
use OpenTelemetry\SDK\Trace\Sampler\AlwaysOnSampler;
use OpenTelemetry\SDK\Trace\SpanExporter\ConsoleSpanExporter;
use OpenTelemetry\SDK\Trace\SpanProcessor\BatchSpanProcessor;
use OpenTelemetry\SDK\Trace\TracerProvider;
use Psr\Http\Message\ResponseInterface;
use React\EventLoop\Loop;
use React\Http\Browser;
use React\Http\Message\Request;

require_once dirname(__DIR__) . '/vendor/autoload.php';

$transport = (new StreamTransportFactory())->create('php://output', 'application/json');
$exporter = new ConsoleSpanExporter($transport);

$tracerProvider = new TracerProvider(
new BatchSpanProcessor($exporter, Clock::getDefault()),
new AlwaysOnSampler(),
ResourceInfoFactory::emptyResource(),
);

Sdk::builder()
->setTracerProvider($tracerProvider)
->setPropagator(TraceContextPropagator::getInstance())
->setAutoShutdown(true)
->buildAndRegisterGlobal();

$root = $tracerProvider->getTracer('react-http-demo')->spanBuilder('root')->startSpan();

Loop::futureTick(static function () use ($root) {
$rootScope = $root->activate();
try {
$browser = new Browser();

$requests = [
new Request('GET', 'https://postman-echo.com/get'),
new Request('POST', 'https://postman-echo.com/post'),
new Request('GET', 'https://httpbin.org/does-not-exist'),
new Request('GET', 'https://httpbin.org/get'),
new Request('PUT', 'localhost:2222/not-found'),
];

foreach ($requests as $request) {
$browser
->request($request->getMethod(), $request->getUri())
->then(function (ResponseInterface $response) {
echo sprintf('[%d] ', $response->getStatusCode()) . json_decode($response->getBody()->getContents())->url . PHP_EOL;
}, function (Throwable $t) {
var_dump($t->getMessage());
});
}
} finally {
$rootScope->detach();
$root->end();
}
});
Loading
Loading