Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The problem of combining the client and server agent - next.js #1370

Open
belimposition opened this issue May 11, 2023 · 3 comments
Open

The problem of combining the client and server agent - next.js #1370

belimposition opened this issue May 11, 2023 · 3 comments

Comments

@belimposition
Copy link

belimposition commented May 11, 2023

Hi. I use apm in my project to build project metrics. I use elastic-apm-node for the server part and @elastic/apm-rum for the client part.

In my next.js project, I initialize elastic-apm-node as follows in server.js

const serverApm = require('elastic-apm-node').start({
  serviceName: `${CORE_SETTINGS.APM_SERVICE_NAME}-server`,
  serverUrl: CORE_SETTINGS.APM_SERVER_URL,
  environment: CORE_SETTINGS.APM_ENVIRONMENT,
  active: CORE_SETTINGS.APM_USE,
});

in the client part another agent is initialized, so

...
const options = {
  serviceName: 'test-web-vitals',
  serverUrl: getConfig().publicRuntimeConfig.APM_SERVER_URL,
  environment: getConfig().publicRuntimeConfig.APM_ENVIRONMENT,
  serviceVersion: packageInfo.version,
  ignoreTransactions: [/.*_next.*/, /.*push-measures.*/],
  active: true,
};

Then I put this client instance into the redux-saga context and use it where I need it.
The problem is that I don't know how to merge the client and server entries into a shared one. Right now I have them separated.

In documentation it says that in order to merge they need to specify additional keys

elasticApm.init({
  serviceName: 'my-frontend-app', // Name of your frontend app
  serverUrl: 'https://example.com:8200', // APM Server host
  pageLoadTraceId: '${transaction.traceId}',
  pageLoadSpanId: '${transaction.ensureParentId()}',
  pageLoadSampled: ${transaction.sampled}
})

https://www.elastic.co/guide/en/apm/agent/nodejs/current/distributed-tracing.html#tracing-rum-correlation

But I don't understand where it's coming from, for

  pageLoadTraceId: '${transaction.traceId}',
  pageLoadSpanId: '${transaction.ensureParentId()}',
  pageLoadSampled: '${transaction.sampled}

Please help - how to correctly merge client and server records

@devcorpio
Copy link
Contributor

devcorpio commented May 12, 2023

Hi @belimposition,

Thanks for the details provided!

To make sure the web browser’s page load appears as the root of the trace of your backend transaction, you should do as indicated in the doc I linked below:

https://www.elastic.co/guide/en/apm/agent/rum-js/current/distributed-tracing-guide.html#dynamic-html-doc

But I don't understand where it's coming from, for

Since this works when generating HTML from the server, those values come from the backend APM agent you are using (node.js in your case)

Example of this is from a Node.js webserver:

// server.js

const apm = require('elastic-apm-node').start({
    serviceName: 'your-server-service-name',
    serverUrl: 'http://localhost:8200'
  });
  const app = require('express')()
  app.set('view engine', 'ejs');
  app.get('/your-route', function (req, res) {
      const transaction = apm.currentTransaction
      res.render('your-template.ejs', {
          pageLoadTraceId: `${transaction.traceId}`,
          pageLoadSpanId: `${transaction.ensureParentId()}`,
          pageLoadSampled: `${transaction.sampled}`
      })
  })
  
  app.listen("3000", () => {
      console.log("listening port 3000")
  })

// your-template.ejs
// ejs template that server.js uses to generate dynamic html
<!DOCTYPE html>
<html>
    <head>
        <script src="https://unpkg.com/@elastic/[email protected]/dist/bundles/elastic-apm-rum.umd.min.js" crossorigin></script>

        </head>
        <body>
                Hello, this is just an example.
<script>
            elasticApm.init({
                    serviceName: 'your-frontend-service',
                    serverUrl: 'http://localhost:8200',
                    pageLoadTraceId: '<%= pageLoadTraceId %>',
                    pageLoadSpanId: '<%= pageLoadSpanId %>',
                    pageLoadSampled: <%= pageLoadSampled %>
                })
        </script>
    </body>
</html>

To execute the example you will need to have the following folder structure:

Screenshot 2023-05-12 at 12 33 25

Make sure you install express, elastic-apm-node, and ejs dependencies.

Recording where you will see how the HTML is generated with those IDs:

example-node-js-dynamic-html.mov

--

If from your client-side you are performing http requests (XHR, Fetch) to your backend-side and you want the RUM agent to add the trace headers automatically, please read this: https://www.elastic.co/guide/en/apm/agent/rum-js/current/distributed-tracing-guide.html#distributed-tracing-guide

Let me know if this helps you.

Thanks,
Alberto

@belimposition
Copy link
Author

Thanks, I need some time to check and try it, I will write here next week

@belimposition
Copy link
Author

@devcorpio
Hi, thanks for the previous reply, I have moved on
My application has an authorization request that I make on the server side

On the client side, I make other requests based on the user data and the page to which he went
I tried to do the above and the ejs template killed my react components, instead it returned ejs

So I did something different and returned pageLoadTraceId, pageLoadSpanId, pageLoadSampled
and on the client I initialize APM with these transactional data

In the end, integration seems to be successful, but there's no data
I don't see authorization request in my js client in kibana, I only see what's happening on the client
I was expecting to see them somewhere here when I go to my page

https://monosnap.com/file/VBxMeUBdCs2NpiyF4q1JKqLfXagbsP

What am I doing wrong? I passed the transactional data, it's something like this:

pageLoadTraceId: b64398208168d342bbd973a43043e34c 
pageLoadSpanId: f3dad00b21917d1c
pageLoadSampled: true

initialized - events go away

the question still stands - how to merge client and server metrics?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants