Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion Gax/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.49.0
1.50.0
3 changes: 2 additions & 1 deletion Gax/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"guzzlehttp/psr7": "^2.6.3||^3.0",
"google/common-protos": "^4.9",
"google/longrunning": "~0.4",
"ramsey/uuid": "^4.0"
"ramsey/uuid": "^4.0",
"open-telemetry/api": "^1.8"
},
"require-dev": {
"phpunit/phpunit": "^9.6",
Expand Down
51 changes: 51 additions & 0 deletions Gax/src/AgentHeader.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@

namespace Google\ApiCore;

use ReflectionClass;
use ReflectionException;

/**
* Class containing functions used to build the Agent header.
*/
Expand Down Expand Up @@ -129,4 +132,52 @@ public static function readGapicVersionFromFile(string $callingClass)

return Version::readVersionFile($versionFile);
}

/**
* Reads the package name from composer.json. In order to determine the file
* location, this method follows this procedure:
* - accepts a class name $callingClass
* - identifies the file defining that class
* - searches up the directory structure for the 'src' directory
* - looks in the directory above 'src' for a file named composer.json
* - parses the file to retrieve the "name" property
*
* @param string $callingClass
* @return string|null The package name or null if not found
*/
public static function readPackageNameFromFile(string $callingClass): ?string
{
static $packageNames = [];

if (array_key_exists($callingClass, $packageNames)) {
return $packageNames[$callingClass];
}

try {
$callingClassFile = (new ReflectionClass($callingClass))->getFileName();
if ($callingClassFile === false) {
return $packageNames[$callingClass] = null;
}
$srcPos = strrpos($callingClassFile, DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR);
if ($srcPos === false) {
return $packageNames[$callingClass] = null;
}
$composerFile = substr($callingClassFile, 0, $srcPos) . DIRECTORY_SEPARATOR . 'composer.json';
if (!file_exists($composerFile)) {
return $packageNames[$callingClass] = null;
}
$content = file_get_contents($composerFile);
if ($content === false) {
return $packageNames[$callingClass] = null;
}
$json = json_decode($content, true);
if (isset($json['name']) && is_string($json['name'])) {
return $packageNames[$callingClass] = $json['name'];
}
} catch (ReflectionException $e) {
return $packageNames[$callingClass] = null;
}

return $packageNames[$callingClass] = null;
}
}
28 changes: 26 additions & 2 deletions Gax/src/ClientOptionsTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
namespace Google\ApiCore;

use Google\ApiCore\Options\ClientOptions;
use Google\ApiCore\Telemetry\TelemetryConfiguration;
use Google\Auth\ApplicationDefaultCredentials;
use Google\Auth\CredentialsLoader;
use Google\Auth\FetchAuthTokenInterface;
Expand Down Expand Up @@ -122,6 +123,8 @@ private function buildClientOptions(array|ClientOptions $options)
'clientCertSource' => null,
'universeDomain' => null,
'logger' => null,
'clientPackageName' => null,
'openTelemetryTracerProvider' => null,
];

$supportedTransports = $this->supportedTransports();
Expand All @@ -147,6 +150,13 @@ private function buildClientOptions(array|ClientOptions $options)
// we will not encounter missing keys
$options += $defaultOptions;

if (empty($options['clientPackageName'])) {
$options['clientPackageName'] = AgentHeader::readPackageNameFromFile(static::class);
}
$options['openTelemetryTracerProvider'] = TelemetryConfiguration::resolveTracerProvider(
$options['openTelemetryTracerProvider']
);

