Skip to content

Commit

Permalink
Merge pull request #75 from carolabadeer/aws-sdk-instrumentation-support
Browse files Browse the repository at this point in the history
Support for AWS SDK PHP Instrumentation
  • Loading branch information
brettmc authored Sep 8, 2022
2 parents d1fceaa + d82f853 commit 4e72ea1
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 1 deletion.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"ext-json": "*",
"open-telemetry/opentelemetry": "^0.0.15",
"php-http/discovery": "^1.14",
"aws/aws-sdk-php": "^3.232",
"php-http/message": "^1.12"
},
"replace": {
Expand Down
90 changes: 90 additions & 0 deletions src/Instrumentation/AwsSdk/AwsSdkInstrumentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,17 @@

namespace OpenTelemetry\Instrumentation\AwsSdk;

use Aws\Middleware;
use Aws\ResultInterface;
use OpenTelemetry\API\Common\Instrumentation\InstrumentationInterface;
use OpenTelemetry\API\Common\Instrumentation\InstrumentationTrait;
use OpenTelemetry\API\Trace\SpanInterface;
use OpenTelemetry\API\Trace\SpanKind;
use OpenTelemetry\API\Trace\TracerInterface;
use OpenTelemetry\API\Trace\TracerProviderInterface;

use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface;
use OpenTelemetry\Context\ScopeInterface;

/**
* @experimental
Expand All @@ -16,6 +25,14 @@ class AwsSdkInstrumentation implements InstrumentationInterface

public const NAME = 'AWS SDK Instrumentation';
public const VERSION = '0.0.1';
public const SPAN_KIND = SpanKind::KIND_CLIENT;
private TextMapPropagatorInterface $propagator;
private TracerProviderInterface $tracerProvider;
private $clients = [] ;
private string $clientName;
private string $region;
private SpanInterface $span;
private ScopeInterface $scope;

public function getName(): string
{
Expand All @@ -37,8 +54,81 @@ public function init(): bool
return true;
}

public function setPropagator(TextMapPropagatorInterface $propagator): void
{
$this->propagator = $propagator;
}

public function getPropagator(): TextMapPropagatorInterface
{
return $this->propagator;
}

public function setTracerProvider(TracerProviderInterface $tracerProvider): void
{
$this->tracerProvider = $tracerProvider;
}

public function getTracerProvider(): TracerProviderInterface
{
return $this->tracerProvider;
}

public function getTracer(): TracerInterface
{
return $this->tracerProvider->getTracer('io.opentelemetry.contrib.php');
}

public function instrumentClients($clientsArray) : void
{
$this->clients = $clientsArray;
}

/** @psalm-suppress ArgumentTypeCoercion */
public function activate(): bool
{
try {
$middleware = Middleware::tap(function ($cmd, $req) {
$tracer = $this->getTracer();
$propagator = $this->getPropagator();

$carrier = [];
/** @phan-suppress-next-line PhanTypeMismatchArgument */
$this->span = $tracer->spanBuilder($this->clientName)->setSpanKind(AwsSdkInstrumentation::SPAN_KIND)->startSpan(); //@phpstan-ignore-line
$this->scope = $this->span->activate();

$propagator->inject($carrier);

$this->span->setAttributes([
'rpc.method' => $cmd->getName(),
'rpc.service' => $this->clientName,
'rpc.system' => 'aws-api',
'aws.region' => $this->region,
]);
});

$end_middleware = Middleware::mapResult(function (ResultInterface $result) {
$this->span->setAttributes([
'http.status_code' => $result['@metadata']['statusCode'],
]);

$this->span->end();
$this->scope->detach();

return $result;
});

foreach ($this->clients as $client) {
$this->clientName = $client->getApi()->getServiceName();
$this->region = $client->getRegion();

$client->getHandlerList()->prependInit($middleware, 'instrumentation');
$client->getHandlerList()->appendSign($end_middleware, 'end_instrumentation');
}
} catch (\Throwable $e) {
return false;
}

return true;
}
}
50 changes: 50 additions & 0 deletions src/Instrumentation/AwsSdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# AWS SDK Instrumentation for OpenTelemetry PHP
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).


