diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f7e2a69 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +# This file is for unifying the coding style for different editors and IDEs +# editorconfig.org + +root = true + +[*] +end_of_line = lf +charset = utf-8 +indent_style = space +indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.neon] +indent_style = tab + +[phars.xml] +indent_size = 2 + +[*.js] +indent_size = 2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2378dde --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +/composer.lock +*.diff +*.err +*.log +*.orig +*.rej +*.swo +*.swp +*.vi +*~ +.idea/* +nbproject/* +.vscode +.DS_Store +.cache +.phpunit.cache +.project +.settings +.svn +errors.err +tags +node_modules +package-lock.json +/.phpunit.result.cache +/nbproject/ +/tools +/vendor +/phpunit.xml +/webroot/css/style.css.map +/webroot/mix.js.map +/webroot/mix-manifest.json +.ddev/* \ No newline at end of file diff --git a/Console/Command/Task/NewRelicTask.php b/Console/Command/Task/NewRelicTask.php deleted file mode 100644 index 386f143..0000000 --- a/Console/Command/Task/NewRelicTask.php +++ /dev/null @@ -1,10 +0,0 @@ -setName($controller->request); - $this->start(); - - if ($controller->Auth) { - $this->user($controller->Auth->user('id'), $controller->Auth->user('email'), ''); - } - - $this->captureParams(true); - - $this->addTracer('CakeRoute::match'); - $this->addTracer('CrudComponent::executeAction'); - $this->addTracer('Controller::render'); - $this->addTracer('View::render'); - $this->addTracer('View::element'); - $this->addTracer('View::renderLayout'); - $this->addTracer('DboSource::_execute'); - $this->addTracer('AttemptChecker::attempt'); - } - -} diff --git a/Lib/NewRelic.php b/Lib/NewRelic.php deleted file mode 100644 index 1e11401..0000000 --- a/Lib/NewRelic.php +++ /dev/null @@ -1,217 +0,0 @@ -hasNewRelic()) { - return; - } - - newrelic_set_appname($name); - } - -/** - * Start a New Relic transaction - * - * @param string $name - * @return void - */ - public function start($name = null) { - if (!$this->hasNewRelic()) { - return; - } - - newrelic_start_transaction(NEW_RELIC_APP_NAME); - newrelic_name_transaction($name); - } - -/** - * End a New Relic transaction - * - * @param boolean $ignore Should the statistics NewRelic gathered be discarded? - * @return void - */ - public function stop($ignore = false) { - if (!$this->hasNewRelic()) { - return; - } - - newrelic_end_transaction($ignore); - } - -/** - * Ignore the current transaction - * - * @return - */ - public function ignoreTransaction() { - if (!$this->hasNewRelic()) { - return; - } - - newrelic_ignore_transaction(); - } - -/** - * Ignore the current apdex - * - * @return - */ - public function ignoreApdex() { - if (!$this->hasNewRelic()) { - return; - } - - newrelic_ignore_apdex(); - } - -/** - * Should NewRelic capture params ? - * - * @param boolean $boolean - * @return void - */ - public function captureParams($boolean) { - if (!$this->hasNewRelic()) { - return; - } - - newrelic_capture_params($boolean); - } - -/** - * Add custom tracer method - * - * @param string $method - */ - public function addTracer($method) { - if (!$this->hasNewRelic()) { - return; - } - - newrelic_add_custom_tracer($method); - } - -/** - * Add a custom parameter to the New Relic transaction - * - * @param string $key - * @param mixed $value - */ - public function parameter($key, $value) { - if (!$this->hasNewRelic()) { - return false; - } - - if (!is_scalar($value)) { - $value = json_encode($value); - } - - newrelic_add_custom_parameter($key, $value); - } - -/** - * Track a custom metric - * - * @param string $key - * @param integer|float $value - * @return - */ - public function metric($key, $value) { - if (!$this->hasNewRelic()) { - return; - } - - if (!is_numeric($value)) { - throw new CakeException('Value must be numeric'); - } - - newrelic_custom_metric($key, $value); - } - -/** - * Add a custom method to have traced by NewRelic - * - * @param string $method - * @return void - */ - public function tracer($method) { - if (!$this->hasNewRelic()) { - return; - } - - newrelic_add_custom_tracer($method); - } - -/** - * Send an exception to New Relic - * - * @param Exception $e - * @return void - */ - public function sendException(Exception $e) { - if (!$this->hasNewRelic()) { - return; - } - - newrelic_notice_error(null, $e); - } - -/** - * Set user attributes - * - * @param string $user - * @param string $account - * @param string $product - * @return void - */ - public function user($user, $account, $product) { - if (!$this->hasNewRelic()) { - return; - } - - newrelic_set_user_attributes($user, $account, $product); - } - -/** - * Check if the NewRelic PHP extension is loaded - * - * @return boolean - */ - public function hasNewRelic() { - return extension_loaded('newrelic'); - } - -} diff --git a/README.md b/README.md index e498b4d..6fc3f99 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # CakePHP <3 NewRelic -You can modify your files like this +You can modify your files like this to have full NewRelic support. ## Things included @@ -9,9 +9,21 @@ You can modify your files like this - NewRelicTrait trait - NewRelic.NewRelic -### Console +## Requirements +- [New Relic PHP agent](https://docs.newrelic.com/docs/agents/php-agent/getting-started/introduction-new-relic-php) installed as a PHP module -Include this snippet in `app/Console/AppShell.php` +## Installation + +Note: This branch is for CakePHP 5.x + +``` +composer require jippi/cakephp-newrelic:dev-cake4 +``` + + +### Shell + +Include this snippet in `src/Shell/AppShell.php` ```php public function startup() { @@ -29,12 +41,11 @@ Include this snippet in `app/Console/AppShell.php` Simply add `NewRelic.NewRelic` to your `$components` list -## app/webroot/index.php +## webroot/index.php Add this in top of your file before `define('DS', 'DIRECTORY_SEPARATOR')` ```php -_deriveNameFromShell($name); - } - - if ($name instanceof CakeRequest) { - $name = $this->_deriveNameFromRequest($name); - } - - $this->_name = $name; - } - -/** - * Get the name - * - * @return string - */ - public function getName() { - return $this->_name; - } - -/** - * Change the application name - * - * @param string $name - * @return void - */ - public function applicationName($name) { - NewRelic::getInstance()->applicationName($name); - } - -/** - * Start a NewRelic transaction - * - * @param null|string $name - * @return void - */ - public function start($name = null) { - NewRelic::getInstance()->start($this->_getTransactionName($name)); - } - -/** - * Stop a transaction - * - * @return void - */ - public function stop($ignore = false) { - NewRelic::getInstance()->stop($ignore); - } - -/** - * Ignore current transaction - * - * @return void - */ - public function ignoreTransaction() { - NewRelic::getInstance()->ignoreTransaction(); - } - -/** - * Ignore current apdex - * - * @return void - */ - public function ignoreApdex() { - NewRelic::getInstance()->ignoreApdex(); - } - -/** - * Add custom parameter to transaction - * - * @param string $key - * @param scalar $value - * @return void - */ - public function parameter($key, $value) { - NewRelic::getInstance()->parameter($key, $value); - } - -/** - * Add custom metric - * - * @param string $key - * @param float $value - * @return void - */ - public function metric($key, $value) { - NewRelic::getInstance()->metric($key, $value); - } - -/** - * capture params - * - * @param boolean $capture - * @return void - */ - public function captureParams($capture) { - NewRelic::getInstance()->captureParams($capture); - } - -/** - * Add custom tracer method - * - * @param string $method - */ - public function addTracer($method) { - NewRelic::getInstance()->addTracer($method); - } - -/** - * Set user attributes - * - * @param string $user - * @param string $account - * @param string $product - * @return void - */ - public function user($user, $account, $product) { - NewRelic::getInstance()->user($user, $account, $product); - } - -/** - * Send an exception to New Relic - * - * @param Exception $e - * @return void - */ - public function sendException(Exception $e) { - NewRelic::getInstance()->sendException($e); - } - -/** - * Get transaction name - * - * @param string $name - * @return string - */ - protected function _getTransactionName($name) { - if (is_string($name)) { - return $name; - } - - return $this->_name; - } - -/** - * Derive the transaction name - * - * @param Shell $name - * @return string - */ - protected function _deriveNameFromShell(Shell $shell) { - $name = []; - - if ($shell->plugin) { - $name[] = $shell->plugin; - } - - $name[] = $shell->name; - $name[] = $shell->command; - - return join('/', $name); - } - - /** - * Compute name based on request information - * - * @param CakeRequest $request - * @return string - */ - protected function _deriveNameFromRequest(CakeRequest $request) { - $name = []; - - if ($request->prefix) { - $name[] = $request->prefix; - } - - if ($request->plugin) { - $name[] = $request->plugin; - } - - $name[] = $request->controller; - $name[] = $request->action; - - $name = join('/', $name); - - if ($request->ext) { - $name .= '.' . $request->ext; - } - - return $name; - } - -} diff --git a/VERSION.txt b/VERSION.txt new file mode 100644 index 0000000..2468aa9 --- /dev/null +++ b/VERSION.txt @@ -0,0 +1 @@ +3.0.0-dev diff --git a/composer.json b/composer.json index 7f3a210..49fa85a 100644 --- a/composer.json +++ b/composer.json @@ -1,32 +1,56 @@ { - "name":"jippi/cakephp-newrelic", - "version": "1.0.0", - "description":"CakePHP <3 NewRelic", - "type":"cakephp-plugin", - "keywords":[ + "name": "jippi/cakephp-newrelic", + "version": "3.0.0", + "description": "CakePHP <3 NewRelic", + "type": "cakephp-plugin", + "keywords": [ "cakephp", "newrelic", "apm", "plugin" ], - "homepage":"https://github.com/jippi/cakephp-newrelic", - "license":"MIT", - "authors":[ + "homepage": "https://github.com/jippi/cakephp-newrelic", + "license": "MIT", + "authors": [ { - "name":"Christian Winther", - "role":"Author", - "homepage":"http://cakephp.nu/" + "name": "Christian Winther", + "role": "Author", + "homepage": "http://cakephp.nu/" } ], - "support":{ - "source":"https://github.com/jippi/cakephp-newrelic", - "issues":"https://github.com/jippi/cakephp-newrelic/issues", - "irc":"irc://irc.freenode.org/friendsofcake" + "support": { + "source": "https://github.com/jippi/cakephp-newrelic", + "issues": "https://github.com/jippi/cakephp-newrelic/issues", + "irc": "irc://irc.freenode.org/friendsofcake" }, - "require":{ - "composer/installers":"*" + "require": { + "php": ">=8.1", + "cakephp/cakephp": "5.x-dev" + }, + "require-dev": { + "phpunit/phpunit": "^10.1.0", + "cakephp/cakephp-codesniffer": "^5.0" + }, + "minimum-stability": "dev", + "prefer-stable": true, + "autoload": { + "psr-4": { + "NewRelic\\": "src", + "NewRelic\\Test\\Fixture\\": "tests\\Fixture" + } + }, + "autoload-dev": { + "psr-4": { + "Cake\\Test\\": "vendor/cakephp/cakephp/tests", + "DebugKit\\Test\\": "tests" + } }, "extra": { "installer-name": "NewRelic" + }, + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } } } diff --git a/config/bootstrap.php b/config/bootstrap.php new file mode 100644 index 0000000..f651d7d --- /dev/null +++ b/config/bootstrap.php @@ -0,0 +1,6 @@ + + + + + + + + + + + + tests/TestCase/ + + + + + + + + src/ + + + diff --git a/psalm-baseline.xml b/psalm-baseline.xml new file mode 100644 index 0000000..d3a0c84 --- /dev/null +++ b/psalm-baseline.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 0000000..0d865a6 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Controller/Component/NewRelicComponent.php b/src/Controller/Component/NewRelicComponent.php new file mode 100644 index 0000000..4c5e714 --- /dev/null +++ b/src/Controller/Component/NewRelicComponent.php @@ -0,0 +1,39 @@ +setName($event->getSubject()->getRequest()); + $this->start(); + + $controller = $event->getSubject(); + if (isset($controller->Auth)) { + $this->user($controller->Auth->user('id'), $controller->Auth->user('email'), ''); + } + + $this->captureParams(true); + } +} diff --git a/src/Lib/NewRelic.php b/src/Lib/NewRelic.php new file mode 100644 index 0000000..6147a24 --- /dev/null +++ b/src/Lib/NewRelic.php @@ -0,0 +1,337 @@ + $value) { + if (!in_array($key, static::$serverVariables)) { + continue; + } + static::parameter('server_' . strtolower($key), $value); + } + + foreach ($_COOKIE as $key => $value) { + if (!in_array($key, static::$cookieVariables)) { + continue; + } + static::parameter('cookie_' . strtolower($key), $value); + } + } +} diff --git a/src/Middleware/NewRelicErrorHandlerMiddleware.php b/src/Middleware/NewRelicErrorHandlerMiddleware.php new file mode 100644 index 0000000..632e9a1 --- /dev/null +++ b/src/Middleware/NewRelicErrorHandlerMiddleware.php @@ -0,0 +1,27 @@ +_deriveNameFromCommand($argument); + } + if ($argument instanceof ServerRequest) { + $name = $this->_deriveNameFromRequest($argument); + } + + $this->_newrelicTransactionName = $name; + } + + /** + * Get the name + * + * @return string + */ + public function getName(): string + { + return $this->_newrelicTransactionName; + } + + /** + * Change the application name + * + * @param string $name + * @return void + */ + public function applicationName(string $name): void + { + NewRelic::applicationName($name); + } + + /** + * Start a NewRelic transaction + * + * @param string|null $name + * @return void + */ + public function start(?string $name = null): void + { + NewRelic::start($this->_getTransactionName($name)); + } + + /** + * Stop a transaction + * + * @param bool $ignore + * @return void + */ + public function stop(bool $ignore = false): void + { + NewRelic::stop($ignore); + } + + /** + * Ignore current transaction + * + * @return void + */ + public function ignoreTransaction(): void + { + NewRelic::ignoreTransaction(); + } + + /** + * Ignore current apdex + * + * @return void + */ + public function ignoreApdex(): void + { + NewRelic::ignoreApdex(); + } + + /** + * Add custom parameter to transaction + * + * @param string $key + * @param mixed $value + * @return void + */ + public function parameter(string $key, mixed $value): void + { + NewRelic::parameter($key, $value); + } + + /** + * Add custom metric + * + * @param string $key + * @param float $value + * @return void + * @throws \Exception + */ + public function metric(string $key, float $value): void + { + NewRelic::metric($key, $value); + } + + /** + * capture params + * + * @param boolean $capture + * @return void + */ + public function captureParams(bool $capture): void + { + NewRelic::captureParams($capture); + } + + /** + * Add custom tracer method + * + * @param string $method + */ + public function addTracer(string $method): void + { + NewRelic::addTracer($method); + } + + /** + * Set user attributes + * + * @param string $user + * @param string $account + * @param string $product + * @return void + */ + public function user(string $user, string $account, string $product): void + { + NewRelic::user($user, $account, $product); + } + + /** + * Send an exception to New Relic + * + * @param \Exception $e + * @return void + */ + public function sendException(Exception $e): void + { + NewRelic::sendException($e); + } + + /** + * Get transaction name + * + * @param string $name + * @return string + */ + protected function _getTransactionName(string $name): string + { + if ($name) { + return $name; + } + + return $this->_newrelicTransactionName; + } + + /** + * Derive the transaction name + * + * @param \Cake\Command\Command $command + * @return string + */ + protected function _deriveNameFromCommand(Command $command): string + { + return $command->getName(); + } + + /** + * Compute name based on request information + * + * @param \Cake\Http\ServerRequest $request + * @return string + */ + protected function _deriveNameFromRequest(ServerRequest $request): string + { + $name = []; + if ($request->getParam('prefix')) { + $name[] = $request->getParam('prefix'); + } + + if ($request->getParam('plugin')) { + $name[] = $request->getParam('plugin'); + } + + $name[] = $request->getParam('controller'); + $name[] = $request->getParam('action'); + + $name = join('/', $name); + + if ($request->getParam('ext')) { + $name .= '.' . $request->getParam('ext'); + } + + return $name; + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..c9f40d2 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,45 @@ +loadInternalFile(env('FIXTURE_SCHEMA_METADATA')); +} diff --git a/tests/schema.php b/tests/schema.php new file mode 100644 index 0000000..353ace1 --- /dev/null +++ b/tests/schema.php @@ -0,0 +1,4 @@ +