Exchanges overwriting request headers #3388
-
I have written an exchange to implement opentelemetry instrumentation. The problem I have is that the traceparent header is not being set on outgoing requests. CORS policy allows the traceparent header. When I log the fetchOptions being set by my exchange in appendHeaders the header is correctly set. Here is the code: import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
import { ZoneContextManager } from '@opentelemetry/context-zone';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
import { W3CTraceContextPropagator } from '@opentelemetry/core';
import { Resource } from '@opentelemetry/resources';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
import { map, pipe, Source } from 'wonka';
import { makeOperation, Exchange, ExchangeInput, ExchangeIO, Operation, OperationResult } from '@urql/core';
import { ROOT_CONTEXT, Span, SpanStatusCode, TextMapSetter, trace } from '@opentelemetry/api';
const appendHeaders = (operation: Operation, headers) => {
const fetchOptions =
typeof operation.context.fetchOptions === 'function'
? operation.context.fetchOptions()
: operation.context.fetchOptions || {};
const op = makeOperation(operation.kind, operation, {
...operation.context,
fetchOptions: {
...fetchOptions,
headers: {
...fetchOptions.headers,
...headers,
},
},
});
console.log('fetchOptions', op);
return op;
};
const myTextMapSetter: TextMapSetter = {
set: (carrier: Operation, key: string, value: string) => {
appendHeaders(carrier, { [key]: value });
},
};
const httpTraceContext = new W3CTraceContextPropagator();
const tracingExchange: Exchange = ({ forward }: ExchangeInput): ExchangeIO => {
return (ops$: Source<Operation>): Source<OperationResult> => {
return pipe(
ops$,
map((operation) => {
const tracer = trace.getTracer('urql-exchange');
operation.context.span = tracer.startSpan(`urql-${operation.kind}`, {
attributes: {
operationName: operation.key,
},
});
const context = trace.setSpanContext(ROOT_CONTEXT, operation.context.span.spanContext());
httpTraceContext.inject(context, operation, myTextMapSetter);
return operation;
}),
forward,
map((result: OperationResult) => {
const span: Span | undefined = result.operation.context.span;
if (span) {
if (result.error) {
span.setStatus({ code: SpanStatusCode.ERROR, message: result.error.message });
}
span.end();
}
return result;
})
);
};
};
const tracerProvider = new WebTracerProvider({
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'svtplay-web',
}),
});
tracerProvider.addSpanProcessor(
new SimpleSpanProcessor(
new OTLPTraceExporter({
url: 'https://otlp-collector.app.borealis.svt.se/v1/traces',
})
)
);
tracerProvider.register({
contextManager: new ZoneContextManager(),
propagator: new W3CTraceContextPropagator(),
});
export default tracingExchange; here is the order of exchanges being used: const config: ClientOptions = {
url,
fetchOptions: extraSettings,
fetch: urqlFetch,
preferGetMethod: true,
exchanges: [
dedupExchange,
errorExchange({
onError: handleError,
}),
cacheExchange,
tracingExchange,
persistedFetchExchange({}) as any,
fetchExchange,
],
}; |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
I quickly created a preproduction but without all the extra exchanges, this worked correctly for me... I however did notice that the A few additional pointers here
|
Beta Was this translation helpful? Give feedback.
-
Thanks, I changed appendHeaders to mutate instead of creating an operation |
Beta Was this translation helpful? Give feedback.
I quickly created a preproduction but without all the extra exchanges, this worked correctly for me... I however did notice that the
const context = trace.setSpanContext(ROOT_CONTEXT, operation.context.span.spanContext());
is creating a new operation inappendHeaders
but thatoperation
is never returned to the exchange so either you would resort to mutating in theappendHeaders
method or you return the operation created there to the exchange.A few additional pointers here
dedupExchange
isn't needed anymore in the latest urql versions (are you using those)errorExchange
after the cache should catch all t…