שימוש בערכות SDK לשליחת מדדים מאפליקציות

במסמך הזה מוסבר איך לשלוח מדדים ישירות מאפליקציות לנקודת הקצה של OTLP באמצעות SDK והמייצא של gRPC, שהוא המייצא המומלץ לשימוש עם ערכות SDK. נקודת הקצה של OTLP תומכת בכל הפרוטוקולים של OTLP, כולל http/proto,‏ http/json ו-grpc. מידע נוסף מופיע במאמר תמיכה בפרוטוקולים.

כששולחים מדדי אפליקציות ישירות לנקודת הקצה של OTLP,‏ OpenTelemetry Collector לא מטפל באימות, בזיהוי משאבים או באצווה בשבילכם. כשמשתמשים בערכות SDK כדי לשלוח מדדים מאפליקציות, צריך לבצע את הפעולות הבאות:

  • מגדירים את פרטי הכניסה של מייצא ה-gRPC של OTLP כך שישתמש ב-Application Default Credentials של Google, כמו שמתואר במאמר קבלת פרטי כניסה לאימות.
  • זיהוי משאבים של Google Cloud ומשאבים ב-Kubernetes מהאפליקציה. האופן שבו עושים את זה תלוי באפליקציה שאתם מריצים.
  • מגדירים גודל אצווה מקסימלי של 200 נקודות נתונים של מדדים לייצוא, כדי שהבקשות לא יחרגו ממכסת בקשות Telemetry API של 200 נקודות נתונים לכל בקשה.

לפני שמתחילים

כדי להריץ את הדוגמאות, צריך להפעיל את ממשקי ה-API הנדרשים ולקבל פרטי כניסה לאימות.

הפעלת ממשקי ה-API

מפעילים את Cloud Monitoring API ואת Telemetry API בפרויקט ב-Google Cloud באמצעות הפקודה הבאה:

gcloud services enable monitoring.googleapis.com telemetry.googleapis.com

קבלת פרטי כניסה לאימות

מריצים את הפקודה הבאה כדי להגדיר את פרטי הכניסה של מייצא ה-gRPC של OTLP, כך שישתמש בApplication Default Credentials של Google:

gcloud auth application-default login

דוגמאות ספציפיות לשפות

בקטע הזה יש דוגמאות בשפות שונות ליצירה ולכתיבה של מדד מונה. כדי לקבל מידע על הרצת הדוגמה ולראות את הקוד, בוחרים כרטיסייה.

המשך

כדי להבין איך מגדירים ומריצים את הדוגמה הזו, קראו את הקובץ README של הדוגמה ב-Go.

// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"context"
	"errors"
	"log"

	"go.opentelemetry.io/contrib/detectors/gcp"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
	"go.opentelemetry.io/otel/metric"
	sdkmetric "go.opentelemetry.io/otel/sdk/metric"
	"go.opentelemetry.io/otel/sdk/resource"
	semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/oauth"
)

func main() {
	ctx := context.Background()

	creds, err := oauth.NewApplicationDefault(ctx)
	if err != nil {
		panic(err)
	}

	res, err := resource.New(
		ctx,
		// Use the GCP resource detector to detect information about the GCP platform
		resource.WithDetectors(gcp.NewDetector()),
		// Keep the default detectors
		resource.WithTelemetrySDK(),
		// Add attributes from environment variables
		resource.WithFromEnv(),
		// Add your own custom attributes to identify your application
		resource.WithAttributes(
			semconv.ServiceNameKey.String("example-application"),
		),
	)
	if errors.Is(err, resource.ErrPartialResource) || errors.Is(err, resource.ErrSchemaURLConflict) {
		log.Println(err)
	} else if err != nil {
		panic(err)
	}

	// Set endpoint with OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
	metricExporter, err := otlpmetricgrpc.New(ctx, otlpmetricgrpc.WithDialOption(grpc.WithPerRPCCredentials(creds)))
	if err != nil {
		panic(err)
	}

	meterProvider := sdkmetric.NewMeterProvider(
		sdkmetric.WithResource(res),
		sdkmetric.WithReader(
			sdkmetric.NewPeriodicReader(
				metricExporter,
				sdkmetric.WithMaxExportBatchSize(200),
			),
		),
	)

	defer func() {
		if err = meterProvider.Shutdown(ctx); err != nil {
			log.Println(err)
		}
	}()

	meter := meterProvider.Meter("github.com/GoogleCloudPlatform/opentelemetry-samples/golang/metric/otlpgrpc")

	// Register counter value
	counter, err := meter.Int64Counter("counter-a")
	if err != nil {
		log.Fatalf("Failed to create counter: %v", err)
	}
	clabels := []attribute.KeyValue{attribute.Key("key").String("value")}
	counter.Add(ctx, 100, metric.WithAttributes(clabels...))
}

Java

למידע על הגדרה והרצה של הדוגמה הזו, עיינו בקובץ README של הדוגמה ב-Java.

