---
title: Claude SDK에서 OpenTelemetry 사용하기
url: https://platform.claude.com/docs/ko/manage-claude/opentelemetry/sdk
description: Claude SDK가 보내는 각 요청과 도구 실행기(tool runner)가 실행하는 각 도구를 트레이스하고, span을 관측성 플랫폼으로 보냅니다.
featureMetadata:
  topic:
    title: OpenTelemetry
    url: https://platform.claude.com/docs/en/manage-claude/opentelemetry
  status: beta
  supportedPlatforms:
    Claude API: beta
    Claude Platform on AWS: beta
    Amazon Bedrock: beta
    Google Cloud: beta
    Microsoft Foundry: beta
  rows:
    - label: 호환 대상
      value: Messages API, Claude Managed Agents
---

OpenTelemetry를 사용하면 Claude SDK는 전송하는 각 요청과 [도구 실행기](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/tool-runner)가 실행하는 각 도구 사용에 대해 *"span"*(스팬)을 내보낼 수 있습니다. 이러한 스팬은 관측성 플랫폼에서 자체 트레이스 안에 중첩된 형태로 확인할 수 있습니다.

SDK는 애플리케이션 내에서 스팬을 생성합니다. 애플리케이션의 OpenTelemetry 익스포터(exporter)가 이를 관측성 플랫폼으로 전송합니다.

<Note>
  베타 기간 동안 SDK 옵션과 스팬 스키마는 코드, 쿼리 또는 대시보드를 손상시키는 방식으로 변경될 수 있습니다. 예를 들어, 새 버전의 SDK에서 스팬, 속성, 옵션 또는 환경 변수의 이름이 바뀔 수 있습니다. 변경 사항을 적용할 시점을 직접 선택하려면 의존성에서 SDK 버전을 고정하세요.
</Note>

## 1. SDK 설치

Claude SDK의 최신 버전을 설치하거나 업그레이드하세요:

