Skip to content

Commit 4e72ea1

Browse files
authored
Merge pull request #75 from carolabadeer/aws-sdk-instrumentation-support
Support for AWS SDK PHP Instrumentation
2 parents d1fceaa + d82f853 commit 4e72ea1

File tree

4 files changed

+188
-1
lines changed

4 files changed

+188
-1
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"ext-json": "*",
1717
"open-telemetry/opentelemetry": "^0.0.15",
1818
"php-http/discovery": "^1.14",
19+
"aws/aws-sdk-php": "^3.232",
1920
"php-http/message": "^1.12"
2021
},
2122
"replace": {

src/Instrumentation/AwsSdk/AwsSdkInstrumentation.php

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,17 @@
44

55
namespace OpenTelemetry\Instrumentation\AwsSdk;
66

7+
use Aws\Middleware;
8+
use Aws\ResultInterface;
79
use OpenTelemetry\API\Common\Instrumentation\InstrumentationInterface;
810
use OpenTelemetry\API\Common\Instrumentation\InstrumentationTrait;
11+
use OpenTelemetry\API\Trace\SpanInterface;
12+
use OpenTelemetry\API\Trace\SpanKind;
13+
use OpenTelemetry\API\Trace\TracerInterface;
14+
use OpenTelemetry\API\Trace\TracerProviderInterface;
15+
16+
use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface;
17+
use OpenTelemetry\Context\ScopeInterface;
918

1019
/**
1120
* @experimental
@@ -16,6 +25,14 @@ class AwsSdkInstrumentation implements InstrumentationInterface
1625

1726
public const NAME = 'AWS SDK Instrumentation';
1827
public const VERSION = '0.0.1';
28+
public const SPAN_KIND = SpanKind::KIND_CLIENT;
29+
private TextMapPropagatorInterface $propagator;
30+
private TracerProviderInterface $tracerProvider;
31+
private $clients = [] ;
32+
private string $clientName;
33+
private string $region;
34+
private SpanInterface $span;
35+
private ScopeInterface $scope;
1936

2037
public function getName(): string
2138
{
@@ -37,8 +54,81 @@ public function init(): bool
3754
return true;
3855
}
3956

57+
public function setPropagator(TextMapPropagatorInterface $propagator): void
58+
{
59+
$this->propagator = $propagator;
60+
}
61+
62+
public function getPropagator(): TextMapPropagatorInterface
63+
{
64+
return $this->propagator;
65+
}
66+
67+
public function setTracerProvider(TracerProviderInterface $tracerProvider): void
68+
{
69+
$this->tracerProvider = $tracerProvider;
70+
}
71+
72+
public function getTracerProvider(): TracerProviderInterface
73+
{
74+
return $this->tracerProvider;
75+
}
76+
77+
public function getTracer(): TracerInterface
78+
{
79+
return $this->tracerProvider->getTracer('io.opentelemetry.contrib.php');
80+
}
81+
82+
public function instrumentClients($clientsArray) : void
83+
{
84+
$this->clients = $clientsArray;
85+
}
86+
87+
/** @psalm-suppress ArgumentTypeCoercion */
4088
public function activate(): bool
4189
{
90+
try {
91+
$middleware = Middleware::tap(function ($cmd, $req) {
92+
$tracer = $this->getTracer();
93+
$propagator = $this->getPropagator();
94+
95+
$carrier = [];
96+
/** @phan-suppress-next-line PhanTypeMismatchArgument */
97+
$this->span = $tracer->spanBuilder($this->clientName)->setSpanKind(AwsSdkInstrumentation::SPAN_KIND)->startSpan(); //@phpstan-ignore-line
98+
$this->scope = $this->span->activate();
99+
100+
$propagator->inject($carrier);
101+
102+
$this->span->setAttributes([
103+
'rpc.method' => $cmd->getName(),
104+
'rpc.service' => $this->clientName,
105+
'rpc.system' => 'aws-api',
106+
'aws.region' => $this->region,
107+
]);
108+
});
109+
110+
$end_middleware = Middleware::mapResult(function (ResultInterface $result) {
111+
$this->span->setAttributes([
112+
'http.status_code' => $result['@metadata']['statusCode'],
113+
]);
114+
115+
$this->span->end();
116+
$this->scope->detach();
117+
118+
return $result;
119+
});
120+
121+
foreach ($this->clients as $client) {
122+
$this->clientName = $client->getApi()->getServiceName();
123+
$this->region = $client->getRegion();
124+
125+
$client->getHandlerList()->prependInit($middleware, 'instrumentation');
126+
$client->getHandlerList()->appendSign($end_middleware, 'end_instrumentation');
127+
}
128+
} catch (\Throwable $e) {
129+
return false;
130+
}
131+
42132
return true;
43133
}
44134
}

