Instrumenter une méthode personnalisée pour analyser en détail votre logique opérationnelle

Temps de lecture : 8 minutes

Vue Analytics

Afin de vous offrir une visibilité optimale sur votre logique opérationnelle, l’APM Datadog vous permet de personnaliser les spans qui composent vos traces en fonction de vos besoins et de votre implémentation. Vous êtes ainsi libre de tracer n’importe quelle méthode utilisée dans votre code, ou même des composants spécifiques au sein d’une méthode. Utilisez cette fonctionnalité pour optimiser et surveiller les zones critiques de votre application avec le niveau de granularité qui vous convient.

En plus d’instrumenter un vaste nombre de frameworks par défaut, tels que des services Web, des bases de données et des caches, Datadog vous permet d’instrumenter votre propre logique opérationnelle afin d’obtenir exactement le niveau de visibilité dont vous avez besoin. En créant des spans pour des méthodes, vous pouvez optimiser les calculs de temps et surveiller les erreurs à l’aide du flamegraph et des monitors de l’APM.

Instrumenter votre code

Suivez cet exemple pour instrumenter votre code.

Ces exemples vous montreront comment tracer l’intégralité de la méthode BackupLedger.write afin de mesurer son temps d’exécution et son statut. BackupLedger.write est une action qui enregistre le statut actuel d’un registre de transactions en mémoire avant d’effectuer un appel vers une base de données de paiements pour publier une nouvelle facturation client. Cette action se produit lorsque l’endpoint charge du service de paiements est atteint :

Vue Analytics

La span http.request POST /charge/ prend beaucoup de temps et ne présente aucune span enfant directe : une instrumentation plus poussée est donc probablement nécessaire pour mieux comprendre son comportement. Selon le langage de programmation que vous utilisez, vous devez décorer vos fonctions différemment :

Dans le langage Java, l’APM Datadog vous permet d’instrumenter votre code pour générer des spans personnalisées en utilisant des décorateurs de méthode ou en instrumentant des blocs de code spécifiques.

Instrumenter une méthode avec un décorateur :

Cet exemple ajoute une span à la méthode BackupLedger.write, qui ajoute de nouvelles lignes à un registre de transactions. Une span est ajoutée pour suivre toutes les transactions publiées en tant qu’une seule unité.

import datadog.trace.api.Trace

public class BackupLedger {

  // Utiliser l'annotation @Trace pour tracer des méthodes personnalisées
  @Trace
  public void write(List<Transaction> transactions) {
    for (Transaction transaction : transactions) {
      ledger.put(transaction.getId(), transaction);
    }

    // [...]
  }
}

Instrumenter un bloc de code spécifique :

Cet exemple ajoute des spans enfant à la span BackupLedger.write créée ci-dessus. Cette méthode ajoute une span enfant pour chaque transaction dans le registre et un tag personnalisé avec l’ID de transaction spécifique.

import datadog.trace.api.Trace;
import io.opentracing.Scope;
import io.opentracing.Tracer;
import io.opentracing.util.GlobalTracer;

public class BackupLedger {

  // Utiliser l'annotation `@Trace` pour tracer des méthodes personnalisées
  @Trace
  public void write(List<Transaction> transactions) {
    for (Transaction transaction : transactions) {
      // Utilisez la fonction `GlobalTracer` pour tracer des blocs de code en ligne
      Tracer tracer = GlobalTracer.get();
      try (Scope scope = tracer.buildSpan("BackupLedger.persist").startActive(true)) {
        // Ajoutez des métadonnées personnalisées à la span
        scope.span().setTag("transaction.id", transaction.getId());
        ledger.put(transaction.getId(), transaction);
      }
    }

    // [...]
  }
}

Dans le langage Python, l’APM Datadog vous permet d’instrumenter votre code pour générer des spans personnalisées en utilisant des décorateurs de méthode ou en instrumentant des blocs de code spécifiques.

Instrumenter une méthode avec un décorateur :

Cet exemple ajoute une span à la méthode BackupLedger.write, qui ajoute de nouvelles lignes à un registre de transactions. Une span est ajoutée pour suivre toutes les transactions publiées en tant qu’une seule unité.

from ddtrace import tracer

class BackupLedger:

    # Utiliser le décorateur `tracer.wrap` pour tracer des méthodes personnalisées
    @tracer.wrap()
    def write(self, transactions):
        for transaction in transactions:
            self.ledger[transaction.id] = transaction

        # [...]

Instrumenter un bloc de code spécifique :