// If logger is explicitly set to false, logging is disabled
if (is_null($options['logger'])) {
$options['logger'] = ApplicationDefaultCredentials::getDefaultLogger();
Expand Down Expand Up @@ -326,11 +336,25 @@ private function createCredentialsWrapper($credentials, array $credentialsConfig
}

if ($credentials instanceof FetchAuthTokenInterface) {
$authHttpHandler = $credentialsConfig['authHttpHandler'] ?? null;
return new CredentialsWrapper($credentials, $authHttpHandler, $universeDomain);
$authHttpHandler = CredentialsWrapper::wrapAuthHttpHandler(
$credentialsConfig['authHttpHandler'] ?? null,
$credentialsConfig['openTelemetryTracerProvider'] ?? null,
$credentialsConfig['clientVersion'] ?? ''
);
return new CredentialsWrapper(
$credentials,
$authHttpHandler,
$universeDomain
);
}

if ($credentials instanceof CredentialsWrapper) {
if (!empty($credentialsConfig['openTelemetryTracerProvider'])) {
$credentials->setOpenTelemetryTracerProvider(
$credentialsConfig['openTelemetryTracerProvider'],
$credentialsConfig['clientVersion'] ?? ''
);
}
return $credentials;
}

Expand Down
58 changes: 57 additions & 1 deletion Gax/src/CredentialsWrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@

use DomainException;
use Exception;
use Google\ApiCore\Telemetry\AuthHttpHandler;
use Google\ApiCore\Telemetry\TelemetryConfiguration;
use Google\Auth\ApplicationDefaultCredentials;
use Google\Auth\Cache\MemoryCacheItemPool;
use Google\Auth\Credentials\GCECredentials;
Expand All @@ -42,8 +44,10 @@
use Google\Auth\FetchAuthTokenInterface;
use Google\Auth\GetQuotaProjectInterface;
use Google\Auth\GetUniverseDomainInterface;
use Google\Auth\HttpHandler\HttpHandlerFactory;
use Google\Auth\ProjectIdProviderInterface;
use Google\Auth\UpdateMetadataInterface;
use OpenTelemetry\API\Trace\TracerProviderInterface;
use Psr\Cache\CacheItemPoolInterface;

/**
Expand All @@ -70,6 +74,8 @@ class CredentialsWrapper implements HeaderCredentialsInterface, ProjectIdProvide
* @param callable $authHttpHandler A handler used to deliver PSR-7 requests
* specifically for authentication. Should match a signature of
* `function (RequestInterface $request, array $options) : ResponseInterface`.
* @param string $universeDomain The expected universe of the credentials. Defaults to
* "googleapis.com"
* @throws ValidationException
*/
public function __construct(
Expand Down Expand Up @@ -138,8 +144,16 @@ public static function build(
'defaultScopes' => null,
'useJwtAccessWithScope' => true,
'enableRegionalAccessBoundary' => false,
'openTelemetryTracerProvider' => null,
'clientVersion' => '',
];

$args['authHttpHandler'] = self::wrapAuthHttpHandler(
$args['authHttpHandler'],
$args['openTelemetryTracerProvider'],
$args['clientVersion']
);

$keyFile = $args['keyFile'];

if (is_null($keyFile)) {
Expand Down Expand Up @@ -190,7 +204,49 @@ public static function build(
);
}

return new CredentialsWrapper($loader, $args['authHttpHandler'], $universeDomain);
return new CredentialsWrapper(
$loader,
$args['authHttpHandler'],
$universeDomain
);
}

/**
* @internal
* @param callable|null $authHttpHandler
* @param TracerProviderInterface|null $openTelemetryTracerProvider
* @param string $clientVersion
* @return callable|null
*/
public static function wrapAuthHttpHandler(
?callable $authHttpHandler,
?TracerProviderInterface $openTelemetryTracerProvider = null,
string $clientVersion = ''
): ?callable {
$tracerProvider = TelemetryConfiguration::resolveTracerProvider($openTelemetryTracerProvider);
if ($tracerProvider && !($authHttpHandler instanceof AuthHttpHandler)) {
$handler = $authHttpHandler ?: HttpHandlerFactory::build();
return new AuthHttpHandler($handler, $tracerProvider, $clientVersion);
}
return $authHttpHandler;
}

/**
* @internal
* @param TracerProviderInterface|null $openTelemetryTracerProvider
* @param string $clientVersion
* @return $this
*/
public function setOpenTelemetryTracerProvider(
?TracerProviderInterface $openTelemetryTracerProvider,
string $clientVersion = ''
): self {
$this->authHttpHandler = self::wrapAuthHttpHandler(
$this->authHttpHandler,
$openTelemetryTracerProvider,
$clientVersion
);
return $this;
}

/**
Expand Down
47 changes: 40 additions & 7 deletions Gax/src/GapicClientTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
use Google\ApiCore\Options\ClientOptions;
use Google\ApiCore\Options\TransportOptions;
use Google\ApiCore\ResumableUpload\ResumableUpload;
use Google\ApiCore\Telemetry\SpanAttributes;
use Google\ApiCore\Transport\GrpcFallbackTransport;
use Google\ApiCore\Transport\GrpcTransport;
use Google\ApiCore\Transport\RestTransport;
Expand All @@ -53,6 +54,7 @@
use Google\LongRunning\Operation;
use Google\Protobuf\Internal\Message;
use GuzzleHttp\Promise\PromiseInterface;
use OpenTelemetry\API\Trace\TracerProviderInterface;

/**
* Common functions used to work with various clients.
Expand All @@ -69,6 +71,9 @@ trait GapicClientTrait

private ?TransportInterface $transport = null;
private ?HeaderCredentialsInterface $credentialsWrapper = null;
/** @var TracerProviderInterface|null */
private $openTelemetryTracerProvider;
private array $telemetryOptions = [];
/** @var RetrySettings[] $retrySettings */
private array $retrySettings = [];
private string $serviceName = '';
Expand Down Expand Up @@ -364,22 +369,40 @@ private function setClientOptions(array $options)
$this->credentialsWrapper = $this->createCredentialsWrapper(
$options['credentials'],
$options['credentialsConfig'] + [
'enableRegionalAccessBoundary' => $enableRegionalAccessBoundary && !$isRegional
'enableRegionalAccessBoundary' => $enableRegionalAccessBoundary && !$isRegional,
'openTelemetryTracerProvider' => $options['openTelemetryTracerProvider'] ?? null,
'clientVersion' => $options['libVersion'] ?? '',
],
$options['universeDomain'],
);
}

