技能 编程开发 Langfuse SDK 追踪最佳实践模式

Langfuse SDK 追踪最佳实践模式

v20260423
langfuse-sdk-patterns
本指南详细介绍了 Langfuse SDK 的生产级最佳实践模式。它涵盖了从单例客户端配置到使用 `observe` 包装器和 `startActiveObservation` 进行精细化控制等高级用法。帮助开发者实现健壮的 LLM 调用追踪、会话管理和错误安全记录,显著提升 AI 应用的可观测性。
获取技能
253 次下载
概览

Langfuse SDK Patterns

Overview

Production-quality patterns for the Langfuse SDK: singleton clients, the observe wrapper, startActiveObservation for nested traces, session tracking, graceful shutdown, and error-safe tracing.

Prerequisites

  • Completed langfuse-install-auth setup
  • Understanding of async/await patterns
  • For v4+: @langfuse/tracing, @langfuse/otel, @opentelemetry/sdk-node

Instructions

Pattern 1: Singleton Client with Graceful Shutdown

// src/lib/langfuse.ts -- single file, import everywhere
import { LangfuseClient } from "@langfuse/client";
import { LangfuseSpanProcessor } from "@langfuse/otel";
import { NodeSDK } from "@opentelemetry/sdk-node";

// Singleton client for prompts, datasets, scores
let client: LangfuseClient | null = null;
export function getLangfuseClient(): LangfuseClient {
  if (!client) {
    client = new LangfuseClient();
  }
  return client;
}

// One-time OTel setup (call at app entry point)
let sdk: NodeSDK | null = null;
export function initTracing(): NodeSDK {
  if (!sdk) {
    sdk = new NodeSDK({
      spanProcessors: [new LangfuseSpanProcessor()],
    });
    sdk.start();

    // Graceful shutdown on process exit
    const shutdown = async () => {
      await sdk?.shutdown();
      process.exit(0);
    };
    process.on("SIGTERM", shutdown);
    process.on("SIGINT", shutdown);
  }
  return sdk;
}

Legacy v3 singleton:

import { Langfuse } from "langfuse";

let instance: Langfuse | null = null;

export function getLangfuse(): Langfuse {
  if (!instance) {
    instance = new Langfuse({
      flushAt: 15,
      flushInterval: 10000,
    });
    process.on("beforeExit", () => instance?.shutdownAsync());
  }
  return instance;
}

Pattern 2: observe Wrapper for Existing Functions

The observe wrapper is the most ergonomic way to add tracing. It wraps any function and auto-creates a span.

import { observe, updateActiveObservation } from "@langfuse/tracing";

// Wrap existing functions -- no internal changes needed
const fetchUserProfile = observe(async (userId: string) => {
  updateActiveObservation({ input: { userId } });
  const profile = await db.users.findById(userId);
  updateActiveObservation({ output: { found: !!profile } });
  return profile;
});

// Mark LLM calls as generations
const summarize = observe(
  { name: "summarize-text", asType: "generation" },
  async (text: string) => {
    updateActiveObservation({ model: "gpt-4o-mini", input: text });
    const result = await openai.chat.completions.create({
      model: "gpt-4o-mini",
      messages: [{ role: "user", content: `Summarize: ${text}` }],
    });
    const output = result.choices[0].message.content;
    updateActiveObservation({
      output,
      usage: {
        promptTokens: result.usage?.prompt_tokens,
        completionTokens: result.usage?.completion_tokens,
      },
    });
    return output;
  }
);

// When called inside another observed function, spans auto-nest
const pipeline = observe(async (userId: string) => {
  const profile = await fetchUserProfile(userId);
  const summary = await summarize(profile.bio);
  return { profile, summary };
});

Pattern 3: startActiveObservation for Inline Control

Use when you need fine-grained control over observation lifecycle within a function:

import { startActiveObservation, updateActiveObservation } from "@langfuse/tracing";

async function processOrder(orderId: string) {
  return await startActiveObservation("process-order", async () => {
    updateActiveObservation({ input: { orderId } });

    // Nested spans are automatic
    const validated = await startActiveObservation("validate", async () => {
      const result = await validateOrder(orderId);
      updateActiveObservation({ output: { valid: result.valid } });
      return result;
    });

    if (!validated.valid) {
      updateActiveObservation({ output: { error: "validation failed" } });
      return { success: false };
    }

    // Generation span for LLM call
    const description = await startActiveObservation(
      { name: "generate-confirmation", asType: "generation" },
      async () => {
        updateActiveObservation({ model: "gpt-4o-mini" });
        const result = await generateConfirmation(orderId);
        updateActiveObservation({ output: result });
        return result;
      }
    );

    updateActiveObservation({ output: { success: true } });
    return { success: true, description };
  });
}

Pattern 4: Session and User Tracking

Link traces across conversation turns for user-level analytics:

// v4+: Set session/user via observation metadata
await startActiveObservation("chat-turn", async () => {
  updateActiveObservation({
    metadata: {
      sessionId: "session-abc-123",
      userId: "user-456",
    },
  });
  // All nested observations inherit this context
  await handleUserMessage(message);
});

// v3: Set directly on trace
const trace = langfuse.trace({
  name: "chat-turn",
  sessionId: "session-abc-123", // Groups traces into a session
  userId: "user-456",           // Links to user analytics
  input: { message },
});

Pattern 5: Error-Safe Tracing

Never let tracing failures break your application:

import { observe, updateActiveObservation } from "@langfuse/tracing";

const safeObserve = <T extends (...args: any[]) => Promise<any>>(
  name: string,
  fn: T
): T => {
  return (async (...args: Parameters<T>) => {
    try {
      return await observe({ name }, async () => {
        updateActiveObservation({ input: args });
        const result = await fn(...args);
        updateActiveObservation({ output: result });
        return result;
      })();
    } catch (tracingError) {
      // If tracing fails, still run the function
      console.warn(`Tracing error in ${name}:`, tracingError);
      return fn(...args);
    }
  }) as T;
};

// Usage -- function works even if Langfuse is down
const processRequest = safeObserve("process-request", async (input: string) => {
  return await callLLM(input);
});

Pattern 6: Legacy v3 -- Always End Spans

// Always use try/finally to ensure .end() is called
const span = trace.span({ name: "risky-operation", input: data });
try {
  const result = await riskyOperation(data);
  span.end({ output: result });
  return result;
} catch (error) {
  span.end({ level: "ERROR", statusMessage: String(error) });
  throw error;
}

Anti-Patterns to Avoid

Anti-Pattern Problem Correct Pattern
new Langfuse() per request Memory leaks, duplicate traces Singleton client
Awaiting flush in hot path Adds latency to every request Background flush, shutdown handler
Logging full request bodies Trace payloads too large Truncate/summarize inputs
Missing .end() on spans (v3) Spans show "in progress" forever Use try/finally or observe wrapper
Hardcoding API keys Security risk Environment variables only

Resources

Next Steps

For OpenAI/LangChain tracing examples, see langfuse-core-workflow-a.

信息
Category 编程开发
Name langfuse-sdk-patterns
版本 v20260423
大小 4.8KB
更新时间 2026-04-28
语言