CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-langfuse--tracing

Langfuse instrumentation methods based on OpenTelemetry

Overview
Eval results
Files

trace-id-generation.mddocs/

Trace ID Generation

The createTraceId() function provides utilities for generating OpenTelemetry-compatible trace IDs with support for both deterministic (seeded) and random generation. This is particularly useful for correlating external IDs with Langfuse traces.

Core Function

createTraceId

Creates a trace ID for OpenTelemetry spans with optional deterministic generation.

/**
 * Creates a trace ID for OpenTelemetry spans.
 *
 * @param seed - A seed string for deterministic trace ID generation.
 *               If provided (non-empty), the same seed will always generate the same trace ID.
 *               If empty or falsy, generates a random trace ID.
 *
 *               Using a seed is especially useful when trying to correlate external,
 *               non-W3C compliant IDs with Langfuse trace IDs. This allows you to later
 *               have a method available for scoring the Langfuse trace given only the
 *               external ID by regenerating the same trace ID from the external ID.
 *
 * @returns A Promise that resolves to a 32-character lowercase hexadecimal string
 *          suitable for use as an OpenTelemetry trace ID.
 */
async function createTraceId(seed?: string): Promise<string>;

Output Format

The function returns a 32-character lowercase hexadecimal string that conforms to the OpenTelemetry trace ID format:

// Example outputs
"a1b2c3d4e5f67890a1b2c3d4e5f67890"  // Random
"f8e7d6c5b4a3928170f8e7d6c5b4a392"  // From seed "my-session-123"
"c4b3a29180f7e6d5c4b3a29180f7e6d5"  // From seed "user-456"

Random Trace IDs

Generate unique, random trace IDs for each operation.

import { createTraceId } from '@langfuse/tracing';

// Generate random trace IDs
const traceId1 = await createTraceId();
const traceId2 = await createTraceId();

console.log(traceId1); // "a1b2c3d4e5f67890a1b2c3d4e5f67890"
console.log(traceId2); // "f8e7d6c5b4a3928170f8e7d6c5b4a392"
console.log(traceId1 === traceId2); // false - different IDs

// Alternative: empty string also generates random ID
const randomId = await createTraceId("");

Deterministic Trace IDs

Generate consistent trace IDs from a seed string for correlation purposes.

import { createTraceId } from '@langfuse/tracing';

// Same seed always produces the same trace ID
const traceId1 = await createTraceId("my-session-123");
const traceId2 = await createTraceId("my-session-123");

console.log(traceId1 === traceId2); // true - identical IDs

// Different seeds produce different IDs
const sessionA = await createTraceId("session-a");
const sessionB = await createTraceId("session-b");

console.log(sessionA === sessionB); // false - different IDs

Use Cases

External ID Correlation

Correlate external system IDs with Langfuse traces.

import { createTraceId, startObservation } from '@langfuse/tracing';

async function processExternalRequest(externalRequestId: string) {
  // Generate deterministic trace ID from external ID
  const traceId = await createTraceId(externalRequestId);

  // Create observation with custom trace ID
  const span = startObservation('process-request', {
    input: { externalRequestId }
  }, {
    parentSpanContext: {
      traceId: traceId,
      spanId: '0123456789abcdef',
      traceFlags: 1
    }
  });

  // Process request...
  const result = await handleRequest(externalRequestId);

  span.update({ output: result });
  span.end();

  return result;
}

// Later, score the trace using the external ID
async function scoreTrace(externalRequestId: string, score: number) {
  // Regenerate the same trace ID
  const traceId = await createTraceId(externalRequestId);

  // Use trace ID to find and score the Langfuse trace
  await langfuseClient.score({
    traceId: traceId,
    name: 'quality',
    value: score
  });
}

// Process request
await processExternalRequest('ext-12345-67890');

// Later, score without storing the trace ID
await scoreTrace('ext-12345-67890', 0.95);

Session-Based Tracing

Group operations by session using deterministic trace IDs.

import { createTraceId, startObservation } from '@langfuse/tracing';

async function trackUserSession(sessionId: string, action: string) {
  // All operations in the same session share the same trace ID
  const traceId = await createTraceId(sessionId);

  const observation = startObservation(action, {
    input: { sessionId, action }
  }, {
    parentSpanContext: {
      traceId: traceId,
      spanId: crypto.randomUUID().replace(/-/g, '').slice(0, 16),
      traceFlags: 1
    }
  });

  // Perform action...
  const result = await performAction(action);

  observation.update({ output: result });
  observation.end();

  return result;
}

// All these operations share the same trace ID
const sessionId = 'session-abc-123';
await trackUserSession(sessionId, 'login');
await trackUserSession(sessionId, 'view-product');
await trackUserSession(sessionId, 'add-to-cart');
await trackUserSession(sessionId, 'checkout');

Distributed Tracing

Propagate deterministic trace IDs across service boundaries.

import { createTraceId, startObservation } from '@langfuse/tracing';