/*
 * Copyright 2026 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.cloud.opentelemetry.samples.otlpmetric;

import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.common.CompletableResultCode;
import java.util.Random;
import java.util.concurrent.TimeUnit;

public class OTLPMetricExample {
  private static final String INSTRUMENTATION_SCOPE_NAME = OTLPMetricExample.class.getName();
  private static final Random RANDOM = new Random();

  private static OpenTelemetrySdk openTelemetrySdk;

  private static void myUseCase() {
    LongCounter counter =
        openTelemetrySdk
            .getMeter(INSTRUMENTATION_SCOPE_NAME)
            .counterBuilder("example_counter")
            .setDescription("Processed jobs")
            .setUnit("1")
            .build();
    doWork(counter);
  }

  private static void doWork(LongCounter counter) {
    try {
      for (int i = 0; i < 10; i++) {
        counter.add(RANDOM.nextInt(100));
        Thread.sleep(RANDOM.nextInt(1000));
      }
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }
  }

  public static void main(String[] args) {
    // Configure the OpenTelemetry pipeline with Auto configuration
    openTelemetrySdk = AutoConfiguredOpenTelemetrySdk.initialize().getOpenTelemetrySdk();

    // Application-specific logic
    myUseCase();

    // Flush all buffered metrics
    CompletableResultCode completableResultCode = openTelemetrySdk.getSdkMeterProvider().shutdown();
    // wait till export finishes
    completableResultCode.join(10000, TimeUnit.MILLISECONDS);
  }
}

NodeJS

כדי לקבל מידע על הגדרה והפעלה של הדוגמה הזו, עיינו בקובץ README של הדוגמה ב-NodeJS.

// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import * as opentelemetry from '@opentelemetry/sdk-node';
import {AuthClient, GoogleAuth} from 'google-auth-library';
import {credentials} from '@grpc/grpc-js';
import {getResourceDetectors as getResourceDetectorsFromEnv} from '@opentelemetry/auto-instrumentations-node';
import {metrics, diag, DiagConsoleLogger} from '@opentelemetry/api';
import {OTLPMetricExporter} from '@opentelemetry/exporter-metrics-otlp-grpc';
import {PeriodicExportingMetricReader} from '@opentelemetry/sdk-metrics';

async function getAuthenticatedClient(): Promise<AuthClient> {
  const auth: GoogleAuth = new GoogleAuth({
    scopes: 'https://www.googleapis.com/auth/cloud-platform',
  });
  return await auth.getClient();
}

diag.setLogger(
  new DiagConsoleLogger(),
  opentelemetry.core.diagLogLevelFromString(
    opentelemetry.core.getStringFromEnv('OTEL_LOG_LEVEL'),
  ),
);

// App that exports metrics via gRPC with protobuf
async function main() {
  const authenticatedClient: AuthClient = await getAuthenticatedClient();

  const sdk = new opentelemetry.NodeSDK({
    metricReaders: [
      new PeriodicExportingMetricReader({
        // Export metrics every 10 seconds. 5 seconds is the smallest sample period allowed by
        // Cloud Monitoring.
        exportIntervalMillis: 10_000,
        // Cloud Monitoring allows at most 200 time series per write request.
        maxExportBatchSize: 200,
        exporter: new OTLPMetricExporter({
          credentials: credentials.combineChannelCredentials(
            credentials.createSsl(),
            credentials.createFromGoogleCredential(authenticatedClient),
          ),
        }),
      }),
    ],
    resourceDetectors: getResourceDetectorsFromEnv(),
  });
  sdk.start();

  // Create a meter
  const meter = metrics.getMeter('metrics-sample');

  // Create a counter instrument
  const counter = meter.createCounter('metric_name');
  // Record a measurement
  counter.add(10, {key: 'value'});

  // Wait for the metric to be exported
  await new Promise(resolve => {
    setTimeout(resolve, 11_000);
  });

  // 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));
  });
}

main().catch(console.error);

Python

כדי להריץ את הדוגמה הזו, עיינו בקובץ README של הדוגמה ב-Python.

#!/usr/bin/env python3
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import time

import google.auth
import google.auth.transport.grpc
import google.auth.transport.requests
import grpc
from google.auth.transport.grpc import AuthMetadataPlugin
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import (
    OTLPMetricExporter,
)
from opentelemetry.resourcedetector.gcp_resource_detector import (
    GoogleCloudResourceDetector,
)
from opentelemetry.sdk.resources import get_aggregated_resources
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader

"""
Sample script exporting OTLP metrics encoded as protobufs via gRPC.
"""

credentials, project_id = google.auth.default()
request = google.auth.transport.requests.Request()
resource = get_aggregated_resources([GoogleCloudResourceDetector(raise_on_error=True)])

auth_metadata_plugin = AuthMetadataPlugin(credentials=credentials, request=request)
channel_creds = grpc.composite_channel_credentials(
    grpc.ssl_channel_credentials(),
    grpc.metadata_call_credentials(auth_metadata_plugin),
)

exporter = OTLPMetricExporter(
    credentials=channel_creds,
    max_export_batch_size=200,
)
reader = PeriodicExportingMetricReader(exporter)
provider = MeterProvider(metric_readers=[reader], resource=resource)
meter = provider.get_meter("gcp.otlp.sample")
counter = meter.create_counter("sample.otlp.counter")


def do_work():
    counter.add(1)
    # do some work that the 'counter' will track
    print("doing some work...")


def do_work_repeatedly():
    try:
        while True:
            do_work()
            time.sleep(1)
    except KeyboardInterrupt:
        print("\nKeyboard Interrupt: Stopping work.")


do_work_repeatedly()

שימוש ב-downward API לאפליקציות שפועלות כ-pods

אם אתם מריצים את האפליקציה כ-pod ב-Kubernetes ולא שולחים נתוני טלמטריה דרך OpenTelemetry Collector, הקפידו להשתמש ב-downward API כדי להגדיר את מאפייני המשאבים ב-Kubernetes:

env:
- name: POD_NAME
  valueFrom:
    fieldRef:
      fieldPath: metadata.name
- name: NAMESPACE_NAME
  valueFrom:
    fieldRef:
      fieldPath: metadata.namespace
- name: CONTAINER_NAME
  value: my-container-name
- name: OTEL_RESOURCE_ATTRIBUTES
  value: k8s.pod.name=$(POD_NAME),k8s.namespace.name=$(NAMESPACE_NAME),k8s.container.name=$(CONTAINER_NAME)

המאמרים הבאים