Langfuse instrumentation methods based on OpenTelemetry
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.
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>;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"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("");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 IDsCorrelate 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);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');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;
}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');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);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();
});
});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("");
}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 differentThe 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 === workerTraceIdCreate 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'
);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');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'
);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 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
}
});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 contextsEnsure 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 typesDocument 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}`);
}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 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 cacheInstall with Tessl CLI
npx tessl i tessl/npm-langfuse--tracing