Skip to content

Commit

Permalink
Added attributes to identify the distribution: telemetry.distro.name … (
Browse files Browse the repository at this point in the history
#93)

* Added attributes to identify the distribution: telemetry.distro.name and telemetry.distro.version

* Use both native part and PHP part versions (if differ)

* Fixed formatting

* Changed log level for versions mismatch
  • Loading branch information
SergeyKleyman authored Sep 13, 2024
1 parent e2c8782 commit 8e0d448
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Elastic\OTel;

final class PhpPartFacade
{
public static function bootstrap(int $maxEnabledLogLevel, float $requestInitStartTime): bool {
public static function bootstrap(string $elasticOTelNativePartVersion, int $maxEnabledLogLevel, float $requestInitStartTime): bool {
return true;
}

Expand Down
4 changes: 3 additions & 1 deletion prod/native/libphpbridge/code/PhpBridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
#include <optional>
#include <string_view>

#include "elastic_otel_version.h"

namespace elasticapm::php {

using namespace std::string_view_literals;
Expand Down Expand Up @@ -179,7 +181,7 @@ bool PhpBridge::callPHPSideEntryPoint(LogLevel logLevel, std::chrono::time_point
return false;
}

std::array<AutoZval, 2> arguments{logLevel, (double)std::chrono::duration_cast<std::chrono::microseconds>(requestInitStart.time_since_epoch()).count()};
std::array<AutoZval, 3> arguments{std::string_view(ELASTIC_OTEL_VERSION), logLevel, (double)std::chrono::duration_cast<std::chrono::microseconds>(requestInitStart.time_since_epoch()).count()};
AutoZval rv;
return callMethod(nullptr, "\\Elastic\\OTel\\PhpPartFacade::bootstrap"sv, arguments.data()->get(), arguments.size(), rv.get());
}
Expand Down
36 changes: 20 additions & 16 deletions prod/php/ElasticOTel/InstrumentationBridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

use Closure;
use Elastic\OTel\Util\SingletonInstanceTrait;
use RuntimeException;
use Throwable;

/**
* Code in this file is part of implementation internals, and thus it is not covered by the backward compatibility.
Expand All @@ -44,16 +46,13 @@ final class InstrumentationBridge
*/
public array $delayedHooks = [];

public function bootstrap(): bool
public function bootstrap(): void
{
if (!$this->hookSplAutoloadRegister()) {
return false;
}
self::elasticOTelHook(null, 'spl_autoload_register', null, Closure::fromCallable([$this, 'retryDelayedHooks']));

require ProdPhpDir::$fullPath . DIRECTORY_SEPARATOR . 'OpenTelemetry' . DIRECTORY_SEPARATOR . 'Instrumentation' . DIRECTORY_SEPARATOR . 'hook.php';

BootstrapStageLogger::logDebug('Finished successfully', __FILE__, __LINE__, __CLASS__, __FUNCTION__);
return true;
}

public function hook(?string $class, string $function, ?Closure $pre = null, ?Closure $post = null): bool
Expand All @@ -65,7 +64,7 @@ public function hook(?string $class, string $function, ?Closure $pre = null, ?Cl
return true;
}

return self::elasticOTelHook($class, $function, $pre, $post);
return self::elasticOTelHookNoThrow($class, $function, $pre, $post);
}

private function addToDelayedHooks(string $class, string $function, ?Closure $pre = null, ?Closure $post = null): void
Expand All @@ -75,7 +74,7 @@ private function addToDelayedHooks(string $class, string $function, ?Closure $pr
$this->delayedHooks[] = [$class, $function, $pre, $post];
}

private static function elasticOTelHook(?string $class, string $function, ?Closure $pre = null, ?Closure $post = null): bool
private static function elasticOTelHook(?string $class, string $function, ?Closure $pre = null, ?Closure $post = null): void
{
$dbgClassAsString = BootstrapStageLogger::nullableToLog($class);
BootstrapStageLogger::logTrace('Entered. class: ' . $dbgClassAsString . ', function: ' . $function, __FILE__, __LINE__, __CLASS__, __FUNCTION__);
Expand All @@ -89,20 +88,25 @@ private static function elasticOTelHook(?string $class, string $function, ?Closu
$retVal = \elastic_otel_hook($class, $function, $pre, $post);
if ($retVal) {
BootstrapStageLogger::logTrace('Successfully hooked. class: ' . $dbgClassAsString . ', function: ' . $function, __FILE__, __LINE__, __CLASS__, __FUNCTION__);
return true;
return;
}

if ($class === null) {
BootstrapStageLogger::logError('elastic_otel_hook returned false. function: ' . $function, __FILE__, __LINE__, __CLASS__, __FUNCTION__);
} else {
BootstrapStageLogger::logError('elastic_otel_hook returned false. class: ' . $dbgClassAsString . ', function: ' . $function, __FILE__, __LINE__, __CLASS__, __FUNCTION__);
}
return false;
throw new RuntimeException(
'elastic_otel_hook returned false'
. ($class === null ? '' : ('; class: ' . $dbgClassAsString))
. '; function: ' . $function
);
}

private function hookSplAutoloadRegister(): bool
private static function elasticOTelHookNoThrow(?string $class, string $function, ?Closure $pre = null, ?Closure $post = null): bool
{
return self::elasticOTelHook(null, 'spl_autoload_register', null, Closure::fromCallable([$this, 'retryDelayedHooks']));
try {
self::elasticOTelHook($class, $function, $pre, $post);
return true;
} catch (Throwable $throwable) {
BootstrapStageLogger::logCriticalThrowable($throwable, 'Call to elasticOTelHook has thrown', __FILE__, __LINE__, __CLASS__, __FUNCTION__);
return false;
}
}

private function retryDelayedHooks(): void
Expand Down
89 changes: 67 additions & 22 deletions prod/php/ElasticOTel/PhpPartFacade.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
namespace Elastic\OTel;

use Elastic\OTel\Util\HiddenConstructorTrait;
use RuntimeException;
use Throwable;

/**
Expand All @@ -44,27 +45,29 @@ final class PhpPartFacade

private static ?self $singletonInstance = null;

private static string $elasticOTelVersion;

/**
* Called by the extension
*
* @noinspection PhpUnused
*
* @param int $maxEnabledLogLevel
* @param float $requestInitStartTime
* @param string $elasticOTelNativePartVersion
* @param int $maxEnabledLogLevel
* @param float $requestInitStartTime
*
* @return bool
*/
public static function bootstrap(int $maxEnabledLogLevel, float $requestInitStartTime): bool
public static function bootstrap(string $elasticOTelNativePartVersion, int $maxEnabledLogLevel, float $requestInitStartTime): bool
{
require __DIR__ . DIRECTORY_SEPARATOR . 'BootstrapStageLogger.php';

BootstrapStageLogger::configure($maxEnabledLogLevel, __DIR__, __NAMESPACE__);
BootstrapStageLogger::logDebug(
'Starting bootstrap sequence...' . "; maxEnabledLogLevel: $maxEnabledLogLevel" . "; requestInitStartTime: $requestInitStartTime",
'Starting bootstrap sequence...'
. "; elasticOTelNativePartVersion: $elasticOTelNativePartVersion" . "; maxEnabledLogLevel: $maxEnabledLogLevel" . "; requestInitStartTime: $requestInitStartTime",
__FILE__, __LINE__, __CLASS__, __FUNCTION__
);

putenv('OTEL_PHP_AUTOLOAD_ENABLED=true');
self::setElasticOTelVersion($elasticOTelNativePartVersion);

if (self::$singletonInstance !== null) {
BootstrapStageLogger::logCritical(
Expand All @@ -79,26 +82,46 @@ public static function bootstrap(int $maxEnabledLogLevel, float $requestInitStar
require __DIR__ . DIRECTORY_SEPARATOR . 'Util' . DIRECTORY_SEPARATOR . 'SingletonInstanceTrait.php';
require __DIR__ . DIRECTORY_SEPARATOR . 'InstrumentationBridge.php';

if (!InstrumentationBridge::singletonInstance()->bootstrap()){
return false;
}
if (!self::registerAutoloader()) {
return false;
}
InstrumentationBridge::singletonInstance()->bootstrap();
self::prepareEnvForOTelSdk();
self::registerAutoloader();

self::$singletonInstance = new self();
} catch (Throwable $throwable) {
BootstrapStageLogger::logCriticalThrowable(
$throwable,
'One of the steps in bootstrap sequence let a throwable escape',
__FILE__, __LINE__, __CLASS__, __FUNCTION__
);
BootstrapStageLogger::logCriticalThrowable($throwable, 'One of the steps in bootstrap sequence has thrown', __FILE__, __LINE__, __CLASS__, __FUNCTION__);
return false;
}

BootstrapStageLogger::logDebug('Successfully completed bootstrap sequence', __FILE__, __LINE__, __CLASS__, __FUNCTION__);
return true;
}

private static function setElasticOTelVersion(string $nativePartVersion): void
{
/** @noinspection PhpIncludeInspection */
require __DIR__ . DIRECTORY_SEPARATOR . 'PhpPartVersion.php';

/**
* Constant Elastic\OTel\ELASTIC_OTEL_PHP_VERSION is defined in the generated file prod/php/ElasticOTel/PhpPartVersion.php
*
* @noinspection PhpUnnecessaryFullyQualifiedNameInspection, PhpUndefinedConstantInspection
* @phpstan-ignore-next-line
*
* @var string $phpPartVersion
*/
$phpPartVersion = \Elastic\OTel\ELASTIC_OTEL_PHP_VERSION;

if ($nativePartVersion === $phpPartVersion) {
self::$elasticOTelVersion = $nativePartVersion;
} else {
BootstrapStageLogger::logWarning(
'Native part and PHP part versions do not match' . "; nativePartVersion: $nativePartVersion" . "; phpPartVersion: $phpPartVersion",
__FILE__, __LINE__, __CLASS__, __FUNCTION__
);
self::$elasticOTelVersion = "$nativePartVersion/$phpPartVersion";
}
}

private static function isInDevMode(): bool
{
$modeIsDevEnvVarVal = getenv('ELASTIC_OTEL_PHP_DEV_INTERNAL_MODE_IS_DEV');
Expand All @@ -113,19 +136,41 @@ private static function isInDevMode(): bool
return false;
}

private static function registerAutoloader(): bool
private static function prepareEnvForOTelAttributes(): void
{
// https://opentelemetry.io/docs/specs/semconv/resource/#telemetry-distribution-experimental

$envVarName = 'OTEL_RESOURCE_ATTRIBUTES';
$envVarValueOnEntry = getenv($envVarName);
$envVarValue = (is_string($envVarValueOnEntry) && strlen($envVarValueOnEntry) !== 0) ? ($envVarValueOnEntry . ',') : '';
$envVarValue .= 'telemetry.distro.name=elastic,telemetry.distro.version=' . self::$elasticOTelVersion;
self::setEnvVar($envVarName, $envVarValue);
}

private static function setEnvVar(string $envVarName, string $envVarValue): void
{
if (!putenv($envVarName . '=' . $envVarValue)) {
throw new RuntimeException('putenv returned false; $envVarName: ' . $envVarName . '; envVarValue: ' . $envVarValue);
}
}

private static function prepareEnvForOTelSdk(): void
{
self::setEnvVar('OTEL_PHP_AUTOLOAD_ENABLED', 'true');
self::prepareEnvForOTelAttributes();
}

private static function registerAutoloader(): void
{
$vendorDir = ProdPhpDir::$fullPath . '/vendor' . (self::isInDevMode() ? '' : '_' . PHP_MAJOR_VERSION . PHP_MINOR_VERSION);
$vendorAutoloadPhp = $vendorDir . '/autoload.php';
if (!file_exists($vendorAutoloadPhp)) {
BootstrapStageLogger::logCritical("File $vendorAutoloadPhp does not exist", __FILE__, __LINE__, __CLASS__, __FUNCTION__);
return false;
throw new RuntimeException("File $vendorAutoloadPhp does not exist");
}
BootstrapStageLogger::logDebug('About to require ' . $vendorAutoloadPhp, __FILE__, __LINE__, __CLASS__, __FUNCTION__);
require $vendorAutoloadPhp;

BootstrapStageLogger::logDebug('Finished successfully', __FILE__, __LINE__, __CLASS__, __FUNCTION__);
return true;
}

/**
Expand Down

0 comments on commit 8e0d448

Please sign in to comment.