Cet exemple ajoute des spans enfant à la span BackupLedger.write créée ci-dessus. Cette méthode ajoute une span enfant pour chaque transaction dans le registre et un tag personnalisé avec l’ID de transaction spécifique.

from ddtrace import tracer

class BackupLedger:

    # Utiliser le décorateur `tracer.wrap` pour tracer des méthodes personnalisées
    @tracer.wrap()
    def write(self, transactions):
        for transaction in transactions:
            # Utiliser le gestionnaire de contexte `tracer.trace` pour tracer directement des blocs de code
            with tracer.trace('BackupLedger.persist') as span:
                # Ajouter des métadonnées à la span persist_transaction
                span.set_tag('transaction.id', transaction.id)
                self.ledger[transaction.id] = transaction

        # [...]

Dans le langage Ruby, l’APM Datadog vous permet d’instrumenter votre code pour générer des spans personnalisées en instrumentant des codes de bloc spécifiques.

Cet exemple crée une span pour l’appel de la méthode BackupLedger.write et une span enfant pour chaque transaction publiée dans le registre avec un tag personnalisé spécifiant l’ID de transaction spécifique.

require 'ddtrace'

class BackupLedger

  def write(transactions)
    # Utiliser la méthode globale `Datadog::Tracing.trace` pour tracer directement des blocs de code
    Datadog::Tracing.trace('BackupLedger.write') do |method_span|
      transactions.each do |transaction|
        Datadog::Tracing.trace('BackupLedger.persist') do |span|
          # Ajouter des métadonnées personnalisées à la span "persist_transaction" span
          span.set_tag('transaction.id', transaction.id)
          ledger[transaction.id] = transaction
        end
      end
    end

    # [...]
  end
end

Dans le langage Go, l’APM Datadog vous permet d’instrumenter votre code pour générer des spans personnalisées en instrumentant des codes de bloc spécifiques.

Cet exemple crée une span pour chaque transaction publiée dans le registre et ajoute un tag personnalisé avec l’ID de transaction spécifique à la span.

package ledger

import "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"

// [...]

func (bl *BackupLedger) write(ctx context.Context, transactions []*Transaction) (err error) {
  // Tracer la fonction `write` et capturer l'erreur le cas échéant
  span, ctx := tracer.StartSpanFromContext(ctx, "BackupLedger.write")
  defer func() {
    span.Finish(tracer.WithError(err))
  }()

  for _, t := range transactions {
    if err := bl.persistTransaction(ctx, t); err != nil {
      return err
    }
  }
  return nil
}

// persistTransaction est une fonction interne que vous pouvez tracer. Vous pouvez utiliser la
// même méthode que précédemment, car le `ctx` transmis inclut des références de span par défaut
// pour créer une relation parent/enfant.
func (bl *BackupLedger) persistTransaction(ctx context.Context, transaction *Transaction) error {
  id := transaction.ID
  span, _ := tracer.StartSpanFromContext(ctx, "BackupLedger.persist", tracer.Tag("transaction_id", id))
  defer span.Finish()

  if t, ok := bl.transactions[id]; ok {
    return errors.New("duplicate entry")
  }
  bl.transactions[id] = transaction
  return nil
}

Dans le langage Node.js, l’APM Datadog vous permet d’instrumenter votre code pour générer des spans personnalisées en instrumentant des codes de bloc spécifiques.

Cet exemple crée une span pour l’appel de la méthode BackupLedger.write et une span enfant pour chaque transaction publiée dans le registre, avec un tag personnalisé spécifiant l’ID de transaction spécifique.

const tracer = require('dd-trace')

function write (transactions) {
  // Utiliser le gestionnaire de contextes `tracer.trace` pour tracer directement des blocs de code
  tracer.trace('BackupLedger.write', () => {
    for (const transaction of transactions) {
      // Ajouter des métadonnées personnalisées à la span persist_transaction
      span.setTag('transaction.id', transaction.id)
      this.ledger[transaction.id] = transaction
    }
  })

  // [...]
}

Dans le langage .NET, l’APM Datadog vous permet d’instrumenter votre code pour générer des spans personnalisées en instrumentant des codes de bloc spécifiques.

Cet exemple crée une span pour chaque transaction publiée dans le registre et ajoute un tag personnalisé avec l’ID de transaction spécifique à la span.

using Datadog.Trace;

