Apm rum js and apm node js does not have the same trace.id

my docker compose :


version: '3.8'

services:
  app:
    build: 
      context: .
      dockerfile: Dockerfile
    restart: always
    environment:
      DB_HOST: database
      DB_PORT: 5432
      DB_PASSWORD: postgres
      DB_USER: postgres
      DB_NAME: postgres
    networks:
      - myevershop
    depends_on:
      - database
    ports:
      - 3000:3000
  
  #The postgres database: 
  database:
    image: postgres:16
    restart: unless-stopped
    volumes:
      - postgres-data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: postgres
      POSTGRES_USER: postgres
      POSTGRES_DB: postgres
    ports:
      - "5432:5432"
    networks:
      - myevershop

networks:
  myevershop:
    name: MyEverShop
    driver: bridge

volumes:
  postgres-data:

here is my apm rum code implement at the app.jsx in
https://evershop.io/docs/development/getting-started/installation-guide


import { init as initApm } from '@elastic/apm-rum';
import PropTypes from 'prop-types';
import React, { useMemo, useState, useCallback, useEffect } from 'react';

const AppStateContext = React.createContext();
const AppContextDispatch = React.createContext();



export function AppProvider({ value, children }) {
  const [data, setData] = useState(value);
  const [fetching, setFetching] = useState(false);

  const apmrum = initApm({
    serviceName: 'frontend-node-js',
    serverUrl: '',
    serviceVersion: '',
    environment: 'dev',
    distributedTracingOrigins: ['http://localhost:3000', 'http://localhost:5432']
    // distributedTracingOrigins: ['http://localhost:3000','http://localhost:5432'],
  });

  const fetchPageData = useCallback(async (url) => {
    try {
      setFetching(true);
      const traceparent = apmrum.currentTransaction?.traceparent;
      const response = await fetch(url, {
        method: 'GET',
        headers: {
          'Content-Type': 'application/json',
          'elastic-apm-traceparent': traceparent || ''
        }
      });
      const dataResponse = await response.json();
      setData(dataResponse.eContext);
    } catch (error) {
      if (apmrum) {
        apmrum.captureError(error);
      } else {
        console.error(error);
      }
    } finally {
      setFetching(false);
    }
  }, [data]);

  useEffect(() => {
    window.onpopstate = async () => {
      try {
        const url = new URL(window.location.href, window.location.origin);
        url.searchParams.append('ajax', true);
        await fetchPageData(url);
      } catch (error) {
        if (apmrum) {
          apmrum.captureError(error);
        } else {
          console.error(error);
        }
      }
    };
  }, [fetchPageData]);

  const contextDispatchValue = useMemo(() => ({ setData, fetchPageData }), [fetchPageData]);
  const contextValue = useMemo(() => ({ ...data, fetching }), [data, fetching]);

  return (
    <AppContextDispatch.Provider value={contextDispatchValue}>
      <AppStateContext.Provider value={contextValue}>
        {children}
      </AppStateContext.Provider>
    </AppContextDispatch.Provider>
  );
}

AppProvider.propTypes = {
  children: PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.node),
    PropTypes.node
  ]).isRequired,
  value: PropTypes.object.isRequired
};

export const useAppState = () => React.useContext(AppStateContext);
export const useAppDispatch = () => React.useContext(AppContextDispatch);

and here is my backend code of app.js


/* eslint-disable no-underscore-dangle */
/* eslint-disable no-param-reassign */
/* eslint-disable global-require */

const express = require('express');
const cookieParser = require('cookie-parser');
const cors = require('cors');

// Include Elastic APM setup
const apm = require('elastic-apm-node').start({
  serviceName: 'backend-node-js',
  secretToken: '',
  serverUrl: '',
  environment: 'dev',
  distributedTracingOrigins: ['http://localhost:3000'],
//   distributedTracingOrigins: ['http://localhost:3000'],
//   captureHeaders: true,
//   distributedTracing: true
});

const { getModuleMiddlewares } = require('@evershop/evershop/src/lib/middleware');
const { getRoutes } = require('@evershop/evershop/src/lib/router/Router');
const { loadModuleRoutes } = require('@evershop/evershop/src/lib/router/loadModuleRoutes');
const { Handler } = require('@evershop/evershop/src/lib/middleware/Handler');
const { error } = require('@evershop/evershop/src/lib/log/logger');
const { getCoreModules } = require('./loadModules');
const { addDefaultMiddlewareFuncs } = require('./addDefaultMiddlewareFuncs');
const { getEnabledExtensions } = require('../extension');

module.exports.createApp = () => {
  /** Create express app */
  const app = express();

  // Enable trust proxy
  app.enable('trust proxy');

  // Load modules and initialize routes, components, and services
  const modules = getCoreModules();


  // Middleware to handle traceparent header
 app.use((req, res, next) => {
    const traceparent = req.headers['elastic-apm-traceparent'];
    if (traceparent) {
      apm.currentTransaction.setLabel('traceparent', traceparent);
    }
    next();
  });


  
  app.use(cors({
    origin: 'http://localhost:3000',
    methods: 'GET,POST,PUT,DELETE',
    allowedHeaders: 'Content-Type,Authorization, elastic-apm-traceparent',
    credentials: true,
  }));

  // Load routes and middleware functions
  modules.forEach((module) => {
    try {
      // Load middleware functions
      getModuleMiddlewares(module.path);
      // Load routes
      loadModuleRoutes(module.path);
    } catch (e) {
      error(e);
      process.exit(1); // Exit with error code
    }
  });

  /** Load extensions */
  const extensions = getEnabledExtensions();
  extensions.forEach((extension) => {
    try {
      // Load middleware functions
      getModuleMiddlewares(extension.resolve);
      // Load routes
      loadModuleRoutes(extension.resolve);
    } catch (e) {
      error(e);
      process.exit(1); // Exit with error code
    }
  });

  const routes = getRoutes();

  // Adding default middlewares
  addDefaultMiddlewareFuncs(app, routes);

  /** Hack for 'no route' case */
  routes.push({ id: 'noRoute', path: '/', method: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'] });

  routes.forEach((route) => {
    // app.all(route.path, Handler.middleware());
    route.method.forEach((method) => {
      switch (method.toUpperCase()) {
        case 'GET':
          app.get(route.path, Handler.middleware());
          break;
        case 'POST':
          app.post(route.path, Handler.middleware());
          break;
        case 'PUT':
          app.put(route.path, Handler.middleware());
          break;
        case 'DELETE':
          app.delete(route.path, Handler.middleware());
          break;
        case 'PATCH':
          app.patch(route.path, Handler.middleware());
          break;
        default:
          app.get(route.path, Handler.middleware());
          break;
      }
    });
  });

  app.use(Handler.middleware());

  return app;
};

Added elastic-stack-monitoring

Hi @kishorkumar

I think maybe is better to use follow the react integration docs if possible. Let me know if there is any impediment.

On the backend side I'd suggest to move the APM setup at the top of the file before any require to express or other packages like the documentation highlights.

For further troubleshooting you may attach a .har file from Chrome dev tools so we can inspect the headers of the requests made by the react app and check which is the trace context.

Cheers

i am not familiar with that frameworks it would be little hectic for me to implement hooks and routes things, this one was simple to implement.

i have also tried to move the code up in backend and frontend both but it does not seems to pass the trace.id, http-request type transaction is not getting labeled as all transactions should have and also the dependency is postgress db that is also not showing in service map.

How do attach the .har file here? there is no attachment option

Hi @kishorkumar

sorry for the late reply. HAR files are in JSON format so I think a good option would be to create a gist in GitHub with the contents and share the link here

Cheers,
David