-
Notifications
You must be signed in to change notification settings - Fork 130
Description
What are you really trying to do?
When using otel if workflow has tracestate header like this:

exporting span from worker fails
Describe the bug
getting this errors:
TypeError: _a.serialize is not a function
at sdkSpanToOtlpSpan (/app/node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@opentelemetry/otlp-transformer/src/trace/internal.ts:44:33)
at <anonymous> (/app/node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@opentelemetry/otlp-transformer/src/trace/internal.ts:159:11)
at Array.map (<anonymous>)
at spanRecordsToResourceSpans (/app/node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@opentelemetry/otlp-transformer/src/trace/internal.ts:158:34)
at createExportTraceServiceRequest (/app/node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@opentelemetry/otlp-transformer/src/trace/internal.ts:110:20)
at Object.serializeRequest (/app/node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@opentelemetry/otlp-transformer/src/trace/json/trace.ts:26:52)
at OTLPExportDelegate.export (/app/node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@opentelemetry/otlp-exporter-base/src/otlp-export-delegate.ts:69:48)
at OTLPTraceExporter.export (/app/node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@opentelemetry/otlp-exporter-base/src/OTLPExporterBase.ts:32:26)
at Object.fn (/app/node_modules/.pnpm/@[email protected]_@[email protected]_@temporalio+c_49fd2775686fed0552daf86155185cd7/node_modules/@temporalio/interceptors-opentelemetry/src/worker/index.ts:86:24)
at <anonymous> (/app/node_modules/.pnpm/@[email protected]/node_modules/@temporalio/worker/src/worker.ts:1364:23)
at Array.map (<anonymous>)
at Worker.processSinkCalls (/app/node_modules/.pnpm/@[email protected]/node_modules/@temporalio/worker/src/worker.ts:1362:21)
at Worker.handleActivation (/app/node_modules/.pnpm/@[email protected]/node_modules/@temporalio/worker/src/worker.ts:1208:22)
Minimal Reproduction
reproducing this locally was bit hacky.
had to add this so tracestate was passed to temporal when invoking a workflow via js client.

then once the trace state is passed to workflow in the makeWorkflowExporter that's passed to Worker.create we get traceState
that is instance of Object and not an instance of expected TraceState. via debugger you can see this:

because of this then at some point when span is converted to an request to otel collector as there is traceState .serialize is called on it but this object has no methods only that _internalState
which is private prop of the TraceState see:

I suppose the trace state is moved across different JS contexts (from workflow code to host) and information that this object is instance of TraceState is lost and on the other end we just get Object.
Environment/Versions
- OS and processor: M1 Mac
- Temporal SDK Version: 1.11.7
- Are you using Docker or Kubernetes or building Temporal from source? no
hacky fix on my end was to do this:
const fixTemporalBug = (traceState: TraceState): TraceState => {
const res = new TraceState();
(res as any)._internalState = (traceState as any)._internalState;
return res;
};
// inlined version of makeWorkflowExporter that fixes traceState bug in Temporal
const makeWorkflowExporterFixed = (
exporter: SpanExporter,
resource: Resource,
): InjectedSink<OpenTelemetryWorkflowExporter> => {
return {
export: {
fn: (info: WorkflowInfo, spanData: SerializableSpan[]): void => {
const spans = spanData.map((serialized) => {
Object.assign(serialized.attributes, info);
// Spans are copied over from the isolate and are converted to ReadableSpan instances
const {
spanContext: { traceState, ...spanContext },
...rest
} = serialized;
return {
spanContext: (): SpanContext => ({
...spanContext,
traceState: traceState == null ? traceState : fixTemporalBug(traceState as any),
}),
resource,
...rest,
};
});
// Ignore the export result for simplicity
exporter.export(spans, () => undefined);
},
},
};
};