> ## Documentation Index
> Fetch the complete documentation index at: https://langchain-5e9cc07a-preview-patchr-1773857969-df0cef9.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Log traces to a specific project

You can change the destination project of your traces both statically through environment variables and dynamically at runtime.

## Set the destination project statically

As mentioned in the [Tracing Concepts](/langsmith/observability-concepts#projects) section, LangSmith uses the concept of a `Project` to group traces. If left unspecified, the project is set to `default`. You can set the `LANGSMITH_PROJECT` environment variable to configure a custom project name for an entire application run. This should be done before executing your application.

```
export LANGSMITH_PROJECT=my-custom-project
```

<Warning>
  The `LANGSMITH_PROJECT` flag is only supported in JS SDK versions >= 0.2.16, use `LANGCHAIN_PROJECT` instead if you are using an older version.
</Warning>

If the project specified does not exist, it will be created automatically when the first trace is ingested.

## Set the destination project dynamically

You can also set the project name at program runtime in various ways, depending on how you are [annotating your code for tracing](/langsmith/annotate-code). This is useful when you want to log traces to different projects within the same application.

<Note>
  Setting the project name dynamically using one of the below methods overrides the project name set by the `LANGSMITH_PROJECT` environment variable.
</Note>

<CodeGroup>
  ```python Python theme={null}
  import openai
  from langsmith import traceable
  from langsmith.run_trees import RunTree

  client = openai.Client()
  messages = [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "Hello!"}
  ]

  # Use the @traceable decorator with the 'project_name' parameter to log traces to LangSmith
  # Ensure that the LANGSMITH_TRACING environment variables is set for @traceable to work
  @traceable(
    run_type="llm",
    name="OpenAI Call Decorator",
    project_name="My Project"
  )
  def call_openai(
    messages: list[dict], model: str = "gpt-4.1-mini"
  ) -> str:
    return client.chat.completions.create(
        model=model,
        messages=messages,
    ).choices[0].message.content

  # Call the decorated function
  call_openai(messages)

  # You can also specify the Project via the project_name parameter
  # This will override the project_name specified in the @traceable decorator
  call_openai(
    messages,
    langsmith_extra={"project_name": "My Overridden Project"},
  )

  # The wrapped OpenAI client accepts all the same langsmith_extra parameters
  # as @traceable decorated functions, and logs traces to LangSmith automatically.
  # Ensure that the LANGSMITH_TRACING environment variables is set for the wrapper to work.
  from langsmith import wrappers
  wrapped_client = wrappers.wrap_openai(client)
  wrapped_client.chat.completions.create(
    model="gpt-4.1-mini",
    messages=messages,
    langsmith_extra={"project_name": "My Project"},
  )

  # Alternatively, create a RunTree object
  # You can set the project name using the project_name parameter
  rt = RunTree(
    run_type="llm",
    name="OpenAI Call RunTree",
    inputs={"messages": messages},
    project_name="My Project"
  )
  chat_completion = client.chat.completions.create(
    model="gpt-4.1-mini",
    messages=messages,
  )
  # End and submit the run
  rt.end(outputs=chat_completion)
  rt.post()
  ```

  ```typescript TypeScript theme={null}
  import OpenAI from "openai";
  import { traceable } from "langsmith/traceable";
  import { wrapOpenAI } from "langsmith/wrappers";
  import { RunTree} from "langsmith";

  const client = new OpenAI();
  const messages = [
    {role: "system", content: "You are a helpful assistant."},
    {role: "user", content: "Hello!"}
  ];

  const traceableCallOpenAI = traceable(async (messages: {role: string, content: string}[], model: string) => {
    const completion = await client.chat.completions.create({
        model: model,
        messages: messages,
    });
    return completion.choices[0].message.content;
  },{
    run_type: "llm",
    name: "OpenAI Call Traceable",
    project_name: "My Project"
  });

  // Call the traceable function
  await traceableCallOpenAI(messages, "gpt-4.1-mini");

  // Create and use a RunTree object
  const rt = new RunTree({
    run_type: "llm",
    name: "OpenAI Call RunTree",
    inputs: { messages },
    project_name: "My Project"
  });
  await rt.postRun();

  // Execute a chat completion and handle it within RunTree
  rt.end({outputs: chatCompletion});
  await rt.patchRun();
  ```

  ```java Client theme={null}
  import com.langchain.smith.otel.OtelConfig;
  import com.langchain.smith.otel.OtelSpanCreator;
  import com.langchain.smith.otel.OtelTraceExporter;
  import io.opentelemetry.api.trace.Span;
  import io.opentelemetry.api.trace.StatusCode;
  import io.opentelemetry.api.trace.Tracer;
  import java.time.Duration;
  import java.util.HashMap;
  import java.util.Map;

  /**
   * Simple example: Send a single OpenTelemetry trace to LangSmith.
   *
   * Usage:
   *   export LANGSMITH_API_KEY=your_api_key
   *   export LANGSMITH_PROJECT=your_project_name  # Optional, defaults to "default"
   */
  public class OtelLangSmithSimpleExample {
      public static void main(String[] args) throws Exception {
          // Get API key and project name
          String apiKey = System.getenv("LANGSMITH_API_KEY");
          if (apiKey == null || apiKey.isEmpty()) {
              System.err.println("ERROR: LANGSMITH_API_KEY environment variable is required!");
              return;
          }

          String projectName = System.getenv("LANGSMITH_PROJECT");
          if (projectName == null || projectName.isEmpty()) {
              projectName = "default";
          }

          // Configure exporter
          Map<String, String> headers = new HashMap<>();
          headers.put("x-api-key", apiKey);
          headers.put("Langsmith-Project", projectName);

          OtelConfig config = OtelConfig.builder()
                  .enabled(true)
                  .endpoint("https://api.smith.langchain.com/otel/v1/traces")
                  .headers(headers)
                  .timeout(Duration.ofSeconds(30))
                  .serviceName("langsmith-java-simple")
                  .build();

          OtelTraceExporter exporter = OtelTraceExporter.fromConfig(config);
          Tracer tracer = exporter.getTracer();

          // Create a simple span
          Span span = OtelSpanCreator.createLlmSpan(
                  tracer, "simple.llm.call", "openai", "gpt-4", projectName, null);

          try {
              OtelSpanCreator.setInput(span, "Hello, world!");
              Thread.sleep(100); // Simulate processing
              OtelSpanCreator.setOutput(span, "Hello! How can I help you?");
              OtelSpanCreator.setTokenUsage(span, 5, 8);
              span.setStatus(StatusCode.OK);
          } finally {
              span.end();
          }

          // Flush and shutdown
          exporter.flush().join(5, java.util.concurrent.TimeUnit.SECONDS);
          exporter.shutdown().join(2, java.util.concurrent.TimeUnit.SECONDS);

          System.out.println("✓ Trace sent to LangSmith!");
      }
  }
  ```
</CodeGroup>

## Set the destination workspace dynamically

If you need to dynamically route traces to different LangSmith [workspaces](/langsmith/administration-overview#workspaces) based on runtime configuration (e.g., routing different users or tenants to separate workspaces), Python users can use workspace-specific LangSmith clients with `tracing_context`, while TypeScript users can pass a custom client to `traceable` or use `LangChainTracer` with callbacks.

This approach is useful for multi-tenant applications where you want to isolate traces by customer, environment, or team at the workspace level.

### Prerequisites

* A [LangSmith API key](/langsmith/create-account-api-key) with access to multiple workspaces.
* The [workspace IDs](/langsmith/set-up-hierarchy#set-up-a-workspace) for each target workspace.

### Generic cross-workspace tracing

Use this approach for general applications where you want to dynamically route traces to different workspaces based on runtime logic (e.g., customer ID, tenant, or environment).

**Key components:**

1. Initialize separate `Client` instances for each workspace with their respective `workspace_id`.
2. Use `tracing_context` (Python) or pass the workspace-specific `client` to `traceable` (TypeScript) to route traces.
3. Pass workspace configuration through your application's runtime config.

<CodeGroup>
  ```python Python theme={null}
  import os
  import contextlib
  from langsmith import Client, traceable, tracing_context

  # API key with access to multiple workspaces
  api_key = os.getenv("LS_CROSS_WORKSPACE_KEY")

  # Initialize clients for different workspaces
  workspace_a_client = Client(
      api_key=api_key,
      api_url="https://api.smith.langchain.com",
      workspace_id="<YOUR_WORKSPACE_A_ID>"  # e.g., "abc123..."
  )

  workspace_b_client = Client(
      api_key=api_key,
      api_url="https://api.smith.langchain.com",
      workspace_id="<YOUR_WORKSPACE_B_ID>"  # e.g., "def456..."
  )

  # Example: Route based on customer ID
  def get_workspace_client(customer_id: str):
      """Route to appropriate workspace based on customer."""
      if customer_id.startswith("premium_"):
          return workspace_a_client, "premium-customer-traces"
      else:
          return workspace_b_client, "standard-customer-traces"

  @traceable
  def process_request(data: dict, customer_id: str):
      """Process a customer request with workspace-specific tracing."""
      # Your business logic here
      return {"status": "success", "data": data}

  # Use tracing_context to route to the appropriate workspace
  def handle_customer_request(customer_id: str, request_data: dict):
      client, project_name = get_workspace_client(customer_id)

      # Everything within this context will be traced to the selected workspace
      with tracing_context(enabled=True, client=client, project_name=project_name):
          result = process_request(request_data, customer_id)

      return result

  # Example usage
  handle_customer_request("premium_user_123", {"query": "Hello"})
  handle_customer_request("standard_user_456", {"query": "Hi"})
  ```

  ```typescript TypeScript theme={null}
  import { Client } from "langsmith";
  import { traceable } from "langsmith/traceable";

  // API key with access to multiple workspaces
  const apiKey = process.env.LS_CROSS_WORKSPACE_KEY;

  // Initialize clients for different workspaces
  const workspaceAClient = new Client({
    apiKey: apiKey,
    apiUrl: "https://api.smith.langchain.com",
    workspaceId: "<YOUR_WORKSPACE_A_ID>", // e.g., "abc123..."
  });

  const workspaceBClient = new Client({
    apiKey: apiKey,
    apiUrl: "https://api.smith.langchain.com",
    workspaceId: "<YOUR_WORKSPACE_B_ID>", // e.g., "def456..."
  });

  // Example: Route based on customer ID
  function getWorkspaceClient(customerId: string): {
    client: Client;
    projectName: string;
  } {
    if (customerId.startsWith("premium_")) {
      return {
        client: workspaceAClient,
        projectName: "premium-customer-traces",
      };
    } else {
      return {
        client: workspaceBClient,
        projectName: "standard-customer-traces",
      };
    }
  }

  // Route traces to the appropriate workspace by passing the client to traceable
  async function handleCustomerRequest(
    customerId: string,
    requestData: Record<string, any>
  ) {
    const { client, projectName } = getWorkspaceClient(customerId);

    // Create a traceable function with the workspace-specific client
    const processRequest = traceable(
      async (data: Record<string, any>, customerId: string) => {
        // Your business logic here
        return { status: "success", data };
      },
      {
        name: "process_request",
        client,
        project_name: projectName,
      }
    );

    return await processRequest(requestData, customerId);
  }

  // Example usage
  await handleCustomerRequest("premium_user_123", { query: "Hello" });
  await handleCustomerRequest("standard_user_456", { query: "Hi" });
  ```
</CodeGroup>

### Override default workspace for LangSmith deployments

When [deploying agents](/langsmith/deployment) to LangSmith, you can override the default workspace that traces are sent to by using a graph lifespan context manager. This is useful when you want to route traces from a deployed agent to different workspaces based on runtime configuration passed through the `config` parameter.

<CodeGroup>
  ```python Python theme={null}
  import os
  import contextlib
  from typing_extensions import TypedDict
  from langgraph.graph import StateGraph
  from langgraph.graph.state import RunnableConfig
  from langsmith import Client, tracing_context

  # API key with access to multiple workspaces
  api_key = os.getenv("LS_CROSS_WORKSPACE_KEY")

  # Initialize clients for different workspaces
  workspace_a_client = Client(
      api_key=api_key,
      api_url="https://api.smith.langchain.com",
      workspace_id="<YOUR_WORKSPACE_A_ID>"
  )

  workspace_b_client = Client(
      api_key=api_key,
      api_url="https://api.smith.langchain.com",
      workspace_id="<YOUR_WORKSPACE_B_ID>"
  )

  # Define configuration schema for workspace routing
  class Configuration(TypedDict):
      workspace_id: str

  # Define the graph state
  class State(TypedDict):
      response: str

  def greeting(state: State, config: RunnableConfig) -> State:
      """Generate a workspace-specific greeting."""
      workspace_id = config.get("configurable", {}).get("workspace_id", "workspace_a")

      if workspace_id == "workspace_a":
          response = "Hello from Workspace A!"
      elif workspace_id == "workspace_b":
          response = "Hello from Workspace B!"
      else:
          response = "Hello from the default workspace!"

      return {"response": response}

  # Build the base graph
  base_graph = (
      StateGraph(state_schema=State, config_schema=Configuration)
      .add_node("greeting", greeting)
      .set_entry_point("greeting")
      .set_finish_point("greeting")
      .compile()
  )

  @contextlib.asynccontextmanager
  async def graph(config):
      """Dynamically route traces to different workspaces based on configuration."""
      # Extract workspace_id from the configuration
      workspace_id = config.get("configurable", {}).get("workspace_id", "workspace_a")

      # Route to the appropriate workspace
      if workspace_id == "workspace_a":
          client = workspace_a_client
          project_name = "production-traces"
      elif workspace_id == "workspace_b":
          client = workspace_b_client
          project_name = "development-traces"
      else:
          client = workspace_a_client
          project_name = "default-traces"

      # Apply the tracing context for the selected workspace
      with tracing_context(enabled=True, client=client, project_name=project_name):
          yield base_graph

  # Usage: Invoke with different workspace configurations
  # await graph({"configurable": {"workspace_id": "workspace_a"}})
  # await graph({"configurable": {"workspace_id": "workspace_b"}})
  ```

  ```typescript TypeScript theme={null}
  import { Client } from "langsmith";
  import { LangChainTracer } from "@langchain/core/tracers/tracer_langchain";
  import { StateGraph, Annotation } from "@langchain/langgraph";

  // API key with access to multiple workspaces
  const apiKey = process.env.LS_CROSS_WORKSPACE_KEY;

  // Initialize clients for different workspaces
  const workspaceAClient = new Client({
    apiKey: apiKey,
    apiUrl: "https://api.smith.langchain.com",
    workspaceId: "<YOUR_WORKSPACE_A_ID>", // e.g., "abc123..."
  });

  const workspaceBClient = new Client({
    apiKey: apiKey,
    apiUrl: "https://api.smith.langchain.com",
    workspaceId: "<YOUR_WORKSPACE_B_ID>", // e.g., "def456..."
  });

  // Define the graph state
  const StateAnnotation = Annotation.Root({
    response: Annotation<string>(),
  });

  async function greeting(state: typeof StateAnnotation.State, config: any) {
    const workspaceId = config?.configurable?.workspace_id || "workspace_a";

    let response: string;
    if (workspaceId === "workspace_a") {
      response = "Hello from Workspace A!";
    } else if (workspaceId === "workspace_b") {
      response = "Hello from Workspace B!";
    } else {
      response = "Hello from the default workspace!";
    }

    return { response };
  }

  // Build the base graph
  const baseGraph = new StateGraph(StateAnnotation)
    .addNode("greeting", greeting)
    .addEdge("__start__", "greeting")
    .addEdge("greeting", "__end__")
    .compile();

  // Helper to get workspace-specific client and project
  function getWorkspaceConfig(workspaceId: string): {
    client: Client;
    projectName: string;
  } {
    if (workspaceId === "workspace_a") {
      return { client: workspaceAClient, projectName: "production-traces" };
    } else if (workspaceId === "workspace_b") {
      return { client: workspaceBClient, projectName: "development-traces" };
    }
    return { client: workspaceAClient, projectName: "default-traces" };
  }

  // Invoke the graph with workspace-specific tracing
  async function invokeWithWorkspaceTracing(
    workspaceId: string,
    input: typeof StateAnnotation.State
  ) {
    const { client, projectName } = getWorkspaceConfig(workspaceId);

    // Create a LangChainTracer with the workspace-specific client
    const tracer = new LangChainTracer({
      client,
      projectName,
    });

    // Invoke the graph with the tracer attached via callbacks
    // All traces will be routed to the selected workspace
    return await baseGraph.invoke(input, {
      configurable: { workspace_id: workspaceId },
      callbacks: [tracer],
    });
  }

  // Example usage
  await invokeWithWorkspaceTracing("workspace_a", { response: "" });
  await invokeWithWorkspaceTracing("workspace_b", { response: "" });
  ```
</CodeGroup>

### Key points

* **Generic cross-workspace tracing**: Use `tracing_context` (Python) or pass a workspace-specific `client` to `traceable` (TypeScript) to dynamically route traces to different workspaces.
* **LangGraph cross-workspace tracing**: For [LangGraph applications](/oss/python/langgraph/overview), use `LangChainTracer` with the workspace-specific client and attach it via the `callbacks` parameter.
* **LangSmith deployment override**: Use a graph lifespan context manager (Python) to override the default deployment workspace based on runtime configuration.
* Each `Client` instance maintains its own connection to a specific workspace via the `workspaceId` parameter.
* You can customize both the workspace and project name for each route.
* This pattern works with any LangSmith-compatible tracing (LangChain, OpenAI, custom functions, etc.).

<Note>
  When deploying with cross-workspace tracing, ensure your API key has the necessary permissions for all target workspaces. For LangSmith deployments, you must add an API key with cross-workspace access to your environment variables (e.g., `LS_CROSS_WORKSPACE_KEY`) to override the default service key generated by your deployment.
</Note>

***

<div className="source-links">
  <Callout icon="edit">
    [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/log-traces-to-project.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
  </Callout>

  <Callout icon="terminal-2">
    [Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers.
  </Callout>
</div>