// Service A: API Gateway
async function handleApiRequest(requestId: string) {
  const traceId = await createTraceId(requestId);

  const span = startObservation('api-gateway', {
    input: { requestId }
  }, {
    parentSpanContext: {
      traceId: traceId,
      spanId: '0123456789abcdef',
      traceFlags: 1
    }
  });

  // Call downstream service with request ID
  const result = await fetch('https://service-b.com/process', {
    method: 'POST',
    headers: {
      'X-Request-Id': requestId,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ data: 'value' })
  });

  span.update({ output: result });
  span.end();

  return result;
}

// Service B: Processing Service
async function processRequest(req: Request) {
  const requestId = req.headers.get('X-Request-Id');

  // Regenerate the same trace ID from request ID
  const traceId = await createTraceId(requestId);

  const span = startObservation('process-request', {
    input: { requestId }
  }, {
    parentSpanContext: {
      traceId: traceId,
      spanId: '123456789abcdef0',
      traceFlags: 1
    }
  });

  // Process...
  const result = await process();

  span.update({ output: result });
  span.end();

  return result;
}

Conversation Threading

Link multiple conversation turns using deterministic trace IDs.

import { createTraceId, startObservation } from '@langfuse/tracing';

async function handleConversationTurn(
  conversationId: string,
  turnNumber: number,
  userMessage: string
) {
  // All turns in conversation share the same trace ID
  const traceId = await createTraceId(conversationId);

  const generation = startObservation(`turn-${turnNumber}`, {
    input: { turnNumber, userMessage },
    model: 'gpt-4'
  }, {
    asType: 'generation',
    parentSpanContext: {
      traceId: traceId,
      spanId: crypto.randomUUID().replace(/-/g, '').slice(0, 16),
      traceFlags: 1
    }
  });

  const response = await llm.chat([
    { role: 'user', content: userMessage }
  ]);

  generation.update({
    output: response,
    usageDetails: { totalTokens: response.usage.totalTokens }
  });
  generation.end();

  return response;
}

// All turns in this conversation share the same trace
const conversationId = 'conv-abc-123';
await handleConversationTurn(conversationId, 1, 'What is AI?');
await handleConversationTurn(conversationId, 2, 'Tell me more');
await handleConversationTurn(conversationId, 3, 'Give an example');

Batch Processing

Group batch items under a single trace using deterministic IDs.

import { createTraceId, startObservation } from '@langfuse/tracing';

async function processBatch(batchId: string, items: any[]) {
  // All items in batch share the same trace ID
  const traceId = await createTraceId(batchId);

  for (const [index, item] of items.entries()) {
    const observation = startObservation(`item-${index}`, {
      input: { batchId, itemId: item.id, index }
    }, {
      parentSpanContext: {
        traceId: traceId,
        spanId: crypto.randomUUID().replace(/-/g, '').slice(0, 16),
        traceFlags: 1
      }
    });

    const result = await processItem(item);

    observation.update({ output: result });
    observation.end();
  }
}

// All items in this batch share the same trace ID
await processBatch('batch-2024-01-01', items);

Testing and Debugging

Generate predictable trace IDs for testing.

import { createTraceId, startObservation } from '@langfuse/tracing';

describe('Tracing tests', () => {
  it('should generate consistent trace IDs', async () => {
    const seed = 'test-seed-123';

    const traceId1 = await createTraceId(seed);
    const traceId2 = await createTraceId(seed);

    expect(traceId1).toBe(traceId2);
  });

  it('should create observations with custom trace ID', async () => {
    const testTraceId = await createTraceId('test-trace');

    const observation = startObservation('test-operation', {
      input: { test: true }
    }, {
      parentSpanContext: {
        traceId: testTraceId,
        spanId: '0123456789abcdef',
        traceFlags: 1
      }
    });

    expect(observation.traceId).toBe(testTraceId);
    observation.end();
  });
});

Implementation Details

SHA-256 Hashing

Deterministic trace IDs are generated using SHA-256 hashing:

// Internal implementation (simplified)
async function createTraceId(seed?: string): Promise<string> {
  if (seed) {
    // Encode seed as UTF-8
    const data = new TextEncoder().encode(seed);

    // Generate SHA-256 hash
    const hashBuffer = await crypto.subtle.digest("SHA-256", data);

    // Convert to hex string and take first 32 characters
    const hashArray = new Uint8Array(hashBuffer);
    const hexString = Array.from(hashArray)
      .map(b => b.toString(16).padStart(2, "0"))
      .join("");

    return hexString.slice(0, 32);
  }

  // Random generation using crypto.getRandomValues
  const randomValues = crypto.getRandomValues(new Uint8Array(16));
  return Array.from(randomValues)
    .map(b => b.toString(16).padStart(2, "0"))
    .join("");
}

Collision Probability

Random trace IDs use 128 bits of entropy, making collisions extremely unlikely:

// Collision probability calculation
// 32 hex characters = 128 bits = 2^128 possible values
// Probability of collision: ~1 in 3.4 × 10^38

