From 4c69a8d43fdf5dcac4f8bbee2f70ac03f5adfa20 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 30 Jul 2025 01:49:21 +0000 Subject: [PATCH 01/16] chore: update dependabot configuration [skip ci] --- .github/dependabot.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 7fdc60cfd..ffc764237 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -57,10 +57,10 @@ updates: # Maintain dependencies for all packages - package-ecosystem: "composer" directories: - - "/examples/aws/AwsClientApp" - "/examples/instrumentation/Wordpress" - "/src/AutoInstrumentationInstaller" - "/src/Aws" + - "/src/Aws/examples/AwsClientApp" - "/src/Context/Swoole" - "/src/Exporter/Instana" - "/src/Instrumentation/AwsSdk" @@ -78,12 +78,12 @@ updates: - "/src/Instrumentation/MySqli" - "/src/Instrumentation/OpenAIPHP" - "/src/Instrumentation/PDO" - - "/src/Instrumentation/Psr3" - - "/src/Instrumentation/Psr6" - "/src/Instrumentation/Psr14" - "/src/Instrumentation/Psr15" - "/src/Instrumentation/Psr16" - "/src/Instrumentation/Psr18" + - "/src/Instrumentation/Psr3" + - "/src/Instrumentation/Psr6" - "/src/Instrumentation/ReactPHP" - "/src/Instrumentation/Slim" - "/src/Instrumentation/Symfony" From 2221d623bdfdee3fe5bd6a834e359d155db622b7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 6 Aug 2025 01:50:03 +0000 Subject: [PATCH 02/16] chore: update dependabot configuration [skip ci] --- .github/dependabot.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ffc764237..9be661d00 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -78,6 +78,7 @@ updates: - "/src/Instrumentation/MySqli" - "/src/Instrumentation/OpenAIPHP" - "/src/Instrumentation/PDO" + - "/src/Instrumentation/PostgreSql" - "/src/Instrumentation/Psr14" - "/src/Instrumentation/Psr15" - "/src/Instrumentation/Psr16" From 11cf30a2492fe4b92998c781fd6b31d09087851b Mon Sep 17 00:00:00 2001 From: HeenaBansal20 Date: Tue, 26 Aug 2025 19:00:04 -0700 Subject: [PATCH 03/16] Add PHp Session extension auto instrumentation hooks --- src/Instrumentation/Session/.gitattributes | 12 ++ src/Instrumentation/Session/.gitignore | 1 + src/Instrumentation/Session/.php-cs-fixer.php | 42 ++++++ src/Instrumentation/Session/README.md | 57 ++++++++ src/Instrumentation/Session/_register.php | 18 +++ src/Instrumentation/Session/composer.json | 31 ++++ src/Instrumentation/Session/phpstan.neon.dist | 9 ++ src/Instrumentation/Session/phpunit.xml.dist | 47 ++++++ src/Instrumentation/Session/psalm.xml.dist | 15 ++ .../Session/src/PhpSessionInstrumentation.php | 134 ++++++++++++++++++ .../tests/Integration/AbstractTest.php | 44 ++++++ .../PhpSessionInstrumentationTest.php | 129 +++++++++++++++++ 12 files changed, 539 insertions(+) create mode 100644 src/Instrumentation/Session/.gitattributes create mode 100644 src/Instrumentation/Session/.gitignore create mode 100644 src/Instrumentation/Session/.php-cs-fixer.php create mode 100644 src/Instrumentation/Session/README.md create mode 100644 src/Instrumentation/Session/_register.php create mode 100644 src/Instrumentation/Session/composer.json create mode 100644 src/Instrumentation/Session/phpstan.neon.dist create mode 100644 src/Instrumentation/Session/phpunit.xml.dist create mode 100644 src/Instrumentation/Session/psalm.xml.dist create mode 100644 src/Instrumentation/Session/src/PhpSessionInstrumentation.php create mode 100644 src/Instrumentation/Session/tests/Integration/AbstractTest.php create mode 100644 src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php diff --git a/src/Instrumentation/Session/.gitattributes b/src/Instrumentation/Session/.gitattributes new file mode 100644 index 000000000..1676cf825 --- /dev/null +++ b/src/Instrumentation/Session/.gitattributes @@ -0,0 +1,12 @@ +* text=auto + +*.md diff=markdown +*.php diff=php + +/.gitattributes export-ignore +/.gitignore export-ignore +/.php-cs-fixer.php export-ignore +/phpstan.neon.dist export-ignore +/phpunit.xml.dist export-ignore +/psalm.xml.dist export-ignore +/tests export-ignore diff --git a/src/Instrumentation/Session/.gitignore b/src/Instrumentation/Session/.gitignore new file mode 100644 index 000000000..57872d0f1 --- /dev/null +++ b/src/Instrumentation/Session/.gitignore @@ -0,0 +1 @@ +/vendor/ diff --git a/src/Instrumentation/Session/.php-cs-fixer.php b/src/Instrumentation/Session/.php-cs-fixer.php new file mode 100644 index 000000000..bc482805d --- /dev/null +++ b/src/Instrumentation/Session/.php-cs-fixer.php @@ -0,0 +1,42 @@ +exclude('vendor') + ->exclude('var/cache') + ->in(__DIR__); + +$config = new PhpCsFixer\Config(); +return $config->setRules([ + 'concat_space' => ['spacing' => 'one'], + 'declare_equal_normalize' => ['space' => 'none'], + 'is_null' => true, + 'modernize_types_casting' => true, + 'ordered_imports' => true, + 'php_unit_construct' => true, + 'single_line_comment_style' => true, + 'yoda_style' => false, + '@PSR2' => true, + 'array_syntax' => ['syntax' => 'short'], + 'blank_line_after_opening_tag' => true, + 'blank_line_before_statement' => true, + 'cast_spaces' => true, + 'declare_strict_types' => true, + 'type_declaration_spaces' => true, + 'include' => true, + 'lowercase_cast' => true, + 'new_with_parentheses' => true, + 'no_extra_blank_lines' => true, + 'no_leading_import_slash' => true, + 'echo_tag_syntax' => true, + 'no_unused_imports' => true, + 'no_useless_else' => true, + 'no_useless_return' => true, + 'phpdoc_order' => true, + 'phpdoc_scalar' => true, + 'phpdoc_types' => true, + 'short_scalar_cast' => true, + 'blank_lines_before_namespace' => true, + 'single_quote' => true, + 'trailing_comma_in_multiline' => true, + ]) + ->setRiskyAllowed(true) + ->setFinder($finder); diff --git a/src/Instrumentation/Session/README.md b/src/Instrumentation/Session/README.md new file mode 100644 index 000000000..ccb5aa83b --- /dev/null +++ b/src/Instrumentation/Session/README.md @@ -0,0 +1,57 @@ +# PHP Session Auto-instrumentation for OpenTelemetry + +This package provides auto-instrumentation for PHP's native session functions. + +## Installation + +```bash +composer require open-telemetry/opentelemetry-auto-php-session +``` + +## Usage + +The instrumentation hooks into PHP's native session functions to provide tracing capabilities. It automatically creates spans for session operations like `session_start()` and `session_destroy()`. + +```php + + + + + + + src + + + + + + + + + + + + + tests/Unit + + + tests/Integration + + + + diff --git a/src/Instrumentation/Session/psalm.xml.dist b/src/Instrumentation/Session/psalm.xml.dist new file mode 100644 index 000000000..155711712 --- /dev/null +++ b/src/Instrumentation/Session/psalm.xml.dist @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/src/Instrumentation/Session/src/PhpSessionInstrumentation.php b/src/Instrumentation/Session/src/PhpSessionInstrumentation.php new file mode 100644 index 000000000..38ca4648a --- /dev/null +++ b/src/Instrumentation/Session/src/PhpSessionInstrumentation.php @@ -0,0 +1,134 @@ +url(), + ); + + self::_hook($instrumentation, null, 'session_start', 'session.start'); + self::_hook($instrumentation, null, 'session_destroy', 'session.destroy'); + } + + /** + * Simple generic hook function which starts and ends a minimal span + * @psalm-suppress UnusedFunctionCall + */ + private static function _hook(CachedInstrumentation $instrumentation, ?string $class, string $function, string $name): void + { + hook( + class: $class, + function: $function, + pre: static function ($object, ?array $params, ?string $class, string $function, ?string $filename, ?int $lineno) use ($instrumentation, $name) { + /** @psalm-suppress ArgumentTypeCoercion */ + $builder = self::makeBuilder($instrumentation, $name, $function, $filename, $lineno); + self::addParams($builder, $function, $params); + $parent = Context::getCurrent(); + $span = $builder->startSpan(); + Context::storage()->attach($span->storeInContext($parent)); + }, + post: static function ($object, ?array $params, mixed $return, ?Throwable $exception) use ($function) { + self::end($exception, $function); + } + ); + } + + private static function makeBuilder( + CachedInstrumentation $instrumentation, + string $name, + string $function, + ?string $filename, + ?int $lineno + ): SpanBuilderInterface { + /** @psalm-suppress ArgumentTypeCoercion */ + return $instrumentation->tracer() + ->spanBuilder($name) + ->setSpanKind(SpanKind::KIND_INTERNAL) + ->setAttribute(TraceAttributes::CODE_FUNCTION_NAME, $function) + ->setAttribute(TraceAttributes::CODE_FILE_PATH, $filename) + ->setAttribute(TraceAttributes::CODE_LINE_NUMBER, $lineno); + } + + private static function end(?Throwable $exception, string $function): void + { + $scope = Context::storage()->scope(); + if (!$scope) { + return; + } + $scope->detach(); + $span = Span::fromContext($scope->context()); + + // Add session-specific attributes in the post hook + if ($function === 'session_start') { + $span->setAttribute('session.id', session_id()); + $span->setAttribute('session.name', session_name()); + $isSessionActive = !empty(session_id()); + $span->setAttribute('session.status', $isSessionActive ? 'active' : 'inactive'); + + // Add session cookie parameters + $cookieParams = session_get_cookie_params(); + foreach ($cookieParams as $key => $value) { + if (is_scalar($value)) { + $span->setAttribute("session.cookie.$key", $value); + } + } + } elseif ($function === 'session_destroy') { + $span->setAttribute('session.destroy.success', $exception === null); + } + + if ($exception) { + $span->recordException($exception); + $span->setStatus(StatusCode::STATUS_ERROR, $exception->getMessage()); + } else { + $span->setStatus(StatusCode::STATUS_OK); + } + + $span->end(); + } + + private static function addParams(SpanBuilderInterface $builder, string $function, ?array $params): void + { + if ($params === null) { + return; + } + + switch ($function) { + case 'session_start': + if (isset($params[0]) && is_array($params[0])) { + foreach ($params[0] as $key => $value) { + if (is_scalar($value)) { + $builder->setAttribute("session.options.$key", $value); + } + } + } + + break; + + case 'session_destroy': + // No parameters to add for session_destroy + break; + } + } +} diff --git a/src/Instrumentation/Session/tests/Integration/AbstractTest.php b/src/Instrumentation/Session/tests/Integration/AbstractTest.php new file mode 100644 index 000000000..6f311469f --- /dev/null +++ b/src/Instrumentation/Session/tests/Integration/AbstractTest.php @@ -0,0 +1,44 @@ +storage = new ArrayObject(); + $tracerProvider = new TracerProvider( + new SimpleSpanProcessor( + new InMemoryExporter($this->storage) + ) + ); + + $this->scope = Configurator::create() + ->withTracerProvider($tracerProvider) + ->withPropagator(TraceContextPropagator::getInstance()) + ->activate(); + + parent::setUp(); + } + + public function tearDown(): void + { + parent::tearDown(); + + $this->scope->detach(); + } +} diff --git a/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php b/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php new file mode 100644 index 000000000..4c9f284ae --- /dev/null +++ b/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php @@ -0,0 +1,129 @@ + true, + 'cookie_lifetime' => 3600, + ]; + + session_start($options); + + // Verify the span was created + $this->assertCount(1, $this->storage); + $span = $this->storage[0]; + + // Check span name + $this->assertEquals('session.start', $span->getName()); + + // Check attributes + $attributes = $span->getAttributes(); + + $this->assertEquals('session_start', $attributes->get(TraceAttributes::CODE_FUNCTION_NAME)); + + // Check session options were recorded + $this->assertTrue($attributes->get('session.options.read_and_close')); + $this->assertEquals(3600, $attributes->get('session.options.cookie_lifetime')); + + // Check session information + $this->assertEquals(session_id(), $attributes->get('session.id')); + $this->assertEquals(session_name(), $attributes->get('session.name')); + $this->assertEquals('active', $attributes->get('session.status')); + + // Check cookie parameters + $cookieParams = session_get_cookie_params(); + foreach ($cookieParams as $key => $value) { + if (is_scalar($value)) { + $this->assertEquals($value, $attributes->get("session.cookie.$key")); + } + } + + // Check status + $this->assertEquals(StatusCode::STATUS_OK, $span->getStatus()->getCode()); + + // Clean up + if (session_status() === PHP_SESSION_ACTIVE) { + session_destroy(); + } + } + + /** + * @runInSeparateProcess + */ + public function test_session_destroy(): void + { + // Start a session first + session_start(); + + // Destroy the session + session_destroy(); + + // Verify the span were created for session_start() and session_destroy() + $this->assertCount(2, $this->storage); + $span = $this->storage[1]; + + // Check span name + $this->assertEquals('session.destroy', $span->getName()); + + // Check attributes + $attributes = $span->getAttributes(); + + $this->assertEquals('session_destroy', $attributes->get(TraceAttributes::CODE_FUNCTION_NAME)); + + // Check status + $this->assertEquals(StatusCode::STATUS_OK, $span->getStatus()->getCode()); + $this->assertTrue($attributes->get('session.destroy.success')); + } + + /** + * @runInSeparateProcess + */ + public function test_session_start_exception_simulation(): void + { + // This test simulates an exception during session_start + // We can't easily trigger a real exception in session_start during testing, + // so we'll verify the exception handling code path by checking the instrumentation code + + // The PhpSessionInstrumentation class has exception handling in the post hook + // that sets the span status to ERROR and records the exception + + $this->assertTrue(true, 'Exception handling is implemented in the instrumentation'); + } +} From b44eecde00cd6a28f1ae5ea01ba3c498c2522247 Mon Sep 17 00:00:00 2001 From: HeenaBansal20 Date: Tue, 26 Aug 2025 19:31:29 -0700 Subject: [PATCH 04/16] Run Session testcase in CI --- composer.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/composer.json b/composer.json index 3c64f2e40..5591c3d21 100644 --- a/composer.json +++ b/composer.json @@ -40,6 +40,7 @@ "OpenTelemetry\\Contrib\\Instrumentation\\Psr16\\": "src/Instrumentation/Psr16/src", "OpenTelemetry\\Contrib\\Instrumentation\\Psr18\\": "src/Instrumentation/Psr18/src", "OpenTelemetry\\Contrib\\Instrumentation\\ReactPHP\\": "src/Instrumentation/ReactPHP/src", + "OpenTelemetry\\Contrib\\Instrumentation\\Session\\": "src/Instrumentation/Session/src", "OpenTelemetry\\Contrib\\Instrumentation\\Slim\\": "src/Instrumentation/Slim/src", "OpenTelemetry\\Contrib\\Instrumentation\\Symfony\\": "src/Instrumentation/Symfony/src", "OpenTelemetry\\Contrib\\Instrumentation\\Wordpress\\": "src/Instrumentation/Wordpress/src", @@ -81,6 +82,7 @@ "src/Instrumentation/Psr16/_register.php", "src/Instrumentation/Psr18/_register.php", "src/Instrumentation/ReactPHP/_register.php", + "src/Instrumentation/Session/_register.php", "src/Instrumentation/Slim/_register.php", "src/Instrumentation/Symfony/_register.php", "src/Instrumentation/Wordpress/_register.php", @@ -115,6 +117,7 @@ "OpenTelemetry\\Instrumentation\\Psr14\\Tests\\": "src/Instrumentation/Psr14/tests", "OpenTelemetry\\Tests\\Instrumentation\\Psr16\\": "src/Instrumentation/Psr16/tests", "OpenTelemetry\\Tests\\Instrumentation\\ReactPHP\\": "src/Instrumentation/ReactPHP/tests", + "OpenTelemetry\\Tests\\Instrumentation\\Session\\": "src/Instrumentation/Session/tests", "OpenTelemetry\\Tests\\Instrumentation\\Slim\\": "src/Instrumentation/Slim/tests", "OpenTelemetry\\Tests\\Instrumentation\\Symfony\\tests\\": "src/Instrumentation/Symfony/tests", "OpenTelemetry\\Tests\\Instrumentation\\Wordpress\\": "src/Instrumentation/Wordpress/tests", @@ -156,6 +159,7 @@ "open-telemetry/opentelemetry-auto-psr16": "self.version", "open-telemetry/opentelemetry-auto-psr18": "self.version", "open-telemetry/opentelemetry-auto-reactphp": "self.version", + "open-telemetry/opentelemetry-auto-php-session": "self.version", "open-telemetry/opentelemetry-auto-slim": "self.version", "open-telemetry/opentelemetry-auto-symfony": "self.version", "open-telemetry/opentelemetry-auto-wordpress": "self.version", From 9c78be3eef7aa7df25832c7da38f792b0c7dbc5c Mon Sep 17 00:00:00 2001 From: HeenaBansal20 Date: Tue, 26 Aug 2025 19:40:41 -0700 Subject: [PATCH 05/16] Run Session testcase in CI --- .github/workflows/php.yml | 3 +++ .gitsplit.yml | 2 ++ 2 files changed, 5 insertions(+) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index e833c354f..a6115f2d0 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -50,6 +50,7 @@ jobs: 'Instrumentation/Psr16', 'Instrumentation/Psr18', 'Instrumentation/ReactPHP', + 'Instrumentation/Session', 'Instrumentation/Slim', 'Instrumentation/Symfony', 'Instrumentation/Yii', @@ -82,6 +83,8 @@ jobs: php-version: 8.1 - project: 'Instrumentation/PostgreSql' php-version: 8.1 + - project: 'Instrumentation/Session' + php-version: 8.1 steps: - uses: actions/checkout@v4 diff --git a/.gitsplit.yml b/.gitsplit.yml index 7830c9b64..358c6bad0 100644 --- a/.gitsplit.yml +++ b/.gitsplit.yml @@ -60,6 +60,8 @@ splits: target: "https://${GH_TOKEN}@github.com/opentelemetry-php/contrib-auto-psr18.git" - prefix: "src/Instrumentation/ReactPHP" target: "https://${GH_TOKEN}@github.com/opentelemetry-php/contrib-auto-reactphp.git" + - prefix: "src/Instrumentation/Session" + target: "https://${GH_TOKEN}@github.com/opentelemetry-php/contrib-auto-php-session.git" - prefix: "src/Instrumentation/Slim" target: "https://${GH_TOKEN}@github.com/opentelemetry-php/contrib-auto-slim.git" - prefix: "src/Instrumentation/Symfony" From b4185d158883eb8f6075666975dfcaa59a9ecd25 Mon Sep 17 00:00:00 2001 From: HeenaBansal20 Date: Tue, 26 Aug 2025 19:51:26 -0700 Subject: [PATCH 06/16] Fix 8.4 CI --- src/Instrumentation/Session/composer.json | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Instrumentation/Session/composer.json b/src/Instrumentation/Session/composer.json index 2e2fe0143..4184e84bf 100644 --- a/src/Instrumentation/Session/composer.json +++ b/src/Instrumentation/Session/composer.json @@ -19,13 +19,14 @@ } }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.0", + "friendsofphp/php-cs-fixer": "^3", "phan/phan": "^5.0", - "phpstan/phpstan": "^1.0", + "php-http/mock-client": "*", + "phpstan/phpstan": "^1.1", "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^9.0", - "psalm/plugin-phpunit": "^0.18.0", - "vimeo/psalm": "^5.0", - "open-telemetry/sdk": "^1.0" + "psalm/plugin-phpunit": "^0.19.2", + "open-telemetry/sdk": "^1.0", + "phpunit/phpunit": "^9.5", + "vimeo/psalm": "6.4.0" } } From 57c56165379e2fa46b7d06265673961b243900ce Mon Sep 17 00:00:00 2001 From: HeenaBansal20 Date: Tue, 26 Aug 2025 19:56:54 -0700 Subject: [PATCH 07/16] Add empty Unit test folder --- src/Instrumentation/Session/tests/Unit/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/Instrumentation/Session/tests/Unit/.gitkeep diff --git a/src/Instrumentation/Session/tests/Unit/.gitkeep b/src/Instrumentation/Session/tests/Unit/.gitkeep new file mode 100644 index 000000000..e69de29bb From 418dfb1984673836fe159fc92fbf63934a089d16 Mon Sep 17 00:00:00 2001 From: HeenaBansal20 Date: Tue, 26 Aug 2025 20:58:12 -0700 Subject: [PATCH 08/16] Add more session functions to the list --- src/Instrumentation/Session/README.md | 35 +++++- .../Session/src/PhpSessionInstrumentation.php | 39 +++++- .../PhpSessionInstrumentationTest.php | 113 ++++++++++++++++++ 3 files changed, 184 insertions(+), 3 deletions(-) diff --git a/src/Instrumentation/Session/README.md b/src/Instrumentation/Session/README.md index ccb5aa83b..3eb404d20 100644 --- a/src/Instrumentation/Session/README.md +++ b/src/Instrumentation/Session/README.md @@ -52,6 +52,39 @@ When `session_destroy()` is called, a span named `session.destroy` is created wi - `session.name`: The session name (if available) - `session.destroy.success`: Boolean indicating if the session was successfully destroyed +### session.write_close + +When `session_write_close()` is called, a span named `session.write_close` is created with the following attributes: + +- `code.function.name`: The function name (`session_write_close`) +- `code.filepath`: The file path where the function was called +- `code.lineno`: The line number where the function was called +- `session.id`: The session ID (if available) +- `session.name`: The session name (if available) +- `session.write_close.success`: Boolean indicating if the session was successfully written and closed + +### session.unset + +When `session_unset()` is called, a span named `session.unset` is created with the following attributes: + +- `code.function.name`: The function name (`session_unset`) +- `code.filepath`: The file path where the function was called +- `code.lineno`: The line number where the function was called +- `session.id`: The session ID (if available) +- `session.name`: The session name (if available) +- `session.unset.success`: Boolean indicating if the session variables were successfully unset + +### session.abort + +When `session_abort()` is called, a span named `session.abort` is created with the following attributes: + +- `code.function.name`: The function name (`session_abort`) +- `code.filepath`: The file path where the function was called +- `code.lineno`: The line number where the function was called +- `session.id`: The session ID (if available) +- `session.name`: The session name (if available) +- `session.abort.success`: Boolean indicating if the session was successfully aborted + ## License -Apache 2.0 \ No newline at end of file +Apache 2.0 diff --git a/src/Instrumentation/Session/src/PhpSessionInstrumentation.php b/src/Instrumentation/Session/src/PhpSessionInstrumentation.php index 38ca4648a..41b75af8d 100644 --- a/src/Instrumentation/Session/src/PhpSessionInstrumentation.php +++ b/src/Instrumentation/Session/src/PhpSessionInstrumentation.php @@ -30,6 +30,9 @@ public static function register(): void self::_hook($instrumentation, null, 'session_start', 'session.start'); self::_hook($instrumentation, null, 'session_destroy', 'session.destroy'); + self::_hook($instrumentation, null, 'session_write_close', 'session.write_close'); + self::_hook($instrumentation, null, 'session_unset', 'session.unset'); + self::_hook($instrumentation, null, 'session_abort', 'session.abort'); } /** @@ -82,9 +85,9 @@ private static function end(?Throwable $exception, string $function): void // Add session-specific attributes in the post hook if ($function === 'session_start') { + $isSessionActive = !empty(session_id()); $span->setAttribute('session.id', session_id()); $span->setAttribute('session.name', session_name()); - $isSessionActive = !empty(session_id()); $span->setAttribute('session.status', $isSessionActive ? 'active' : 'inactive'); // Add session cookie parameters @@ -96,8 +99,40 @@ private static function end(?Throwable $exception, string $function): void } } elseif ($function === 'session_destroy') { $span->setAttribute('session.destroy.success', $exception === null); + } elseif ($function === 'session_write_close') { + $sessionId = session_id(); + $sessionName = session_name(); + + // Add session information + if (!empty($sessionId)) { + $span->setAttribute('session.id', $sessionId); + $span->setAttribute('session.name', $sessionName); + } + + $span->setAttribute('session.write_close.success', $exception === null); + } elseif ($function === 'session_unset') { + $sessionId = session_id(); + $sessionName = session_name(); + + // Add session information + if (!empty($sessionId)) { + $span->setAttribute('session.id', $sessionId); + $span->setAttribute('session.name', $sessionName); + } + + $span->setAttribute('session.unset.success', $exception === null); + } elseif ($function === 'session_abort') { + $sessionId = session_id(); + $sessionName = session_name(); + + // Add session information + if (!empty($sessionId)) { + $span->setAttribute('session.id', $sessionId); + $span->setAttribute('session.name', $sessionName); + } + + $span->setAttribute('session.abort.success', $exception === null); } - if ($exception) { $span->recordException($exception); $span->setStatus(StatusCode::STATUS_ERROR, $exception->getMessage()); diff --git a/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php b/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php index 4c9f284ae..4a5f44bf4 100644 --- a/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php +++ b/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php @@ -111,6 +111,119 @@ public function test_session_destroy(): void $this->assertEquals(StatusCode::STATUS_OK, $span->getStatus()->getCode()); $this->assertTrue($attributes->get('session.destroy.success')); } + /** + * @runInSeparateProcess + */ + public function test_session_write_close(): void + { + // Start a session first + session_start(); + + // Set a session variable + $_SESSION['test'] = 'value'; + + // Clear the storage to only capture the write_close operation + $this->storage->exchangeArray([]); + + // Write and close the session + session_write_close(); + + // Verify the span was created + $this->assertCount(1, $this->storage); + $span = $this->storage[0]; + + // Check span name + $this->assertEquals('session.write_close', $span->getName()); + + // Check attributes + $attributes = $span->getAttributes(); + $this->assertEquals('session_write_close', $attributes->get(TraceAttributes::CODE_FUNCTION_NAME)); + + // Check session information + $this->assertNotNull($attributes->get('session.id')); + $this->assertNotNull($attributes->get('session.name')); + + // Check status + $this->assertEquals(StatusCode::STATUS_OK, $span->getStatus()->getCode()); + $this->assertTrue($attributes->get('session.write_close.success')); + } + + /** + * @runInSeparateProcess + */ + public function test_session_unset(): void + { + // Start a session first + session_start(); + + // Set a session variable + $_SESSION['test'] = 'value'; + + // Clear the storage to only capture the unset operation + $this->storage->exchangeArray([]); + + // Unset all session variables + session_unset(); + + // Verify the span was created + $this->assertCount(1, $this->storage); + $span = $this->storage[0]; + + // Check span name + $this->assertEquals('session.unset', $span->getName()); + + // Check attributes + $attributes = $span->getAttributes(); + $this->assertEquals('session_unset', $attributes->get(TraceAttributes::CODE_FUNCTION_NAME)); + + // Check session information + $this->assertNotNull($attributes->get('session.id')); + $this->assertNotNull($attributes->get('session.name')); + + // Check status + $this->assertEquals(StatusCode::STATUS_OK, $span->getStatus()->getCode()); + $this->assertTrue($attributes->get('session.unset.success')); + + // Clean up + session_destroy(); + } + + /** + * @runInSeparateProcess + */ + public function test_session_abort(): void + { + // Start a session first + session_start(); + + // Set a session variable + $_SESSION['test'] = 'value'; + + // Clear the storage to only capture the abort operation + $this->storage->exchangeArray([]); + + // Abort the session + session_abort(); + + // Verify the span was created + $this->assertCount(1, $this->storage); + $span = $this->storage[0]; + + // Check span name + $this->assertEquals('session.abort', $span->getName()); + + // Check attributes + $attributes = $span->getAttributes(); + $this->assertEquals('session_abort', $attributes->get(TraceAttributes::CODE_FUNCTION_NAME)); + + // Check session information + $this->assertNotNull($attributes->get('session.id')); + $this->assertNotNull($attributes->get('session.name')); + + // Check status + $this->assertEquals(StatusCode::STATUS_OK, $span->getStatus()->getCode()); + $this->assertTrue($attributes->get('session.abort.success')); + } /** * @runInSeparateProcess From f2c03c78bb6aac0eb7a0acf466e0a740a1985b2b Mon Sep 17 00:00:00 2001 From: HeenaBansal20 Date: Tue, 26 Aug 2025 21:27:05 -0700 Subject: [PATCH 09/16] Addressed review comments --- .gitsplit.yml | 2 +- src/Instrumentation/Session/README.md | 42 +++++++------------ src/Instrumentation/Session/_register.php | 2 +- src/Instrumentation/Session/composer.json | 2 +- .../Session/src/PhpSessionInstrumentation.php | 40 +++++++----------- .../PhpSessionInstrumentationTest.php | 37 +++++++--------- 6 files changed, 48 insertions(+), 77 deletions(-) diff --git a/.gitsplit.yml b/.gitsplit.yml index 358c6bad0..b4f77feaf 100644 --- a/.gitsplit.yml +++ b/.gitsplit.yml @@ -61,7 +61,7 @@ splits: - prefix: "src/Instrumentation/ReactPHP" target: "https://${GH_TOKEN}@github.com/opentelemetry-php/contrib-auto-reactphp.git" - prefix: "src/Instrumentation/Session" - target: "https://${GH_TOKEN}@github.com/opentelemetry-php/contrib-auto-php-session.git" + target: "https://${GH_TOKEN}@github.com/opentelemetry-php/contrib-auto-session.git" - prefix: "src/Instrumentation/Slim" target: "https://${GH_TOKEN}@github.com/opentelemetry-php/contrib-auto-slim.git" - prefix: "src/Instrumentation/Symfony" diff --git a/src/Instrumentation/Session/README.md b/src/Instrumentation/Session/README.md index 3eb404d20..73e4b4fa9 100644 --- a/src/Instrumentation/Session/README.md +++ b/src/Instrumentation/Session/README.md @@ -5,7 +5,7 @@ This package provides auto-instrumentation for PHP's native session functions. ## Installation ```bash -composer require open-telemetry/opentelemetry-auto-php-session +composer require open-telemetry/opentelemetry-auto-session ``` ## Usage @@ -33,57 +33,43 @@ session_destroy(); When `session_start()` is called, a span named `session.start` is created with the following attributes: - `code.function_name`: The function name (`session_start`) -- `code.file_path`: The file path where the function was called -- `code.line_number`: The line number where the function was called -- `session.options.*`: Any options passed to `session_start()` -- `session.id`: The session ID (if session was successfully started) -- `session.name`: The session name (if session was successfully started) -- `session.status`: Either "active" or "inactive" -- `session.cookie.*`: Session cookie parameters +- `php.session.options.*`: Any options passed to `session_start()` +- `php.session.id`: The session ID (if session was successfully started) +- `php.session.name`: The session name (if session was successfully started) +- `php.session.status`: Either "active" or "inactive" +- `php.session.cookie.*`: Session cookie parameters ### session.destroy When `session_destroy()` is called, a span named `session.destroy` is created with the following attributes: - `code.function_name`: The function name (`session_destroy`) -- `code.file_path`: The file path where the function was called -- `code.line_number`: The line number where the function was called -- `session.id`: The session ID (if available) -- `session.name`: The session name (if available) -- `session.destroy.success`: Boolean indicating if the session was successfully destroyed +- `php.session.id`: The session ID (if available) +- `php.session.name`: The session name (if available) ### session.write_close When `session_write_close()` is called, a span named `session.write_close` is created with the following attributes: - `code.function.name`: The function name (`session_write_close`) -- `code.filepath`: The file path where the function was called -- `code.lineno`: The line number where the function was called -- `session.id`: The session ID (if available) -- `session.name`: The session name (if available) -- `session.write_close.success`: Boolean indicating if the session was successfully written and closed +- `php.session.id`: The session ID (if available) +- `php.session.name`: The session name (if available) ### session.unset When `session_unset()` is called, a span named `session.unset` is created with the following attributes: - `code.function.name`: The function name (`session_unset`) -- `code.filepath`: The file path where the function was called -- `code.lineno`: The line number where the function was called -- `session.id`: The session ID (if available) -- `session.name`: The session name (if available) -- `session.unset.success`: Boolean indicating if the session variables were successfully unset +- `php.session.id`: The session ID (if available) +- `php.session.name`: The session name (if available) ### session.abort When `session_abort()` is called, a span named `session.abort` is created with the following attributes: - `code.function.name`: The function name (`session_abort`) -- `code.filepath`: The file path where the function was called -- `code.lineno`: The line number where the function was called -- `session.id`: The session ID (if available) -- `session.name`: The session name (if available) -- `session.abort.success`: Boolean indicating if the session was successfully aborted +- `php.session.id`: The session ID (if available) +- `php.session.name`: The session name (if available) ## License diff --git a/src/Instrumentation/Session/_register.php b/src/Instrumentation/Session/_register.php index d4bf02f67..0189ea761 100644 --- a/src/Instrumentation/Session/_register.php +++ b/src/Instrumentation/Session/_register.php @@ -10,7 +10,7 @@ } if (extension_loaded('opentelemetry') === false) { - trigger_error('The opentelemetry extension must be loaded in order to autoload the OpenTelemetry IO auto-instrumentation', E_USER_WARNING); + trigger_error('The opentelemetry extension must be loaded in order to autoload the OpenTelemetry Session auto-instrumentation', E_USER_WARNING); return; } diff --git a/src/Instrumentation/Session/composer.json b/src/Instrumentation/Session/composer.json index 4184e84bf..e8988fd94 100644 --- a/src/Instrumentation/Session/composer.json +++ b/src/Instrumentation/Session/composer.json @@ -1,5 +1,5 @@ { - "name": "open-telemetry/opentelemetry-auto-php-session", + "name": "open-telemetry/opentelemetry-auto-session", "description": "OpenTelemetry auto-instrumentation for PHP Session functions", "keywords": ["opentelemetry", "otel", "open-telemetry", "tracing", "php", "session"], "type": "library", diff --git a/src/Instrumentation/Session/src/PhpSessionInstrumentation.php b/src/Instrumentation/Session/src/PhpSessionInstrumentation.php index 41b75af8d..a1021a1f2 100644 --- a/src/Instrumentation/Session/src/PhpSessionInstrumentation.php +++ b/src/Instrumentation/Session/src/PhpSessionInstrumentation.php @@ -25,7 +25,7 @@ public static function register(): void $instrumentation = new CachedInstrumentation( 'io.opentelemetry.contrib.php.session', null, - Version::VERSION_1_32_0->url(), + Version::VERSION_1_36_0->url(), ); self::_hook($instrumentation, null, 'session_start', 'session.start'); @@ -44,9 +44,10 @@ private static function _hook(CachedInstrumentation $instrumentation, ?string $c hook( class: $class, function: $function, + /** @psalm-suppress UnusedClosureParam */ pre: static function ($object, ?array $params, ?string $class, string $function, ?string $filename, ?int $lineno) use ($instrumentation, $name) { /** @psalm-suppress ArgumentTypeCoercion */ - $builder = self::makeBuilder($instrumentation, $name, $function, $filename, $lineno); + $builder = self::makeBuilder($instrumentation, $name, $function); self::addParams($builder, $function, $params); $parent = Context::getCurrent(); $span = $builder->startSpan(); @@ -61,17 +62,13 @@ function: $function, private static function makeBuilder( CachedInstrumentation $instrumentation, string $name, - string $function, - ?string $filename, - ?int $lineno + string $function ): SpanBuilderInterface { /** @psalm-suppress ArgumentTypeCoercion */ return $instrumentation->tracer() ->spanBuilder($name) ->setSpanKind(SpanKind::KIND_INTERNAL) - ->setAttribute(TraceAttributes::CODE_FUNCTION_NAME, $function) - ->setAttribute(TraceAttributes::CODE_FILE_PATH, $filename) - ->setAttribute(TraceAttributes::CODE_LINE_NUMBER, $lineno); + ->setAttribute(TraceAttributes::CODE_FUNCTION_NAME, $function); } private static function end(?Throwable $exception, string $function): void @@ -86,52 +83,47 @@ private static function end(?Throwable $exception, string $function): void // Add session-specific attributes in the post hook if ($function === 'session_start') { $isSessionActive = !empty(session_id()); - $span->setAttribute('session.id', session_id()); - $span->setAttribute('session.name', session_name()); - $span->setAttribute('session.status', $isSessionActive ? 'active' : 'inactive'); + $span->setAttribute('php.session.id', session_id()); + $span->setAttribute('php.session.name', session_name()); + $span->setAttribute('php.session.status', $isSessionActive ? 'active' : 'inactive'); // Add session cookie parameters $cookieParams = session_get_cookie_params(); foreach ($cookieParams as $key => $value) { if (is_scalar($value)) { - $span->setAttribute("session.cookie.$key", $value); + $span->setAttribute("php.session.cookie.$key", $value); } } - } elseif ($function === 'session_destroy') { - $span->setAttribute('session.destroy.success', $exception === null); } elseif ($function === 'session_write_close') { $sessionId = session_id(); $sessionName = session_name(); // Add session information if (!empty($sessionId)) { - $span->setAttribute('session.id', $sessionId); - $span->setAttribute('session.name', $sessionName); + $span->setAttribute('php.session.id', $sessionId); + $span->setAttribute('php.session.name', $sessionName); } - $span->setAttribute('session.write_close.success', $exception === null); } elseif ($function === 'session_unset') { $sessionId = session_id(); $sessionName = session_name(); // Add session information if (!empty($sessionId)) { - $span->setAttribute('session.id', $sessionId); - $span->setAttribute('session.name', $sessionName); + $span->setAttribute('php.session.id', $sessionId); + $span->setAttribute('php.session.name', $sessionName); } - $span->setAttribute('session.unset.success', $exception === null); } elseif ($function === 'session_abort') { $sessionId = session_id(); $sessionName = session_name(); // Add session information if (!empty($sessionId)) { - $span->setAttribute('session.id', $sessionId); - $span->setAttribute('session.name', $sessionName); + $span->setAttribute('php.session.id', $sessionId); + $span->setAttribute('php.session.name', $sessionName); } - $span->setAttribute('session.abort.success', $exception === null); } if ($exception) { $span->recordException($exception); @@ -154,7 +146,7 @@ private static function addParams(SpanBuilderInterface $builder, string $functio if (isset($params[0]) && is_array($params[0])) { foreach ($params[0] as $key => $value) { if (is_scalar($value)) { - $builder->setAttribute("session.options.$key", $value); + $builder->setAttribute("php.session.options.$key", $value); } } } diff --git a/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php b/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php index 4a5f44bf4..f8b6e193b 100644 --- a/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php +++ b/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php @@ -59,19 +59,19 @@ public function test_session_start(): void $this->assertEquals('session_start', $attributes->get(TraceAttributes::CODE_FUNCTION_NAME)); // Check session options were recorded - $this->assertTrue($attributes->get('session.options.read_and_close')); - $this->assertEquals(3600, $attributes->get('session.options.cookie_lifetime')); + $this->assertTrue($attributes->get('php.session.options.read_and_close')); + $this->assertEquals(3600, $attributes->get('php.session.options.cookie_lifetime')); // Check session information - $this->assertEquals(session_id(), $attributes->get('session.id')); - $this->assertEquals(session_name(), $attributes->get('session.name')); - $this->assertEquals('active', $attributes->get('session.status')); + $this->assertEquals(session_id(), $attributes->get('php.session.id')); + $this->assertEquals(session_name(), $attributes->get('php.session.name')); + $this->assertEquals('active', $attributes->get('php.session.status')); // Check cookie parameters $cookieParams = session_get_cookie_params(); foreach ($cookieParams as $key => $value) { if (is_scalar($value)) { - $this->assertEquals($value, $attributes->get("session.cookie.$key")); + $this->assertEquals($value, $attributes->get("php.session.cookie.$key")); } } @@ -109,7 +109,6 @@ public function test_session_destroy(): void // Check status $this->assertEquals(StatusCode::STATUS_OK, $span->getStatus()->getCode()); - $this->assertTrue($attributes->get('session.destroy.success')); } /** * @runInSeparateProcess @@ -121,16 +120,13 @@ public function test_session_write_close(): void // Set a session variable $_SESSION['test'] = 'value'; - - // Clear the storage to only capture the write_close operation - $this->storage->exchangeArray([]); - + // Write and close the session session_write_close(); // Verify the span was created - $this->assertCount(1, $this->storage); - $span = $this->storage[0]; + $this->assertCount(2, $this->storage); + $span = $this->storage[1]; // Check span name $this->assertEquals('session.write_close', $span->getName()); @@ -140,12 +136,11 @@ public function test_session_write_close(): void $this->assertEquals('session_write_close', $attributes->get(TraceAttributes::CODE_FUNCTION_NAME)); // Check session information - $this->assertNotNull($attributes->get('session.id')); - $this->assertNotNull($attributes->get('session.name')); + $this->assertEquals(session_id(), $attributes->get('php.session.id')); + $this->assertNotNull($attributes->get('php.session.name')); // Check status $this->assertEquals(StatusCode::STATUS_OK, $span->getStatus()->getCode()); - $this->assertTrue($attributes->get('session.write_close.success')); } /** @@ -177,12 +172,11 @@ public function test_session_unset(): void $this->assertEquals('session_unset', $attributes->get(TraceAttributes::CODE_FUNCTION_NAME)); // Check session information - $this->assertNotNull($attributes->get('session.id')); - $this->assertNotNull($attributes->get('session.name')); + $this->assertNotNull($attributes->get('php.session.id')); + $this->assertNotNull($attributes->get('php.session.name')); // Check status $this->assertEquals(StatusCode::STATUS_OK, $span->getStatus()->getCode()); - $this->assertTrue($attributes->get('session.unset.success')); // Clean up session_destroy(); @@ -217,12 +211,11 @@ public function test_session_abort(): void $this->assertEquals('session_abort', $attributes->get(TraceAttributes::CODE_FUNCTION_NAME)); // Check session information - $this->assertNotNull($attributes->get('session.id')); - $this->assertNotNull($attributes->get('session.name')); + $this->assertNotNull($attributes->get('php.session.id')); + $this->assertNotNull($attributes->get('php.session.name')); // Check status $this->assertEquals(StatusCode::STATUS_OK, $span->getStatus()->getCode()); - $this->assertTrue($attributes->get('session.abort.success')); } /** From f1833eaf657a7e0badf15a816496b904a5bb5837 Mon Sep 17 00:00:00 2001 From: HeenaBansal20 Date: Wed, 27 Aug 2025 03:44:08 -0700 Subject: [PATCH 10/16] redacted cookie value from span --- src/Instrumentation/Session/src/PhpSessionInstrumentation.php | 2 +- .../tests/Integration/PhpSessionInstrumentationTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Instrumentation/Session/src/PhpSessionInstrumentation.php b/src/Instrumentation/Session/src/PhpSessionInstrumentation.php index a1021a1f2..70657d4d7 100644 --- a/src/Instrumentation/Session/src/PhpSessionInstrumentation.php +++ b/src/Instrumentation/Session/src/PhpSessionInstrumentation.php @@ -91,7 +91,7 @@ private static function end(?Throwable $exception, string $function): void $cookieParams = session_get_cookie_params(); foreach ($cookieParams as $key => $value) { if (is_scalar($value)) { - $span->setAttribute("php.session.cookie.$key", $value); + $span->setAttribute("php.session.cookie.$key", ''); } } } elseif ($function === 'session_write_close') { diff --git a/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php b/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php index f8b6e193b..81bae7f93 100644 --- a/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php +++ b/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php @@ -43,7 +43,7 @@ public function test_session_start(): void 'read_and_close' => true, 'cookie_lifetime' => 3600, ]; - + session_start($options); // Verify the span was created @@ -71,7 +71,7 @@ public function test_session_start(): void $cookieParams = session_get_cookie_params(); foreach ($cookieParams as $key => $value) { if (is_scalar($value)) { - $this->assertEquals($value, $attributes->get("php.session.cookie.$key")); + $this->assertEquals('', $attributes->get("php.session.cookie.$key")); } } From e4b7335819f3d19a7ee2368085e9fb2e0af4b73c Mon Sep 17 00:00:00 2001 From: HeenaBansal20 Date: Wed, 27 Aug 2025 08:36:17 -0700 Subject: [PATCH 11/16] Refactored code to use switch statement --- .../Session/src/PhpSessionInstrumentation.php | 71 ++++++++----------- .../PhpSessionInstrumentationTest.php | 7 +- 2 files changed, 36 insertions(+), 42 deletions(-) diff --git a/src/Instrumentation/Session/src/PhpSessionInstrumentation.php b/src/Instrumentation/Session/src/PhpSessionInstrumentation.php index 70657d4d7..add76455d 100644 --- a/src/Instrumentation/Session/src/PhpSessionInstrumentation.php +++ b/src/Instrumentation/Session/src/PhpSessionInstrumentation.php @@ -79,52 +79,41 @@ private static function end(?Throwable $exception, string $function): void } $scope->detach(); $span = Span::fromContext($scope->context()); - - // Add session-specific attributes in the post hook - if ($function === 'session_start') { - $isSessionActive = !empty(session_id()); - $span->setAttribute('php.session.id', session_id()); - $span->setAttribute('php.session.name', session_name()); - $span->setAttribute('php.session.status', $isSessionActive ? 'active' : 'inactive'); - // Add session cookie parameters - $cookieParams = session_get_cookie_params(); - foreach ($cookieParams as $key => $value) { - if (is_scalar($value)) { - $span->setAttribute("php.session.cookie.$key", ''); + switch ($function) { + case 'session_start': + $isSessionActive = !empty(session_id()); + $span->setAttribute('php.session.status', $isSessionActive ? 'active' : 'inactive'); + // Add session cookie parameters + $cookieParams = session_get_cookie_params(); + $index = 0; + ksort($cookieParams); + foreach ($cookieParams as $key => $value) { + if (is_scalar($value)) { + $span->setAttribute("php.session.cookie.key[$index]", $key); + } + $index++; + } - } - } elseif ($function === 'session_write_close') { - $sessionId = session_id(); - $sessionName = session_name(); - - // Add session information - if (!empty($sessionId)) { - $span->setAttribute('php.session.id', $sessionId); - $span->setAttribute('php.session.name', $sessionName); - } - - } elseif ($function === 'session_unset') { - $sessionId = session_id(); - $sessionName = session_name(); - - // Add session information - if (!empty($sessionId)) { - $span->setAttribute('php.session.id', $sessionId); - $span->setAttribute('php.session.name', $sessionName); - } - - } elseif ($function === 'session_abort') { - $sessionId = session_id(); - $sessionName = session_name(); + // no break + case 'session_write_close': + case 'session_unset': + case 'session_abort': + $sessionId = session_id(); + $sessionName = session_name(); - // Add session information - if (!empty($sessionId)) { - $span->setAttribute('php.session.id', $sessionId); - $span->setAttribute('php.session.name', $sessionName); - } + // Add session information + if (!empty($sessionId)) { + $span->setAttribute('php.session.id', $sessionId); + $span->setAttribute('php.session.name', $sessionName); + } + + break; + case 'session_destroy': + break; } + if ($exception) { $span->recordException($exception); $span->setStatus(StatusCode::STATUS_ERROR, $exception->getMessage()); diff --git a/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php b/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php index 81bae7f93..dd5b6a22c 100644 --- a/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php +++ b/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php @@ -69,9 +69,14 @@ public function test_session_start(): void // Check cookie parameters $cookieParams = session_get_cookie_params(); + $index = 0; + ksort($cookieParams); foreach ($cookieParams as $key => $value) { if (is_scalar($value)) { - $this->assertEquals('', $attributes->get("php.session.cookie.$key")); + $expected = $key; + $actual = $attributes->get("php.session.cookie.key[$index]"); + $this->assertEquals($expected, $actual); + $index++; } } From a80ab768898dcfb42dc5544b17d91772f247c87d Mon Sep 17 00:00:00 2001 From: HeenaBansal20 Date: Wed, 27 Aug 2025 09:56:24 -0700 Subject: [PATCH 12/16] Updated session-destroy instrumention to ad sessionid and name --- .../Session/src/PhpSessionInstrumentation.php | 27 ++++++++++++------- .../PhpSessionInstrumentationTest.php | 8 ++++-- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/Instrumentation/Session/src/PhpSessionInstrumentation.php b/src/Instrumentation/Session/src/PhpSessionInstrumentation.php index add76455d..38a0c6107 100644 --- a/src/Instrumentation/Session/src/PhpSessionInstrumentation.php +++ b/src/Instrumentation/Session/src/PhpSessionInstrumentation.php @@ -53,7 +53,9 @@ function: $function, $span = $builder->startSpan(); Context::storage()->attach($span->storeInContext($parent)); }, - post: static function ($object, ?array $params, mixed $return, ?Throwable $exception) use ($function) { + post: static function ($object, ?array $params, mixed $return, ?Throwable $exception) use ($instrumentation, $name, $function) { + $builder = self::makeBuilder($instrumentation, $name, $function); + self::addParams($builder, $function, $params); self::end($exception, $function); } ); @@ -82,8 +84,10 @@ private static function end(?Throwable $exception, string $function): void switch ($function) { case 'session_start': - $isSessionActive = !empty(session_id()); - $span->setAttribute('php.session.status', $isSessionActive ? 'active' : 'inactive'); + $sessionId = session_id(); + if (!empty($sessionId)) { + $span->setAttribute('php.session.status', session_status()); + } // Add session cookie parameters $cookieParams = session_get_cookie_params(); $index = 0; @@ -99,19 +103,15 @@ private static function end(?Throwable $exception, string $function): void case 'session_write_close': case 'session_unset': case 'session_abort': - $sessionId = session_id(); - $sessionName = session_name(); - + $sessionId = session_id(); // Add session information if (!empty($sessionId)) { $span->setAttribute('php.session.id', $sessionId); - $span->setAttribute('php.session.name', $sessionName); + $span->setAttribute('php.session.name', session_name()); } - - break; + // no break case 'session_destroy': break; - } if ($exception) { @@ -143,6 +143,13 @@ private static function addParams(SpanBuilderInterface $builder, string $functio break; case 'session_destroy': + $sessionId = session_id(); + // Add session information + if (!empty($sessionId)) { + $builder->setAttribute('php.session.id', $sessionId); + $builder->setAttribute('php.session.name', session_name()); + } + // No parameters to add for session_destroy break; } diff --git a/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php b/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php index dd5b6a22c..cbe740544 100644 --- a/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php +++ b/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php @@ -49,7 +49,7 @@ public function test_session_start(): void // Verify the span was created $this->assertCount(1, $this->storage); $span = $this->storage[0]; - + echo session_status(); // Check span name $this->assertEquals('session.start', $span->getName()); @@ -65,7 +65,7 @@ public function test_session_start(): void // Check session information $this->assertEquals(session_id(), $attributes->get('php.session.id')); $this->assertEquals(session_name(), $attributes->get('php.session.name')); - $this->assertEquals('active', $attributes->get('php.session.status')); + $this->assertEquals(session_status(), $attributes->get('php.session.status')); // Check cookie parameters $cookieParams = session_get_cookie_params(); @@ -111,6 +111,10 @@ public function test_session_destroy(): void $attributes = $span->getAttributes(); $this->assertEquals('session_destroy', $attributes->get(TraceAttributes::CODE_FUNCTION_NAME)); + + // Check session information + $this->assertNotNull($attributes->get('php.session.id')); + $this->assertNotNull($attributes->get('php.session.name')); // Check status $this->assertEquals(StatusCode::STATUS_OK, $span->getStatus()->getCode()); From a9b9f0ef589707e4e6504d08b808f646d9c07672 Mon Sep 17 00:00:00 2001 From: HeenaBansal20 Date: Wed, 27 Aug 2025 18:25:13 -0700 Subject: [PATCH 13/16] Using return value to set the span status --- .../Session/src/PhpSessionInstrumentation.php | 19 +++++++++++++------ .../PhpSessionInstrumentationTest.php | 4 ++-- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/Instrumentation/Session/src/PhpSessionInstrumentation.php b/src/Instrumentation/Session/src/PhpSessionInstrumentation.php index 38a0c6107..58d81cb96 100644 --- a/src/Instrumentation/Session/src/PhpSessionInstrumentation.php +++ b/src/Instrumentation/Session/src/PhpSessionInstrumentation.php @@ -56,7 +56,7 @@ function: $function, post: static function ($object, ?array $params, mixed $return, ?Throwable $exception) use ($instrumentation, $name, $function) { $builder = self::makeBuilder($instrumentation, $name, $function); self::addParams($builder, $function, $params); - self::end($exception, $function); + self::end($exception, $function, $return); } ); } @@ -73,7 +73,7 @@ private static function makeBuilder( ->setAttribute(TraceAttributes::CODE_FUNCTION_NAME, $function); } - private static function end(?Throwable $exception, string $function): void + private static function end(?Throwable $exception, string $function, mixed $return = null): void { $scope = Context::storage()->scope(); if (!$scope) { @@ -84,10 +84,10 @@ private static function end(?Throwable $exception, string $function): void switch ($function) { case 'session_start': - $sessionId = session_id(); - if (!empty($sessionId)) { - $span->setAttribute('php.session.status', session_status()); - } + // Use the return value to determine if session was successfully started + $sessionStartSuccess = $exception === null && $return === true; + $span->setAttribute('php.session.status', $sessionStartSuccess ? 'active' : 'inactive'); + // Add session cookie parameters $cookieParams = session_get_cookie_params(); $index = 0; @@ -99,6 +99,9 @@ private static function end(?Throwable $exception, string $function): void $index++; } + if (!$sessionStartSuccess && $exception === null) { + $span->setStatus(StatusCode::STATUS_ERROR, 'Session start failed'); + } // no break case 'session_write_close': case 'session_unset': @@ -114,9 +117,13 @@ private static function end(?Throwable $exception, string $function): void break; } + // Set span status based on return value + $Success = $exception === null && $return === true; if ($exception) { $span->recordException($exception); $span->setStatus(StatusCode::STATUS_ERROR, $exception->getMessage()); + } elseif (!$Success) { + $span->setStatus(StatusCode::STATUS_ERROR, "$function failed with return code $return"); } else { $span->setStatus(StatusCode::STATUS_OK); } diff --git a/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php b/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php index cbe740544..6be98fefa 100644 --- a/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php +++ b/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php @@ -44,7 +44,7 @@ public function test_session_start(): void 'cookie_lifetime' => 3600, ]; - session_start($options); + $return = session_start($options); // Verify the span was created $this->assertCount(1, $this->storage); @@ -65,7 +65,7 @@ public function test_session_start(): void // Check session information $this->assertEquals(session_id(), $attributes->get('php.session.id')); $this->assertEquals(session_name(), $attributes->get('php.session.name')); - $this->assertEquals(session_status(), $attributes->get('php.session.status')); + $this->assertEquals($return, $attributes->get('php.session.status')); // Check cookie parameters $cookieParams = session_get_cookie_params(); From 409efaea4c4392a0fb7fa62c7c1ccf358d2f86b9 Mon Sep 17 00:00:00 2001 From: HeenaBansal20 Date: Thu, 28 Aug 2025 14:47:21 -0700 Subject: [PATCH 14/16] removed exception handler since session function doesnt throw exception in any case --- .../Session/src/PhpSessionInstrumentation.php | 17 ++++++----------- .../PhpSessionInstrumentationTest.php | 18 ++---------------- 2 files changed, 8 insertions(+), 27 deletions(-) diff --git a/src/Instrumentation/Session/src/PhpSessionInstrumentation.php b/src/Instrumentation/Session/src/PhpSessionInstrumentation.php index 58d81cb96..eef75d87a 100644 --- a/src/Instrumentation/Session/src/PhpSessionInstrumentation.php +++ b/src/Instrumentation/Session/src/PhpSessionInstrumentation.php @@ -53,9 +53,7 @@ function: $function, $span = $builder->startSpan(); Context::storage()->attach($span->storeInContext($parent)); }, - post: static function ($object, ?array $params, mixed $return, ?Throwable $exception) use ($instrumentation, $name, $function) { - $builder = self::makeBuilder($instrumentation, $name, $function); - self::addParams($builder, $function, $params); + post: static function ($object, ?array $params, mixed $return, ?Throwable $exception) use ($function) { self::end($exception, $function, $return); } ); @@ -85,7 +83,7 @@ private static function end(?Throwable $exception, string $function, mixed $retu switch ($function) { case 'session_start': // Use the return value to determine if session was successfully started - $sessionStartSuccess = $exception === null && $return === true; + $sessionStartSuccess = $return === true; $span->setAttribute('php.session.status', $sessionStartSuccess ? 'active' : 'inactive'); // Add session cookie parameters @@ -99,8 +97,8 @@ private static function end(?Throwable $exception, string $function, mixed $retu $index++; } - if (!$sessionStartSuccess && $exception === null) { - $span->setStatus(StatusCode::STATUS_ERROR, 'Session start failed'); + if (!$sessionStartSuccess) { + $span->setStatus(StatusCode::STATUS_ERROR, "$function failed with return code $return"); } // no break case 'session_write_close': @@ -118,11 +116,8 @@ private static function end(?Throwable $exception, string $function, mixed $retu } // Set span status based on return value - $Success = $exception === null && $return === true; - if ($exception) { - $span->recordException($exception); - $span->setStatus(StatusCode::STATUS_ERROR, $exception->getMessage()); - } elseif (!$Success) { + $Success = $return === true; + if (!$Success) { $span->setStatus(StatusCode::STATUS_ERROR, "$function failed with return code $return"); } else { $span->setStatus(StatusCode::STATUS_OK); diff --git a/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php b/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php index 6be98fefa..19b76c1c6 100644 --- a/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php +++ b/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php @@ -49,7 +49,7 @@ public function test_session_start(): void // Verify the span was created $this->assertCount(1, $this->storage); $span = $this->storage[0]; - echo session_status(); + // Check span name $this->assertEquals('session.start', $span->getName()); @@ -226,19 +226,5 @@ public function test_session_abort(): void // Check status $this->assertEquals(StatusCode::STATUS_OK, $span->getStatus()->getCode()); } - - /** - * @runInSeparateProcess - */ - public function test_session_start_exception_simulation(): void - { - // This test simulates an exception during session_start - // We can't easily trigger a real exception in session_start during testing, - // so we'll verify the exception handling code path by checking the instrumentation code - - // The PhpSessionInstrumentation class has exception handling in the post hook - // that sets the span status to ERROR and records the exception - - $this->assertTrue(true, 'Exception handling is implemented in the instrumentation'); - } + } From 9620c914c9ed63659028e2c0adac57fbfb3e200a Mon Sep 17 00:00:00 2001 From: HeenaBansal20 Date: Mon, 1 Sep 2025 20:22:40 -0700 Subject: [PATCH 15/16] Storing cookie keys in array and setting only error status --- .../Session/src/PhpSessionInstrumentation.php | 9 +++----- .../PhpSessionInstrumentationTest.php | 21 +++++++++---------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/Instrumentation/Session/src/PhpSessionInstrumentation.php b/src/Instrumentation/Session/src/PhpSessionInstrumentation.php index eef75d87a..c410d9ef2 100644 --- a/src/Instrumentation/Session/src/PhpSessionInstrumentation.php +++ b/src/Instrumentation/Session/src/PhpSessionInstrumentation.php @@ -88,15 +88,14 @@ private static function end(?Throwable $exception, string $function, mixed $retu // Add session cookie parameters $cookieParams = session_get_cookie_params(); - $index = 0; + $cookieKeys = []; ksort($cookieParams); foreach ($cookieParams as $key => $value) { if (is_scalar($value)) { - $span->setAttribute("php.session.cookie.key[$index]", $key); + $cookieKeys[] = $key; } - $index++; - } + $span->setAttribute('php.session.cookie.keys', $cookieKeys); if (!$sessionStartSuccess) { $span->setStatus(StatusCode::STATUS_ERROR, "$function failed with return code $return"); } @@ -119,8 +118,6 @@ private static function end(?Throwable $exception, string $function, mixed $retu $Success = $return === true; if (!$Success) { $span->setStatus(StatusCode::STATUS_ERROR, "$function failed with return code $return"); - } else { - $span->setStatus(StatusCode::STATUS_OK); } $span->end(); diff --git a/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php b/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php index 19b76c1c6..9af5ea5d4 100644 --- a/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php +++ b/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php @@ -69,19 +69,18 @@ public function test_session_start(): void // Check cookie parameters $cookieParams = session_get_cookie_params(); - $index = 0; + $expectedKeys = []; ksort($cookieParams); foreach ($cookieParams as $key => $value) { if (is_scalar($value)) { - $expected = $key; - $actual = $attributes->get("php.session.cookie.key[$index]"); - $this->assertEquals($expected, $actual); - $index++; + $expectedKeys[] = $key; } } - + $actualKeys = $attributes->get('php.session.cookie.keys'); + $this->assertEquals($expectedKeys, $actualKeys); + // Check status - $this->assertEquals(StatusCode::STATUS_OK, $span->getStatus()->getCode()); + $this->assertEquals(StatusCode::STATUS_UNSET, $span->getStatus()->getCode()); // Clean up if (session_status() === PHP_SESSION_ACTIVE) { @@ -117,7 +116,7 @@ public function test_session_destroy(): void $this->assertNotNull($attributes->get('php.session.name')); // Check status - $this->assertEquals(StatusCode::STATUS_OK, $span->getStatus()->getCode()); + $this->assertEquals(StatusCode::STATUS_UNSET, $span->getStatus()->getCode()); } /** * @runInSeparateProcess @@ -149,7 +148,7 @@ public function test_session_write_close(): void $this->assertNotNull($attributes->get('php.session.name')); // Check status - $this->assertEquals(StatusCode::STATUS_OK, $span->getStatus()->getCode()); + $this->assertEquals(StatusCode::STATUS_UNSET, $span->getStatus()->getCode()); } /** @@ -185,7 +184,7 @@ public function test_session_unset(): void $this->assertNotNull($attributes->get('php.session.name')); // Check status - $this->assertEquals(StatusCode::STATUS_OK, $span->getStatus()->getCode()); + $this->assertEquals(StatusCode::STATUS_UNSET, $span->getStatus()->getCode()); // Clean up session_destroy(); @@ -224,7 +223,7 @@ public function test_session_abort(): void $this->assertNotNull($attributes->get('php.session.name')); // Check status - $this->assertEquals(StatusCode::STATUS_OK, $span->getStatus()->getCode()); + $this->assertEquals(StatusCode::STATUS_UNSET, $span->getStatus()->getCode()); } } From 66efcf7ad098190488bc5c51e92f8d787d687bb6 Mon Sep 17 00:00:00 2001 From: HeenaBansal20 Date: Wed, 3 Sep 2025 21:07:19 -0700 Subject: [PATCH 16/16] Removed unneceesary checkaround cookie value --- .../Session/src/PhpSessionInstrumentation.php | 10 ++-------- .../Integration/PhpSessionInstrumentationTest.php | 11 ++++------- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/Instrumentation/Session/src/PhpSessionInstrumentation.php b/src/Instrumentation/Session/src/PhpSessionInstrumentation.php index c410d9ef2..6195f3237 100644 --- a/src/Instrumentation/Session/src/PhpSessionInstrumentation.php +++ b/src/Instrumentation/Session/src/PhpSessionInstrumentation.php @@ -87,14 +87,8 @@ private static function end(?Throwable $exception, string $function, mixed $retu $span->setAttribute('php.session.status', $sessionStartSuccess ? 'active' : 'inactive'); // Add session cookie parameters - $cookieParams = session_get_cookie_params(); - $cookieKeys = []; - ksort($cookieParams); - foreach ($cookieParams as $key => $value) { - if (is_scalar($value)) { - $cookieKeys[] = $key; - } - } + $cookieKeys = array_keys(session_get_cookie_params()); + sort($cookieKeys); $span->setAttribute('php.session.cookie.keys', $cookieKeys); if (!$sessionStartSuccess) { $span->setStatus(StatusCode::STATUS_ERROR, "$function failed with return code $return"); diff --git a/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php b/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php index 9af5ea5d4..cb79b40d3 100644 --- a/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php +++ b/src/Instrumentation/Session/tests/Integration/PhpSessionInstrumentationTest.php @@ -69,13 +69,10 @@ public function test_session_start(): void // Check cookie parameters $cookieParams = session_get_cookie_params(); - $expectedKeys = []; - ksort($cookieParams); - foreach ($cookieParams as $key => $value) { - if (is_scalar($value)) { - $expectedKeys[] = $key; - } - } + $expectedKeys = array_keys( + array_filter($cookieParams, 'is_scalar') + ); + sort($expectedKeys); $actualKeys = $attributes->get('php.session.cookie.keys'); $this->assertEquals($expectedKeys, $actualKeys);