Examiner la latence dans un composant Spanner avec OpenTelemetry

Cette section explique comment examiner un composant Spanner pour identifier la source de latence et la visualiser à l'aide d'OpenTelemetry. Pour une présentation générale des composants de cette rubrique, consultez Points de latence dans une requête Spanner

OpenTelemetry est un kit et un framework d'observabilité Open Source qui permet vous créez et gérez des données de télémétrie telles que des traces, des métriques et des journaux. Il est le résultat d'une fusion entre OpenTracing et OpenCensus. Pour en savoir plus, consultez la page Qu'est-ce qu'OpenTelemetry ?

Les bibliothèques clientes Spanner fournissent des métriques et des traces avec du framework d'observabilité OpenTelemetry. Suivez la procédure décrite dans Identifiez le point de latence pour trouver la qui présentent une latence dans Spanner.

Avant de commencer

Avant de commencer à collecter des métriques de latence, familiarisez-vous avec la avec OpenTelemetry. Vous devez configurer le SDK OpenTelemetry avec les options appropriées pour exporter vos données de télémétrie. Plusieurs options d'exportateur OpenTelemetry sont disponibles. Nous vous recommandons d'utiliser OpenTelemetry Exportateur de protocole (OTLP, Protocol). Vous pouvez également utiliser un collecteur OTel avec l'exportateur Google Cloud ou l'exportateur Google Managed Service pour Prometheus.

Si vous exécutez votre application sur Compute Engine, vous pouvez utiliser l'agent Ops pour collecter des métriques et des traces du protocole OpenTelemetry. Pour en savoir plus, consultez la section Collecter les métriques et les traces OTLP.

Ajouter des dépendances

Pour configurer le SDK OpenTelemetry et l'exportateur OTLP, ajoutez les éléments suivants : les dépendances à votre application.

Java

<dependency>
  <groupId>com.google.cloud</groupId>
  <artifactId>google-cloud-spanner</artifactId>
</dependency>
<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-sdk</artifactId>
</dependency>
<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-sdk-metrics</artifactId>
</dependency>
<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-sdk-trace</artifactId>
</dependency>
<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>

Go

go.opentelemetry.io/otel v1.24.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.23.1
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.23.1
go.opentelemetry.io/otel/metric v1.24.0
go.opentelemetry.io/otel/sdk v1.24.0
go.opentelemetry.io/otel/sdk/metric v1.23.1

Injecter l'objet OpenTelemetry

Ensuite, créez un objet OpenTelemetry avec l'exportateur OTLP et injectez l'objet OpenTelemetry à l'aide de SpannerOptions.

Java

// Enable OpenTelemetry metrics and traces before Injecting OpenTelemetry
SpannerOptions.enableOpenTelemetryMetrics();
SpannerOptions.enableOpenTelemetryTraces();

// Create a new meter provider
SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder()
    // Use Otlp exporter or any other exporter of your choice.
    .registerMetricReader(
        PeriodicMetricReader.builder(OtlpGrpcMetricExporter.builder().build()).build())
    .build();

// Create a new tracer provider
SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder()
    // Use Otlp exporter or any other exporter of your choice.
    .addSpanProcessor(SimpleSpanProcessor.builder(OtlpGrpcSpanExporter
        .builder().build()).build())
        .build();

// Configure OpenTelemetry object using Meter Provider and Tracer Provider
OpenTelemetry openTelemetry = OpenTelemetrySdk.builder()
    .setMeterProvider(sdkMeterProvider)
    .setTracerProvider(sdkTracerProvider)
    .build();

// Inject OpenTelemetry object via Spanner options or register as GlobalOpenTelemetry.
SpannerOptions options = SpannerOptions.newBuilder()
    .setOpenTelemetry(openTelemetry)
    .build();
Spanner spanner = options.getService();

Go