<Tabs>
  <Tab title="Python">
    ```bash
    pip install --upgrade anthropic
    ```

    애플리케이션에서 아직 OpenTelemetry SDK를 사용하지 않는다면, OpenTelemetry SDK를 콘솔 익스포터와 함께 설치하세요. 콘솔 익스포터는 다음 단계에서 스팬을 출력하는 데 사용합니다:

    ```bash
    pip install opentelemetry-sdk
    ```
  </Tab>

  <Tab title="TypeScript">
    ```bash
    npm install @anthropic-ai/sdk@latest
    ```

    애플리케이션에서 아직 OpenTelemetry SDK를 사용하지 않는 경우, 다음 단계에서 스팬을 출력하는 데 사용할 콘솔 익스포터와 함께 설치하세요:

    ```bash
    npm install @opentelemetry/sdk-trace-node
    ```
  </Tab>

  <Tab title="C#">
    ```bash
    dotnet add package Anthropic
    ```

    애플리케이션에서 아직 OpenTelemetry SDK를 사용하지 않는 경우, 다음 단계에서 스팬을 출력하는 데 사용할 콘솔 익스포터와 함께 설치하세요:

    ```bash
    dotnet add package OpenTelemetry.Exporter.Console
    ```
  </Tab>

  <Tab title="Go">
    ```bash
    go get github.com/anthropics/anthropic-sdk-go@latest
    ```

    애플리케이션에서 아직 OpenTelemetry SDK를 사용하지 않는 경우, 다음 단계에서 스팬을 출력하는 데 사용할 콘솔 익스포터와 함께 설치하세요:

    ```bash
    go get go.opentelemetry.io/otel/sdk go.opentelemetry.io/otel/exporters/stdout/stdouttrace
    ```
  </Tab>

  <Tab title="Java">
    Claude SDK를 의존성에 추가하거나, 빌드 파일에 이미 선언되어 있다면 최소한 다음 버전으로 업데이트하세요:

    <CodeGroup exclude="shell, python, typescript, csharp, go, php, ruby">
      ```kotlin Gradle
      implementation("com.anthropic:anthropic-java:2.72.0")
      ```

      ```xml Maven
      <dependency>
          <groupId>com.anthropic</groupId>
          <artifactId>anthropic-java</artifactId>
          <version>2.72.0</version>
      </dependency>
      ```
    </CodeGroup>

    애플리케이션에서 아직 OpenTelemetry SDK를 사용하지 않는 경우, 다음 단계에서 스팬을 출력하는 데 사용할 콘솔 익스포터와 함께 설치하세요. `io.opentelemetry:opentelemetry-sdk`와 `io.opentelemetry:opentelemetry-exporter-logging`을 모두 [Maven Central](https://central.sonatype.com/artifact/io.opentelemetry/opentelemetry-sdk)의 최신 버전으로 빌드에 추가하세요.
  </Tab>

  <Tab title="PHP">
    ```bash
    composer require "anthropic-ai/sdk" "guzzlehttp/guzzle:^7"
    ```

    애플리케이션에서 아직 OpenTelemetry SDK를 사용하지 않는 경우, 다음 단계에서 스팬을 출력하는 데 사용할 콘솔 익스포터와 함께 설치하세요:

    ```bash
    composer require open-telemetry/sdk
    ```
  </Tab>

  <Tab title="Ruby">
    `Gemfile`에 아직 `anthropic`이 없다면 gem을 추가하세요:

    ```bash
    bundle add anthropic
    ```

    `Gemfile`에 이미 `anthropic`이 있다면 gem을 업데이트하세요:

    ```bash
    bundle update anthropic
    ```

    애플리케이션에서 아직 OpenTelemetry SDK를 사용하지 않는 경우, 다음 단계에서 스팬을 출력하는 데 사용할 콘솔 익스포터와 함께 설치하세요:

    ```bash
    bundle add opentelemetry-sdk
    ```
  </Tab>
</Tabs>

## 2. 트레이서 프로바이더 등록

*"Tracer provider"*(트레이서 프로바이더)는 스팬을 생성하고 이를 익스포터에 전달하는 OpenTelemetry 객체입니다. SDK는 애플리케이션이 등록한 트레이서 프로바이더를 사용하므로 SDK 호출 코드는 그대로 유지됩니다. 추적하려는 첫 번째 호출 전에 등록하세요.

다음 예제는 각 스팬을 콘솔에 출력하는 트레이서 프로바이더를 등록한 다음 한 번의 호출을 수행합니다. 관측성 플랫폼을 연결하기 전에 스팬을 확인하는 데 사용하세요:

<CodeGroup exclude="shell">
  ```python Python
  from anthropic import Anthropic
  from opentelemetry import trace
  from opentelemetry.sdk.trace import TracerProvider
  from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor

  # 각 span을 콘솔에 출력하는 tracer provider를 등록합니다.
  provider = TracerProvider()
  provider.add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter()))
  trace.set_tracer_provider(provider)

  client = Anthropic()

  message = client.messages.create(
      model="claude-opus-5-5",
      max_tokens=1024,
      messages=[{"role": "user", "content": "Hello, Claude"}],
  )
  print(message.content)
  ```

  ```typescript TypeScript
  import Anthropic from "@anthropic-ai/sdk";
  import {
    ConsoleSpanExporter,
    NodeTracerProvider,
    SimpleSpanProcessor
  } from "@opentelemetry/sdk-trace-node";

  // 각 span을 콘솔에 출력하는 tracer provider를 등록합니다.
  const provider = new NodeTracerProvider({
    spanProcessors: [new SimpleSpanProcessor(new ConsoleSpanExporter())]
  });
  // register()는 context manager도 설치하므로 SDK span이 사용자의 span 아래에 중첩될 수 있습니다.
  provider.register();

  const client = new Anthropic();

  const message = await client.messages.create({
    model: "claude-opus-5-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Hello, Claude" }]
  });
  console.log(message.content);
  ```

  ```csharp C#
  using Anthropic;
  using Anthropic.Models.Messages;
  using OpenTelemetry;
  using OpenTelemetry.Trace;

  // 각 span을 콘솔에 출력하는 tracer provider를 등록합니다.
  // SDK는 com.anthropic.sdk.csharp 소스에서 span을 내보냅니다. 클라우드 플랫폼용 SDK 클라이언트도 포함하려면
  // 대신 com.anthropic.sdk.csharp*를 추가하세요.
  using var tracerProvider = Sdk.CreateTracerProviderBuilder()
      .AddSource("com.anthropic.sdk.csharp")
      .AddConsoleExporter()
      .Build();

  AnthropicClient client = new();

  var message = await client.Messages.Create(new MessageCreateParams
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      Messages = [new() { Role = Role.User, Content = "Hello, Claude" }],
  });
  Console.WriteLine(message);
  ```

  ```go Go
  package main

  import (
  	"context"
  	"fmt"

  	"github.com/anthropics/anthropic-sdk-go"
  	"go.opentelemetry.io/otel"
  	"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
  	"go.opentelemetry.io/otel/propagation"
  	sdktrace "go.opentelemetry.io/otel/sdk/trace"
  )

  func main() {
  	ctx := context.Background()

  	// 각 span을 콘솔에 출력하는 tracer provider를 등록합니다.
  	exporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint())
  	if err != nil {
  		panic(err)
  	}
  	provider := sdktrace.NewTracerProvider(sdktrace.WithSyncer(exporter))
  	defer provider.Shutdown(ctx)
  	otel.SetTracerProvider(provider)
  	// Go용 OpenTelemetry는 기본적으로 propagator를 등록하지 않으므로
  	// W3C trace context propagator를 등록합니다.
  	otel.SetTextMapPropagator(propagation.TraceContext{})

  	client := anthropic.NewClient()

  	message, err := client.Messages.New(ctx, anthropic.MessageNewParams{
  		Model:     anthropic.ModelClaudeOpus5_5,
  		MaxTokens: 1024,
  		Messages: []anthropic.MessageParam{
  			anthropic.NewUserMessage(anthropic.NewTextBlock("Hello, Claude")),
  		},
  	})
  	if err != nil {
  		panic(err)
  	}
  	fmt.Println(message.Content)
  }
  ```

  ```java Java
  import com.anthropic.client.okhttp.AnthropicOkHttpClient;
  import com.anthropic.models.messages.MessageCreateParams;
  import com.anthropic.models.messages.Model;
  import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
  import io.opentelemetry.context.propagation.ContextPropagators;
  import io.opentelemetry.exporter.logging.LoggingSpanExporter;
  import io.opentelemetry.sdk.OpenTelemetrySdk;
  import io.opentelemetry.sdk.trace.SdkTracerProvider;
  import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;

  void main() {
      // 각 span을 로그에 기록하는 tracer provider를 등록합니다.
      var tracerProvider = SdkTracerProvider.builder()
          .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create()))
          .build();
      OpenTelemetrySdk.builder()
          .setTracerProvider(tracerProvider)
          // Java용 OpenTelemetry는 코드에서 직접 빌드할 때 propagator를 등록하지 않으므로
          // W3C trace context propagator를 등록합니다.
          .setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance()))
          .buildAndRegisterGlobal();

      var client = AnthropicOkHttpClient.fromEnv();

      var message = client.messages().create(MessageCreateParams.builder()
          .model(Model.CLAUDE_OPUS_5_5)
          .maxTokens(1024)
          .addUserMessage("Hello, Claude")
          .build());
      IO.println(message.content());

      tracerProvider.close();
  }
  ```

  ```php PHP
  use Anthropic\Client;
  use Anthropic\Messages\Model;
  use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator;
  use OpenTelemetry\SDK\Sdk;
  use OpenTelemetry\SDK\Trace\SpanExporter\ConsoleSpanExporterFactory;
  use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor;
  use OpenTelemetry\SDK\Trace\TracerProvider;

  // 각 span을 콘솔에 출력하는 tracer provider를 등록합니다.
  $tracerProvider = new TracerProvider(
      new SimpleSpanProcessor((new ConsoleSpanExporterFactory())->create()),
  );
  Sdk::builder()
      ->setTracerProvider($tracerProvider)
      // PHP용 OpenTelemetry는 코드에서 직접 구성할 때 propagator를 등록하지 않으므로,
      // W3C trace context propagator를 등록합니다.
      ->setPropagator(TraceContextPropagator::getInstance())
      ->buildAndRegisterGlobal();

  $client = new Client();

  $message = $client->messages->create(
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'Hello, Claude']],
      model: Model::CLAUDE_OPUS_5_5,
  );
  echo $message;
  ```

  ```ruby Ruby
  require "anthropic"
  require "opentelemetry/sdk"

  # 각 span을 콘솔에 출력하는 tracer provider를 등록합니다.
  OpenTelemetry::SDK.configure do |config|
    config.add_span_processor(
      OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(
        OpenTelemetry::SDK::Trace::Export::ConsoleSpanExporter.new
      )
    )
  end

  client = Anthropic::Client.new

  message = client.messages.create(
    model: Anthropic::Model::CLAUDE_OPUS_5_5,
    max_tokens: 1024,
    messages: [{role: "user", content: "Hello, Claude"}]
  )
  puts(message.content)
  ```
</CodeGroup>

콘솔에 하나의 [`anthropic.messages.create`](https://platform.claude.com/docs/ko/api/opentelemetry/spans#anthropic-messages-create) 스팬이 표시됩니다.

## 3. 관측성 플랫폼으로 스팬 전송

콘솔 익스포터를 관측성 플랫폼을 가리키는 OTLP 익스포터로 교체하세요. 사용하는 언어의 설정 방법은 OpenTelemetry 문서의 [Python 익스포터](https://opentelemetry.io/docs/languages/python/exporters/), [TypeScript 익스포터](https://opentelemetry.io/docs/languages/js/exporters/), [.NET 익스포터](https://opentelemetry.io/docs/languages/dotnet/exporters/), [Go 익스포터](https://opentelemetry.io/docs/languages/go/exporters/), [Java SDK 구성](https://opentelemetry.io/docs/languages/java/configuration/), [PHP 익스포터](https://opentelemetry.io/docs/languages/php/exporters/), 또는 [Ruby 익스포터](https://opentelemetry.io/docs/languages/ruby/exporters/)를 참조하세요.

## 4. SDK 구성

다음 옵션을 사용하여 스팬에 포함될 내용과 스팬이 전송될 위치를 결정하세요. 각 옵션은 코드에서 설정하거나, 환경 변수가 있는 경우 옵션 이름 아래에 표시된 환경 변수로 설정할 수 있습니다.

<Note>
  클라이언트에 `open_telemetry` (typescript, java, php: `openTelemetry`; csharp: `OpenTelemetry`; go: `option.With…()`) 옵션을 전달하면, 중첩 키를 포함하여 해당 값이 그 옵션의 환경 변수 전체보다 우선합니다. 생략한 중첩 키는 해당 환경 변수가 설정되어 있더라도 표에 나온 기본값을 사용합니다.
</Note>

| 옵션                                                                                                                                                                                                                             | 용도                                                                                                                                                                 | 기본값                                                                                                                              |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| `traces.content_mode` (typescript, java, php: `traces.contentMode`; csharp: `Traces.ContentMode`; go: `option.WithOpenTelemetryTracesContentMode()`)<br />`ANTHROPIC_OPEN_TELEMETRY_TRACES_CONTENT_MODE=content`               | 스팬에 프롬프트와 응답을 포함합니다. [콘텐츠 모드](https://platform.claude.com/docs/ko/manage-claude/opentelemetry#choose-a-content-mode)를 참조하세요.                                       | `metadata_only`                                                                                                                  |
| `traces.max_content_bytes` (typescript, java, php: `traces.maxContentBytes`; csharp: `Traces.MaxContentBytes`; go: `option.WithOpenTelemetryTracesMaxContentBytes()`)<br />`ANTHROPIC_OPEN_TELEMETRY_TRACES_MAX_CONTENT_BYTES` | 각 스팬의 콘텐츠 제한을 변경합니다. [콘텐츠 제한](https://platform.claude.com/docs/ko/manage-claude/opentelemetry#content-limits)을 참조하세요.                                              | 0.75 MB                                                                                                                          |
| `conversation_id` (typescript, java, php: `conversationId`; csharp: `ConversationId`; go: `option.WithOpenTelemetryConversationID()`)                                                                                          | 여러 호출을 하나의 대화로 연결합니다. SDK는 ID를 `gen_ai.conversation.id`에 기록합니다. ID는 두 콘텐츠 모드 모두에서 내보내지므로 개인 데이터나 비밀 정보를 넣지 마세요.                                                    | 없음                                                                                                                               |
| `tracer_provider` (typescript, java, php: `tracerProvider`; csharp: `AddSource("com.anthropic.sdk.csharp")`; go: `option.WithTracerProvider()`)                                                                                | SDK의 스팬을 받을 트레이서 프로바이더를 선택합니다.                                                                                                                                     | [트레이서 프로바이더 등록](https://platform.claude.com/docs/ko/manage-claude/opentelemetry/sdk#register-a-tracer-provider)에서 설정한 트레이서 프로바이더 |
| `propagation` (go: `option.WithOpenTelemetryPropagation()`)<br />`ANTHROPIC_OPEN_TELEMETRY_PROPAGATION=false`                                                                                                                  | 요청과 함께 트레이스 컨텍스트 및 baggage 헤더를 보내지 않습니다. 예를 들어 HTTP 클라이언트 계측이 이미 이 헤더를 보내는 경우에 사용합니다. SDK는 계속 스팬을 생성합니다. Python, TypeScript, Go, Java, Ruby, PHP SDK에서 사용할 수 있습니다. | 켜짐                                                                                                                               |
| `open_telemetry` (typescript, java, php: `openTelemetry`; csharp: `OpenTelemetry`; go: `option.WithoutOpenTelemetry()`)<br />`ANTHROPIC_OPEN_TELEMETRY=false`                                                                  | 하나의 클라이언트에 대해 스팬을 끕니다. 예를 들어 다른 라이브러리가 이미 SDK를 추적하는 경우에 사용합니다.                                                                                                     | 켜짐                                                                                                                               |

다음 예제는 각 옵션을 사용하는 방법을 보여줍니다:

<CodeGroup exclude="shell">
  ```python Python
  # 특정 tracer provider를 사용하고 콘텐츠를 기록합니다.
  # open_telemetry를 설정하면 모든 ANTHROPIC_OPEN_TELEMETRY* 환경 변수가 재정의됩니다.
  client = Anthropic(
      open_telemetry={"tracer_provider": provider, "traces": {"content_mode": "content"}}
  )

  # 한 클라이언트에 대해 span을 끕니다.
  quiet_client = Anthropic(open_telemetry=False)

  # 클라이언트의 복사본을 반환하는 with_options()로 대화 ID를 설정합니다.
  # with_options()는 옵션 전체를 대체하므로 client.open_telemetry에서 시작하세요.
  message = client.with_options(
      open_telemetry={**client.open_telemetry, "conversation_id": "conv_42"}
  ).messages.create(
      model="claude-opus-5-5",
      max_tokens=1024,
      messages=[{"role": "user", "content": "Hello, Claude"}],
  )
  ```

  ```typescript TypeScript
  // 특정 tracer provider를 사용하고 콘텐츠를 기록합니다.
  // openTelemetry를 설정하면 모든 ANTHROPIC_OPEN_TELEMETRY* 환경 변수가 재정의됩니다.
  const client = new Anthropic({
    openTelemetry: { tracerProvider: provider, traces: { contentMode: "content" } }
  });

  // 한 클라이언트에 대해 span을 끕니다.
  const quietClient = new Anthropic({ openTelemetry: false });

  // 한 호출의 요청 옵션에 대화 ID를 설정합니다.
  const message = await client.messages.create(
    {
      model: "claude-opus-5-5",
      max_tokens: 1024,
      messages: [{ role: "user", content: "Hello, Claude" }]
    },
    { openTelemetry: { conversationId: "conv_42" } }
  );
  ```

  ```csharp C#
  // 특정 tracer provider를 사용하려면 해당 tracer provider에 SDK의 소스를 추가하세요.
  using var tracerProvider = Sdk.CreateTracerProviderBuilder()
      .AddSource("com.anthropic.sdk.csharp")
      .AddConsoleExporter()
      .Build();

  // 콘텐츠를 기록합니다.
  // OpenTelemetry를 설정하면 모든 ANTHROPIC_OPEN_TELEMETRY* 환경 변수가 재정의됩니다.
  AnthropicClient client = new()
  {
      OpenTelemetry = new() { Traces = new() { ContentMode = OpenTelemetryContentMode.Content } },
  };

  // 하나의 클라이언트에 대해 span을 끕니다.
  AnthropicClient quietClient = new() { OpenTelemetry = false };

  // 하나의 대화에 속한 호출에 대화 ID를 설정합니다.
  var conversation = client.WithOptions(options => options with
  {
      OpenTelemetryRequest = new() { ConversationId = "conv_42" },
  });
  var message = await conversation.Messages.Create(new MessageCreateParams
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      Messages = [new() { Role = Role.User, Content = "Hello, Claude" }],
  });
  ```

  ```go Go
  // 특정 tracer provider를 사용하고 콘텐츠를 기록합니다.
  client := anthropic.NewClient(
  	option.WithTracerProvider(provider),
  	option.WithOpenTelemetryTracesContentMode(option.OpenTelemetryContentModeContent),
  )

  // 한 클라이언트에 대해 span을 끕니다.
  quietClient := anthropic.NewClient(option.WithoutOpenTelemetry())

  // 한 호출의 요청 옵션에 대화 ID를 설정합니다.
  message, err := client.Messages.New(ctx, anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 1024,
  	Messages: []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock("Hello, Claude")),
  	},
  }, option.WithOpenTelemetryConversationID("conv_42"))
  ```

  ```java Java
  // 클라이언트는 fromEnv()로 빌드할 때만 환경 변수를 읽습니다.
  // 시스템 속성 anthropic.openTelemetry, anthropic.openTelemetryTracesContentMode,
  // anthropic.openTelemetryTracesMaxContentBytes는 환경 변수보다
  // 우선합니다.

  // 특정 tracer provider를 사용하고 콘텐츠를 기록합니다. fromEnv()가 읽은 옵션을 유지하려면
  // OpenTelemetryOptions.fromEnv()에서 시작하세요.
  var envOptions = OpenTelemetryOptions.fromEnv();
  var client = AnthropicOkHttpClient.builder()
      .fromEnv()
      .openTelemetry(envOptions.toBuilder()
          .tracerProvider(tracerProvider)
          .traces(envOptions.traces().toBuilder()
              .contentMode(OpenTelemetryContentMode.CONTENT)
              .build())
          .build())
      .build();

  // 한 클라이언트에 대해 span을 끕니다.
  var quietClient = AnthropicOkHttpClient.builder().fromEnv().openTelemetry(false).build();

  // 한 호출의 요청 옵션에 대화 ID를 설정합니다.
  var params = MessageCreateParams.builder()
      .model(Model.CLAUDE_OPUS_5_5)
      .maxTokens(1024)
      .addUserMessage("Hello, Claude")
      .build();
  var message = client.messages().create(params, RequestOptions.builder()
      .openTelemetry(OpenTelemetryRequestOptions.builder().conversationId("conv_42").build())
      .build());
  ```

  ```php PHP
  // 특정 tracer provider를 사용하고 콘텐츠를 기록합니다.
  // openTelemetry를 설정하면 모든 ANTHROPIC_OPEN_TELEMETRY* 환경 변수가 재정의됩니다.
  $client = new Client(
      openTelemetry: ['tracerProvider' => $tracerProvider, 'traces' => ['contentMode' => 'content']],
  );

  // 한 클라이언트에 대해 span을 끕니다.
  $quietClient = new Client(openTelemetry: false);

  // 한 호출의 요청 옵션에 대화 ID를 설정합니다.
  $message = $client->messages->create(
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'Hello, Claude']],
      model: Model::CLAUDE_OPUS_5_5,
      requestOptions: ['openTelemetry' => ['conversationId' => 'conv_42']],
  );
  ```

  ```ruby Ruby
  # 특정 tracer provider를 사용하고 콘텐츠를 기록합니다.
  # open_telemetry를 설정하면 모든 ANTHROPIC_OPEN_TELEMETRY* 환경 변수를 재정의합니다.
  client = Anthropic::Client.new(open_telemetry: {tracer_provider: provider, traces: {content_mode: :content}})

  # 한 클라이언트에 대해 span을 끕니다.
  quiet_client = Anthropic::Client.new(open_telemetry: false)

  # 한 호출의 요청 옵션에 대화 ID를 설정합니다.
  message = client.messages.create(
    model: Anthropic::Model::CLAUDE_OPUS_5_5,
    max_tokens: 1024,
    messages: [{role: "user", content: "Hello, Claude"}],
    request_options: {open_telemetry: {conversation_id: "conv_42"}}
  )
  ```
</CodeGroup>

## 다음 단계

<CardGroup cols={2}>
  <Card title="API 추적 설정" icon="cloud" href="https://platform.claude.com/docs/ko/manage-claude/opentelemetry/api">
    Claude API에 대한 관측성을 확보합니다.
  </Card>

  <Card title="예제 트레이스 보기" icon="sitemap" href="https://platform.claude.com/docs/ko/manage-claude/opentelemetry#example-traces-and-how-to-read-them">
    클라이언트에서 Claude API로 이어지는 요청을 따라가 보세요.
  </Card>
</CardGroup>

## 스트림 추적

스트림의 `anthropic.messages.create` 스팬은 코드가 스트림 전체를 읽거나 스트림을 닫을 때 종료되므로, 중간에 읽기를 멈춘 스트림은 닫아 주세요. 콘텐츠 모드에서는 `messages.stream()` (go: `Messages.NewStreaming()`; java: `messages().createStreaming()`; csharp: `Messages.CreateStreaming()`; php: `messages->createStream()`)로 스트림을 읽을 때만 스팬에 모델 응답이 포함됩니다.

스트림을 읽는 동안 시작한 스팬은 SDK 스팬의 자식이 아니라 형제 스팬이 됩니다. 이를 요청과 함께 그룹화하려면 호출과 읽기 루프를 모두 감싸는 자체 스팬을 시작하세요.

## Managed Agents 자체 호스팅 샌드박스 추적

Managed Agents [자체 호스팅 샌드박스](https://platform.claude.com/docs/ko/managed-agents/self-hosted-sandboxes)에서는 샌드박스가 도구를 실행하므로 도구 스팬이 API가 아닌 SDK에서 생성됩니다. Python, TypeScript, Go SDK의 환경 워커는 자신이 실행하는 각 도구와 보내는 각 요청에 대해 스팬을 생성합니다. 워커는 이러한 스팬을 각 작업 항목마다 하나의 [`anthropic.environment.work`](https://platform.claude.com/docs/ko/api/opentelemetry/spans#anthropic-environment-work) 스팬 아래에 묶습니다.

이러한 스팬을 받으려면 다음을 수행하세요:

1. 워커를 실행하는 프로세스에서 1\~4단계와 같이 [SDK 추적을 설정](https://platform.claude.com/docs/ko/manage-claude/opentelemetry/sdk#install-the-sdk)하세요.
2. 샌드박스에서 관측성 플랫폼 또는 OpenTelemetry Collector로의 네트워크 트래픽을 허용하세요.

워커가 도구 결과를 보내면 해당 요청의 API 스팬이 그 SDK 스팬 아래에 중첩됩니다. 워커가 Claude Platform on AWS로 보내는 요청의 경우, API 스팬은 대신 별도의 트레이스에 있습니다.

## 트레이스 컨텍스트와 baggage

SDK는 각 요청에서 애플리케이션의 전파기를 사용해 `traceparent` 같은 헤더로 트레이스 컨텍스트를 API에 보냅니다. 이 전파기가 baggage도 작성하면 SDK는 baggage도 보냅니다. baggage 없이 트레이스 컨텍스트를 보내려면 W3C 트레이스 컨텍스트 전파기만 등록하세요.

SDK가 이 헤더를 보내지 않게 하려면(예: HTTP 클라이언트 계측이 이미 이 헤더를 보내는 경우) SDK의 전파를 끄세요:

<CodeGroup exclude="shell">
  ```python Python
  client = Anthropic(open_telemetry={"propagation": False})
  ```

  <CodeGroupItem>
    ```typescript TypeScript
    const client = new Anthropic({ openTelemetry: { propagation: false } });
    ```

    <Warning>
      `@opentelemetry/auto-instrumentations-node`

      에 포함된 

      `@opentelemetry/instrumentation-undici`

      를 사용하는 경우 

      `propagation: false`

      를 설정하세요. 그렇지 않으면 

      [이 패키지의 버그](https://github.com/open-telemetry/opentelemetry-js-contrib/issues/3711)

      로 각 요청에 두 번째 

      `traceparent`

       헤더가 추가되어 트레이스 컨텍스트가 유효하지 않게 됩니다.
    </Warning>
  </CodeGroupItem>

  ```csharp C#
  // SDK는 .NET의 DistributedContextPropagator로 이 헤더를 작성합니다.
  // 출력 없는 전파기는 프로세스의 모든 HttpClient에서 이 헤더를 막습니다.
  // 클라이언트를 만들기 전에 설정하세요.
  DistributedContextPropagator.Current = DistributedContextPropagator.CreateNoOutputPropagator();

  AnthropicClient client = new();
  ```

  ```go Go
  client := anthropic.NewClient(option.WithOpenTelemetryPropagation(false))
  ```

  ```java Java
  var envOptions = OpenTelemetryOptions.fromEnv();
  var client = AnthropicOkHttpClient.builder()
      .fromEnv()
      .openTelemetry(envOptions.toBuilder().propagation(false).build())
      .build();
  ```

  ```php PHP
  $client = new Client(openTelemetry: ['propagation' => false]);
  ```

  ```ruby Ruby
  client = Anthropic::Client.new(open_telemetry: {propagation: false})
  ```
</CodeGroup>

SDK가 [MCP 도구](https://platform.claude.com/docs/ko/agents-and-tools/mcp-connector#client-side-mcp-helpers)를 호출하는 동안에는 SDK의 도구 스팬이 현재 스팬입니다. 일부 MCP 클라이언트 라이브러리는 트레이스 컨텍스트를 `_meta`에 담아 MCP 서버로 보내므로, 이를 읽는 서버는 해당 도구 스팬 아래에 자신의 스팬을 트레이스에 추가할 수 있습니다. 버전 2.0 이상의 Python `mcp` 패키지와 C# `ModelContextProtocol` 패키지가 이렇게 합니다. TypeScript, Go, Java, Ruby, PHP용 MCP SDK는 자체적으로 이렇게 하지 않습니다.

## 문제 해결

| 문제                                                                   | 확인할 사항                                                                                                                                                                                                                |
| -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SDK에서 span이 없음                                                       | 첫 번째 호출 전에 tracer provider를 등록하세요. [SDK 설치](https://platform.claude.com/docs/ko/manage-claude/opentelemetry/sdk#install-the-sdk)에 따라 SDK를 설치했는지, 그리고 `ANTHROPIC_OPEN_TELEMETRY`가 `false`가 아닌지 확인하세요.                  |
| Java SDK가 등록된 tracer provider로 span을 보내지 않거나, API로 트레이스 컨텍스트를 보내지 않음 | 다른 종속성이 `opentelemetry-api`를 1.57.0 미만으로 설정하고 있습니다. `opentelemetry-api`를 1.57.0 이상으로 업그레이드하세요.                                                                                                                        |
| SDK 스팬이 사용자의 스팬 아래에 중첩되지 않거나, 도구가 생성하는 스팬이 해당 도구 스팬 아래에 중첩되지 않음      | [API 스팬을 자체 트레이스에 연결의 예시](https://platform.claude.com/docs/ko/manage-claude/opentelemetry/api#connect-api-spans-to-your-own-traces)처럼 자체 스팬 중 하나의 내부에서 SDK를 호출하세요. Go에서는 도구 실행기가 도구에 전달하는 `ctx`에서 도구가 생성하는 스팬을 시작하세요. |
| SDK의 요청에 `traceparent` 헤더가 없음                                        | OpenTelemetry에 W3C 트레이스 컨텍스트 전파기를 등록하세요. SDK는 애플리케이션의 전파기를 사용하여 헤더를 작성합니다.                                                                                                                                            |
| 스트림의 SDK span이 종료되지 않거나 응답이 없음                                       | [스트림 추적](https://platform.claude.com/docs/ko/manage-claude/opentelemetry/sdk#trace-a-stream)을 참조하세요.                                                                                                                  |
| 각 요청이 두 번 나타남                                                        | 요청의 SDK span과 API span은 이름이 같습니다. span 종류 또는 `service.name`을 사용하여 구분하세요. 다른 라이브러리도 SDK를 트레이싱하는 경우 둘 중 하나를 끄세요.                                                                                                        |