const randomId1 = await createTraceId();
const randomId2 = await createTraceId();
// Virtually guaranteed to be different

Seed Consistency

The same seed always produces the same trace ID across all platforms:

// These will always produce identical trace IDs
const nodeTraceId = await createTraceId("my-seed"); // Node.js
const browserTraceId = await createTraceId("my-seed"); // Browser
const workerTraceId = await createTraceId("my-seed"); // Worker

// All equal: nodeTraceId === browserTraceId === workerTraceId

Advanced Patterns

Hierarchical IDs

Create hierarchical trace ID structure using seed composition.

async function createHierarchicalTrace(
  organizationId: string,
  projectId: string,
  requestId: string
) {
  // Compose seed from hierarchy
  const seed = `${organizationId}:${projectId}:${requestId}`;
  const traceId = await createTraceId(seed);

  // All operations with same hierarchy share trace ID
  return traceId;
}

const traceId = await createHierarchicalTrace(
  'org-123',
  'proj-456',
  'req-789'
);

Time-Bucketed Traces

Group operations by time windows using date-based seeds.

async function createTimeBucketedTrace(
  userId: string,
  bucketSizeMs: number = 3600000 // 1 hour
) {
  const now = Date.now();
  const bucket = Math.floor(now / bucketSizeMs);

  // All operations in same time bucket share trace ID
  const seed = `${userId}:${bucket}`;
  return await createTraceId(seed);
}

// All operations in same hour share trace ID
const traceId = await createTimeBucketedTrace('user-123');

Multi-Tenant Isolation

Ensure trace ID uniqueness per tenant.

async function createTenantTrace(
  tenantId: string,
  resourceType: string,
  resourceId: string
) {
  // Include tenant in seed for isolation
  const seed = `tenant:${tenantId}:${resourceType}:${resourceId}`;
  return await createTraceId(seed);
}

const traceId = await createTenantTrace(
  'tenant-abc',
  'order',
  'order-123'
);

Best Practices

Use Deterministic IDs for Correlation

Use seeded generation when you need to correlate with external systems.

// Good: Deterministic for correlation
const externalId = 'payment-12345';
const traceId = await createTraceId(externalId);

// Later, retrieve trace using external ID
const sameTraceId = await createTraceId(externalId);

Use Random IDs for Independence

Use random generation for independent, uncorrelated operations.

// Good: Random for independent operations
const traceId = await createTraceId();

const observation = startObservation('one-time-operation', {
  input: { data: 'value' }
}, {
  parentSpanContext: {
    traceId: traceId,
    spanId: '0123456789abcdef',
    traceFlags: 1
  }
});

Include Context in Seeds

Make seeds descriptive and include relevant context.

// Good: Descriptive seed with context
const seed = `user:${userId}:session:${sessionId}:request:${requestId}`;
const traceId = await createTraceId(seed);

// Avoid: Generic seed
const traceId = await createTraceId(userId); // May collide across contexts

Validate Seed Uniqueness

Ensure seeds are unique enough to avoid collisions.

// Good: Unique per operation
const seed = `${resourceType}:${resourceId}:${timestamp}`;

// Risky: May collide
const seed = resourceId; // If resourceId repeats across types

Document Seed Format

Document seed formats for consistent regeneration.

/**
 * Creates a trace ID for user session operations.
 *
 * Seed format: "session:{sessionId}:user:{userId}"
 *
 * @example
 * const traceId = await createSessionTraceId('sess-123', 'user-456');
 * // Can regenerate later using same IDs
 */
async function createSessionTraceId(
  sessionId: string,
  userId: string
): Promise<string> {
  return await createTraceId(`session:${sessionId}:user:${userId}`);
}

Handle Async Nature

Remember that createTraceId() is async and returns a Promise.

// Good: Await the result
const traceId = await createTraceId("my-seed");

// Wrong: Missing await
const traceId = createTraceId("my-seed"); // Promise object, not string

// Good: Use in async context
async function createTrace() {
  const traceId = await createTraceId();
  return traceId;
}

Cache Generated IDs

Cache deterministic trace IDs to avoid repeated hashing.

const traceIdCache = new Map<string, string>();

async function getCachedTraceId(seed: string): Promise<string> {
  if (traceIdCache.has(seed)) {
    return traceIdCache.get(seed)!;
  }

  const traceId = await createTraceId(seed);
  traceIdCache.set(seed, traceId);
  return traceId;
}

// Faster for repeated calls with same seed
const traceId1 = await getCachedTraceId('session-123');
const traceId2 = await getCachedTraceId('session-123'); // From cache

Install with Tessl CLI

npx tessl i tessl/npm-langfuse--tracing

docs

active-observations.md

attribute-creation.md

context-management.md

index.md

manual-observations.md

observation-types.md

observe-decorator.md

otel-span-attributes.md

trace-id-generation.md

tracer-provider.md

tile.json