## Using the AWS SDK Instrumentation with AWS X-Ray


```
use OpenTelemetry\Instrumentation\AwsSdk\AwsSdkInstrumentation;
// Initialize Span Processor, X-Ray ID generator, Tracer Provider, and Propagator
$spanProcessor = new SimpleSpanProcessor(new OTLPExporter());
$xrayIdGenerator = new IdGenerator();
$tracerProvider = new TracerProvider($spanProcessor, null, null, null, $xrayIdGenerator);
$xrayPropagator = new Propagator();
// Create new instance of AWS SDK Instrumentation class
$awssdkinstrumentation = new AwsSdkInstrumentation();
// Configure AWS SDK Instrumentation with Propagator and set Tracer Provider (created above)
$awssdkinstrumentation->setPropagator($xrayPropagator);
$awssdkinstrumentation->setTracerProvider($tracerProvider);
// Create and activate root span
$root = $awssdkinstrumentation->getTracer()->spanBuilder('AwsSDKInstrumentation')->setSpanKind(SpanKind::KIND_SERVER)->startSpan();
$rootScope = $root->activate();
// Initialize all AWS Client instances
$s3Client = new S3Client([
'region' => 'us-west-2',
'version' => '2006-03-01',
]);
// Pass client instances to AWS SDK
$awssdkinstrumentation->instrumentClients([$s3Client]);
// Activate Instrumentation -- all AWS Client calls will be automatically instrumented
$awssdkinstrumentation->activate();
// Make S3 client call
$result = $s3Client->listBuckets();
// End the root span after all the calls to the AWS SDK have been made
$root->end();
$rootScope->detach();
```

## Useful Links and Resources
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).
48 changes: 47 additions & 1 deletion tests/Unit/Instrumentation/AwsSdk/AwsSdkInstrumentationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,23 @@

namespace OpenTelemetry\Test\Unit\Instrumentation\AwsSdk;

use DG\BypassFinals;
use OpenTelemetry\API\Trace\TracerInterface;
use OpenTelemetry\Aws\Xray\Propagator;
use OpenTelemetry\Instrumentation\AwsSdk\AwsSdkInstrumentation;
use OpenTelemetry\SDK\Trace\TracerProviderInterface;
use PHPUnit\Framework\TestCase;

class AwsSdkInstrumentationTest extends TestCase
{
private AwsSdkInstrumentation $awsSdkInstrumentation;

protected function setUp(): void
{
BypassFinals::enable();
$this->awsSdkInstrumentation = new AwsSdkInstrumentation();
}

public function testInstrumentationClassName()
{
$this->assertEquals(
Expand Down Expand Up @@ -37,10 +49,44 @@ public function testInstrumentationInit()
);
}

public function testGetXrayPropagator()
{
$propagator = new Propagator();
$this->awsSdkInstrumentation->setPropagator($propagator);

$this->assertSame(
$this->awsSdkInstrumentation->getPropagator(),
$propagator
);
}

public function testGetTracerProvider()
{
$tracerProvider = $this->createMock(TracerProviderInterface::class);
$this->awsSdkInstrumentation->setTracerProvider($tracerProvider);

$this->assertSame(
$this->awsSdkInstrumentation->getTracerProvider(),
$tracerProvider
);
}

public function testGetTracer()
{
$tracer = $this->createMock(TracerInterface::class);
$tracerProvider = $this->createMock(TracerProviderInterface::class);
$tracerProvider->expects($this->once())
->method('getTracer')
->willReturn($tracer);

$this->awsSdkInstrumentation->setTracerProvider($tracerProvider);
$this->assertSame($tracer, $this->awsSdkInstrumentation->getTracer());
}

public function testInstrumentationActivated()
{
$this->assertTrue(
(new AwsSdkInstrumentation())->activate()
($this->awsSdkInstrumentation)->activate()
);
}
}

0 comments on commit 4e72ea1

Please sign in to comment.