elastic / apm-agent-rum-js

https://www.elastic.co/guide/en/apm/agent/rum-js/current/index.html
MIT License
275 stars 133 forks source link

Multiple instances, micro frontends context #1197

Open brevalessio24 opened 2 years ago

brevalessio24 commented 2 years ago

I have an application shell that loads and mounts X different micro frontends application. Each of them is indipendently deployed and has its own .map file.

My desiderata would be do have X instances of the apm, with different serviceName, working together. I would also like to avoid having the code bundled X times in each micro frontend.

I tried to declare the dependency as external in webpack config, loading the dependency at runtime from unpckg (umd), using System.js and import-maps, but it doesn't seem to work.

Is what I am trying to do achievable somehow? Are there projects in a similar situation, how did they solve the problem?

devcorpio commented 2 years ago

Hi @brevalessio24,

Thanks for reaching out!

The current RUM implementation doesn't provide support for multiple services with a single agent instance. And if you want multiple instances they should be in different pages (iframes for instance) loading each time the RUM agent.

What we suggested in similar scenarios is to use transaction.addLabels from the transaction API. What this api allows you is to add labels to transactions and filter based on these labels in the UI.

How to use that API will depend on how the transaction has been created. On one hand you have the ones created because of the automatic instrumentation like user interactions, page-load, xhr/fetch requests, errors, etc and on the other hand the ones manually created (custom transactions)

The former gives you the chance of adding a label leveraging the "end" event:

apm.observe('transaction:end',  (transaction) => {
  // e.g. look for information in the transaction that can help you to distinguish which application triggered this
  // and based on that create a label

  transaction.addLabels({ 'custom-label': 'custom-value' })
})

The latter gives more control since you create the transactions manually

const transaction = apm.startTransaction('custom transaction', 'custom')
transaction.addLabels({ 'custom-label': 'custom-value' })
// etc

Thanks, Alberto

brevalessio24 commented 2 years ago

Hi Alberto, thank you for the response. We guessed that labels can help distinguish transactions from different souces, but our main concern for the moment is to link the, eg. runtime error, to the correct .map.js file.

Since we have app1.js, app2.js, each has its own .map.js file and we didn't find a method to specify which map file to pick each time. Could this also be achievable with labels somehow?

devcorpio commented 2 years ago

Hi @brevalessio24,

The Create and upload source maps (RUM) guide indicates in the Kibana section that it is possible to upload more than one sourcemap for your application.

Does the guide help you?

Thanks, Alberto

devcorpio commented 2 years ago

Hi @brevalessio24 ,

Have you find a workable-solution for this?

Thanks, Alberto

brevalessio24 commented 2 years ago

Hi @devcorpio , we tried to follow the guide but it didn't work.

We uploaded an app2.js.map file for App2 with the shared service_name and bundle_filepath pointing to /app2.js. Do you know how the match between the .js file and the .js.map file happens? We supposed it is using service_name, bundle_filepath and service_version to create a match but maybe it is not enough in case of multiple map files.

Thanks, Alessio

devcorpio commented 2 years ago

Hi @brevalessio24,

Are you able to see the sourcemaps uploaded in Kibana?

One way to see them is openinghttp://localhost:5601/api/apm/sourcemaps on your browser (or using the domain you have configured)

I have done tests locally, and I can upload several sourcemaps for the same application:

Screenshot 2022-04-29 at 13 21 01

Main details of the first one:

Screenshot 2022-04-29 at 13 21 36

and main details of the second one:

Screenshot 2022-04-29 at 13 22 40

This test uploads a sourcemap, maybe this can help you too.

Could you share the code that you are using for uploading the sourcemaps?

Btw, If you think that having a look at the kibana code might also help you, then you can also check this and this

Hope this helps.

Thanks, Alberto

brevalessio24 commented 2 years ago

Hi @devcorpio, the upload is not the problem, on kibana we find the uploads done correctly

