本文說明如何使用 SDK 和 gRPC 匯出工具,直接從應用程式將指標傳送至 OTLP 端點。建議搭配 SDK 使用 gRPC 匯出工具。OTLP 端點支援所有 OTLP 通訊協定,包括 http/proto、http/json 和 grpc。詳情請參閱通訊協定支援。
直接將應用程式指標傳送至 OTLP 端點時,OpenTelemetry Collector 不會為您處理驗證和擴充作業。使用 SDK 從應用程式傳送指標時,請務必採取以下做法:
- 如要設定 OTLP gRPC 匯出工具的憑證,請使用 Google 應用程式預設憑證,詳情請參閱「取得驗證憑證」。
- 從應用程式偵測 Google Cloud Kubernetes 資源。操作方式取決於您執行的應用程式。
事前準備
如要執行範例,請務必啟用必要的 API 並取得驗證憑證。
啟用 API
在Google Cloud 專案中執行下列指令,啟用 Cloud Monitoring API 和 Telemetry API:
gcloud services enable monitoring.googleapis.com telemetry.googleapis.com
取得驗證憑證
執行下列指令,為 OTLP gRPC 匯出工具設定憑證,以使用 Google Application Default Credentials:
gcloud auth application-default login
特定語言的範例
本節提供各種語言的範例,說明如何建立及寫入計數器指標。選取分頁標籤,即可查看執行範例的相關資訊和程式碼。
Go
如要瞭解如何設定及執行這個範例,請參閱 Go 範例的 README 檔案。
// Copyright 2024 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)),
)
defer func() {
if err = meterProvider.Shutdown(ctx); err != nil {
log.Println(err)
}
}()
meter := meterProvider.Meter("github.com/GoogleCloudPlatform/opentelemetry-operations-go/example/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
如要瞭解如何設定及執行這個範例,請參閱 Java 範例的 README 檔案。
/*
* Copyright 2025 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.example.otlpmetric;
import com.google.auth.oauth2.GoogleCredentials;
import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter;
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
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 OpenTelemetrySdk setupMetricExporter() throws IOException {
GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
// Update the SDK configured using environment variables and system properties
// TODO: Replace this with the use of gcp-auth-extension
AutoConfiguredOpenTelemetrySdk autoConfOTelSdk =
AutoConfiguredOpenTelemetrySdk.builder()
.addMetricExporterCustomizer(
(exporter, configProperties) -> addAuthorizationHeaders(exporter, credentials))
.build();
return autoConfOTelSdk.getOpenTelemetrySdk();
}
// Modifies the metric exporter initially auto-configured using environment variables
// This will invoke the header supplier function to compute the headers, which takes care of the
// refresh.
private static MetricExporter addAuthorizationHeaders(
MetricExporter exporter, GoogleCredentials credentials) {
if (exporter instanceof OtlpHttpMetricExporter) {
return ((OtlpHttpMetricExporter) exporter)
.toBuilder().setHeaders(() -> getRequiredHeaderMap(credentials)).build();
} else if (exporter instanceof OtlpGrpcMetricExporter) {
return ((OtlpGrpcMetricExporter) exporter)
.toBuilder().setHeaders(() -> getRequiredHeaderMap(credentials)).build();
}
return exporter;
}
private static Map<String, String> getRequiredHeaderMap(GoogleCredentials credentials) {
Map<String, String> gcpHeaders = new HashMap<>();
try {
credentials.refreshIfExpired();
} catch (IOException e) {
throw new RuntimeException(e);
}
gcpHeaders.put("Authorization", "Bearer " + credentials.getAccessToken().getTokenValue());
String configuredQuotaProjectId = credentials.getQuotaProjectId();
if (configuredQuotaProjectId != null && !configuredQuotaProjectId.isEmpty()) {
gcpHeaders.put("x-goog-user-project", configuredQuotaProjectId);
}
return gcpHeaders;
}
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) throws IOException {
// Configure the OpenTelemetry pipeline with CloudMetric exporter
openTelemetrySdk = setupMetricExporter();
// Application-specific logic
myUseCase();
// Flush all buffered metrics
CompletableResultCode completableResultCode = openTelemetrySdk.getSdkMeterProvider().shutdown();
// wait till export finishes
completableResultCode.join(10000, TimeUnit.MILLISECONDS);
}
}
NodeJS
如要瞭解如何設定及執行這個範例,請參閱 NodeJS 範例的 README 檔案。
// Copyright 2025 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({
metricReader: new PeriodicExportingMetricReader({
// Export metrics every 10 seconds. 5 seconds is the smallest sample period allowed by
// Cloud Monitoring.
exportIntervalMillis: 10_000,
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
如要瞭解如何執行這個範例,請參閱 Python 範例的 README 檔案。README
#!/usr/bin/env python3
# Copyright 2025 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
"""
This is a sample script that exports 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)
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()
針對以 Pod 形式執行的應用程式使用 Downward API
如果您在 Kubernetes 中以 Pod 形式執行應用程式,且未透過 OpenTelemetry Collector 傳送資料,請務必使用向下 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)
後續步驟
- 如要瞭解如何搭配使用 OpenTelemetry Collector 和 Telemetry API,以及 OpenTelemetry 零程式碼檢測功能,請參閱「使用 Java 適用的 OpenTelemetry 零程式碼檢測功能」。
- 如要瞭解如何從其他匯出工具遷移至
otlphttp匯出工具,請參閱「遷移至 OTLP 匯出工具」。 - 如要進一步瞭解 Telemetry API,請參閱「Telemetry (OTLP) API 總覽」。