Générer des traces et des métriques avec Node.js

Ce document explique comment modifier une application JavaScript Node.js pour collecter des données de trace et de métrique à l'aide du framework Open Source OpenTelemetry et comment écrire des journaux JSON structurés pour la sortie standard. Ce document fournit également des informations sur un exemple d'application Node.js que vous pouvez installer et exécuter. L'application utilise le framework Web Fastify et est configurée pour générer des métriques, des traces et des journaux.

Avant de commencer

Activer les API Cloud Logging API, Cloud Monitoring API, and Cloud Trace API.

Activer les API

Instrumenter votre application pour collecter des traces, des métriques et des journaux

Pour instrumenter votre application afin de collecter des données de trace et de métrique, et d'écrire un code JSON structuré pour la sortie standard, procédez comme suit, comme décrit dans les sections suivantes de ce document:

  1. Configurer OpenTelementry
  2. Configurer votre application pour précharger la configuration OpenTelemetry
  3. Configurer la journalisation structurée
  4. Écrire des journaux structurés

Configurer OpenTelementry

La configuration par défaut du SDK OpenTelemetry Node.js exporte les traces à l'aide du protocole OTLP. Il configure également OpenTelemetry afin d'utiliser le format Contexte de trace W3C pour propager le contexte de trace. Cette configuration garantit que les délais ont la bonne relation parent-enfant au sein d'une trace.

L'exemple de code suivant illustre un module JavaScript permettant de configurer OpenTelemetry:


diag.setLogger(
  new DiagConsoleLogger(),
  opentelemetry.core.getEnv().OTEL_LOG_LEVEL
);

const sdk = new opentelemetry.NodeSDK({
  instrumentations: getNodeAutoInstrumentations({
    // Disable noisy instrumentations
    '@opentelemetry/instrumentation-fs': {enabled: false},
  }),
  resourceDetectors: getResourceDetectorsFromEnv(),
  metricReader: getMetricReader(),
});

try {
  sdk.start();
  diag.info('OpenTelemetry automatic instrumentation started successfully');
} catch (error) {
  diag.error(
    'Error initializing OpenTelemetry SDK. Your application is not instrumented and will not produce telemetry',
    error
  );
}

// Gracefully shut down the SDK to flush telemetry when the program exits
process.on('SIGTERM', () => {
  sdk
    .shutdown()
    .then(() => diag.debug('OpenTelemetry SDK terminated'))
    .catch(error => diag.error('Error terminating OpenTelemetry SDK', error));
});

L'exemple de code précédent configure OpenTelemetry pour exporter des métriques à l'aide du protocole OTLP et utilise le package @opentelemetry/auto-instrumentations-node pour configurer toutes les instrumentations Node.js disponibles.

Pour garantir que toutes les données de télémétrie en attente sont vidées et que les connexions sont fermées correctement avant l'arrêt de l'application, le gestionnaire SIGTERM appelle shutdown.

Pour en savoir plus et pour connaître les options de configuration, consultez la page Instrumentation automatique Node.js OpenTelemetry.

Configurer votre application pour précharger la configuration OpenTelemetry

Pour configurer l'application afin qu'elle écrive des journaux structurés et pour collecter des métriques et des données de trace à l'aide d'OpenTelemetry, mettez à jour l'appel de votre application pour précharger le module d'instrumentation avec Node.jsoption --require. L'option --require garantit qu'OpenTelemetry est initialisé avant le démarrage de votre application. Pour en savoir plus, consultez la page Premiers pas avec OpenTelemetry Node.js.

L'exemple de code suivant illustre un fichier Dockerfile transmettant l'option --require:

CMD node --require ./build/src/instrumentation.js build/src/index.js 2>&1 | tee /var/log/app.log

Configurer la journalisation structurée

Pour inclure les informations de trace dans les journaux au format JSON écrits dans la sortie standard, configurez votre application de sorte qu'elle génère des journaux structurés au format JSON. Fastify utilise le framework de journalisation Pino et fournit un enregistreur dans chaque gestionnaire de requêtes. L'exemple de code suivant illustre un objet Pino LoggerOptions qui configure l'application pour générer des journaux structurés JSON:


// Expected attributes that OpenTelemetry adds to correlate logs with spans
interface LogRecord {
  trace_id?: string;
  span_id?: string;
  trace_flags?: string;
  [key: string]: unknown;
}

// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#logseverity
const PinoLevelToSeverityLookup: Record<string, string | undefined> = {
  trace: 'DEBUG',
  debug: 'DEBUG',
  info: 'INFO',
  warn: 'WARNING',
  error: 'ERROR',
  fatal: 'CRITICAL',
};

