Overview
엔드포인트 세부 정보
/otel/v1/traces
Method: POST
Content-Type: application/x-protobuf
Base URL: OTel 트레이스 엔드포인트의 기본 URL은 W&B 배포 유형에 따라 달라집니다.
-
Multi-tenant Cloud:
https://trace.wandb.ai/otel/v1/traces -
Dedicated Cloud 및 Self-Managed 인스턴스:
https://<your-subdomain>.wandb.io/traces/otel/v1/traces
<your-subdomain>을 조직의 고유한 W&B 도메인으로 바꾸세요. 예: acme.wandb.io.
인증 및 라우팅
wandb-api-key 헤더에 W&B API 키를 전달한 후, TracerProvider 클래스에서 다음 키를 OpenTelemetry 리소스 속성으로 지정합니다:
wandb.entity: W&B 팀 이름 또는 사용자 이름입니다.wandb.project: 트레이스를 보낼 프로젝트 이름입니다.
import os
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk import trace as trace_sdk
from opentelemetry.sdk.resources import Resource
WANDB_BASE_URL = "https://trace.wandb.ai"
ENTITY = "<your-team-name>"
PROJECT = "<your-project-name>"
OTEL_EXPORTER_OTLP_ENDPOINT = f"{WANDB_BASE_URL}/otel/v1/traces"
# https://wandb.ai/settings 에서 API 키를 생성하세요
WANDB_API_KEY = os.environ["WANDB_API_KEY"]
exporter = OTLPSpanExporter(
endpoint=OTEL_EXPORTER_OTLP_ENDPOINT,
headers={"wandb-api-key": WANDB_API_KEY},
)
tracer_provider = trace_sdk.TracerProvider(resource=Resource({
"wandb.entity": ENTITY,
"wandb.project": PROJECT,
}))
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
import { Resource } from "@opentelemetry/resources";
const WANDB_BASE_URL = "https://trace.wandb.ai";
const ENTITY = "<your-team-name>";
const PROJECT = "<your-project-name>";
const OTEL_EXPORTER_OTLP_ENDPOINT = `${WANDB_BASE_URL}/otel/v1/traces`;
// https://wandb.ai/settings 에서 API 키를 생성하세요
const WANDB_API_KEY = process.env.WANDB_API_KEY!;
const exporter = new OTLPTraceExporter({
url: OTEL_EXPORTER_OTLP_ENDPOINT,
headers: { "wandb-api-key": WANDB_API_KEY },
});
const provider = new NodeTracerProvider({
resource: new Resource({
"wandb.entity": ENTITY,
"wandb.project": PROJECT,
}),
});
예시
WANDB_API_KEY: User Settings에서 확인할 수 있습니다.- Entity: 액세스 권한이 있는 entity 아래의 프로젝트에만 트레이스를 로그할 수 있습니다. W&B 대시보드([https://wandb.ai/home])로 이동한 다음 왼쪽 사이드바에서 Teams 필드를 확인하면 entity 이름을 찾을 수 있습니다.
- Project Name: 재미있는 이름으로 지어 보세요!
OPENAI_API_KEY: OpenAI dashboard에서 확인할 수 있습니다.
OpenInference 계측
- Python
- TypeScript
pip install openai openinference-instrumentation-openai opentelemetry-exporter-otlp-proto-http
npm install openai @opentelemetry/sdk-trace-node @opentelemetry/sdk-trace-base @opentelemetry/resources @opentelemetry/exporter-trace-otlp-proto @arizeai/openinference-instrumentation-openai @opentelemetry/api
성능 권장 사항: Weave로 트레이스를 전송할 때는 항상
SimpleSpanProcessor 대신 BatchSpanProcessor를 사용하세요. SimpleSpanProcessor는 스팬을 동기식으로 내보내므로 다른 워크로드의 성능에 영향을 줄 수 있습니다. 이 예제들에서는 BatchSpanProcessor를 사용합니다. 이 프로세서는 스팬을 비동기적으로 효율적으로 일괄 처리하므로 프로덕션 환경에서 권장됩니다.- Python
- TypeScript
다음 코드를 예를 들어 코드를 실행하세요:
openinference_example.py와 같은 Python 파일에 붙여 넣으세요:import os
import openai
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk import trace as trace_sdk
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, BatchSpanProcessor
from openinference.instrumentation.openai import OpenAIInstrumentor
OPENAI_API_KEY = "YOUR_OPENAI_API_KEY"
WANDB_BASE_URL = "https://trace.wandb.ai"
ENTITY = "<your-team-name>"
PROJECT = "<your-project-name>"
OTEL_EXPORTER_OTLP_ENDPOINT = f"{WANDB_BASE_URL}/otel/v1/traces"
# https://wandb.ai/settings 에서 API 키를 생성하세요.
WANDB_API_KEY = os.environ["WANDB_API_KEY"]
exporter = OTLPSpanExporter(
endpoint=OTEL_EXPORTER_OTLP_ENDPOINT,
headers={"wandb-api-key": WANDB_API_KEY},
)
tracer_provider = trace_sdk.TracerProvider(resource=Resource({
"wandb.entity": ENTITY,
"wandb.project": PROJECT,
}))
tracer_provider.add_span_processor(BatchSpanProcessor(exporter))
# 선택 사항: 스팬을 콘솔에 출력합니다.
tracer_provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))
OpenAIInstrumentor().instrument(tracer_provider=tracer_provider)
def main():
client = openai.OpenAI(api_key=OPENAI_API_KEY)
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "Describe OTel in a single sentence."}],
max_tokens=20,
stream=True,
stream_options={"include_usage": True},
)
for chunk in response:
if chunk.choices and (content := chunk.choices[0].delta.content):
print(content, end="")
if __name__ == "__main__":
main()
python openinference_example.py
이 예제의 TypeScript 구현에는 Python 구현과 비교했을 때 다음과 같은 주요 차이점이 있습니다:코드를 실행하세요:
- 계측을 등록하기 전에 OpenAI를 먼저 임포트해야 합니다(ESM 모듈에서는 이를 요구합니다).
- W&B 엔드포인트는 protobuf만 허용하므로, HTTP exporter 대신
@opentelemetry/exporter-trace-otlp-proto(protobuf 형식)를 사용합니다. BatchSpanProcessor는 비동기적으로 플러시되므로, span이 모두 플러시되도록 종료 전에 지연 시간을 두고provider.shutdown()을 명시적으로 호출해야 합니다.
openinference_example.ts와 같은 TypeScript 파일에 붙여 넣으세요:// 중요: 계측이 패치할 수 있도록 OpenAI를 먼저 임포트하세요
import OpenAI from "openai";
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
import { resourceFromAttributes } from "@opentelemetry/resources";
import { OpenAIInstrumentation, isPatched } from "@arizeai/openinference-instrumentation-openai";
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
const WANDB_BASE_URL = "https://trace.wandb.ai";
const ENTITY = "<your-team-name>";
const PROJECT = "<your-project-name>";
const OTEL_EXPORTER_OTLP_ENDPOINT = `${WANDB_BASE_URL}/otel/v1/traces`;
// https://wandb.ai/settings 에서 API 키를 생성하세요
const WANDB_API_KEY = process.env.WANDB_API_KEY!;
const exporter = new OTLPTraceExporter({
url: OTEL_EXPORTER_OTLP_ENDPOINT,
headers: { "wandb-api-key": WANDB_API_KEY },
});
const provider = new NodeTracerProvider({
resource: resourceFromAttributes({
"wandb.entity": ENTITY,
"wandb.project": PROJECT,
}),
spanProcessors: [
new BatchSpanProcessor(exporter)
],
});
provider.register();
// 트레이서 프로바이더에 OpenAI 계측을 등록합니다
const openAIInstrumentation = new OpenAIInstrumentation();
openAIInstrumentation.setTracerProvider(provider);
// ESM을 사용하므로 OpenAI를 수동으로 계측합니다
openAIInstrumentation.manuallyInstrument(OpenAI);
async function main() {
console.log("OpenAI is patched?", isPatched());
const client = new OpenAI({ apiKey: OPENAI_API_KEY });
console.log("Making OpenAI API call...");
const response = await client.chat.completions.create({
model: "gpt-3.5-turbo",
messages: [{ role: "user", content: "Describe OTel in a single sentence." }],
max_tokens: 50,
});
console.log("Response:", response.choices[0]?.message?.content);
console.log("Waiting for spans to flush...");
}
(async () => {
await main();
// 스팬이 플러시될 시간을 확보합니다
console.log("Waiting 2 seconds for spans to flush...");
await new Promise(resolve => setTimeout(resolve, 2000));
await provider.shutdown(); // 종료 전 대기 중인 모든 스팬을 플러시합니다
console.log("Shutdown complete");
})();
npx ts-node openinference_example.ts
OpenLLMetry 계측
- Python
- TypeScript
pip install openai opentelemetry-instrumentation-openai opentelemetry-exporter-otlp-proto-http
npm install openai @traceloop/instrumentation-openai @opentelemetry/sdk-trace-node @opentelemetry/resources @opentelemetry/exporter-trace-otlp-http
- Python
- TypeScript
다음 코드를 코드를 실행하세요:
openllmetry_example.py와 같은 Python 파일에 붙여넣으세요. 위 코드와 동일하지만, OpenAIInstrumentor를 openinference.instrumentation.openai가 아니라 opentelemetry.instrumentation.openai에서 임포트한다는 점만 다릅니다:import os
import openai
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk import trace as trace_sdk
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, BatchSpanProcessor
from opentelemetry.instrumentation.openai import OpenAIInstrumentor
OPENAI_API_KEY = "YOUR_OPENAI_API_KEY"
WANDB_BASE_URL = "https://trace.wandb.ai"
ENTITY = "<your-team-name>"
PROJECT = "<your-project-name>"
OTEL_EXPORTER_OTLP_ENDPOINT = f"{WANDB_BASE_URL}/otel/v1/traces"
# https://wandb.ai/settings 에서 API 키를 생성하세요.
WANDB_API_KEY = os.environ["WANDB_API_KEY"]
exporter = OTLPSpanExporter(
endpoint=OTEL_EXPORTER_OTLP_ENDPOINT,
headers={"wandb-api-key": WANDB_API_KEY},
)
tracer_provider = trace_sdk.TracerProvider(resource=Resource({
"wandb.entity": ENTITY,
"wandb.project": PROJECT,
}))
tracer_provider.add_span_processor(BatchSpanProcessor(exporter))
# 선택 사항: 스팬을 콘솔에 출력합니다.
tracer_provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))
OpenAIInstrumentor().instrument(tracer_provider=tracer_provider)
def main():
client = openai.OpenAI(api_key=OPENAI_API_KEY)
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "Describe OTel in a single sentence."}],
max_tokens=20,
stream=True,
stream_options={"include_usage": True},
)
for chunk in response:
if chunk.choices and (content := chunk.choices[0].delta.content):
print(content, end="")
if __name__ == "__main__":
main()
python openllmetry_example.py
다음 코드를 코드를 실행하세요:
openllmetry_example.ts 같은 TypeScript 파일에 붙여넣으세요. 이 예제에서는 Traceloop OpenAI instrumentation 패키지를 사용합니다:import OpenAI from "openai";
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
import { BatchSpanProcessor, ConsoleSpanExporter } from "@opentelemetry/sdk-trace-base";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
import { Resource } from "@opentelemetry/resources";
import { OpenAIInstrumentation } from "@traceloop/instrumentation-openai";
import { registerInstrumentations } from "@opentelemetry/instrumentation";
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
const WANDB_BASE_URL = "https://trace.wandb.ai";
const ENTITY = "<your-team-name>";
const PROJECT = "<your-project-name>";
const OTEL_EXPORTER_OTLP_ENDPOINT = `${WANDB_BASE_URL}/otel/v1/traces`;
// https://wandb.ai/settings 에서 API 키를 생성하세요.
const WANDB_API_KEY = process.env.WANDB_API_KEY!;
const exporter = new OTLPTraceExporter({
url: OTEL_EXPORTER_OTLP_ENDPOINT,
headers: { "wandb-api-key": WANDB_API_KEY },
});
const provider = new NodeTracerProvider({
resource: new Resource({
"wandb.entity": ENTITY,
"wandb.project": PROJECT,
}),
spanProcessors: [
new BatchSpanProcessor(exporter),
// 선택 사항: 스팬을 콘솔에 출력합니다.
new BatchSpanProcessor(new ConsoleSpanExporter()),
],
});
provider.register();
// 트레이서 프로바이더에 OpenAI 계측을 등록합니다.
const openAIInstrumentation = new OpenAIInstrumentation();
registerInstrumentations({
tracerProvider: provider,
instrumentations: [openAIInstrumentation],
});
// ESM을 사용하므로 OpenAI를 수동으로 계측합니다.
openAIInstrumentation.manuallyInstrument(OpenAI);
async function main() {
const client = new OpenAI({ apiKey: OPENAI_API_KEY });
const stream = await client.chat.completions.create({
model: "gpt-3.5-turbo",
messages: [{ role: "user", content: "Describe OTel in a single sentence." }],
max_tokens: 20,
stream: true,
});
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content;
if (content) {
process.stdout.write(content);
}
}
console.log(); // 스트리밍 후 줄바꿈
}
(async () => {
await main();
// 스팬을 플러시할 시간을 확보합니다.
await new Promise(resolve => setTimeout(resolve, 2000));
await provider.shutdown(); // 종료 전 대기 중인 모든 스팬을 플러시합니다.
})();
npx ts-node openllmetry_example.ts
계측 없이
- Python
- TypeScript
pip install openai opentelemetry-sdk opentelemetry-api opentelemetry-exporter-otlp-proto-http
npm install openai @opentelemetry/api @opentelemetry/sdk-trace-node @opentelemetry/resources @opentelemetry/exporter-trace-otlp-http
- Python
- TypeScript
다음 코드를 코드를 실행하세요:
opentelemetry_example.py와 같은 Python 파일에 붙여 넣으세요:import json
import os
import openai
from opentelemetry import trace
from opentelemetry.sdk import trace as trace_sdk
from opentelemetry.sdk.resources import Resource
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, BatchSpanProcessor
OPENAI_API_KEY = "YOUR_OPENAI_API_KEY"
WANDB_BASE_URL = "https://trace.wandb.ai"
ENTITY = "<your-team-name>"
PROJECT = "<your-project-name>"
OTEL_EXPORTER_OTLP_ENDPOINT = f"{WANDB_BASE_URL}/otel/v1/traces"
# https://wandb.ai/settings 에서 API 키를 생성하세요.
WANDB_API_KEY = os.environ["WANDB_API_KEY"]
# OTLP 내보내기 도구를 구성합니다.
exporter = OTLPSpanExporter(
endpoint=OTEL_EXPORTER_OTLP_ENDPOINT,
headers={"wandb-api-key": WANDB_API_KEY},
)
tracer_provider = trace_sdk.TracerProvider(resource=Resource({
"wandb.entity": ENTITY,
"wandb.project": PROJECT,
}))
tracer_provider.add_span_processor(BatchSpanProcessor(exporter))
# 필요에 따라 스팬을 콘솔에 출력합니다.
tracer_provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))
# 트레이서 프로바이더를 설정합니다.
trace.set_tracer_provider(tracer_provider)
# 전역 트레이서 프로바이더에서 트레이서를 생성합니다.
tracer = trace.get_tracer(__name__)
def my_function():
with tracer.start_as_current_span("outer_span") as outer_span:
client = openai.OpenAI()
input_messages = [{"role": "user", "content": "Describe OTel in a single sentence."}]
outer_span.set_attribute("input.value", json.dumps(input_messages))
outer_span.set_attribute("gen_ai.system", "openai")
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=input_messages,
max_tokens=20,
stream=True,
stream_options={"include_usage": True},
)
out = ""
for chunk in response:
if chunk.choices and (content := chunk.choices[0].delta.content):
out += content
outer_span.set_attribute("output.value", json.dumps({"content": out}))
if __name__ == "__main__":
my_function()
python opentelemetry_example.py
다음 코드를 코드를 실행하세요:
opentelemetry_example.ts 같은 TypeScript 파일에 붙여 넣으세요:import OpenAI from "openai";
import { trace } from "@opentelemetry/api";
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
import { BatchSpanProcessor, ConsoleSpanExporter } from "@opentelemetry/sdk-trace-base";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { Resource } from "@opentelemetry/resources";
const OPENAI_API_KEY = "YOUR_OPENAI_API_KEY";
const WANDB_BASE_URL = "https://trace.wandb.ai";
const ENTITY = "<your-team-name>";
const PROJECT = "<your-project-name>";
const OTEL_EXPORTER_OTLP_ENDPOINT = `${WANDB_BASE_URL}/otel/v1/traces`;
// https://wandb.ai/settings 에서 API 키를 생성하세요.
const WANDB_API_KEY = process.env.WANDB_API_KEY!;
const exporter = new OTLPTraceExporter({
url: OTEL_EXPORTER_OTLP_ENDPOINT,
headers: { "wandb-api-key": WANDB_API_KEY },
});
const provider = new NodeTracerProvider({
resource: new Resource({
"wandb.entity": ENTITY,
"wandb.project": PROJECT,
}),
spanProcessors: [
new BatchSpanProcessor(exporter),
// 선택 사항: 스팬을 콘솔에 출력합니다.
new BatchSpanProcessor(new ConsoleSpanExporter()),
],
});
provider.register();
// 전역 트레이서 프로바이더에서 트레이서를 생성합니다.
const tracer = trace.getTracer("my-app");
async function myFunction() {
const span = tracer.startSpan("outer_span");
try {
const client = new OpenAI({ apiKey: OPENAI_API_KEY });
const inputMessages = [
{ role: "user" as const, content: "Describe OTel in a single sentence." },
];
// 사이드 패널에만 표시됩니다.
span.setAttribute("input.value", JSON.stringify(inputMessages));
// 규칙을 따르며 대시보드에 표시됩니다.
span.setAttribute("gen_ai.system", "openai");
const stream = await client.chat.completions.create({
model: "gpt-3.5-turbo",
messages: inputMessages,
max_tokens: 20,
stream: true,
});
let output = "";
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content;
if (content) {
output += content;
}
}
// 사이드 패널에만 표시됩니다.
span.setAttribute("output.value", JSON.stringify({ content: output }));
} finally {
span.end();
}
}
myFunction();
npx ts-node opentelemetry_example.ts
gen_ai와 openinference를 사용합니다. 두 키가 모두 감지되지 않으면 모든 스팬 속성이 트레이스 뷰에 표시됩니다. 트레이스를 선택하면 사이드 패널에서 전체 스팬을 볼 수 있습니다.
OpenTelemetry Collector 사용하기
collector 설정하기
- OTLP 트레이스를 수신하고, 이를 일괄 처리한 뒤 Weave로 전달하는 로컬 서버(collector)를 배포하는 Docker 설정 파일을 설정합니다.
- Docker를 사용해 로컬에서 collector를 실행합니다.
- 도커 컨테이너에서 실행 중인 collector로 트레이스를 전달하는 기본 OpenAI Call을 전송합니다.
collector-config.yaml 파일을 만드세요:
collector-config.yaml
receivers:
otlp:
protocols:
http:
endpoint: 0.0.0.0:4318
exporters:
otlphttp/weave:
endpoint: ${env:WANDB_OTLP_ENDPOINT}
headers:
wandb-api-key: ${env:WANDB_API_KEY}
sending_queue:
batch:
processors:
resource:
attributes:
- key: wandb.entity # 리소스 속성 필드
value: ${env:DEFAULT_WANDB_ENTITY} # 주입할 값
action: insert # 아직 설정되어 있지 않은 경우에만 주입
- key: wandb.project
value: ${env:DEFAULT_WANDB_PROJECT}
action: insert
service:
pipelines:
traces:
receivers: [otlp]
processors: [resource]
exporters: [otlphttp/weave]
- 포트
4318(HTTP)에서 OTLP 트레이스를 수신합니다. wandb-api-key헤더를 사용해 Weave의 OTLP 엔드포인트로 트레이스를 내보내며, 엔드포인트 URL은WANDB_OTLP_ENDPOINT에서, API 키는WANDB_API_KEY에서 읽어옵니다.resource프로세서를 사용해wandb.entity와wandb.project를 리소스 속성으로 설정하고, 값은DEFAULT_WANDB_ENTITY와DEFAULT_WANDB_PROJECT에서 읽어옵니다.insertaction은 애플리케이션 코드에서 이러한 속성을 이미 설정하지 않은 경우에만 속성을 주입합니다.- 네트워크 오버헤드를 줄이기 위해 exporter에 내장된
sending_queue를 배치 처리와 함께 활성화합니다.
docker run \
-v ./config.yaml:/etc/otelcol-contrib/config.yaml \
-e WANDB_API_KEY="<your-wandb-api-key>" \
-e WANDB_OTLP_ENDPOINT="https://trace.wandb.ai/otel" \
-e DEFAULT_WANDB_ENTITY="<your-team-name>" \
-e DEFAULT_WANDB_PROJECT="YOUR_PROJECT" \
-p 4318:4318 \
otel/opentelemetry-collector-contrib:latest
OTEL_EXPORTER_OTLP_ENDPOINT 환경 변수를 설정해 애플리케이션이 해당 엔드포인트로 트레이스를 내보내도록 구성하세요. OTel SDK는 이 변수를 자동으로 읽으므로 exporter에 엔드포인트를 전달할 필요가 없습니다.
애플리케이션의 TracerProvider에서 wandb.entity 또는 wandb.project를 리소스 속성으로 설정하면 collector 설정에 정의된 기본값보다 우선합니다.
import os
import openai
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk import trace as trace_sdk
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from openinference.instrumentation.openai import OpenAIInstrumentor
os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "http://localhost:4318"
OPENAI_API_KEY = "YOUR_OPENAI_API_KEY"
tracer_provider = trace_sdk.TracerProvider()
tracer_provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
OpenAIInstrumentor().instrument(tracer_provider=tracer_provider)
def main():
client = openai.OpenAI(api_key=OPENAI_API_KEY)
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "Describe OTel in a single sentence."}],
max_tokens=20,
)
print(response.choices[0].message.content)
if __name__ == "__main__":
main()
import OpenAI from "openai";
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
import { OpenAIInstrumentation, isPatched } from "@arizeai/openinference-instrumentation-openai";
process.env.OTEL_EXPORTER_OTLP_ENDPOINT = "http://localhost:4318";
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
const provider = new NodeTracerProvider({
spanProcessors: [new BatchSpanProcessor(new OTLPTraceExporter())],
});
provider.register();
const openAIInstrumentation = new OpenAIInstrumentation();
openAIInstrumentation.setTracerProvider(provider);
openAIInstrumentation.manuallyInstrument(OpenAI);
async function main() {
console.log("OpenAI is patched?", isPatched());
const client = new OpenAI({ apiKey: OPENAI_API_KEY });
const response = await client.chat.completions.create({
model: "gpt-3.5-turbo",
messages: [{ role: "user", content: "Describe OTel in a single sentence." }],
max_tokens: 20,
});
console.log("Response:", response.choices[0]?.message?.content);
}
(async () => {
await main();
await new Promise(resolve => setTimeout(resolve, 2000));
await provider.shutdown();
})();
OpenAIInstrumentor는 OpenAI 호출을 자동으로 래핑하고 트레이스를 생성한 뒤 collector로 내보냅니다. collector는 인증과 Weave로의 라우팅을 처리합니다.
스크립트를 실행한 후에는 Weave UI에서 트레이스를 확인할 수 있습니다.
트레이스를 추가 백엔드로 전송하려면 exporter를 더 추가하고 service.pipelines.traces.exporters 목록에 포함하세요. 예를 들어 동일한 Collector 인스턴스에서 Weave와 Jaeger 둘 다로 내보낼 수 있습니다.
OTel 트레이스를 스레드로 정리하기
wandb.thread_id: 스팬을 특정 스레드로 그룹화합니다wandb.is_turn: 스팬을 대화 턴으로 표시합니다(스레드 view에서 행으로 표시됨)
wandb.thread_id를 사용해 관련 오퍼레이션을 그룹화하고, wandb.is_turn을 사용해 스레드 view에서 행으로 표시되는 상위 수준 오퍼레이션을 표시합니다.
초기 설정
초기 설정
이 예제를 실행하려면 다음 설정을 사용하세요:
- Python
- TypeScript
import json
import os
from opentelemetry import trace
from opentelemetry.sdk import trace as trace_sdk
from opentelemetry.sdk.resources import Resource
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, BatchSpanProcessor
# 설정
ENTITY = "<your-team-name>"
PROJECT = "<your-project-name>"
WANDB_API_KEY = os.environ["WANDB_API_KEY"]
OTEL_EXPORTER_OTLP_ENDPOINT = "https://trace.wandb.ai/otel/v1/traces"
exporter = OTLPSpanExporter(
endpoint=OTEL_EXPORTER_OTLP_ENDPOINT,
headers={"wandb-api-key": WANDB_API_KEY},
)
tracer_provider = trace_sdk.TracerProvider(resource=Resource({
"wandb.entity": ENTITY,
"wandb.project": PROJECT,
}))
tracer_provider.add_span_processor(BatchSpanProcessor(exporter))
# 선택적으로 스팬을 콘솔에 출력합니다
tracer_provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))
trace.set_tracer_provider(tracer_provider)
# 전역 tracer provider에서 tracer를 생성합니다
tracer = trace.get_tracer(__name__)
import { trace, context } from "@opentelemetry/api";
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
import {
BatchSpanProcessor,
ConsoleSpanExporter,
} from "@opentelemetry/sdk-trace-base";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
import { Resource } from "@opentelemetry/resources";
// 설정
const ENTITY = "<your-team-name>";
const PROJECT = "<your-project-name>";
const WANDB_API_KEY = process.env.WANDB_API_KEY;
if (!WANDB_API_KEY) {
console.error("Error: WANDB_API_KEY environment variable is not set");
console.error("Run: export WANDB_API_KEY=your_api_key_here");
process.exit(1);
}
// OTel 설정
const OTEL_EXPORTER_OTLP_ENDPOINT = "https://trace.wandb.ai/otel/v1/traces";
const exporter = new OTLPTraceExporter({
url: OTEL_EXPORTER_OTLP_ENDPOINT,
headers: { "wandb-api-key": WANDB_API_KEY },
});
// span processor로 tracer provider를 초기화합니다
const provider = new NodeTracerProvider({
resource: new Resource({
"wandb.entity": ENTITY,
"wandb.project": PROJECT,
}),
spanProcessors: [
new BatchSpanProcessor(exporter),
new BatchSpanProcessor(new ConsoleSpanExporter()),
],
});
// tracer provider를 등록합니다
provider.register();
// 전역 tracer provider에서 tracer를 생성합니다
const tracer = trace.getTracer("threads-examples");
기본 단일 턴 스레드 트레이스하기
기본 단일 턴 스레드 트레이스하기
- Python
- TypeScript
def example_1_basic_thread_and_turn():
"""예제 1: 단일 턴이 있는 기본 스레드"""
print("\n=== Example 1: Basic Thread and Turn ===")
# 스레드 컨텍스트 생성
thread_id = "thread_example_1"
# 이 span은 턴을 나타냅니다(스레드의 직접 자식)
with tracer.start_as_current_span("process_user_message") as turn_span:
# 스레드 속성 설정
turn_span.set_attribute("wandb.thread_id", thread_id)
turn_span.set_attribute("wandb.is_turn", True)
# 예시 속성 몇 가지 추가
turn_span.set_attribute("input.value", "Hello, help me with setup")
# 중첩된 span을 사용해 일부 작업 시뮬레이션
with tracer.start_as_current_span("generate_response") as nested_span:
# 이는 턴 내부의 중첩 Call이므로 is_turn은 false이거나 설정되지 않아야 합니다
nested_span.set_attribute("wandb.thread_id", thread_id)
# 중첩 Call의 경우 wandb.is_turn은 설정하지 않거나 False로 설정합니다
response = "I'll help you get started with the setup process."
nested_span.set_attribute("output.value", response)
turn_span.set_attribute("output.value", response)
print(f"Turn completed in thread: {thread_id}")
def main():
example_1_basic_thread_and_turn()
if __name__ == "__main__":
main()
function example_1_basic_thread_and_turn() {
console.log("\n=== Example 1: Basic Thread and Turn ===");
// 스레드 컨텍스트 생성
const threadId = "thread_example_1";
// 이 span은 턴을 나타냅니다(스레드의 직접 자식)
tracer.startActiveSpan("process_user_message", (turnSpan) => {
// 스레드 속성 설정
turnSpan.setAttribute("wandb.thread_id", threadId);
turnSpan.setAttribute("wandb.is_turn", true);
// 예시 속성 몇 가지 추가
turnSpan.setAttribute("input.value", "Hello, help me with setup");
let response: string;
// 중첩된 span을 사용해 일부 작업 시뮬레이션
tracer.startActiveSpan("generate_response", (nestedSpan) => {
// 이는 턴 내부의 중첩 Call이므로 is_turn은 false이거나 설정되지 않아야 합니다
nestedSpan.setAttribute("wandb.thread_id", threadId);
// 중첩 Call의 경우 wandb.is_turn은 설정하지 않거나 false로 설정합니다
response = "I'll help you get started with the setup process.";
nestedSpan.setAttribute("output.value", response);
nestedSpan.end();
});
turnSpan.setAttribute("output.value", response!);
console.log(`Turn completed in thread: ${threadId}`);
turnSpan.end();
});
}
function main() {
example_1_basic_thread_and_turn();
}
main();
동일한 스레드 ID를 공유하는 멀티턴 대화 트레이스하기
동일한 스레드 ID를 공유하는 멀티턴 대화 트레이스하기
- Python
- TypeScript
def example_2_multiple_turns():
"""Example 2: Multiple turns in a single thread"""
print("\n=== Example 2: Multiple Turns in Thread ===")
thread_id = "thread_conversation_123"
# 턴 1
with tracer.start_as_current_span("process_message_turn1") as turn1_span:
turn1_span.set_attribute("wandb.thread_id", thread_id)
turn1_span.set_attribute("wandb.is_turn", True)
turn1_span.set_attribute("input.value", "What programming languages do you recommend?")
# 중첩된 오퍼레이션
with tracer.start_as_current_span("analyze_query") as analyze_span:
analyze_span.set_attribute("wandb.thread_id", thread_id)
# 중첩 span의 경우 is_turn 속성을 지정하지 않거나 False로 설정
response1 = "I recommend Python for beginners and JavaScript for web development."
turn1_span.set_attribute("output.value", response1)
print(f"Turn 1 completed in thread: {thread_id}")
# 턴 2
with tracer.start_as_current_span("process_message_turn2") as turn2_span:
turn2_span.set_attribute("wandb.thread_id", thread_id)
turn2_span.set_attribute("wandb.is_turn", True)
turn2_span.set_attribute("input.value", "Can you explain Python vs JavaScript?")
# 중첩된 오퍼레이션
with tracer.start_as_current_span("comparison_analysis") as compare_span:
compare_span.set_attribute("wandb.thread_id", thread_id)
compare_span.set_attribute("wandb.is_turn", False) # 중첩 span의 경우 명시적으로 false로 설정
response2 = "Python excels at data science while JavaScript dominates web development."
turn2_span.set_attribute("output.value", response2)
print(f"Turn 2 completed in thread: {thread_id}")
def main():
example_2_multiple_turns()
if __name__ == "__main__":
main()
function example_2_multiple_turns() {
console.log("\n=== Example 2: Multiple Turns in Thread ===");
const threadId = "thread_conversation_123";
// 턴 1
tracer.startActiveSpan("process_message_turn1", (turn1Span) => {
turn1Span.setAttribute("wandb.thread_id", threadId);
turn1Span.setAttribute("wandb.is_turn", true);
turn1Span.setAttribute(
"input.value",
"What programming languages do you recommend?"
);
// 중첩된 오퍼레이션
tracer.startActiveSpan("analyze_query", (analyzeSpan) => {
analyzeSpan.setAttribute("wandb.thread_id", threadId);
// 중첩 span의 경우 is_turn 속성을 지정하지 않거나 false로 설정
analyzeSpan.end();
});
const response1 =
"I recommend Python for beginners and JavaScript for web development.";
turn1Span.setAttribute("output.value", response1);
console.log(`Turn 1 completed in thread: ${threadId}`);
turn1Span.end();
});
// 턴 2
tracer.startActiveSpan("process_message_turn2", (turn2Span) => {
turn2Span.setAttribute("wandb.thread_id", threadId);
turn2Span.setAttribute("wandb.is_turn", true);
turn2Span.setAttribute("input.value", "Can you explain Python vs JavaScript?");
// 중첩된 오퍼레이션
tracer.startActiveSpan("comparison_analysis", (compareSpan) => {
compareSpan.setAttribute("wandb.thread_id", threadId);
compareSpan.setAttribute("wandb.is_turn", false); // 중첩 span의 경우 명시적으로 false로 설정
compareSpan.end();
});
const response2 =
"Python excels at data science while JavaScript dominates web development.";
turn2Span.setAttribute("output.value", response2);
console.log(`Turn 2 completed in thread: ${threadId}`);
turn2Span.end();
});
}
function main() {
example_2_multiple_turns();
}
main();
여러 단계로 깊게 중첩된 오퍼레이션을 트레이스하고 최외곽 스팬만 턴으로 표시
여러 단계로 깊게 중첩된 오퍼레이션을 트레이스하고 최외곽 스팬만 턴으로 표시
- Python
- TypeScript
def example_3_complex_nested_structure():
"""Example 3: Complex nested structure with multiple levels"""
print("\n=== Example 3: Complex Nested Structure ===")
thread_id = "thread_complex_456"
# 여러 단계로 중첩된 turn
with tracer.start_as_current_span("handle_complex_request") as turn_span:
turn_span.set_attribute("wandb.thread_id", thread_id)
turn_span.set_attribute("wandb.is_turn", True)
turn_span.set_attribute("input.value", "Analyze this code and suggest improvements")
# 레벨 1 중첩 오퍼레이션
with tracer.start_as_current_span("code_analysis") as analysis_span:
analysis_span.set_attribute("wandb.thread_id", thread_id)
# 중첩 오퍼레이션에는 is_turn을 설정하지 않음
# 레벨 2 중첩 오퍼레이션
with tracer.start_as_current_span("syntax_check") as syntax_span:
syntax_span.set_attribute("wandb.thread_id", thread_id)
syntax_span.set_attribute("result", "No syntax errors found")
# 또 다른 레벨 2 중첩 오퍼레이션
with tracer.start_as_current_span("performance_check") as perf_span:
perf_span.set_attribute("wandb.thread_id", thread_id)
perf_span.set_attribute("result", "Found 2 optimization opportunities")
# 또 다른 레벨 1 중첩 오퍼레이션
with tracer.start_as_current_span("generate_suggestions") as suggest_span:
suggest_span.set_attribute("wandb.thread_id", thread_id)
suggestions = ["Use list comprehension", "Consider caching results"]
suggest_span.set_attribute("suggestions", json.dumps(suggestions))
turn_span.set_attribute("output.value", "Analysis complete with 2 improvement suggestions")
print(f"Complex turn completed in thread: {thread_id}")
def main():
example_3_complex_nested_structure()
if __name__ == "__main__":
main()
function example_3_complex_nested_structure() {
console.log("\n=== Example 3: Complex Nested Structure ===");
const threadId = "thread_complex_456";
// 여러 단계로 중첩된 turn
tracer.startActiveSpan("handle_complex_request", (turnSpan) => {
turnSpan.setAttribute("wandb.thread_id", threadId);
turnSpan.setAttribute("wandb.is_turn", true);
turnSpan.setAttribute(
"input.value",
"Analyze this code and suggest improvements"
);
// 레벨 1 중첩 오퍼레이션
tracer.startActiveSpan("code_analysis", (analysisSpan) => {
analysisSpan.setAttribute("wandb.thread_id", threadId);
// 중첩 오퍼레이션에는 is_turn을 설정하지 않음
// 레벨 2 중첩 오퍼레이션
tracer.startActiveSpan("syntax_check", (syntaxSpan) => {
syntaxSpan.setAttribute("wandb.thread_id", threadId);
syntaxSpan.setAttribute("result", "No syntax errors found");
syntaxSpan.end();
});
// 또 다른 레벨 2 중첩 오퍼레이션
tracer.startActiveSpan("performance_check", (perfSpan) => {
perfSpan.setAttribute("wandb.thread_id", threadId);
perfSpan.setAttribute("result", "Found 2 optimization opportunities");
perfSpan.end();
});
analysisSpan.end();
});
// 또 다른 레벨 1 중첩 오퍼레이션
tracer.startActiveSpan("generate_suggestions", (suggestSpan) => {
suggestSpan.setAttribute("wandb.thread_id", threadId);
const suggestions = ["Use list comprehension", "Consider caching results"];
suggestSpan.setAttribute("suggestions", JSON.stringify(suggestions));
suggestSpan.end();
});
turnSpan.setAttribute(
"output.value",
"Analysis complete with 2 improvement suggestions"
);
console.log(`Complex turn completed in thread: ${threadId}`);
turnSpan.end();
});
}
function main() {
example_3_complex_nested_structure();
}
main();
스레드에 속하지만 턴은 아닌 백그라운드 오퍼레이션 트레이스
스레드에 속하지만 턴은 아닌 백그라운드 오퍼레이션 트레이스
- Python
- TypeScript
def example_4_non_turn_operations():
"""Example 4: Operations that are part of a thread but not turns"""
print("\n=== Example 4: Non-Turn Thread Operations ===")
thread_id = "thread_background_789"
# 스레드에 속하지만 turn은 아닌 백그라운드 오퍼레이션
with tracer.start_as_current_span("background_indexing") as bg_span:
bg_span.set_attribute("wandb.thread_id", thread_id)
# wandb.is_turn이 설정되지 않았거나 false인 경우 - 이는 turn이 아닙니다
bg_span.set_attribute("wandb.is_turn", False)
bg_span.set_attribute("operation", "Indexing conversation history")
print(f"Background operation in thread: {thread_id}")
# 같은 스레드의 실제 turn
with tracer.start_as_current_span("user_query") as turn_span:
turn_span.set_attribute("wandb.thread_id", thread_id)
turn_span.set_attribute("wandb.is_turn", True)
turn_span.set_attribute("input.value", "Search my previous conversations")
turn_span.set_attribute("output.value", "Found 5 relevant conversations")
print(f"Turn completed in thread: {thread_id}")
def main():
example_4_non_turn_operations()
if __name__ == "__main__":
main()
function example_4_non_turn_operations() {
console.log("\n=== Example 4: Non-Turn Thread Operations ===");
const threadId = "thread_background_789";
// 스레드에 속하지만 turn은 아닌 백그라운드 오퍼레이션
tracer.startActiveSpan("background_indexing", (bgSpan) => {
bgSpan.setAttribute("wandb.thread_id", threadId);
// wandb.is_turn이 설정되지 않았거나 false인 경우 - 이는 turn이 아닙니다
bgSpan.setAttribute("wandb.is_turn", false);
bgSpan.setAttribute("operation", "Indexing conversation history");
console.log(`Background operation in thread: ${threadId}`);
bgSpan.end();
});
// 같은 스레드의 실제 turn
tracer.startActiveSpan("user_query", (turnSpan) => {
turnSpan.setAttribute("wandb.thread_id", threadId);
turnSpan.setAttribute("wandb.is_turn", true);
turnSpan.setAttribute("input.value", "Search my previous conversations");
turnSpan.setAttribute("output.value", "Found 5 relevant conversations");
console.log(`Turn completed in thread: ${threadId}`);
turnSpan.end();
});
}
function main() {
example_4_non_turn_operations();
}
main();
thread_id별로 그룹화되며, 각 턴은 별도의 행으로 표시됩니다.
속성 매핑
지원되는 프레임워크
- OpenTelemetry GenAI: 생성형 AI용 표준 시맨틱 규약 (
gen_ai.*) - OpenInference: Arize AI의 계측 라이브러리 (
input.value,output.value,llm.*,openinference.*) - Vercel AI SDK: Vercel의 AI SDK 속성 (
ai.prompt,ai.response,ai.model.*,ai.usage.*) - MLflow: MLflow tracking 속성 (
mlflow.spanInputs,mlflow.spanOutputs) - Traceloop: OpenLLMetry 계측 속성 (
traceloop.entity.*,traceloop.span.kind) - Google Vertex AI: Vertex AI agent 속성 (
gcp.vertex.agent.*) - OpenLit: OpenLit 옵저버빌리티 속성 (
gen_ai.content.completion) - Langfuse: Langfuse 트레이싱 속성 (
langfuse.startTime,langfuse.endTime)
속성 레퍼런스
| 속성 필드명 | W&B 매핑 | 설명 | 유형 | 예시 |
|---|---|---|---|---|
ai.prompt | inputs | 사용자 프롬프트 텍스트 또는 메시지. | 문자열, 목록, dict | "여름에 관한 짧은 하이쿠를 써줘." |
gen_ai.prompt | inputs | AI 모델 프롬프트 또는 메시지 배열입니다. | 목록, dict, 문자열 | [{"role":"user","content":"abc"}] |
input.value | inputs | 모델 호출을 위한 입력 값입니다. | 문자열, 목록, dict | {"text":"농담 하나 해줘"} |
mlflow.spanInputs | inputs | Span 입력 데이터입니다. | 문자열, 목록, dict | ["프롬프트 텍스트"] |
traceloop.entity.input | inputs | entity 입력 데이터. | 문자열, 목록, dict | "이것을 프랑스어로 번역하세요" |
gcp.vertex.agent.tool_call_args | inputs | 도구 call 인수. | Dict | {"args":{"query":"weather in SF"}} |
gcp.vertex.agent.llm_request | inputs | LLM 요청 페이로드. | Dict | {"contents":[{"role":"user","parts":[...]}]} |
input | inputs | 범용 입력값. | 문자열, 목록, dict | "이 텍스트를 요약해 줘" |
inputs | inputs | 범용 입력 배열. | 목록, dict, 문자열 | ["이 텍스트를 요약해 줘"] |
ai.response | outputs | 모델 응답 텍스트 또는 데이터. | 문자열, 목록, dict | "하이쿠를 하나 들려드릴게요..." |
gen_ai.completion | outputs | AI completion 결과. | 문자열, 목록, dict | "생성된 텍스트" |
output.value | outputs | 모델의 출력값. | 문자열, 목록, dict | {"text":"답변 텍스트"} |
mlflow.spanOutputs | outputs | 스팬 출력 데이터. | String, 목록, dict | ["answer"] |
gen_ai.content.completion | outputs | 콘텐츠 완성 결과. | String | "답변 텍스트" |
traceloop.entity.output | outputs | entity 출력 데이터. | String, 목록, dict | "답변 텍스트" |
gcp.vertex.agent.tool_response | outputs | 도구 실행 응답. | 딕셔너리, 문자열 | {"toolResponse":"ok"} |
gcp.vertex.agent.llm_response | outputs | LLM 응답 페이로드. | 딕셔너리, 문자열 | {"candidates":[...]} |
output | outputs | 일반 출력 값. | 문자열, 목록, 딕셔너리 | "답변 텍스트" |
outputs | outputs | 일반 출력 배열. | 목록, dict, 문자열 | ["답변 텍스트"] |
gen_ai.usage.input_tokens | usage.input_tokens | 사용된 입력 토큰 수. | Int | 42 |
gen_ai.usage.prompt_tokens | usage.prompt_tokens | 사용된 프롬프트 토큰 수. | Int | 30 |
llm.token_count.prompt | usage.prompt_tokens | 프롬프트 토큰 수. | Int | 30 |
ai.usage.promptTokens | usage.prompt_tokens | 사용된 프롬프트 토큰 수. | Int | 30 |
gen_ai.usage.completion_tokens | usage.completion_tokens | 생성된 완료 토큰 수. | Int | 40 |
llm.token_count.completion | usage.completion_tokens | 완료 토큰 수. | Int | 40 |
ai.usage.completionTokens | usage.completion_tokens | 생성된 completion token 수. | Int | 40 |
llm.usage.total_tokens | usage.total_tokens | Request에 사용된 총 token 수. | Int | 70 |
llm.token_count.total | usage.total_tokens | 총 token 수. | 정수 | 70 |
gen_ai.system | attributes.system | 시스템 프롬프트 또는 지침. | 문자열 | "당신은 유용한 도우미입니다." |
llm.system | attributes.system | 시스템 프롬프트 또는 지침. | 문자열 | "당신은 도움이 되는 어시스턴트입니다." |
weave.span.kind | attributes.kind | 스팬 유형 또는 카테고리. | 문자열 | "llm" |
traceloop.span.kind | attributes.kind | Span 유형 또는 범주. | 문자열 | "llm" |
openinference.span.kind | attributes.kind | Span 유형 또는 범주. | 문자열 | "llm" |
gen_ai.response.model | attributes.model | 모델 식별자. | 문자열 | "gpt-4o" |
llm.model_name | attributes.model | 모델 식별자. | 문자열 | "gpt-4o-mini" |
ai.model.id | attributes.model | 모델 식별자. | 문자열 | "gpt-4o" |
llm.provider | attributes.provider | 모델 제공업체 이름. | 문자열 | "openai" |
ai.model.provider | attributes.provider | 모델 제공업체 이름. | String | "openai" |
gen_ai.request | attributes.model_parameters | 모델 생성 파라미터. | Dict | {"temperature":0.7,"max_tokens":256} |
llm.invocation_parameters | attributes.model_parameters | 모델 호출 파라미터. | Dict | {"temperature":0.2} |
wandb.display_name | display_name | UI에 표시할 맞춤형 표시 이름. | 문자열 | "사용자 메시지" |
gcp.vertex.agent.session_id | thread_id | 세션 또는 스레드 식별자입니다. | 문자열 | "thread_123" |
wandb.thread_id | thread_id | 대화의 스레드 식별자입니다. | 문자열 | "thread_123" |
wb_run_id | wb_run_id | 연관된 W&B run 식별자입니다. | 문자열 | "abc123" |
wandb.wb_run_id | wb_run_id | 연결된 W&B run 식별자입니다. | 문자열 | "abc123" |
gcp.vertex.agent.session_id | is_turn | span을 대화 턴으로 표시합니다. | 불리언 | true |
wandb.is_turn | is_turn | span을 대화 턴으로 표시합니다. | 불리언 | true |
langfuse.startTime | start_time (재정의) | span 시작 타임스탬프를 재정의합니다. | 타임스탬프 (ISO8601/unix ns) | "2024-01-01T12:00:00Z" |
langfuse.endTime | end_time (재정의) | span 종료 타임스탬프를 재정의합니다. | 타임스탬프 (ISO8601/unix ns) | "2024-01-01T12:00:01Z" |
제한 사항
- Weave UI는 Chat 뷰에서 OTel 트레이스 도구 call을 렌더링하는 기능을 지원하지 않습니다. 대신 원시 JSON으로 표시됩니다.