Skip to content

Commit ca9221f

Browse files
committed
Re-add src/Instrumentation/ReactHttp directory
1 parent 1d7c89d commit ca9221f

19 files changed

+1225
-0
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
* text=auto
2+
3+
*.md diff=markdown
4+
*.php diff=php
5+
6+
/.gitattributes export-ignore
7+
/.gitignore export-ignore
8+
/.phan export-ignore
9+
/.php-cs-fixer.php export-ignore
10+
/examples export-ignore
11+
/phpstan.neon.dist export-ignore
12+
/phpunit.xml.dist export-ignore
13+
/psalm.xml.dist export-ignore
14+
/tests export-ignore
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/vendor/

src/Instrumentation/ReactHttp/.phan/config.php

Lines changed: 371 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
$finder = PhpCsFixer\Finder::create()
3+
->exclude('vendor')
4+
->exclude('var/cache')
5+
->in(__DIR__);
6+
7+
$config = new PhpCsFixer\Config();
8+
return $config->setRules([
9+
'concat_space' => ['spacing' => 'one'],
10+
'declare_equal_normalize' => ['space' => 'none'],
11+
'is_null' => true,
12+
'modernize_types_casting' => true,
13+
'ordered_imports' => true,
14+
'php_unit_construct' => true,
15+
'single_line_comment_style' => true,
16+
'yoda_style' => false,
17+
'@PSR2' => true,
18+
'array_syntax' => ['syntax' => 'short'],
19+
'blank_line_after_opening_tag' => true,
20+
'blank_line_before_statement' => true,
21+
'cast_spaces' => true,
22+
'declare_strict_types' => true,
23+
'type_declaration_spaces' => true,
24+
'include' => true,
25+
'lowercase_cast' => true,
26+
'new_with_parentheses' => true,
27+
'no_extra_blank_lines' => true,
28+
'no_leading_import_slash' => true,
29+
'echo_tag_syntax' => true,
30+
'no_unused_imports' => true,
31+
'no_useless_else' => true,
32+
'no_useless_return' => true,
33+
'phpdoc_order' => true,
34+
'phpdoc_scalar' => true,
35+
'phpdoc_types' => true,
36+
'short_scalar_cast' => true,
37+
'blank_lines_before_namespace' => true,
38+
'single_quote' => true,
39+
'trailing_comma_in_multiline' => true,
40+
])
41+
->setRiskyAllowed(true)
42+
->setFinder($finder);
43+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# OpenTelemetry ReactPHP HTTP Browser auto-instrumentation
2+
3+
Please read https://opentelemetry.io/docs/instrumentation/php/automatic/ for instructions on how to
4+
install and configure the extension and SDK.
5+
6+
## Overview
7+
8+
Auto-instrumentation hooks are registered via composer, which will:
9+
10+
* create spans automatically for each React HTTP request that is sent
11+
* add a `traceparent` header to the request to facilitate distributed tracing
12+
13+
## Configuration
14+
15+
The extension can be disabled via [runtime configuration](https://opentelemetry.io/docs/instrumentation/php/sdk/#configuration):
16+
17+
```shell
18+
OTEL_PHP_DISABLED_INSTRUMENTATIONS=react-http
19+
```
20+
21+
Request and/or response headers can be added as span attributes by adding to the `php.ini`:
22+
23+
```ini
24+
otel.instrumentation.http.request_headers[]="Accept"
25+
otel.instrumentation.http.response_headers[]="Content-Type"
26+
```
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use OpenTelemetry\Contrib\Instrumentation\ReactHttp\ReactHttpInstrumentation;
6+
use OpenTelemetry\SDK\Sdk;
7+
8+
if (class_exists(Sdk::class) && Sdk::isInstrumentationDisabled(ReactHttpInstrumentation::NAME) === true) {
9+
return;
10+
}
11+
12+
if (extension_loaded('opentelemetry') === false) {
13+
trigger_error('The opentelemetry extension must be loaded in order to autoload the OpenTelemetry ReactPHP HTTP auto-instrumentation', E_USER_WARNING);
14+
15+
return;
16+
}
17+
18+
ReactHttpInstrumentation::register();
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"name": "open-telemetry/opentelemetry-auto-react-http",
3+
"description": "OpenTelemetry auto-instrumentation for ReactPHP's HTTP Browser.",
4+
"keywords": ["opentelemetry", "otel", "open-telemetry", "tracing", "reactphp", "async", "instrumentation"],
5+
"type": "library",
6+
"homepage": "https://opentelemetry.io/docs/php",
7+
"readme": "./README.md",
8+
"license": "Apache-2.0",
9+
"minimum-stability": "dev",
10+
"prefer-stable": true,
11+
"require": {
12+
"php": "^8.1",
13+
"ext-opentelemetry": "*",
14+
"open-telemetry/api": "^1.0",
15+
"open-telemetry/sem-conv": "^1.32",
16+
"react/http": "^1.11.0"
17+
},
18+
"require-dev": {
19+
"friendsofphp/php-cs-fixer": "^3",
20+
"nyholm/psr7": "*",
21+
"phan/phan": "^5.0",
22+
"phpstan/phpstan": "^1.1",
23+
"phpstan/phpstan-phpunit": "^1.0",
24+
"psalm/plugin-phpunit": "^0.19.2",
25+
"open-telemetry/sdk": "^1.0",
26+
"phpunit/phpunit": "^9.5",
27+
"react/async": "^4.3.0",
28+
"vimeo/psalm": "6.4.0"
29+
},
30+
"autoload": {
31+
"psr-4": {
32+
"OpenTelemetry\\Contrib\\Instrumentation\\ReactHttp\\": "src/"
33+
},
34+
"files": [
35+
"_register.php"
36+
]
37+
},
38+
"autoload-dev": {
39+
"psr-4": {
40+
"OpenTelemetry\\Tests\\Instrumentation\\ReactHttp\\": "tests/"
41+
}
42+
},
43+
"config": {
44+
"allow-plugins": {
45+
"php-http/discovery": false
46+
}
47+
}
48+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# OpenTelemetry ReactPHP HTTP Browser Examples
2+
3+
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/).
4+
5+
- [http_only.php](http_only.php) - a “pure” example using only the ReactPHP HTTP library.
6+
- [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.
7+
- [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.
8+
- [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.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use OpenTelemetry\API\Common\Time\Clock;
6+
use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator;
7+
use OpenTelemetry\SDK\Common\Export\Stream\StreamTransportFactory;
8+
use OpenTelemetry\SDK\Resource\ResourceInfoFactory;
9+
use OpenTelemetry\SDK\Sdk;
10+
use OpenTelemetry\SDK\Trace\Sampler\AlwaysOnSampler;
11+
use OpenTelemetry\SDK\Trace\SpanExporter\ConsoleSpanExporter;
12+
use OpenTelemetry\SDK\Trace\SpanProcessor\BatchSpanProcessor;
13+
use OpenTelemetry\SDK\Trace\TracerProvider;
14+
use function React\Async\await;
15+
use React\Http\Browser;
16+
use React\Http\Message\Request;
17+
18+
require_once dirname(__DIR__) . '/vendor/autoload.php';
19+
20+
$transport = (new StreamTransportFactory())->create('php://output', 'application/json');
21+
$exporter = new ConsoleSpanExporter($transport);
22+
23+
$tracerProvider = new TracerProvider(
24+
new BatchSpanProcessor($exporter, Clock::getDefault()),
25+
new AlwaysOnSampler(),
26+
ResourceInfoFactory::emptyResource(),
27+
);
28+
29+
Sdk::builder()
30+
->setTracerProvider($tracerProvider)
31+
->setPropagator(TraceContextPropagator::getInstance())
32+
->setAutoShutdown(true)
33+
->buildAndRegisterGlobal();
34+
35+
$root = $tracerProvider->getTracer('react-http-demo')->spanBuilder('root')->startSpan();
36+
$rootScope = $root->activate();
37+
38+
try {
39+
$browser = new Browser();
40+
41+
$requests = [
42+
new Request('GET', 'https://postman-echo.com/get'),
43+
new Request('POST', 'https://postman-echo.com/post'),
44+
new Request('GET', 'https://httpbin.org/does-not-exist'),
45+
new Request('GET', 'https://httpbin.org/get'),
46+
new Request('PUT', 'localhost:2222/not-found'),
47+
];
48+
49+
foreach ($requests as $request) {
50+
try {
51+
$response = await($browser->request($request->getMethod(), $request->getUri()));
52+
echo sprintf('[%d] ', $response->getStatusCode()) . json_decode($response->getBody()->getContents())->url . PHP_EOL;
53+
} catch (Throwable $t) {
54+
var_dump($t->getMessage());
55+
}
56+
}
57+
} finally {
58+
$rootScope->detach();
59+
$root->end();
60+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use OpenTelemetry\API\Common\Time\Clock;
6+
use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator;
7+
use OpenTelemetry\Context\Context;
8+
use OpenTelemetry\SDK\Common\Export\Stream\StreamTransportFactory;
9+
use OpenTelemetry\SDK\Resource\ResourceInfoFactory;
10+
use OpenTelemetry\SDK\Sdk;
11+
use OpenTelemetry\SDK\Trace\Sampler\AlwaysOnSampler;
12+
use OpenTelemetry\SDK\Trace\SpanExporter\ConsoleSpanExporter;
13+
use OpenTelemetry\SDK\Trace\SpanProcessor\BatchSpanProcessor;
14+
use OpenTelemetry\SDK\Trace\TracerProvider;
15+
use function React\Async\async;
16+
use function React\Async\await;
17+
use React\EventLoop\Loop;
18+
use React\Http\Browser;
19+
use React\Http\Message\Request;
20+
21+
require_once dirname(__DIR__) . '/vendor/autoload.php';
22+
23+
$transport = (new StreamTransportFactory())->create('php://output', 'application/json');
24+
$exporter = new ConsoleSpanExporter($transport);
25+
26+
$tracerProvider = new TracerProvider(
27+
new BatchSpanProcessor($exporter, Clock::getDefault()),
28+
new AlwaysOnSampler(),
29+
ResourceInfoFactory::emptyResource(),
30+
);
31+
32+
Sdk::builder()
33+
->setTracerProvider($tracerProvider)
34+
->setPropagator(TraceContextPropagator::getInstance())
35+
->setAutoShutdown(true)
36+
->buildAndRegisterGlobal();
37+
38+
$context = Context::getCurrent();
39+
40+
$root = $tracerProvider->getTracer('react-http-demo')->spanBuilder('root')->startSpan();
41+
42+
Loop::futureTick(async(static function () use ($context, $root) {
43+
$contextScope = $context->activate();
44+
$rootScope = $root->activate();
45+
46+
try {
47+
$browser = new Browser();
48+
49+
$requests = [
50+
new Request('GET', 'https://postman-echo.com/get'),
51+
new Request('POST', 'https://postman-echo.com/post'),
52+
new Request('GET', 'https://httpbin.org/does-not-exist'),
53+
new Request('GET', 'https://httpbin.org/get'),
54+
new Request('PUT', 'localhost:2222/not-found'),
55+
];
56+
57+
foreach ($requests as $request) {
58+
try {
59+
$response = await($browser->request($request->getMethod(), $request->getUri()));
60+
echo sprintf('[%d] ', $response->getStatusCode()) . json_decode($response->getBody()->getContents())->url . PHP_EOL;
61+
} catch (Throwable $t) {
62+
var_dump($t->getMessage());
63+
}
64+
}
65+
} finally {
66+
$rootScope->detach();
67+
$root->end();
68+
$contextScope->detach();
69+
}
70+
}));

0 commit comments

Comments
 (0)