第4章 実践アプリケーション実装:Python と C# による計測
本章では、第 3 章で構築したフルスタック基盤(Collector)へ向けて、Python と C# のアプリケーションから「トレース」と「メトリクス」の双方を計測・送信する完全な実装例を解説します。
Python によるトレースとメトリクスの同時計測
OpenTelemetry の Python SDK を使用し、注文処理のトレース(Span)と、注文数・処理時間のメトリクス(Counter、Histogram)を計測します。
ライブラリの準備
pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp
実装コード(app_order.py)
import random
import time
from opentelemetry import metrics, trace
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
# 共通のリソース(サービス情報)を定義
resource = Resource.create(
attributes={
"service.name": "order-service-python",
"service.version": "2.0.0",
"deployment.environment": "production",
}
)
# 1. トレース設定 (TracerProvider)
trace_provider = TracerProvider(resource=resource)
trace_processor = BatchSpanProcessor(
OTLPSpanExporter(endpoint="localhost:4317", insecure=True)
)
trace_provider.add_span_processor(trace_processor)
trace.set_tracer_provider(trace_provider)
tracer = trace.get_tracer("order.tracer")
# 2. メトリクス設定 (MeterProvider)
# 5秒ごとに Collector へメトリクスを送信
metric_reader = PeriodicExportingMetricReader(
OTLPMetricExporter(endpoint="localhost:4317", insecure=True),
export_interval_millis=5000,
)
meter_provider = MeterProvider(resource=resource, metric_readers=[metric_reader])
metrics.set_meter_provider(meter_provider)
meter = metrics.get_meter("order.meter")
# 計測器(Instruments)の定義
order_counter = meter.create_counter(
name="order_requests_total",
description="注文リクエストの総数",
unit="1",
)
order_duration_histogram = meter.create_histogram(
name="order_duration_seconds",
description="注文処理の所要時間(秒)",
unit="s",
)
def handle_order(order_id: str, item_category: str):
start_time = time.time()
with tracer.start_as_current_span("handle_order") as span:
span.set_attribute("order.id", order_id)
span.set_attribute("order.category", item_category)
# 疑似的な業務処理と子スパン
with tracer.start_as_current_span("check_stock"):
time.sleep(random.uniform(0.05, 0.15))
with tracer.start_as_current_span("record_order"):
time.sleep(random.uniform(0.05, 0.10))
# 処理結果のメトリクスを記録
elapsed = time.time() - start_time
span.set_attribute("order.duration", elapsed)
order_counter.add(1, {"category": item_category, "status": "success"})
order_duration_histogram.record(elapsed, {"category": item_category})
print(f"[Python] 注文完了: {order_id} ({item_category}) - {elapsed:.3f}s")
if __name__ == "__main__":
print("Python 注文サービスを開始します(10回のリクエストをシミュレート)...")
categories = ["electronics", "books", "clothing"]
for i in range(1, 11):
handle_order(f"ORD-PY-{i:03d}", random.choice(categories))
time.sleep(0.5)
print("テレメトリデータを送信中...")
# プロバイダーをシャットダウンして未送信データを Collector へフラッシュ
meter_provider.shutdown()
trace_provider.shutdown()
print("Python アプリケーションが正常終了しました。")
C# によるトレースとメトリクスの同時計測
.NET では、標準の ActivitySource(トレース)と Meter(メトリクス)を使用し、OpenTelemetry SDK 経由で Collector へ送信します。
ライブラリの準備
dotnet new console -n OtelFullstackCSharp
cd OtelFullstackCSharp
dotnet add package OpenTelemetry
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol
実装コード(Program.cs)
using System.Diagnostics;
using System.Diagnostics.Metrics;
using OpenTelemetry;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
const string serviceName = "payment-service-csharp";
const string serviceVersion = "2.0.0";
// 1. .NET 組み込みの ActivitySource(トレース)と Meter(メトリクス)を宣言
var activitySource = new ActivitySource(serviceName, serviceVersion);
var meter = new Meter(serviceName, serviceVersion);
// 計測器(Instruments)の作成
var transactionCounter = meter.CreateCounter<long>(
name: "payment_transactions_total",
unit: "1",
description: "決済トランザクションの総数");
var transactionAmountHistogram = meter.CreateHistogram<double>(
name: "payment_amount_yen",
unit: "JPY",
description: "決済金額の分布");
// 2. OpenTelemetry TracerProvider の構築
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
.ConfigureResource(r => r.AddService(serviceName, serviceVersion: serviceVersion))
.AddSource(serviceName)
.AddOtlpExporter(opt =>
{
opt.Endpoint = new Uri("http://localhost:4317");
opt.Protocol = OpenTelemetry.Exporter.OtlpExportProtocol.Grpc;
})
.Build();
// 3. OpenTelemetry MeterProvider の構築
using var meterProvider = Sdk.CreateMeterProviderBuilder()
.ConfigureResource(r => r.AddService(serviceName, serviceVersion: serviceVersion))
.AddMeter(serviceName)
.AddOtlpExporter(opt =>
{
opt.Endpoint = new Uri("http://localhost:4317");
opt.Protocol = OpenTelemetry.Exporter.OtlpExportProtocol.Grpc;
})
.Build();
Console.WriteLine("C# 決済サービスを開始します(10件の決済をシミュレート)...");
var random = new Random();
var methods = new[] { "credit_card", "qr_code", "bank_transfer" };
for (int i = 1; i <= 10; i++)
{
ExecutePayment($"PAY-CS-{i:03d}", methods[random.Next(methods.Length)], random.Next(1000, 15000));
Thread.Sleep(500);
}
Console.WriteLine("C# アプリケーションが正常終了しました。");
void ExecutePayment(string transactionId, string method, double amount)
{
// トレーススパンの作成
using (var activity = activitySource.StartActivity("ExecutePayment"))
{
activity?.SetTag("payment.transaction_id", transactionId);
activity?.SetTag("payment.method", method);
using (var authActivity = activitySource.StartActivity("ValidateAccount"))
{
Thread.Sleep(random.Next(40, 100));
authActivity?.AddEvent(new ActivityEvent("口座確認完了"));
}
using (var transferActivity = activitySource.StartActivity("ExecuteTransfer"))
{
Thread.Sleep(random.Next(60, 120));
}
// メトリクスの記録
var tags = new TagList { { "method", method }, { "status", "success" } };
transactionCounter.Add(1, tags);
transactionAmountHistogram.Record(amount, tags);
}
Console.WriteLine($"[C#] 決済完了: {transactionId} ({method}) - ¥{amount:N0}");
}
可視化とデータ確認
両アプリケーションを実行した後、バックエンドツールで収集結果を確認します。
1. Prometheus でメトリクスを確認
ブラウザで http://localhost:9090 にアクセスし、上部の検索欄に以下のクエリを入力して Execute をクリックします。
- Python の注文総数:
app_order_requests_total - C# の決済トランザクション総数:
app_payment_transactions_total - 平均処理時間の計算:
rate(app_order_duration_seconds_sum[1m]) / rate(app_order_duration_seconds_count[1m])
ラベル(category や method)ごとに分類された時系列データが取得できていることを確認します。
2. Jaeger v2 でトレースを確認
ブラウザで http://localhost:16686 にアクセスします。
- Service に
order-service-pythonおよびpayment-service-csharpが表示されます。 - トレースを検索すると、各関数の呼び出し関係(
handle_order->check_stock->record_orderなど)がガントチャート形式で正確に可視化されます。
3. Grafana でダッシュボード作成
ブラウザで http://localhost:3000 にアクセスします。
- 左メニューの Explore を開きます。
- データソースとして Prometheus を選択し、
app_order_requests_totalをグラフ描画します。 - 次にデータソースを Jaeger に切り替え、
Trace IDで検索を行うことで、メトリクスのスパイク発生箇所から該当トレースへ直接深掘り(ドリルダウン)することが可能になります。