src/Instrumentation/AwsSdk/README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# AWS SDK Instrumentation for OpenTelemetry PHP
2+
This package supports manual instrumentation for the AWS SDK for PHP. For more information on how to use the AWS SDK, see the [AWS SDK for PHP Developer's Guide](https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/welcome.html).
3+
4+
5+
## Using the AWS SDK Instrumentation with AWS X-Ray
6+
7+
8+
```
9+
use OpenTelemetry\Instrumentation\AwsSdk\AwsSdkInstrumentation;
10+
11+
// Initialize Span Processor, X-Ray ID generator, Tracer Provider, and Propagator
12+
$spanProcessor = new SimpleSpanProcessor(new OTLPExporter());
13+
$xrayIdGenerator = new IdGenerator();
14+
$tracerProvider = new TracerProvider($spanProcessor, null, null, null, $xrayIdGenerator);
15+
$xrayPropagator = new Propagator();
16+
17+
// Create new instance of AWS SDK Instrumentation class
18+
$awssdkinstrumentation = new AwsSdkInstrumentation();
19+
20+
// Configure AWS SDK Instrumentation with Propagator and set Tracer Provider (created above)
21+
$awssdkinstrumentation->setPropagator($xrayPropagator);
22+
$awssdkinstrumentation->setTracerProvider($tracerProvider);
23+
24+
// Create and activate root span
25+
$root = $awssdkinstrumentation->getTracer()->spanBuilder('AwsSDKInstrumentation')->setSpanKind(SpanKind::KIND_SERVER)->startSpan();
26+
$rootScope = $root->activate();
27+
28+
// Initialize all AWS Client instances
29+
$s3Client = new S3Client([
30+
'region' => 'us-west-2',
31+
'version' => '2006-03-01',
32+
]);
33+
34+
// Pass client instances to AWS SDK
35+
$awssdkinstrumentation->instrumentClients([$s3Client]);
36+
37+
// Activate Instrumentation -- all AWS Client calls will be automatically instrumented
38+
$awssdkinstrumentation->activate();
39+
40+
// Make S3 client call
41+
$result = $s3Client->listBuckets();
42+
43+
// End the root span after all the calls to the AWS SDK have been made
44+
$root->end();
45+
$rootScope->detach();
46+
47+
```
48+
49+
## Useful Links and Resources
50+
For more information on how to use the AWS SDK for PHP with AWS X-Ray and using the [AWS Distro for OpenTelemetry](https://aws-otel.github.io/), please see the [aws-otel-php repository](https://github.com/aws-observability/aws-otel-php).

tests/Unit/Instrumentation/AwsSdk/AwsSdkInstrumentationTest.php

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,23 @@
44

55
namespace OpenTelemetry\Test\Unit\Instrumentation\AwsSdk;
66

7+
use DG\BypassFinals;
8+
use OpenTelemetry\API\Trace\TracerInterface;
9+
use OpenTelemetry\Aws\Xray\Propagator;
710
use OpenTelemetry\Instrumentation\AwsSdk\AwsSdkInstrumentation;
11+
use OpenTelemetry\SDK\Trace\TracerProviderInterface;
812
use PHPUnit\Framework\TestCase;
913

1014
class AwsSdkInstrumentationTest extends TestCase
1115
{
16+
private AwsSdkInstrumentation $awsSdkInstrumentation;
17+
18+
protected function setUp(): void
19+
{
20+
BypassFinals::enable();
21+
$this->awsSdkInstrumentation = new AwsSdkInstrumentation();
22+
}
23+
1224
public function testInstrumentationClassName()
1325
{
1426
$this->assertEquals(
@@ -37,10 +49,44 @@ public function testInstrumentationInit()
3749
);
3850
}
3951

52+
public function testGetXrayPropagator()
53+
{
54+
$propagator = new Propagator();
55+
$this->awsSdkInstrumentation->setPropagator($propagator);
56+
57+
$this->assertSame(
58+
$this->awsSdkInstrumentation->getPropagator(),
59+
$propagator
60+
);
61+
}
62+
63+
public function testGetTracerProvider()
64+
{
65+
$tracerProvider = $this->createMock(TracerProviderInterface::class);
66+
$this->awsSdkInstrumentation->setTracerProvider($tracerProvider);
67+
68+
$this->assertSame(
69+
$this->awsSdkInstrumentation->getTracerProvider(),
70+
$tracerProvider
71+
);
72+
}
73+
74+
public function testGetTracer()
75+
{
76+
$tracer = $this->createMock(TracerInterface::class);
77+
$tracerProvider = $this->createMock(TracerProviderInterface::class);
78+
$tracerProvider->expects($this->once())
79+
->method('getTracer')
80+
->willReturn($tracer);
81+
82+
$this->awsSdkInstrumentation->setTracerProvider($tracerProvider);
83+
$this->assertSame($tracer, $this->awsSdkInstrumentation->getTracer());
84+
}
85+
4086
public function testInstrumentationActivated()
4187
{
4288
$this->assertTrue(
43-
(new AwsSdkInstrumentation())->activate()
89+
($this->awsSdkInstrumentation)->activate()
4490
);
4591
}
4692
}

0 commit comments

Comments
 (0)