Use this file to discover all available pages before exploring further.
Effectively capturing the inputs and outputs of your LLM application’s operations is crucial for observability. LangWatch provides flexible ways to manage this data, whether you prefer automatic capture or explicit control to map complex objects, format data, or redact sensitive information.This tutorial covers how to:
Understand automatic input/output capture.
Explicitly set inputs and outputs for traces and spans.
Dynamically update this data on active traces/spans.
Handle different data formats, especially for chat messages.
By default, when you use tracer.withActiveSpan() or tracer.startActiveSpan(), the SDK attempts to automatically capture:
Inputs: The arguments passed to the function within the span context.
Outputs: The value returned by the function within the span context.
This behavior can be controlled using the data capture configuration in your observability setup.
import { setupObservability } from "langwatch/observability/node";import { getLangWatchTracer } from "langwatch";// Setup observability with data capture configurationsetupObservability({ dataCapture: "all", // Capture both input and output (default)});const tracer = getLangWatchTracer("input-output-example");// Automatic capture exampleawait tracer.withActiveSpan("GreetUser", async (span) => { // Function arguments and return value will be automatically captured const name = "Alice"; const greeting = "Hello"; span.setAttributes({ operation: "greeting" }); return `${greeting}, ${name}!`;});// Disable automatic capture for sensitive operationsawait tracer.withActiveSpan("SensitiveOperation", async (span) => { // Inputs and outputs for this span will not be automatically captured // You might explicitly set a sanitized version if needed console.log("Processing sensitive data..."); return { status: "processed" };}, { dataCapture: "none" });
You often need more control over what data is recorded. You can explicitly set inputs and outputs using the setInput() and setOutput() methods on span objects.This is useful for:
Capturing only specific parts of complex objects.
Formatting data in a more readable or structured way (e.g., as a list of ChatMessage objects).
Redacting sensitive information before it’s sent to LangWatch.
Providing inputs/outputs when automatic capture is disabled.
When using tracer.withActiveSpan() or tracer.startActiveSpan(), you can set inputs and outputs directly on the span object.
import { setupObservability } from "langwatch/observability/node";import { getLangWatchTracer } from "langwatch";// Setup observabilitysetupObservability();const tracer = getLangWatchTracer("input-output-example");await tracer.withActiveSpan("UserIntentProcessing", async (span) => { // Set explicit input for the span span.setInput("json", { user_query: "Book a flight to London" }); // raw_query_data might be large or contain sensitive info // The setInput() call above provides a clean version const rawQueryData = { query: "Book a flight to London", user_id: "123" }; const intent = "book_flight"; const entities = { destination: "London" }; // Explicitly set the output for the span span.setOutput("json", { intent, entities }); return { status: "success", intent }; // Actual function return});
You can modify the input or output of an active span using its setInput() and setOutput() methods. This is particularly useful when the input/output data is determined or refined during the operation.
import { setupObservability } from "langwatch/observability/node";import { getLangWatchTracer } from "langwatch";// Setup observabilitysetupObservability();const tracer = getLangWatchTracer("pipeline-example");await tracer.withActiveSpan("DataTransformationPipeline", async (span) => { // Initial input is automatically captured if dataCapture is enabled await tracer.withActiveSpan("Step1_CleanData", async (step1Span) => { // Suppose initial_data is complex, we want to record a summary as input const initialData = { a: 1, b: null, c: 3 }; step1Span.setInput("json", { data_keys: Object.keys(initialData) }); const cleanedData = Object.fromEntries( Object.entries(initialData).filter(([_, v]) => v !== null) ); step1Span.setOutput("json", { cleaned_item_count: Object.keys(cleanedData).length }); }); // ... further steps ... // Update the root span's output for the entire trace const finalResult = { status: "completed", items_processed: 2 }; span.setOutput("json", finalResult); return finalResult;});
The setInput() and setOutput() methods on LangWatchSpan objects are versatile and support multiple data types. See the reference for LangWatchSpan methods.
Redacting Sensitive Information: If your function arguments or return values contain sensitive data (PII, API keys), disable automatic capture and explicitly set sanitized versions using setInput() and setOutput().
Mapping Complex Objects: If your inputs/outputs are complex JavaScript objects, map them to a simplified object or string representation for clearer display in LangWatch.
Improving Readability: For long text inputs/outputs (e.g., full documents), consider capturing a summary or metadata instead of the entire content to reduce noise, unless the full content is essential for debugging or evaluating.
Error Handling: Use try-catch blocks within spans to capture error information and set appropriate outputs.
Clearing Captured Data: You can set input or output to null or an empty object via the setInput() or setOutput() methods to remove previously captured data if it’s no longer relevant.
import { setupObservability } from "langwatch/observability/node";import { getLangWatchTracer } from "langwatch";// Setup observabilitysetupObservability();const tracer = getLangWatchTracer("redaction-example");await tracer.withActiveSpan("DataRedactionExample", async (span) => { // user_profile might contain PII const userProfile = { id: "user_xyz", email: "test@example.com", name: "Sensitive Name" }; // Update the input to a redacted version const redactedInput = { user_id: userProfile.id, has_email: "email" in userProfile }; span.setInput("json", redactedInput); // Process data... const result = { status: "processed", user_id: userProfile.id }; span.setOutput("json", result); return result; // Actual function return can still be the full data});
Controlling how inputs and outputs are captured in LangWatch allows you to tailor the observability data to your specific needs. By using data capture configuration, explicit setInput() and setOutput() methods, and appropriate data formatting (especially "chat_messages" for conversations), you can ensure that your traces provide clear, relevant, and secure insights into your LLM application’s behavior.