// Ensure that your Go runtime version is supported by the OpenTelemetry-Go compatibility policy before enabling OpenTelemetry instrumentation.
// Refer to compatibility here https://github.com/googleapis/google-cloud-go/blob/main/debug.md#opentelemetry

import (
	"context"
	"fmt"
	"io"
	"log"
	"strconv"
	"strings"

	"cloud.google.com/go/spanner"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
	"go.opentelemetry.io/otel/metric"
	sdkmetric "go.opentelemetry.io/otel/sdk/metric"
	"go.opentelemetry.io/otel/sdk/resource"
	sdktrace "go.opentelemetry.io/otel/sdk/trace"
	semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
	"google.golang.org/api/iterator"
)

func enableOpenTelemetryMetricsAndTraces(w io.Writer, db string) error {
	// db = `projects/<project>/instances/<instance-id>/database/<database-id>`
	ctx := context.Background()

	// Create a new resource to uniquely identify the application
	res, err := newResource()
	if err != nil {
		log.Fatal(err)
	}

	// Enable OpenTelemetry traces by setting environment variable GOOGLE_API_GO_EXPERIMENTAL_TELEMETRY_PLATFORM_TRACING to the case-insensitive value "opentelemetry" before loading the client library.
	// Enable OpenTelemetry metrics before injecting meter provider.
	spanner.EnableOpenTelemetryMetrics()

	// Create a new tracer provider
	tracerProvider, err := getOtlpTracerProvider(ctx, res)
	defer tracerProvider.ForceFlush(ctx)
	if err != nil {
		log.Fatal(err)
	}
	// Register tracer provider as global
	otel.SetTracerProvider(tracerProvider)

	// Create a new meter provider
	meterProvider := getOtlpMeterProvider(ctx, res)
	defer meterProvider.ForceFlush(ctx)

	// Inject meter provider locally via ClientConfig when creating a spanner client or set globally via setMeterProvider.
	client, err := spanner.NewClientWithConfig(ctx, db, spanner.ClientConfig{OpenTelemetryMeterProvider: meterProvider})
	if err != nil {
		return err
	}
	defer client.Close()
	return nil
}

func getOtlpMeterProvider(ctx context.Context, res *resource.Resource) *sdkmetric.MeterProvider {
	otlpExporter, err := otlpmetricgrpc.New(ctx)
	if err != nil {
		log.Fatal(err)
	}
	meterProvider := sdkmetric.NewMeterProvider(
		sdkmetric.WithResource(res),
		sdkmetric.WithReader(sdkmetric.NewPeriodicReader(otlpExporter)),
	)
	return meterProvider
}

func getOtlpTracerProvider(ctx context.Context, res *resource.Resource) (*sdktrace.TracerProvider, error) {
	traceExporter, err := otlptracegrpc.New(ctx)
	if err != nil {
		return nil, err
	}

	tracerProvider := sdktrace.NewTracerProvider(
		sdktrace.WithResource(res),
		sdktrace.WithBatcher(traceExporter),
		sdktrace.WithSampler(sdktrace.AlwaysSample()),
	)

	return tracerProvider, nil
}

func newResource() (*resource.Resource, error) {
	return resource.Merge(resource.Default(),
		resource.NewWithAttributes(semconv.SchemaURL,
			semconv.ServiceName("otlp-service"),
			semconv.ServiceVersion("0.1.0"),
		))
}

Capturer et visualiser la latence aller-retour du client

La latence aller-retour du client correspond à la durée (en millisecondes) entre les premier octet de la requête API Spanner que le client envoie la base de données (via le GFE et l'interface de l'API Spanner), et le dernier octet de réponse que le client reçoit de la base de données.

Capturer la latence aller-retour du client

La métrique de latence aller-retour du client Spanner n'est pas compatible à l'aide d'OpenTelemetry. Vous pouvez instrumenter la métrique à l'aide d'OpenCensus avec un pont et migrer les données vers OpenTelemetry.

Visualiser la latence aller-retour du client

