11
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【LLMOps】Langfuseのインフラを立てず、テキストファイルからトレースをインポートしたい

Last updated at Posted at 2025-12-08

BrainPad Advent Calendar 2025 8日目の記事になります

--

株式会社ブレインパッド プロダクトユニットの津久井です。

弊社は「データ活用の促進を通じて持続可能な未来をつくる」をミッションに、データ分析支援やSaaSプロダクトの提供を通じて、企業の「データ活用の日常化」を推進しております。

現在私は、AI SaaSのエージェントリンリーの開発リードを担当しています。

Langfuseのインフラを立てたくない侍と申す

(若干語弊がありますが、) 以前記事にも書いたLangfuseですが、
LLMのトレースには Langfuse webのエンドポイントに対してアプリケーション側から直接トレースを送信するのが基本です。

しかし、ネットワーク環境の都合・管理リソースを増やしたくない...等で
Langfuseエンドポイントを常時起動したくないという想いもあり、
トレースを一旦ファイルに書き出し、後からローカルの Langfuse インスタンスにインポートする(オレオレ過ぎない)方法を実現してみました。

実現手順

1. LiteLLMのコールバック先をOpenTelemetryに設定

以前の記事同様、今回もLiteLLMを使います。

LiteLLMはコールバックが非常に簡潔に記載可能で、かつOpenTelemetry (以下、OTEL) のエンドポイントにも対応しています。

具体的には、LiteLLMの実行環境でOTELのエンドポイントを環境変数で設定 + コールバックを"otel"に設定するだけで、LLMへの問い合わせ時に自動的にトレースが送信されます。

OTELのエンドポイントを環境変数で設定

docker-compose.yamlの例
      ...
      # Collector への エンドポイント
      - OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
      # のプロトコル (HTTP/Protobuf)
      - OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
      ...

コールバックを"otel"に設定

東京は日本のどの地方にあるか?をGeminiに聞くPythonスクリプト
import os
import litellm

# Litellm の OpenTelemetry コールバックを有効化します
litellm.callbacks = ["otel"]

# このサンプルは、Gemini 2.5 Flash 呼び出します。
# プロジェクトIDを環境変数から取得(設定されていない場合はデフォルト値を使用)
vertex_project = os.getenv("GOOGLE_CLOUD_PROJECT", "xxxxxxx")
response = litellm.completion(
    model="vertex_ai/gemini-2.5-flash",
    messages=[{"role": "user", "content": "東京は日本のどの地方にありますか?"}],
    vertex_project=vertex_project,
)

2. OpenTelemetry Collectorで、受け取ったトレースログをテキスト出力

受け取ったトレースをファイルに出力します。OpenTelemetry Collector には様々なエクスポーターが用意されており、その中の file exporter を使うと、トレースデータを行単位のJSONL (OTLP フォーマット) として保存できます。

(以下、Collector の設定ファイル例)

receivers:
  otlp:
    protocols:
      http:
        endpoint: 0.0.0.0:4318

processors:
  batch: {}

exporters:
  file:
    path: "./traces.jsonl"        # 出力先のファイル

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [file]

このCollectorを起動すると、アプリから送られてきたトレースが traces.jsonl に 1 行ずつ記録されます。
ファイルは JSON Lines 形式なので各行が 1 つの TracesData オブジェクトになっており、Langfuse エンドポイントでもそのまま受け取れる形式です。

ログファイルの1行のフォーマット例
{"resourceSpans":[{"resource":{"attributes":[{"key":"telemetry.sdk.language","value":{"stringValue":"python"}},{ ...

たとえばログを保存するディレクトリを gcsfuse でマウントし、Sidecar等でOTELエンドポイントを起動しておけば、そのまま GCS に保存することも可能です。
( Cloud Runなどの場合は、GCSへの保存完了までコンテナ起動を維持する必要あり )

3. トレースデータを Langfuse にインポート

保存した traces.jsonl をローカルの Langfuse にインポートします。(GCS等に保存したファイルを、ダウンロードしておく)

Langfuse は Basic 認証つきの OTLP/HTTP エンドポイントを持っており、/api/public/otel/v1/traces に POST するとトレースを取り込んでくれます。(OTLP JSON Lines ファイルの各行を、そのまま HTTP ボディに投げればOK)

以下の Python スクリプトでは ファイルを 1 行ずつ読み込み、Langfuse に POST しています。

import base64
import requests

# Langfuse API キー
public_key = "pk-lf-XXXXXXXXXXX"
secret_key = "sk-lf-YYYYYYYYYYY"
auth = base64.b64encode(f"{public_key}:{secret_key}".encode()).decode()

# ヘッダ設定
headers = {
    "Authorization": f"Basic {auth}",
    "Content-Type": "application/json",
}

# ローカル Langfuse のエンドポイント
url = "http://localhost:3000/api/public/otel/v1/traces"  

with open("./traces.jsonl", "r", encoding="utf-8") as f:
    for line in f:
        line = line.strip()
        if not line:
            continue
        res = requests.post(url, headers=headers, data=line)
        if res.status_code != 200:
            print("Error", res.status_code, res.text)

このスクリプトを実行すると、ファイルのトレースが Langfuse に一気に取り込まれ、Web UI で閲覧できるようになります。

スクリーンショット 2025-12-07 23.20.27.png
スクリーンショット 2025-12-07 23.20.45.png

注意

インポート重複排除等はもちろん無いので、
jsonlファイルの持ち方やインポート指針は ある程度工夫が必要です。(ローカルの場合は、一度環境を捨ててインポートし直す、もアリかもしれません)

余談

OpenTelemetry Collector には File Log Receiver も存在するので、おそらくhttp送信スクリプトを書かなくともOTELのエコシステムでjsonから送信は可能なのではと考えています。
(まだ試していないので、もしうまくいったらまた記載します)

11
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?