Feature Idea: Magento 2 - GraphQL API Transaction Naming

It seems that the PHP agent has support for Magento2 Transaction Naming; this includes REST and SOAP. (See: https://docs.newrelic.com/docs/agents/php-agent/frameworks-libraries/magento-specific-functionality).

Is it at all possible to add GraphQl Naming automatically?
I think named queries/mutations at-least can be identified similar to how REST/SOAP is done.


New Relic Edit

  • I want this too
  • I have more info to share (reply below)
  • I have a solution for this

0 voters

We take feature ideas seriously and our product managers review every one when plotting their roadmaps. However, there is no guarantee this feature will be implemented. This post ensures the idea is put on the table and discussed though. So please vote and share your extra details with our team.

Hi there @Steven.Hoffman -

You are correct that this is a Feature Idea. There is no automatic way to do this, but you can manually use the agent API call to name GraphQL transactions if that helps as a workaround:

In the meantime, I will get this logged with our product team and add a poll here so that others can weigh in. If you have additional details you want to share about your use case, please do so!

1 Like

I do not have much more detail to provide.
The Use Case is that Magento2 PWA primary uses GraphQl to interact with the Magento Backend.
In order to properly track usage and performance issues, the transactions need to be broken out a bit.

1 Like

Gotcha! Thanks @Steven.Hoffman

I am going to try out these composer patches to see if we can get decent log output in New Relic.

--- vendor/magento/module-graph-ql/Controller/GraphQl.php	2020-06-08 15:33:59.000000000 +0100
+++ vendor/magento/module-graph-ql/Controller/GraphQl.php	2020-06-08 15:35:00.000000000 +0100
@@ -145,6 +145,12 @@
             $this->requestProcessor->validateRequest($request);

             $data = $this->getDataFromRequest($request);
+
+            if (function_exists('newrelic_name_transaction')) {
+                $operationName = $data['operationName'] ?? 'operationNameNotSet';
+                newrelic_name_transaction('GraphQL-' . $operationName);
+            }
+
             $query = $data['query'] ?? '';
             $variables = $data['variables'] ?? null;

--- vendor/magento/framework/GraphQl/Query/ErrorHandler.php	2020-05-22 15:37:02.000000000 +0100
+++ vendor/magento/framework/GraphQl/Query/ErrorHandler.php	2020-05-22 15:32:58.000000000 +0100
@@ -38,6 +38,9 @@
     {
         $formattedErrors = [];
         foreach ($errors as $error) {
+            if ($error instanceof \Exception && function_exists('newrelic_notice_error')) {
+                newrelic_notice_error($error->getMessage(), $error);
+            }
             $this->logger->error($error);
             $previousError = $error->getPrevious();
             if ($previousError instanceof AggregateExceptionInterface && !empty($previousError->getErrors())) {

1 Like

Let us know how that goes for you :smiley:

This was half working because not every magento query has operationName set. So it needs to be pulled from the query. The below seems to be working so far, although is a bit hacky.

--- Controller/GraphQl.php	2020-04-13 17:35:40.000000000 +0100
+++ Controller/GraphQl.php	2020-06-29 11:29:05.000000000 +0100
@@ -145,6 +145,11 @@
             $this->requestProcessor->validateRequest($request);

             $data = $this->getDataFromRequest($request);
+
+            if (function_exists('newrelic_name_transaction')) {
+                newrelic_name_transaction('GraphQL-' . $this->getOperationNameFromData($data));
+            }
+
             $query = $data['query'] ?? '';
             $variables = $data['variables'] ?? null;

@@ -172,6 +177,39 @@
     }

     /**
+     * @param array $data
+     * @return string
+     */
+    private function getOperationNameFromData($data)
+    {
+        $operationName = 'operationNameNotSet';
+
+        try {
+            if (!(is_array($data) && isset($data['query']) && is_string($data['query']) && strlen($data['query']))) {
+                return $operationName;
+            }
+
+            $string = str_replace([' ', '\n', PHP_EOL], '', $data['query']);
+            preg_match('/([^\W]+)/', $string, $matches, PREG_OFFSET_CAPTURE);
+
+            $operationName = 'operationNameNotFound';
+            if (is_array($matches) && isset($matches[0][0]) && is_string($matches[0][0])) {
+                $operationName = $matches[0][0];
+            }
+            if (stripos($operationName, 'query') === 0) {
+                $operationName = substr($operationName, 5); // Remove query prefix
+            }
+            if (stripos($operationName, 'mutation') === 0) {
+                $operationName = substr($operationName, 8); // Remove mutation prefix
+            }
+        } catch (\Error $error) {
+            $operationName = 'operationNameResolutionError';
+        }
+
+        return $operationName;
+    }
+
+    /**
      * Get data from request body or query string
      *
      * @param RequestInterface $request

Hi,

We created one for our build out.
You can find it here. https://github.com/joma-webdevs/automatic-GraphQL-transaction-naming-for-New-Relic.

Awesome! Thank you both @Steven.Hoffman & @convenient