public void Write(List<Transaction> transactions)
{
    // Utiliser le traceur global pour tracer directement des blocs de code
    using (var scope = Tracer.Instance.StartActive("BackupLedger.write"))
    {
        foreach (var transaction in transactions)
        {
            using (var scope = Tracer.Instance.StartActive("BackupLedger.persist"))
            {
                // Ajouter des métadonnées personnalisées à la span
                scope.Span.SetTag("transaction.id", transaction.Id);
                this.ledger[transaction.Id] = transaction;
            }
        }
    }

    // [...]
}

Dans le langage PHP, l’APM Datadog vous permet d’instrumenter votre code pour générer des spans personnalisées en utilisant des wrappers de méthode ou en instrumentant des blocs de code spécifiques.

Instrumenter une méthode avec un wrapper :

Cet exemple ajoute une span à la méthode BackupLedger.write, qui ajoute de nouvelles lignes à un registre de transactions. Une span est ajoutée pour suivre toutes les transactions publiées en tant qu’une seule unité via la fonction DDTrace\trace_method().

<?php
  class BackupLedger {

    public function write(array $transactions) {
      foreach ($transactions as $transaction) {
        $this->transactions[$transaction->getId()] = $transaction;
      }

      # [...]
    }
  }

  // Pour ddtrace < v0.47.0 utiliser \dd_trace_method()
  \DDTrace\trace_method('BackupLedger', 'write', function (\DDTrace\SpanData $span) {
    // SpanData::$name correspond à 'ClassName.methodName' par défaut si non défini (>= v0.47.0)
    $span->name = 'BackupLedger.write';
    // SpanData::$resource correspond à SpanData::$name par défaut si non défini (>= v0.47.0)
    $span->resource = 'BackupLedger.write';
    $span->service = 'php';
  });
?>

Instrumenter un bloc de code spécifique :

Cet exemple ajoute des spans enfant à la span BackupLedger.write créée ci-dessus. Cette méthode ajoute une span enfant pour chaque transaction dans le registre et un tag personnalisé avec l’ID de transaction spécifique.

<?php
  class BackupLedger {

    public function write(array $transactions) {
      foreach ($transactions as $transaction) {
        // Utiliser le traceur global pour tracer directement des blocs de code
        $scope = \DDTrace\GlobalTracer::get()->startActiveSpan('BackupLedger.persist');

        // Ajouter des métadonnées personnalisées à la span
        $scope->getSpan()->setTag('transaction.id', $transaction->getId());
        $this->transactions[$transaction->getId()] = $transaction;

        // Fermer la span
        $scope->close();
      }

      # [...]
    }
  }

  // Pour ddtrace < v0.47.0 utilisez \dd_trace_method()
  \DDTrace\trace_method('BackupLedger', 'write', function (\DDTrace\SpanData $span) {
    // SpanData::$name correspond à 'ClassName.methodName' par défaut si non défini (>= v0.47.0)
    $span->name = 'BackupLedger.write';
    // SpanData::$resource correspond à SpanData::$name par défaut si non défini (>= v0.47.0)
    $span->resource = 'BackupLedger.write';
    $span->service = 'php';
  });
?>

Visualiser vos nouvelles spans personnalisées depuis l’interface de Datadog

Maintenant que vous avez instrumenté votre logique opérationnelle, il est temps de découvrir les résultats dans l’interface de l’APM Datadog.

  1. Accédez à la liste des services, identifiez le service auquel vous avez ajouté des spans personnalisées, puis accédez à la page Service. Sur la page Service, cliquez sur la ressource spécifique que vous avez ajoutée, modifiez le filtre d’intervalle sur The past 15 minutes et faites défiler la page jusqu’au tableau Span Summary :

    Tableau Span Summary

    Vous devriez maintenant apercevoir les nouvelles spans que vous avez ajoutées

Le tableau Span Summary affiche des informations agrégées concernant les spans qui composent vos traces. Utilisez-le pour identifier les spans qui se répètent pendant des durées anormales, ce qui peut être le signe de boucles ou de mauvaises performances d’accès à la base de données (comme le problème n+1).

  1. Faites défiler la page jusqu’à la liste des traces et cliquez sur l’une de vos traces.

    Vue Analytics

Vous avez maintenant réussi à ajouter des spans personnalisées à votre code, et celles-ci sont désormais visibles sur le flamegraph ainsi que sur la page App Analytics. Pour les rendre encore plus performantes et continuer à tirer pleinement parti des outils Datadog, vous pouvez maintenant ajouter des tags personnalisés à vos spans.

Pour aller plus loin