$this->openTelemetryTracerProvider = $options['openTelemetryTracerProvider'] ?? null;
$telemetryOptions = [
'openTelemetryTracerProvider' => $options['openTelemetryTracerProvider'] ?? null,
SpanAttributes::GCP_CLIENT_REPO => 'googleapis/google-cloud-php',
SpanAttributes::GCP_CLIENT_ARTIFACT => $options['clientPackageName'] ?? null,
SpanAttributes::GCP_CLIENT_SERVICE => $this->serviceName,
SpanAttributes::GCP_CLIENT_VERSION => $options['libVersion'] ?? null,
];
$this->telemetryOptions = $telemetryOptions;

$transport = $options['transport'] ?: self::defaultTransport();
$this->transport = $transport instanceof TransportInterface
? $transport
: $this->createTransport(
if ($transport instanceof TransportInterface) {
if (method_exists($transport, 'setTelemetryOptions')) {
$transport->setTelemetryOptions($telemetryOptions);
}
$this->transport = $transport;
} else {
$this->transport = $this->createTransport(
$options['apiEndpoint'],
$transport,
$options['transportConfig'],
$options['clientCertSource'],
$hasEmulator
$hasEmulator,
$telemetryOptions
);
}
}

/**
Expand All @@ -396,7 +419,8 @@ private function createTransport(
$transport,
$transportConfig,
?callable $clientCertSource = null,
bool $hasEmulator = false
bool $hasEmulator = false,
array $telemetryOptions = []
) {
if (!is_string($transport)) {
throw new ValidationException(
Expand All @@ -419,6 +443,7 @@ private function createTransport(
$configForSpecifiedTransport->setClientCertSource($clientCertSource);
$configForSpecifiedTransport = $configForSpecifiedTransport->toArray();
}
$configForSpecifiedTransport += $telemetryOptions;
switch ($transport) {
case 'grpc':
// Setting the user agent for gRPC requires special handling
Expand Down Expand Up @@ -731,7 +756,15 @@ private function createCallStack(array $callConstructionOptions)

$callStack = new CredentialsWrapperMiddleware($callStack, $this->credentialsWrapper);
$callStack = new FixedHeaderMiddleware($callStack, $fixedHeaders, true);
$callStack = new RetryMiddleware($callStack, $callConstructionOptions['retrySettings']);
$callStack = new RetryMiddleware(
$callStack,
$callConstructionOptions['retrySettings'],
null,
0,
null,
$this->openTelemetryTracerProvider ?? null,
$this->telemetryOptions
);
$callStack = new RequestAutoPopulationMiddleware(
$callStack,
$callConstructionOptions['autoPopulationSettings'],
Expand Down
Loading
Loading