export const loggerConfig = {
  messageKey: 'message',
  // Same as pino.stdTimeFunctions.isoTime but uses "timestamp" key instead of "time"
  timestamp(): string {
    return `,"timestamp":"${new Date(Date.now()).toISOString()}"`;
  },
  formatters: {
    log(object: LogRecord): Record<string, unknown> {
      // Add trace context attributes following Cloud Logging structured log format described
      // in https://cloud.google.com/logging/docs/structured-logging#special-payload-fields
      const {trace_id, span_id, trace_flags, ...rest} = object;

      return {
        'logging.googleapis.com/trace': trace_id,
        'logging.googleapis.com/spanId': span_id,
        'logging.googleapis.com/trace_sampled': trace_flags
          ? trace_flags === '01'
          : undefined,
        ...rest,
      };
    },
    // See
    // https://getpino.io/#/docs/help?id=mapping-pino-log-levels-to-google-cloud-logging-stackdriver-severity-levels
    level(label: string) {
      return {
        severity:
          PinoLevelToSeverityLookup[label] ?? PinoLevelToSeverityLookup['info'],
      };
    },
  },
} satisfies LoggerOptions;

La configuration précédente extrait des informations sur le délai actif du message de journal, puis ajoute ces informations en tant qu'attributs au journal structuré JSON. Ces attributs peuvent ensuite être utilisés pour mettre en corrélation un journal et une trace:

  • logging.googleapis.com/trace: nom de ressource de la trace associée à l'entrée de journal.
  • logging.googleapis.com/spanId: ID de délai avec la trace associée à l'entrée de journal.
  • logging.googleapis.com/trace_sampled: la valeur de ce champ doit être true ou false.

Pour en savoir plus sur ces champs, consultez la structure LogEntry.

Pour utiliser la configuration Pino avec Fastify, transmettez l'objet de configuration de l'enregistreur lors de la création de l'application Fastify:

// Create the Fastify app providing the Pino logger config
const fastify = Fastify({
  logger: loggerConfig,
});

Écrire des journaux structurés

Pour écrire des journaux structurés qui renvoient vers une trace, utilisez l'enregistreur Pino fourni par Fastify. Par exemple, l'instruction suivante montre comment appeler la méthode Logger.info():

request.log.info({subRequests}, 'handle /multi request');

OpenTelemetry renseigne automatiquement les entrées de journal Pino avec le contexte de segment du délai actif actuel dans le contexte OpenTelemetry. Ce contexte de segment est ensuite inclus dans les journaux JSON, comme décrit dans la section Configurer la journalisation structurée.

Exécuter un exemple d'application configuré pour collecter les données de télémétrie

L'exemple d'application utilise des formats indépendants du fournisseur, tels que JSON pour les journaux, OTLP pour les métriques et les traces, et le framework Fastify. Pour acheminer la télémétrie vers Google Cloud, cet exemple utilise le Collector OpenTelemetry configuré avec les exportateurs Google. L'application possède deux points de terminaison:

  • Le point de terminaison /multi est géré par la fonction handleMulti. Le générateur de charge de l'application envoie des requêtes au point de terminaison /multi. Lorsque ce point de terminaison reçoit une requête, il envoie entre trois et sept requêtes au point de terminaison /single sur le serveur local.

    /**
     * handleMulti handles an http request by making 3-7 http requests to the /single endpoint.
     *
     * OpenTelemetry instrumentation requires no changes here. It will automatically generate a
     * span for the handler body.
     */
    fastify.get('/multi', async request => {
      const subRequests = randInt(3, 8);
      request.log.info({subRequests}, 'handle /multi request');
    
      for (let i = 0; i < subRequests; i++) {
        await axios.get(`http://localhost:${port}/single`);
      }
      return 'ok';
    });
  • Le point de terminaison /single est géré par la fonction handleSingle. Lorsque ce point de terminaison reçoit une requête, il reste en veille pendant un court délai, puis répond par une chaîne.

    /**
     * handleSingle handles an http request by sleeping for 100-200 ms. It writes the number of
     * milliseconds slept as its response.
     */
    fastify.get('/single', async request => {
      // Sleep between 100-200 milliseconds
      const sleepMillis = randInt(100, 200);
      request.log.info({sleepMillis}, 'Going to sleep');
      await sleep(sleepMillis);
      return `slept ${sleepMillis}\n`;
    });

Télécharger et déployer l'application

