Closed DuudeXX8 closed 2 weeks ago
Hi @DuudeXX8
The beforeSend
function is called for each item before sending it to the collector.
Means if a new trace is send via pushTrace()
the beforeSend
hook it is called again.
Can you elaborate on your use case? Like why do you want to manually mutate the traceID in a span? Each span already contains the parent traceID.
I also do would not recommend to re-initialize the whole Faro if e. g. the traceId changes. This can lead to problems in the worst and but puts a lot of extra load on your users browsers.
@codecapitano hi and thanks for the answer. Could you please show me a example how to send traceId
via pushTrace
?
About my case why I need to mutate traceId
, this need for track current process start and end phases. When traceId
is not the same we can't track when process start and when it ends. If you have solution for track processes start/end in application without mutate traceId
please share with me. But I can't find one.
It looks like you use Faro + Otel in the browser?
If yes, there are two ways you can connect faro with otel.
Either you add the web-tracing package which adds otel xhr and fetch instrumentation under the hood. It can be configured to you needs.
Or if you need it more flexible you can connect Faro and otel like this:
I'm not using opentelemetry only grafana and related packages. Grafana packages list and versions.
"@grafana/faro-react": "^1.7.3", "@grafana/faro-web-sdk": "^1.7.3", "@grafana/faro-web-tracing": "^1.7.3",
My grafana configuration
serviceName: string,
traceId,
options: IFaroOptionalParams<H>,
) => {
const {
collectorURL, version, history, Route,
} = options ?? {};
return {
url: collectorURL,
app: {
// The version of the application.
version,
// The name of the application, combining a static prefix with the service name.
name: myurl,
// The deployment environment, defaulting to PREPROD if not specified.
environment: process.env.DEPLOY_ENV || EEnv.PREPROD,
},
dedupe: true,
instrumentations: [
// Mandatory, overwriting the instrumentations array would cause the default instrumentations to be omitted.
...getWebInstrumentations(),
// Initialization of the tracing package.
// This package is optional because it increases the bundle size noticeably. Only add it if you want tracing data.
new TracingInstrumentation(),
// React Integration for Faro, including React Router v6 dependencies.
new ReactIntegration({
router: createReactRouterV5Options({
history, // the history object used by react-router
Route, // Route component imported from react-router package
}),
}),
],
isolate: true,
internalLoggerLevel: InternalLoggerLevel.INFO,
beforeSend: (item: TransportItem<APIEvent>) => {
const getTraceId = (traceIdFromMap: string | Uint8Array): string | Uint8Array => {
if (traceId) {
return traceId as string;
}
return traceIdFromMap;
};
if (item.type === 'trace' && 'resourceSpans' in item.payload) {
const updatedItem = {
...item,
payload: {
...item.payload,
resourceSpans: item.payload.resourceSpans.map((resourceSpan) => ({
...resourceSpan,
scopeSpans: resourceSpan.scopeSpans.map((scopeSpan) => ({
...scopeSpan,
spans: scopeSpan.spans.map((span) => ({
...span,
traceId: getTraceId(span.traceId),
})),
})),
})),
},
};
return updatedItem;
}
return item;
},
};
};```
And then use this hook `useGrafanaFaro(SERVICE_NAME, uniqueKeyForGrafana);` in each microfrontend applications.
Question: Is this possible to somehow change `traceId` which grafana sends in each payload to collect url?
I tried almost everything. This solution also does not work.
```const tracer = trace.getTracer('MyApp-Tracer');
const createNewTrace = () => {
const span = tracer.startSpan('New Trace Span', { root: true });
context.with(trace.setSpan(context.active(), span), () => {
let z = trace.getActiveSpan()?.spanContext()?.traceId;
z = traceId;
span.end();
});
};```
Any ideas?
The "@grafana/faro-web-tracing": "^1.7.3",
uses otel under the hood and creates otel traces.
Where does the new traceID which you want add to add come from?
What is your goal with that approach? Like when do you want to change the traceId or start a new trace?
What may work (untested) is the following: Since you use the web-tracing package you have otel available
const otel = faro.api.getOTEL();
Create a new span context
const newCtx = otel?.trace.setSpanContext(otel.context.active(), {
traceId: '123',
spanId: '456',
traceFlags: 1 /* or 0 depending if you use sampling or not */,
});
Set your new span context as the new context
otel?.context.with(newCtx, () => {
// ..
});
Maybe this helps?
Where does the new traceID which you want add to add come from?
traceId come from each microfrontend app which use this hook. useGrafanaFaro(SERVICE_NAME, grafanaKey);
. This grafanaKey
is the traceId
which I need to put inside of payload. I run this hook in the main parent component.
When do you want to change the traceId
or start a new trace?
When user will be in a different route or user information will change I run this code for update my grafanaKey
. In component, I run this code below.
useEffect(() => {
setGrafanaKey(uuidv4().replace(/-/g, ''));
}, [customer?.data?.mobilePhone]);
This traceId
come to my custom hook as argument
export const useGrafanaFaro = (
serviceName: string,
traceId?: string,
options: IFaroOptionalParams<typeof useHistory> = {
version: DEFAULT_VERSION,
collectorURL: DEFAULT_COLLECTOR_URL,
},
) => {
And I put in to my faro config
const faroConfig = {
// other configuration
beforeSend: (item: TransportItem<APIEvent>): TransportItem<APIEvent> => {
const getTraceId = (
traceIdFromMap: string | Uint8Array
): string | Uint8Array => {
if (traceId) { // if trace id exists use it
return traceId as string;
}
return traceIdFromMap; // if it's not, use default traceId
};
if (item?.type === 'trace' && 'resourceSpans' in item.payload) {
updatedItem = {
...item,
payload: {
...item.payload,
resourceSpans: item.payload.resourceSpans.map((resourceSpan) => ({
...resourceSpan,
scopeSpans: resourceSpan.scopeSpans.map((scopeSpan) => ({
...scopeSpan,
spans: scopeSpan.spans.map((span) => ({
...span,
traceId: getTraceId(span.traceId), // Here my
})),
})),
})),
},
};
return updatedItem;
}
return item;
},
And in useEffect
inside of useGrafanaFaro
hook, I initialize my faro.
export const useGrafanaFaro = (
serviceName: string,
traceId?: string,
options: IFaroOptionalParams<typeof useHistory> = {
version: DEFAULT_VERSION,
collectorURL: DEFAULT_COLLECTOR_URL,
},
) => {
const faroConfig = {
// initial configuration
}
useEffect(
/** Initialize Grafana Faro with the specified service name and options. */
() => {
initializeFaro(faroConfig);
},
[serviceName, options, traceId, faroConfig],
);
Here's full map how it works in application.
Also tried code which you provide. But every time in console I didn't see any changes. Same (first) traceId
was sent to collect url in payload. It does not change.
const otel = faro?.api?.getOTEL();
useEffect(
/** Initialize Grafana Faro with the specified service name and options. */
() => {
const newCtx = otel?.trace.setSpanContext(otel.context.active(), {
traceId,
spanId: '123',
traceFlags: 0,
});
otel?.context.with(newCtx, () => {});
initializeFaro(faroConfig);
},
[serviceName, options, traceId, faroConfig],
);
Please help 😫
I need send traceId to beforeSend method. To get track whole process from start to end. But each time it get's first value which I applied. It does not update. Even if traceId value does change multiple times it does not update traceId. Always use initial traceId. How can I make this
initializeFaro
update when new traceId sended to this hook?I try almost everything. Any help would be appreciated.