ワークロードをOpenTelemetryで計装する¶
ワークロードAPIは、コンテナが処理するHTTPトラフィックから、リクエストレベルのメトリクス(カウント、エラー率、応答時間、同時実行数)を自動的に取得します。各リクエスト内でコードが何を行っているかを確認するには、コンテナをOpenTelemetryで計装し、トレース、メトリクス、およびログを出力します。
このガイドでは、Pythonワークロードからこれら3つのシグナルをすべて連携させ、データがDataRobotのオブザーバビリティ基盤に流れ込むことを確認する手順を説明します。最終的には、構造化されたトレース、カスタムメトリクス、およびプラットフォームに組み込まれたワークロードの統計情報と同じ場所に表示されるOTLP経由のログを出力するコンテナが完成します。
オブザーバビリティ基盤が公開するものに関するリファレンスドキュメントについては、監視の概念およびアプリケーションのOpenTelemetryテレメトリを参照してください。
前提条件¶
開始する前に以下が必要です。
| 前提条件 | 備考 |
|---|---|
| Pythonベースのワークロード | lockedアーティファクトを推奨します。チュートリアル:本番環境に対応したコンテナのデプロイを参照してください。 |
| 再ビルドと再デプロイの機能 | コンテナイメージを再ビルドするか、チュートリアル:実行中のワークロードの背後にあるアーティファクトの置き換えの手順で新しいアーティファクトバージョンをロールアウトします。 |
| シェル内のAPIエンドポイントとトークン | 次に示す環境変数を設定します。 |
jq(オプション) |
データフローの検証でJSONレスポンスをフォーマットするために使用します。これがない場合は、末尾の\| jq ...を削除して生のJSONレスポンスを読むか、jqlang.orgからjqをインストールしてください。 |
export DATAROBOT_ENDPOINT=https://app.datarobot.com/api/v2
export DATAROBOT_API_TOKEN=<your-api-token>
export WORKLOAD_ID=<your-workload-id>
シグナルがプラットフォームに到達する仕組み¶
3つのシグナル(トレース、メトリクス、ログ)はすべて、プラットフォームがOTEL_EXPORTER_OTLP_ENDPOINTとしてコンテナに注入するエンドポイントにOTLP HTTP経由で送信されます。アプリケーションには何もハードコードされません。OTelエクスポーターは環境変数を自動的に取得します。
ログにはOTLPプッシュが必要です。stdoutのスクレイピングは適用されません
ログに関する従来のOTelのパターンは、アプリケーションがstdoutに書き込み、クラスター上のOTelコレクターDaemonSetがポッドのログファイルをスクレイピングするというものです。DataRobotのコレクターは、OTelオブザーバビリティ基盤のためにコンテナのstdoutをスクレイピングしません。 単純なprint()呼び出しや、設定されていないstdlibのloggingは(自動stdoutキャプチャを介して)ワークロードのアクティビティログ > ログタブに引き続き表示されますが、OTelオブザーバビリティスタックには到達しません。構造化されたレコードとしてログをオブザーバビリティ基盤に取り込むには、このガイドの後半で説明するようにOTelロギングハンドラーをインストールします。アプリケーションは、トレースやメトリクスと同じトランスポートであるOTLP HTTPを介してログレコードをプッシュします。
依存関係のインストール¶
OTel SDKとOTLP HTTPエクスポーターをコンテナのrequirements.txt(または同等のもの)に追加します。
pip install opentelemetry-sdk opentelemetry-exporter-otlp
opentelemetry-exporter-otlpメタパッケージには、HTTPとgRPCの両方の3つのエクスポーター(トレース、メトリクス、ログ)がすべてバンドルされています。DataRobotのコレクターがHTTPを受け入れるため、このガイドのスニペットではHTTPバリアントを使用します。
トレースの計装¶
グローバルトレーサープロバイダーを設定し、コード内の作業単位をスパン呼び出しでラップします。スパンは、「このリクエストが何をしたか」の構造化された表現になります。これらはネストされ、属性を持ち、例外を記録します。
"""
Required: opentelemetry-sdk, opentelemetry-exporter-otlp
"""
from opentelemetry import trace
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
# Resource describing the service. Pick a stable service.namespace
# so spans can be filtered in the observability UI.
resource = Resource.create({"service.namespace": "my-service"})
def configure_tracer() -> TracerProvider:
trace_exporter = OTLPSpanExporter() # picks up OTEL_EXPORTER_OTLP_ENDPOINT
trace_provider = TracerProvider(resource=resource)
trace_provider.add_span_processor(BatchSpanProcessor(trace_exporter))
trace.set_tracer_provider(trace_provider)
return trace_provider
# Initialize once, at app startup.
trace_provider = configure_tracer()
tracer = trace.get_tracer(__name__)
次に、リクエスト処理コードでトレーサーを使用します。
with tracer.start_as_current_span("Generate Text") as span:
span.set_attribute("foo", "bar")
span.add_event(name="ack", attributes={"john": "doe"})
# Inner span: spans nest naturally inside their parent.
with tracer.start_as_current_span("Fake an Error") as inner:
try:
raise Exception("This is a fake error for demonstration purposes")
except Exception as e:
inner.record_exception(e)
inner.set_status(trace.StatusCode.ERROR, str(e))
スパンの対象:後で時間を計測したり、属性を付加したりしたいものすべて。一般的な単位は、モデル呼び出し、ベクターストアルックアップ、ダウンストリームHTTP呼び出し、ツール呼び出しです。後でフィルター処理やグループ化を行いたい値(モデル名、ユーザー層、検索戦略)の属性を設定します。例外ブロックでrecord_exceptionとset_status(StatusCode.ERROR, ...)を使用して、エラーがトレースUIに正しく表示されるようにします。
トレーサープロバイダーの設定¶
前述のTracerProviderは意図的に最小限に抑えられています。本番環境のワークロードでは、通常、いくつか追加の設定を調整します。
| 設定 | 設定場所 | 設定する理由 |
|---|---|---|
| リソース属性 | Resource.create({...}) |
単なるservice.namespaceを超えて、トレースUIでサービスを識別します。一般的な追加項目:service.name、service.version、deployment.environment。 |
| サンプリング | TracerProvider(sampler=...) |
すべてのリクエストをトレースする高スループットのワークロードでは、必要以上のスパンが生成される可能性があります。ParentBased(TraceIdRatioBased(...))サンプラーを使用して、トレースの数分の1をサンプリングしつつ、アップストリームの呼び出し元によってすでにサンプリングされているトレースは100%キャプチャします。 |
| スパンプロセッサー | BatchSpanProcessor対SimpleSpanProcessor |
BatchSpanProcessor(このガイドで使用)は、非同期にバッチ処理してエクスポートします。各スパンでブロックされないため、リクエスト処理コードに適した選択肢です。SimpleSpanProcessorは同期的にエクスポートし、プロセスの終了前にスパンをフラッシュしたい、短期間のスクリプトやデバッグにのみ役立ちます。 |
| 複数のエクスポーター | add_span_processorの複数回呼び出し |
複数のBatchSpanProcessorをアタッチします。たとえば、1つはDataRobotのOTLPエンドポイントにエクスポートし、2つ目はローカルデバッグ用にコンソールまたはベンダーのエクスポーターにエクスポートします。各プロセッサーは独自のエクスポーターインスタンスを取得します。 |
"""
Required: opentelemetry-sdk, opentelemetry-exporter-otlp
"""
from opentelemetry import trace
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.sdk.trace.sampling import ParentBased, TraceIdRatioBased
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
resource = Resource.create({
"service.name": "my-agent",
"service.namespace": "my-service",
"service.version": "1.4.2",
"deployment.environment": "production",
})
def configure_tracer() -> TracerProvider:
# Sample 20% of new traces; always sample if a parent trace was already sampled.
sampler = ParentBased(TraceIdRatioBased(0.2))
trace_provider = TracerProvider(resource=resource, sampler=sampler)
trace_provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
trace.set_tracer_provider(trace_provider)
return trace_provider
環境変数の設定
前述の設定のほとんどには環境変数と同等のものもあります。これは、コードに触れることなくデプロイごとにサンプリングやリソース属性を変更するのに便利です。アーティファクト仕様からOTEL_TRACES_SAMPLERとOTEL_TRACES_SAMPLER_ARGを設定する例については、宣言型設定(Pulumi)を参照してください。明示的な属性が渡されない場合、OTEL_RESOURCE_ATTRIBUTES(カンマ区切りのkey=valueリスト)はResource.create()によって自動的に読み取られるため、リソース属性を完全に環境から設定することもできます。
エージェントフレームワークによるトレースの出力¶
いくつかの人気のあるエージェントフレームワークはOTelネイティブです。トレースの計装に示されているようにTracerProviderが設定されると、フレームワークはエージェントの実行、ツール呼び出し、モデルリクエスト、および検索ステップごとにスパンを自動的に出力します。カスタムスパンが必要なのは、フレームワーク外のロジック(データ準備ステップ、ダウンストリームの非LLM HTTP呼び出し)のみです。
| フレームワーク | OTelサポート |
|---|---|
| Google ADK(Python ≥ 1.17、ADK Go ≥ 1.0) | ネイティブ。TracerProviderをプラグインすると、ADKはエージェントの実行、ツールの呼び出し、モデルのリクエストごとにスパンを出力します。 |
| CrewAI | ネイティブなOTel準拠のスパンを出力します。 |
| LangChain / LangGraph | ネイティブのOTelサポートに加え、古いバージョン向けにはOpenInferenceおよびOpenLLMetryを介した自動計装があります。 |
| LlamaIndex | OpenInference自動計装パッケージを介したOTel。 |
| AutoGen / AG2 | OTel準拠のスパンを出力します。 |
| Semantic Kernel | フレームワーク固有のOTel計装を提供します。 |
これらのフレームワークが出力するスパンは、OpenTelemetryのGenAIセマンティック規則(標準のgen_ai.*属性ネームスペース(モデル名、トークン数、終了理由、ツールの入力と出力))に従っているため、異なるフレームワークからのトレースを一様にクエリできます。この規則はまだ実験的とマークされていますが、ほとんどのオブザーバビリティベンダーによってサポートされています。OpenInferenceの自動計装は、前方互換性のためにOpenInference属性とOTel GenAI属性の両方を出力します。
自動計装はトレースのみを対象としています
メトリクスとログには、依然として明示的な設定が必要です。カスタムカウンター、ヒストグラム、およびアプリケーションログについては、次の2つのセクションに進んでください。
メトリクスの計装¶
メトリクスは、単一のリクエストスパンにきれいに収まらないカウントやレート(トークン消費量、キャッシュヒット数、キューの深さ、モデル選択の分布など)に最適です。定期的なリーダーを備えたメータープロバイダーを設定し、必要に応じてカウンター、ゲージ、またはヒストグラムを作成します。
"""
Required: opentelemetry-sdk, opentelemetry-exporter-otlp
"""
from opentelemetry import metrics
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.sdk.resources import Resource
resource = Resource.create({"service.namespace": "my-service"})
def configure_metrics(resource: Resource) -> MeterProvider:
metric_exporter = OTLPMetricExporter() # picks up OTEL_EXPORTER_OTLP_ENDPOINT
reader = PeriodicExportingMetricReader(metric_exporter, export_interval_millis=5000)
meter_provider = MeterProvider(resource=resource, metric_readers=[reader])
metrics.set_meter_provider(meter_provider)
return meter_provider
metric_provider = configure_metrics(resource)
meter = metric_provider.get_meter(__name__)
# Define instruments once, at startup.
my_counter = meter.create_counter(
name="my.counter",
description="Example custom counter.",
unit="1",
)
次に、リクエスト処理コードから値を記録します。
my_counter.add(1, {"environment": "demo"})
PeriodicExportingMetricReaderは、export_interval_millisの間隔(前の例では5秒)でバッチ化されたメトリクスを送信します。コレクターの過負荷を避けるため、カーディナリティの高いワークロードではより長い間隔(15〜60秒)を選択してください。
OTel SDKは、利用できる3つの計器タイプを提供します。
| タイプ | OTelメソッド | ユースケースの例 |
|---|---|---|
| カウンター | create_counter |
単調に増加する値。リクエスト数、消費されたトークン、再試行の試行に使用します。 |
| ヒストグラム | create_histogram |
値の分布。レイテンシー、リクエストあたりのトークン、ペイロードサイズに使用します。 |
| 観測可能なゲージ | create_observable_gauge |
サンプリングされた値。キューの深さ、キャッシュサイズ、接続数に使用します。 |
ログの計装¶
ログがオブザーバビリティ基盤に到達するために、この手順は必須です。OTelロギングハンドラーは、Pythonのstdlib loggingモジュールをOTLP HTTPエクスポートにブリッジするため、コード内のすべてのlogger.info(...)はプラットフォームが取り込めるログレコードになります。
"""
Required: opentelemetry-sdk, opentelemetry-exporter-otlp
"""
import logging
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
from opentelemetry._logs import set_logger_provider
resource = Resource.create({"service.namespace": "my-service"})
def configure_logging() -> LoggerProvider:
log_exporter = OTLPLogExporter() # picks up OTEL_EXPORTER_OTLP_ENDPOINT
log_provider = LoggerProvider(resource=resource)
log_provider.add_log_record_processor(BatchLogRecordProcessor(log_exporter))
set_logger_provider(log_provider)
# Silence urllib3's own DEBUG logging before widening the root logger
# to DEBUG below, or each export logs a line that gets re-exported,
# triggering another export. See "Avoid a self-sustaining export loop".
for transport_logger in ("urllib3", "urllib3.connectionpool", "requests"):
logging.getLogger(transport_logger).setLevel(logging.WARNING)
# Bridge Python's stdlib logging into OpenTelemetry.
root_logger = logging.getLogger()
otel_handler = LoggingHandler(level=logging.NOTSET, logger_provider=log_provider)
root_logger.addHandler(otel_handler)
root_logger.setLevel(logging.DEBUG) # capture every level; filter downstream
return log_provider
log_provider = configure_logging()
logger = logging.getLogger(__name__)
これ以降、通常のstdlibロギング呼び出しは自動的にOTLPにエクスポートされます。
logger.info("Logging info.", extra={"extra": "INFO details"})
logger.warning("Logging warning.", extra={"extra": "WARNING details"})
logger.error("Logging error.", extra={"extra": "ERROR details"})
logger.debug("Logging debug.", extra={"extra": "DEBUG details"})
extra=辞書は、ログレコードの構造化された属性として添付されます。これは、オブザーバビリティUIがメッセージ文字列を解析することなく、それらでフィルター処理できることを意味します。クエリ可能であるべきすべてのものにextraを使用し、人間が読めるサマリーのためにメッセージを予約します。
アプリ全体に1つのイニシャライザ
トレーサー、メーター、およびロガーのプロバイダーは、アプリの起動時に一度だけ設定します。理想的には、エントリーポイントが他の何よりも先にインポートする単一のobservability.pyモジュール内で行います。リクエストごとに再初期化すると、バックグラウンドスレッドがリークし、エクスポートがドロップされます。
自己維持エクスポートループを回避する¶
ルートロガーをDEBUGでブリッジすると、エクスポーター自身のHTTPクライアントもキャプチャされ、そのOTLPエクスポートリクエストがDEBUGでログに記録されます。 上記の抑制処理を行わない場合、各エクスポートが1行をログに記録し、それがキャプチャされてエクスポートされ、さらに別の行がログに記録される状態になります。
解決策は、上記のlogging.getLogger(...).setLevel(logging.WARNING)の呼び出しです。これは、エクスポーターが使用するHTTPライブラリ(ここではurllib3/requests。gRPCエクスポーターの場合はgrpcも無効化してください)に適用します。 ルートロガーをDEBUGに拡大する前に、これを適用します。
DataRobot Moderationsの追加(オプション)¶
DataRobot Moderationsライブラリは、LLMのプロンプトおよび回答に対して、ガードベースのコンテンツモデレーションを適用します。 LLMの呼び出し前にはprescoreガードを、呼び出し後にはpostscoreガードを実行し、設定に基づいてコンテンツをブロック、置換、または記録することができます。
ワークロードプラットフォームでは、すでにOTEL_EXPORTER_OTLP_ENDPOINTがコンテナに注入されているため、モデレーションのトレースは自動的にDataRobotのオブザーバビリティ基盤に送られます。追加のエクスポーター設定は不要です。 モデレーションのスパンは、アプリケーションのスパンとともにモニタリング > データ探索に表示されます。
モデレーションライブラリのインストール¶
コンテナのrequirements.txtにdatarobot-moderationsを追加します。依存関係をインストールした場合、OTelパッケージはすでに存在しています。
pip install datarobot-moderations
モデレーションガードの設定¶
コンテナイメージ内にmoderation_config.yamlを作成します。
guards:
- name: Toxicity
type: ootb
ootb_type: toxicity
stage: response
intervention:
action: block
message: "Response blocked: content policy."
- name: Cost
type: ootb
ootb_type: cost
stage: response
リクエストハンドラーでの使用¶
from datarobot_dome.api import ModerationPipeline
# Initialize once at startup.
pipeline = ModerationPipeline.from_yaml("moderation_config.yaml")
@app.post("/generate")
def generate(prompt: str):
with tracer.start_as_current_span("generate"):
# Prescore: evaluate the prompt before calling the LLM.
pre, _, _ = pipeline.evaluate_prompt(prompt)
if pre.blocked:
return {"error": pre.blocked_message}
response = call_your_llm(prompt)
# Postscore: evaluate the LLM response.
post, _, _ = pipeline.evaluate_response(response, prompt=prompt)
if post.blocked:
return {"error": post.blocked_message}
return {"answer": response}
モデレーションスパンは現在のOTelトレースコンテキストにアタッチされるため、トレースの計装で作成されたスパン内にネストされます。 また、このライブラリは各ガード評価でOTelメトリクスも出力します。これらは、モニタリング > OTelメトリクスにおいて、datarobot.moderations.*で始まる名前で表示されます。
トレーステーブルのコスト列
コストガードが設定されている場合、ライブラリはスパンにdatarobot.moderation.costを付加します。 モニタリング > データ探索のトレーステーブルでは、トレース内のすべてのスパンについてこの属性を集計し、コスト列に値を設定します。
ガードに関する完全なリファレンス(毒性、忠実度、タスクの順守、カスタムメトリクス、YAMLスキーマ、ブロッキングセマンティクスなど)については、Moderationsのガードレールを参照してください。
小さなハンドラーでのまとめ¶
次の最小限のFastAPIアプリは、3つのシグナルすべてを設定し、リクエストごとに何かを出力します。
import logging
from fastapi import FastAPI
from opentelemetry import trace, metrics
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry._logs import set_logger_provider
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
resource = Resource.create({"service.namespace": "my-agent"})
# Traces
tp = TracerProvider(resource=resource)
tp.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
trace.set_tracer_provider(tp)
tracer = trace.get_tracer(__name__)
# Metrics
mp = MeterProvider(
resource=resource,
metric_readers=[PeriodicExportingMetricReader(OTLPMetricExporter(), export_interval_millis=5000)],
)
metrics.set_meter_provider(mp)
meter = mp.get_meter(__name__)
request_counter = meter.create_counter("requests.handled", unit="1")
# Logs
lp = LoggerProvider(resource=resource)
lp.add_log_record_processor(BatchLogRecordProcessor(OTLPLogExporter()))
set_logger_provider(lp)
# Silence the exporter's own HTTP client before bridging the root logger.
# See "Avoid a self-sustaining export loop" under Instrument logs.
for transport_logger in ("urllib3", "urllib3.connectionpool", "requests"):
logging.getLogger(transport_logger).setLevel(logging.WARNING)
logging.getLogger().addHandler(LoggingHandler(level=logging.NOTSET, logger_provider=lp))
logging.getLogger().setLevel(logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI()
@app.get("/healthz")
def healthz():
return {"ok": True}
@app.post("/generate")
def generate(prompt: str):
with tracer.start_as_current_span("generate") as span:
span.set_attribute("prompt.length", len(prompt))
logger.info("Handling generate request", extra={"prompt_length": len(prompt)})
request_counter.add(1, {"route": "/generate"})
# ... your model call here ...
return {"answer": "hello"}
これをコンテナイメージにビルドし、ワークロードとしてデプロイして、/generateを数回呼び出します。
データフローの検証¶
プラットフォームは、各シグナルを独自の読み取りエンドポイントで公開します。ワークロードにいくつかのリクエストを送信した後、それらにアクセスします。
curl -s "${DATAROBOT_ENDPOINT}/otel/workload/${WORKLOAD_ID}/traces/" \
-H "Authorization: Bearer ${DATAROBOT_API_TOKEN}" | jq '.'
curl -s "${DATAROBOT_ENDPOINT}/otel/workload/${WORKLOAD_ID}/metrics/" \
-H "Authorization: Bearer ${DATAROBOT_API_TOKEN}" | jq '.'
curl -s "${DATAROBOT_ENDPOINT}/otel/workload/${WORKLOAD_ID}/logs/" \
-H "Authorization: Bearer ${DATAROBOT_API_TOKEN}" | jq '.'
期待される結果:
service.namespace=my-agentを持つgenerateスパン(実際のハンドラーが作成した場合はネストされたスパンを含む)。/generateルートで増加するrequests.handledカウンター。- 構造化属性として
prompt_lengthを持つ"Handling generate request"ログ行。
トレースまたはメトリクスの呼び出しが空のペイロードを返すのにログ呼び出しが機能する場合(またはその逆)、最も一般的な原因は、アプリの起動時に対応するプロバイダーを登録し忘れていることです。小さなハンドラーでのまとめを再確認して、最初のリクエストの前に3つのset_*_provider呼び出しすべてが行われていることを確認してください。
トラブルシューティング¶
| 症状 | 考えられる原因 |
|---|---|
| トレース、メトリクス、ログがない | OTEL_EXPORTER_OTLP_ENDPOINTがコンテナの環境に設定されていません。プロトンのstatusDetailsで確認するか(環境変数はレプリカの詳細に表示されます)、起動時にos.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT")を出力して確認してください。 |
| トレースとメトリクスは機能するが、ログは機能しない | OTelのLoggingHandlerがルートロガーにインストールされていません。単純なprint()や設定されていないstdlibのロギングでは、オブザーバビリティ基盤に到達しません。ログの計装を参照してください。 |
| ログは表示されるが属性がない | フィールドがextra={...}ではなく位置引数として渡されています。クエリ可能な属性にはlogger.info("msg", extra={"key": "value"})を使用してください。 |
| ログの量が増加し続け、トランスポート関連のメッセージが繰り返される | ルートロガーは、エクスポーターのHTTPクライアントを無効化することなくDEBUGでブリッジされているため、トランスポートログが繰り返し再エクスポートされてしまいます。 自己維持エクスポートループを回避するを参照してください。 |
| メトリクス値が動かない、または表示されない | PeriodicExportingMetricReaderがまだ動作していません。最初のエクスポートはexport_interval_millisが経過した後にのみ発生します。1サイクル待つか、開発中は間隔を短くしてください。 |
| スパンがネストしない | 子スパンがstart_as_current_spanではなくstart_spanで開始されています。「as current」バリアントは、ネストされた呼び出しのコンテキストを設定します。 |
宣言型設定(Pulumi)¶
リソース属性やサンプリングのオーバーライドなど、OTel関連の安定した環境変数のセットが準備できたら、それらをアーティファクトのenvironmentVarsに組み込み、そのアーティファクトの各デプロイで同じオブザーバビリティ設定が適用されるようにします。
import pulumi
import pulumi_datarobot as datarobot
artifact = datarobot.Artifact(
"my-agent-artifact",
name="my-agent-artifact",
type="service",
spec={"container_groups": [{"containers": [{
"name": "agent",
"image_uri": "ghcr.io/myorg/my-agent:v1",
"port": 8080,
"primary": True,
"environment_vars": [
# OTEL_EXPORTER_OTLP_ENDPOINT is injected by the platform; do not override it.
{"name": "OTEL_SERVICE_NAME", "value": "my-agent"},
{"name": "OTEL_RESOURCE_ATTRIBUTES", "value": "service.namespace=my-service,deployment.environment=prod"},
# Optional: tune sampling for high-traffic workloads.
{"name": "OTEL_TRACES_SAMPLER", "value": "parentbased_traceidratio"},
{"name": "OTEL_TRACES_SAMPLER_ARG", "value": "0.1"},
],
"readiness_probe": {"path": "/healthz", "port": 8080},
}]}]},
)
OTEL_EXPORTER_OTLP_ENDPOINT変数はプラットフォームによって注入されます。テレメトリをカスタムコレクターにルーティングする場合にのみ、明示的に設定してください。その他すべて(サンプリング、リソース属性、サービス名)はご自身で制御できます。Pulumiの完全なセットアップについては、Pulumiによるワークロードの管理を参照してください。