Pour exécuter l'exemple , procédez comme suit :

  1. Dans la console Google Cloud, activez Cloud Shell.

    Activer Cloud Shell

    En bas de la fenêtre de la console Google Cloud, une session Cloud Shell démarre et affiche une invite de ligne de commande. Cloud Shell est un environnement shell dans lequel Google Cloud CLI est déjà installé, et dans lequel des valeurs sont déjà définies pour votre projet actuel. L'initialisation de la session peut prendre quelques secondes.

  2. Clonez le dépôt :

    git clone https://github.com/GoogleCloudPlatform/opentelemetry-operations-js
    
  3. Accédez au répertoire de l'exemple :

    cd opentelemetry-operations-js/samples/instrumentation-quickstart
    
  4. Compilez et exécutez l'exemple.

    docker compose up --abort-on-container-exit
    

    Si vous n'exécutez pas sur Cloud Shell, exécutez l'application avec la variable d'environnement GOOGLE_APPLICATION_CREDENTIALS pointant vers un fichier d'identifiants. Les identifiants par défaut de l'application fournissent un fichier d'identifiants à l'adresse $HOME/.config/gcloud/application_default_credentials.json.

    # Set environment variables
    export GOOGLE_CLOUD_PROJECT="PROJECT_ID"
    export GOOGLE_APPLICATION_CREDENTIALS="$HOME/.config/gcloud/application_default_credentials.json"
    export USERID="$(id -u)"
    
    # Run
    docker compose -f docker-compose.yaml -f docker-compose.creds.yaml up --abort-on-container-exit
    

Afficher vos métriques

L'instrumentation OpenTelementry dans l'exemple d'application génère des métriques Prometheus que vous pouvez afficher à l'aide de l'explorateur de métriques:

  • Prometheus/http_server_duration_milliseconds/histogram enregistre la durée des requêtes du serveur et stocke les résultats dans un histogramme.

  • Prometheus/http_client_duration_milliseconds/histogram enregistre la durée des requêtes des clients et stocke les résultats dans un histogramme.

Pour afficher les métriques générées par l'exemple d'application, procédez comme suit :
  1. Dans le panneau de navigation de la console Google Cloud, sélectionnez Surveillance, puis  Explorateur de métriques :

    Accéder à l'explorateur de métriques

  2. Dans l'élément Métrique, développez le menu Sélectionner une métrique, saisissez http_server dans la barre de filtre, puis utilisez les sous-menus pour sélectionner un type de ressource et des métriques spécifiques :
    1. Dans le menu Ressources actives, sélectionnez Cible Prometheus.
    2. Dans le menu Catégories de métriques actives, sélectionnez Http.
    3. Dans le menu Métriques actives, sélectionnez une métrique.
    4. Cliquez sur Appliquer.
  3. Configurez le mode d'affichage des données.

    Lorsque les mesures d'une métrique sont cumulatives, l'explorateur de métriques normalise automatiquement les données mesurées par période d'alignement, ce qui permet d'afficher un taux dans le graphique. Pour en savoir plus, consultez la section Genres, types et conversions.

    Lorsque des valeurs entières ou doubles sont mesurées, par exemple avec les deux métriques counter, l'explorateur de métriques additionne automatiquement toutes les séries temporelles. Pour afficher les données des routes HTTP /multi et /single, définissez le premier menu de l'entrée Agrégation sur Aucun.

    Pour plus d'informations sur la configuration d'un graphique, consultez la page Sélectionner des métriques lors de l'utilisation de l'explorateur de métriques.

Afficher vos traces

Pour afficher vos données de trace, procédez comme suit:

  1. Dans le panneau de navigation de la console Google Cloud, sélectionnez Trace, puis Explorateur Trace:

    Accéder à Explorateur Trace

  2. Dans le graphique à nuage de points, sélectionnez une trace avec l'URI /multi.
  3. Dans le graphique de Gantt du panneau Détails des traces, sélectionnez le délai intitulé /multi.

    Un panneau contenant des informations sur la requête HTTP s'affiche. Ces informations incluent la méthode, le code d'état, le nombre d'octets et le user-agent de l'appelant.

  4. Pour afficher les journaux associés à cette trace, sélectionnez l'onglet Logs & Events (Journaux et événements).

    Cet onglet affiche les journaux individuels. Pour afficher les détails de l'entrée de journal, développez-la. Vous pouvez également cliquer sur Afficher les journaux et afficher le journal à l'aide de l'explorateur de journaux.

Pour en savoir plus sur l'utilisation de l'explorateur Cloud Trace, consultez la page Rechercher et explorer des traces.

Afficher les journaux

L'explorateur de journaux vous permet d'inspecter vos journaux et d'afficher les traces associées, lorsqu'elles existent.

  1. Dans le panneau de navigation de la console Google Cloud, sélectionnez Logging, puis Explorateur de journaux :

    Accéder à l'explorateur de journaux

  2. Recherchez un journal avec la description suivante : handle /multi request.

    Pour afficher les détails du journal, développez l'entrée de journal.

  3. Cliquez sur Traces sur une entrée de journal contenant le message "handle /multi request", puis sélectionnez Afficher les détails des traces.

    Un panneau Trace details (Informations sur la trace) s'ouvre et affiche la trace sélectionnée.

Pour en savoir plus sur l'utilisation de l'explorateur de journaux, consultez la page Afficher les journaux à l'aide de l'explorateur de journaux.

Étapes suivantes