skip to Main Content

I’m playing around with instrumentation using Sentry and I’m so discouraged by how many lines of code I have to add everywhere.

According to the Sentry docs, I have to add all these lines every time I want to measure something:

        $sentryTransactionContext = (new TransactionContext('Something that needs measuring'));
        $sentryTransactionContext->setOp('http.server');
        $sentryTransaction = startTransaction($sentryTransactionContext);
        SentrySdk::getCurrentHub()->setSpan($sentryTransaction);
        $spanContext = (new SpanContext());
        $spanContext->setOp('something.that.needs.measuring');
        $span1 = $sentryTransaction->startChild($spanContext);
        SentrySentrySdk::getCurrentHub()->setSpan($span1);

        // Do something that needs to be measured...

        $span1->finish();
        SentrySdk::getCurrentHub()->setSpan($sentryTransaction);
        $sentryTransaction->finish();

Is all that stuff really supposed to go in all my different Controller methods, or places where I need to measure how long a piece of code takes? It would be so much duplicate code.

Ideally, I would like to just do this:

public function create(HttpRequest $request)
{
        sentry_measure_start('slow.task');

        // Something slow that needs to be measured

        sentry_measure_stop('slow.task');
}

Is that possible?

2

Answers


  1. Chosen as BEST ANSWER

    I found that Sentry's SDK has a less verbose static method that apparently isn't mentioned in the documentation. Here's how I use it. It's still a little cumbersome, but tolerable.

            $transaction = SentrySdk::getCurrentHub()->getTransaction()
                ?: startTransaction(TransactionContext::make()->setName('slow.task')->setOp('slow.task1'));
            $child = $transaction->startChild(SpanContext::make()->setOp('slow.task1.subtask1'));
    
            // measure something
    
            $child->finish();
            $transaction->finish();
    

  2. You could write a Service class that handles and simplifies the syntax for starting and stopping Sentry transactions and spans.

    1. Create a app/Services/SentryMeasureService.php

      namespace AppServices;
      
      use SentryTracingTransactionContext;
      use SentryTracingSpanContext;
      use SentrySentrySdk;
      
      class SentryMeasure {
          private static $transactions = [];
          private static $spans = [];
      
          /**
           * Start measuring a transaction or span
           * 
           * @param string $name Unique identifier for the transaction or span
           * @param string $type Type of measurement (transaction or span)
           * @param string|null $parentName Parent transaction/span name (optional)
           */
          public static function start(string $name, string $type = 'transaction', ?string $parentName = null)
          {
              try {
                  if ($type === 'transaction') {
                      // Create and start a new transaction
                      $transactionContext = new SentryTracingTransactionContext($name);
                      $transactionContext->setOp('http.server');
                      $transaction = SentrystartTransaction($transactionContext);
      
                      SentrySentrySdk::getCurrentHub()->setSpan($transaction);
      
                      self::$transactions[$name] = $transaction;
                  } elseif ($type === 'span') {
                      if (!isset(self::$transactions[$parentName])) {
                          throw new Exception("Parent transaction '{$parentName}' not found");
                      }
      
                      $parentTransaction = self::$transactions[$parentName];
      
                      $spanContext = new SentryTracingSpanContext();
                      $spanContext->setOp($name);
                      $span = $parentTransaction->startChild($spanContext);
      
                      SentrySentrySdk::getCurrentHub()->setSpan($span);
      
                      self::$spans[$name] = $span;
                  } else {
                      throw new InvalidArgumentException("Invalid measurement type. Use 'transaction' or 'span'.");
                  }
              } catch (Exception $e) {
                  error_log("Sentry measurement start error: " . $e->getMessage());
              }
          }
      
          /**
           * Stop measuring a transaction or span
           * 
           * @param string $name Unique identifier for the transaction or span to stop
           * @param string $type Type of measurement (transaction or span)
           */
          public static function stop(string $name, string $type = 'transaction')
          {
              try {
                  if ($type === 'transaction') {
                      if (isset(self::$transactions[$name])) {
                          $transaction = self::$transactions[$name];
                          $transaction->finish();
      
                          unset(self::$transactions[$name]);
                      }
                  } elseif ($type === 'span') {
                      if (isset(self::$spans[$name])) {
                          $span = self::$spans[$name];
                          $span->finish();
      
                          unset(self::$spans[$name]);
      
                          if (!empty(self::$transactions)) {
                              $lastTransactionName = array_key_last(self::$transactions);
                              $lastTransaction = self::$transactions[$lastTransactionName];
                              SentrySentrySdk::getCurrentHub()->setSpan($lastTransaction);
                          }
                      }
                  } else {
                      throw new InvalidArgumentException("Invalid measurement type. Use 'transaction' or 'span'.");
                  }
              } catch (Exception $e) {
                  error_log("Sentry measurement stop error: " . $e->getMessage());
              }
          }
      }
      
    2. Create a app/helpers.php file to implement your helper functions

      use AppServicesSentryMeasureService;
      
      if (!function_exists('sentry_measure_start')) {
          function sentry_measure_start(string $name, ?string $parentName = null)
          {
              SentryMeasureService::start($name, $parentName === null ? 'transaction' : 'span', $parentName);
          }
      }
      
      if (!function_exists('sentry_measure_stop')) {
          function sentry_measure_stop(string $name)
          {
              SentryMeasureService::stop($name, 'span');
              SentryMeasureService::stop($name, 'transaction');
          }
      }
      
    3. To make the helper functions visible you need to modify your composer.json more specifically the autoload key. Add a files array

      inside autoload.

      "autoload": {
          "files": [
              "app/helpers.php"
          ],
          "classmap": [
              "database/seeds",
              "database/factories"
          ],
          "psr-4": {
              "App\": "app/"
          }
      },
      
    4. Once you added the file you need to dump the autloader

      composer dump-autoload
      

    Now you should be able to use the functions the way you suggested:

    public function create(Request $request)
    {
        sentry_measure_start('slow.task');
    
        sleep(2); // example of a slow operation
    
        sentry_measure_stop('slow.task');
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search