Après avoir récupéré les métriques, vous pouvez visualiser la latence aller-retour du client dans Cloud Monitoring.

Voici un exemple de graphique illustrant la latence du 5e centile pour le de latence aller-retour du client. Pour modifier la latence en centiles : (50e ou 99e centiles), utilisez le menu Agrégateur.

Le programme crée une vue appelée roundtrip_latency. Cette chaîne est intégrée au nom de la métrique lors de son exportation vers Cloud Monitoring.

Latence aller-retour de client Cloud Monitoring

Capturer et visualiser la latence de GFE

La latence du Google Front End (GFE) correspond à la durée (en millisecondes) entre le moment où le réseau Google reçoit un appel de procédure à distance du client et le moment où le GFE reçoit le premier octet de la réponse.

Capturer la latence du GFE

Vous pouvez capturer les métriques de latence GFE en activant les options suivantes à l'aide de la bibliothèque cliente Spanner.

Java

static void captureGfeMetric(DatabaseClient dbClient) {
  // GFE_latency and other Spanner metrics are automatically collected
  // when OpenTelemetry metrics are enabled.

  try (ResultSet resultSet =
      dbClient
          .singleUse() // Execute a single read or query against Cloud Spanner.
          .executeQuery(Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums"))) {
    while (resultSet.next()) {
      System.out.printf(
          "%d %d %s", resultSet.getLong(0), resultSet.getLong(1), resultSet.getString(2));
    }
  }
}

Go

// GFE_Latency and other Spanner metrics are automatically collected
// when OpenTelemetry metrics are enabled.
func captureGFELatencyMetric(ctx context.Context, client spanner.Client) error {
	stmt := spanner.Statement{SQL: `SELECT SingerId, AlbumId, AlbumTitle FROM Albums`}
	iter := client.Single().Query(ctx, stmt)
	defer iter.Stop()
	for {
		row, err := iter.Next()
		if err == iterator.Done {
			return nil
		}
		if err != nil {
			return err
		}
		var singerID, albumID int64
		var albumTitle string
		if err := row.Columns(&singerID, &albumID, &albumTitle); err != nil {
			return err
		}
	}
}

Visualiser la latence du GFE

Après avoir récupéré les métriques, vous pouvez visualiser la latence du GFE dans Cloud Monitoring.

Voici un exemple de graphique illustrant l'agrégation de distribution pour la métrique de latence du GFE. Pour modifier la latence du centile sur le 5e, 50e, 95e ou 99e centile, utilisez le menu Agrégateur.

Le programme crée une vue appelée spanner/gfe_latency. Ce devient une partie du nom de la métrique lorsqu'elle est exportée vers Cloud Monitoring.

Latence du GFE dans Cloud Monitoring

Capturer et visualiser la latence des requêtes de l'API Spanner

La latence de requête de l'API Spanner correspond à la durée (en secondes) entre le premier octet de requête envoyé par l'interface de l'API Spanner et le dernier octet de réponse envoyé par l'interface.

Capturer la latence des requêtes de l'API Spanner

Par défaut, cette latence est disponible dans les métriques Cloud Monitoring. Vous n'avez rien à faire pour la capturer et l'exporter.

Visualiser la latence des requêtes de l'API Spanner

Vous pouvez utiliser l'outil de graphique Explorateur de métriques pour visualiser un graphique de la métrique spanner.googleapis.com/api/request_latencies dans Cloud Monitoring.

Voici un exemple de graphique illustrant la latence du 5e centile pour le Métrique de latence des requêtes de l'API Spanner. Pour modifier le centile au 50e ou au 99e centile, utilisez l'outil Aggregator (Agrégateur). .

Latence des requêtes de l&#39;API Cloud Monitoring

Capturer et visualiser la latence des requêtes

La latence de requête correspond à la durée (en millisecondes) d'exécution des requêtes SQL dans la base de données Spanner.

Capturer la latence des requêtes

Vous pouvez capturer la latence des requêtes pour les langages suivants :

Java

static void captureQueryStatsMetric(OpenTelemetry openTelemetry, DatabaseClient dbClient) {
  // Register query stats metric.
  // This should be done once before start recording the data.
  Meter meter = openTelemetry.getMeter("cloud.google.com/java");
  DoubleHistogram queryStatsMetricLatencies =
      meter
          .histogramBuilder("spanner/query_stats_elapsed")
          .setDescription("The execution of the query")
          .setUnit("ms")
          .build();

  // Capture query stats metric data.
  try (ResultSet resultSet = dbClient.singleUse()
      .analyzeQuery(Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums"),
          QueryAnalyzeMode.PROFILE)) {

    while (resultSet.next()) {
      System.out.printf(
          "%d %d %s", resultSet.getLong(0), resultSet.getLong(1), resultSet.getString(2));
    }

    String value = resultSet.getStats().getQueryStats()
        .getFieldsOrDefault("elapsed_time", Value.newBuilder().setStringValue("0 msecs").build())
        .getStringValue();
    double elapsedTime = value.contains("msecs")
        ? Double.parseDouble(value.replaceAll(" msecs", ""))
        : Double.parseDouble(value.replaceAll(" secs", "")) * 1000;
    queryStatsMetricLatencies.record(elapsedTime);
  }
}

Go

func captureQueryStatsMetric(ctx context.Context, mp metric.MeterProvider, client spanner.Client) error {
	meter := mp.Meter(spanner.OtInstrumentationScope)
	// Register query stats metric with OpenTelemetry to record the data.
	// This should be done once before start recording the data.
	queryStats, err := meter.Float64Histogram(
		"spanner/query_stats_elapsed",
		metric.WithDescription("The execution of the query"),
		metric.WithUnit("ms"),
		metric.WithExplicitBucketBoundaries(0.0, 0.01, 0.05, 0.1, 0.3, 0.6, 0.8, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 13.0,
			16.0, 20.0, 25.0, 30.0, 40.0, 50.0, 65.0, 80.0, 100.0, 130.0, 160.0, 200.0, 250.0,
			300.0, 400.0, 500.0, 650.0, 800.0, 1000.0, 2000.0, 5000.0, 10000.0, 20000.0, 50000.0,
			100000.0),
	)
	if err != nil {
		fmt.Print(err)
	}

	stmt := spanner.Statement{SQL: `SELECT SingerId, AlbumId, AlbumTitle FROM Albums`}
	iter := client.Single().QueryWithStats(ctx, stmt)
	defer iter.Stop()
	for {
		row, err := iter.Next()
		if err == iterator.Done {
			// Record query execution time with OpenTelemetry.
			elapasedTime := iter.QueryStats["elapsed_time"].(string)
			elapasedTimeMs, err := strconv.ParseFloat(strings.TrimSuffix(elapasedTime, " msecs"), 64)
			if err != nil {
				return err
			}
			queryStats.Record(ctx, elapasedTimeMs)
			return nil
		}
		if err != nil {
			return err
		}
		var singerID, albumID int64
		var albumTitle string
		if err := row.Columns(&singerID, &albumID, &albumTitle); err != nil {
			return err
		}
	}
}

Visualiser la latence des requêtes

Après avoir récupéré les métriques, vous pouvez visualiser la latence des requêtes dans Cloud Monitoring.

Voici un exemple de graphique illustrant l'agrégation de la distribution pour la métrique de latence du GFE. Pour définir la latence en centiles sur le 5e, Pour les 50e, 95e ou 99e centiles, utilisez le menu Agrégateur.

Le programme crée une vue OpenCensus appelée query_stats_elapsed. Cette chaîne est intégrée au nom de la métrique lors de son exportation vers Cloud Monitoring.

Latence des requêtes Cloud Monitoring.

Étape suivante