From 1acecf10ad99d6817345ab0672f988548c641248 Mon Sep 17 00:00:00 2001 From: AA2512 <58102909+AA2512@users.noreply.github.com> Date: Fri, 3 May 2024 05:31:12 +0530 Subject: [PATCH] Feature/auto instrumentation/cake php (#253) --- .gitattributes | 13 ++++++ .gitignore | 1 + .php-cs-fixer.php | 43 ++++++++++++++++++ README.md | 18 ++++++++ _register.php | 18 ++++++++ composer.json | 50 +++++++++++++++++++++ phpstan.neon.dist | 11 +++++ phpunit.xml.dist | 47 +++++++++++++++++++ psalm.xml.dist | 15 +++++++ src/CakePHPInstrumentation.php | 82 ++++++++++++++++++++++++++++++++++ tests/Integration/.gitkeep | 0 tests/Unit/.gitkeep | 0 12 files changed, 298 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .php-cs-fixer.php create mode 100644 README.md create mode 100644 _register.php create mode 100644 composer.json create mode 100644 phpstan.neon.dist create mode 100644 phpunit.xml.dist create mode 100644 psalm.xml.dist create mode 100644 src/CakePHPInstrumentation.php create mode 100644 tests/Integration/.gitkeep create mode 100644 tests/Unit/.gitkeep diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..ac40e9f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,13 @@ +* text=auto + +*.md diff=markdown +*.php diff=php + +/.gitattributes export-ignore +/.gitignore export-ignore +/.phan 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/.gitignore b/.gitignore new file mode 100644 index 0000000..57872d0 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/vendor/ diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php new file mode 100644 index 0000000..248b4b9 --- /dev/null +++ b/.php-cs-fixer.php @@ -0,0 +1,43 @@ +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, + 'function_typehint_space' => true, + 'include' => true, + 'lowercase_cast' => true, + 'new_with_braces' => 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, + 'single_blank_line_before_namespace' => true, + 'single_quote' => true, + 'trailing_comma_in_multiline' => true, + ]) + ->setRiskyAllowed(true) + ->setFinder($finder); + diff --git a/README.md b/README.md new file mode 100644 index 0000000..3b08b02 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ + +This is a read-only subtree split of https://github.com/open-telemetry/opentelemetry-php-contrib. + +# OpenTelemetry Slim Framework auto-instrumentation +Please read https://opentelemetry.io/docs/instrumentation/php/automatic/ for instructions on how to +install and configure the extension and SDK. + +## Overview +Auto-instrumentation hooks are registered via composer, and spans will automatically be created for: +- `::handle()` - root span + +## Configuration + +The extension can be disabled via [runtime configuration](https://opentelemetry.io/docs/instrumentation/php/sdk/#configuration): + +```shell +OTEL_PHP_DISABLED_INSTRUMENTATIONS=cakephp +``` diff --git a/_register.php b/_register.php new file mode 100644 index 0000000..2f46d14 --- /dev/null +++ b/_register.php @@ -0,0 +1,18 @@ +=4.0", + "open-telemetry/api": "^1.0", + "open-telemetry/sem-conv": "^1.23" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3", + "mockery/mockery": "^1.5", + "nyholm/psr7": "*", + "phan/phan": "^5.0", + "php-http/mock-client": "*", + "phpstan/phpstan-mockery": "^1.1.0", + "phpstan/phpstan": "^1.1", + "phpstan/phpstan-phpunit": "^1.0", + "psalm/plugin-phpunit": "^0.18.4", + "open-telemetry/sdk": "^1.0", + "phpunit/phpunit": "^9.5", + "vimeo/psalm": "^5.24" + }, + "autoload": { + "psr-4": { + "OpenTelemetry\\Contrib\\Instrumentation\\CakePHP\\": "src/" + }, + "files": [ + "_register.php" + ] + }, + "autoload-dev": { + "psr-4": { + "OpenTelemetry\\Tests\\Instrumentation\\CakePHP\\": "tests/" + } + }, + "config": { + "allow-plugins": { + "php-http/discovery": true + } + } +} diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..4033c4d --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,11 @@ +includes: + - vendor/phpstan/phpstan-phpunit/extension.neon + +parameters: + tmpDir: var/cache/phpstan + level: 5 + paths: + - src + - tests + excludePaths: + - tests/* diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..d854885 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,47 @@ + + + + + + + src + + + + + + + + + + + + + tests/Unit + + + tests/Integration + + + + diff --git a/psalm.xml.dist b/psalm.xml.dist new file mode 100644 index 0000000..1557117 --- /dev/null +++ b/psalm.xml.dist @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/src/CakePHPInstrumentation.php b/src/CakePHPInstrumentation.php new file mode 100644 index 0000000..bd71d8f --- /dev/null +++ b/src/CakePHPInstrumentation.php @@ -0,0 +1,82 @@ +getRequest(); + /** @psalm-suppress ArgumentTypeCoercion */ + $builder = $instrumentation->tracer() + ->spanBuilder($request->getMethod()) + ->setSpanKind(SpanKind::KIND_SERVER) + ->setAttribute(TraceAttributes::CODE_FUNCTION, $function) + ->setAttribute(TraceAttributes::CODE_NAMESPACE, $class) + ->setAttribute(TraceAttributes::CODE_FILEPATH, $filename) + ->setAttribute(TraceAttributes::CODE_LINENO, $lineno); + + $parent = Globals::propagator()->extract($request->getHeaders()); + $span = $builder + ->setParent($parent) + ->setAttribute(TraceAttributes::URL_FULL, $request->getUri()->__toString()) + ->setAttribute(TraceAttributes::HTTP_REQUEST_METHOD, $request->getMethod()) + ->setAttribute(TraceAttributes::HTTP_REQUEST_BODY_SIZE, $request->getHeaderLine('Content-Length')) + ->setAttribute(TraceAttributes::USER_AGENT_ORIGINAL, $request->getHeaderLine('User-Agent')) + ->setAttribute(TraceAttributes::SERVER_ADDRESS, $request->getUri()->getHost()) + ->setAttribute(TraceAttributes::SERVER_PORT, $request->getUri()->getPort()) + ->setAttribute(TraceAttributes::URL_SCHEME, $request->getUri()->getScheme()) + ->setAttribute(TraceAttributes::URL_PATH, $request->getUri()->getPath()) + ->startSpan(); + + Context::storage()->attach($span->storeInContext($parent)); + }, + post: static function (Controller $app, array $params, ?ResponseInterface $response, ?Throwable $exception) { + $scope = Context::storage()->scope(); + if (!$scope) { + return; + } + $scope->detach(); + $span = Span::fromContext($scope->context()); + $response = $app->getResponse(); + if ($exception) { + $span->recordException($exception, [TraceAttributes::EXCEPTION_ESCAPED => true]); + $span->setStatus(StatusCode::STATUS_ERROR, $exception->getMessage()); + } + /** @var ResponseInterface|null $response */ + if ($response) { + if ($response->getStatusCode() >= 400) { + $span->setStatus(StatusCode::STATUS_ERROR); + } + $span->setAttribute(TraceAttributes::HTTP_RESPONSE_STATUS_CODE, $response->getStatusCode()); + $span->setAttribute(TraceAttributes::NETWORK_PROTOCOL_VERSION, $response->getProtocolVersion()); + $span->setAttribute(TraceAttributes::HTTP_RESPONSE_BODY_SIZE, $response->getHeaderLine('Content-Length') ?: null); + } + + $span->end(); + }, + ); + } +} diff --git a/tests/Integration/.gitkeep b/tests/Integration/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/Unit/.gitkeep b/tests/Unit/.gitkeep new file mode 100644 index 0000000..e69de29