{
"artifacts": [
  {
    "type": "sourcemap",
    "identifier": "fe-mf-notifications-0.2.0-unstable.10",
    "relative_url": "/api/fleet/artifacts/fe-mf-notifications-0.2.0-unstable.10/7eb4da36b03b96c134ca6d2fad8eeb2b04592733d65f2a66bd9a55e5d0dec55e",
    "body": {
    "serviceName": "fe-mf-notifications",
    "serviceVersion": "0.2.0-unstable.10",
    "bundleFilepath": "[https://example.com/mf/fe-mf-notifications/app.js"](https://example.com/mf/fe-mf-notifications/app.js%22),
    "sourceMap": {
    "version": 3,
    "sources": [],
    "names": [ "..." ],
    "mappings": "...",
    "file": "app.js",
    "sourcesContent": [ "..." ],
    "sourceRoot": ""
  }
  },
  ...
  },
  {
    "type": "sourcemap",
    "identifier": "fe-mf-notifications-0.2.0-unstable.11",
    "relative_url": "/api/fleet/artifacts/fe-mf-notifications-0.2.0-unstable.11/f1ce169c24dd4cebfe58f24e753f725a743c2323ae07af79874fdea0f6094477",
    "body": {
    "serviceName": "fe-mf-notifications",
    "serviceVersion": "0.2.0-unstable.11",
    "bundleFilepath": "[https://example.com/mf/fe-mf-notifications/app.js"](https://example.com/mf/fe-mf-notifications/app.js%22),
    "sourceMap": {
    "version": 3,
    "sources": [ "..."],
    "names": [ "..." ],
    "mappings": "...",
    "file": "app.js",
    "sourcesContent": [ "..." ],
    "sourceRoot": ""
  }
},
...
}
]
}

Above we tried to load a service name for each micro frontend.

The problem is highlighted in apm, we do not see the stacktrace on our "octopus-client" service (the only one we can have).

Then we also tried to load all the micro frontend maps with a single service name (the only registered one) "octopus-client"

{
  "type": "sourcemap",
  "identifier": "octopus-client-0.2.0-unstable.11",
  "relative_url": "/api/fleet/artifacts/octopus-client-0.2.0-unstable.11/9852a7ca370767b6d7519aad75b9c1874b6fee77c82fb9bd633871cacfcff3e4",
  "body": {
      "serviceName": "octopus-client",
      "serviceVersion": "0.2.0-unstable.11",
      "bundleFilepath": "[https://example.com/mf/fe-mf-notifications/app.js"](https://example.com/mf/fe-mf-notifications/app.js%22),
      "sourceMap": {
        "version": 3,
        "sources": [ "..."],
        "names": [ "... "],
        "mappings": "...",
        "file": "app.js",
        "sourcesContent": ["..."],
        "sourceRoot": ""
      }
    },
  ...
}

but the result does not change.

In APM how do I match the "maps" and the service name?

Thanks

devcorpio commented 2 years ago

Hi @elastic/apm-ui,

Would it be possible for someone to help @brevalessio24 with this?

Thanks, Alberto

cauemarcondes commented 2 years ago

@devcorpio / @brevalessio24 the apm-server is responsible to enrich the stachtrace when a source map is found. It uses the service name and the service version to find an uploaded source map.

@stuartnelson3 would you know what could be happening on apm-server that it is not finding the source map?

stuartnelson3 commented 2 years ago

It uses the service name, service version, and also the path:

https://github.com/elastic/apm-server/blob/main/sourcemap/processor.go#L119-L120

I believe "path" is referring to the bundleFilepath in this case (@simitt can you confirm?).

We uploaded an app2.js.map file for App2 with the shared service_name and bundle_filepath pointing to /app2.js.

I believe it needs to be the full URL, not just /app2.js.

The example listed is:

    "serviceName": "fe-mf-notifications",
    "serviceVersion": "0.2.0-unstable.11",
    "bundleFilepath": "[https://example.com/mf/fe-mf-notifications/app.js]"

Can you confirm that these are the values being sent with your traces?

You can also turn on debug logging for the apm-server, and check for the incoming request to see what it's trying to match on: https://github.com/elastic/apm-server/blob/main/sourcemap/processor.go#L123

devcorpio commented 2 years ago

Thanks a lot @cauemarcondes and @stuartnelson3 for your answers!

@brevalessio24 let us know if this helps you.

Thanks, Alberto

brevalessio24 commented 2 years ago

I can confirm that we are sending those values along with traces, we'll try with the debug enabled.

Thanks for now.

devcorpio commented 2 years ago

@brevalessio24 I have forgotten to ask before, apologies. What version of the elastic stack are you using?

Thanks!

brevalessio24 commented 2 years ago

@devcorpio the version is 8.1.0 and the APM is server binary (legacy).

simitt commented 2 years ago

The shared bundleFilepath looks quite odd to me (like a URL in markdown format, but with a replaced " - look at "[https vs app.js"]): "bundleFilepath": "[https://example.com/mf/fe-mf-notifications/app.js"](https://example.com/mf/fe-mf-notifications/app.js%22)

As @stuartnelson3 pointed out the service.name, service.version and path (== bundlePath) need to be aligned for the APM Server to find the matching sourcemap.

Can you share a sample document that you expect to have the sourcemap mapping applied, but doesn't (please readact any sensitive information).

devcorpio commented 2 years ago

Hi @brevalessio24,

Have you had time to check my colleague's comment?

Thanks, Alberto