Integrating User Tracebility (FESR) Into Your CAP NodeJS App For SAP Cloud ALM

Simon Laursen
5 min readJul 9, 2024

--

It’s been a while since my last post about the wonderous new OpenTelemetry support for SAP BTP through Cloud ALM, and by extension CAP applications. If you haven’t read that post, feel free to read it here.

We’ll use the previous post as a starting off point for this next step, in helping your app become more end-2-end traceable!

What You’ll Need

In order to setup your CAP service to support the FESR tracebility, you’ll need a couple of key components. The first one being the access to SAP’s npm packages designed for the purpose, which you should already have, if not then refer to the previous article.

Second thing you’ll need is a UI5 application (preferably, although it is somewhat possible with other frameworks) that can integrate with your telemetry integration.

I’m not going to go through the process of setting up the integration on the UI5 side this time around, but leave me a comment if you’ll like to see that covered.

And that’s pretty much it! Let’s get to it!

Step 1: Installing Dependencies

First thing we’ll need to do is install the npm dependency provided by SAP for the FESR integration. To do this, simply run the following command:

npm install @sap/fesr-to-otel-js

Once it is done installing, we’ll quickly check out our package.json file, to make sure that it now includes the FESR module:

{

"dependencies": {

"@sap/fesr-to-otel-js": "https://73555000100200018064.npmsrv.cdn.repositories.cloud.sap/@sap/fesr-to-otel-js/-/fesr-to-otel-js-1.5.6.tgz",

},

}

Should you have any issue installing this dependency, make sure that your .npmrc file contains the token necessary to access SAP’s registry for the module.
If you followed the previous article you should already have this setup, but if not, your file should look like this:

//73555000100200018064.npmsrv.cdn.repositories.cloud.sap/:_auth=<your-auth-token-here>

And from here, it is on to do the configuration in our app.

Step 2: Configuring CAP

So to configure the CAP application to make use of the FESR module, we’ll need to make sure that it is setup during our bootstrapping process.
There are a couple of ways of going about this, as outlined by SAP themselves in their documentation, but for this specific case I’ll use the CAP specific approach.

First thing we’ll need to do, is to create a server.js or server.ts file (depending on your preferred flavour) if you don’t already have one.
I myself make use of TypeScript, so that’s what’ll be depicting here.

import cds from "@sap/cds";
import * as fesr from "@sap/fesr-to-otel-js";

cds.on('bootstrap', (app) => fesr.registerFesrEndpoint(app));

export default cds.server;

Since CAP by default looks for a server file first before executing any service specific implementations, we can hook into the bootstrap early on and ensure that the FESR module gets registered with its endpoints as a part of the core Express application found underneath.

Essentially what we’re doing is adding an extra endpoint to our CAP application which can be found at the root URL, that is look as such:

<base-url-of-app>/fesr

This will allow any UI5 application that is configured with the FESR utils, to automatically make requests to this endpoint and transfer any traced data from the UI to the service layer and then onwards to Cloud ALM.

But how do make sure that the UI knows of this endpoint? Well, let’s take a look at it!

Step 3: Connecting the Dots

So there are a couple of key points to getting the two sides to work together here. Unfortunately, it isn’t enough to just enable things and then just wait for the magic to happen.
We’ll now have to go to BTP to make the final adjustments, or at least point to BTP for the correct integration points.

One of the key pain points that I ran into the first time I went to set this up, was the fact that all my BTP destinations for my service were set to by default refer to “<url>/odata/v4/<service>/” to make it easier for the front-end devs.

What I didn’t think of was the fact that the FESR endpoint is not registered on the OData service endpoint, but rather on the core application level, as shown in my previous example.

Therefore, as part of the deployment process of the service, I configured it such that everytime my service is deployed, a destination is made specifically for the FESR integration, meaning my MTA descriptor ends up looking somewhat like this:

modules:
# API Service
- name: api-service
type: nodejs
path: ../../api-service/gen/srv
parameters:
readiness-health-check-type: http
readiness-health-check-http-endpoint: /health
disk-quota: 1024M
memory: 512M
instances: 5
properties:
SAP_CALM_SERVICE_NAME: api-service
SAP_CALM_SERVICE_TYPE: SAP_CP_CF
SAP_CALM_DCI_AGG_THRESHOLD: 100
SAP_CALM_DCI_AGG_USER: true
#OTEL_RESOURCE_ATTRIBUTES: sap.tenancy.tenant_id=<your-btp-tenant-id>
SAP_CALM_FESR_LOG_LEVEL: debug
SAP_CALM_DCI_LOG_LEVEL: debug
OTEL_LOG_LEVEL: info
requires:
- name: destination-srv
- name: xsuaa-srv
provides:
- name: service-api
properties:
url: ${default-url}

# Destination Deployer
- name: destination-content
type: com.sap.application.content
build-parameters:
no-source: true
requires:
- name: service-api
- name: xsuaa-srv
parameters:
service-key:
name: xsuaa-key
- name: destination-srv
parameters:
content-target: true
parameters:
content:
subaccount: # Can also be instance level, it depends on your cloud setup
existing_destinations_policy: update
destinations:
- Name: SERVICE-API-DESTINATION
Description: Principal propagation access to service API
URL: ~{service-api/url}/odata/v4/api/
Authentication: OAuth2UserTokenExchange
TokenServiceInstanceName: xsuaa-srv
TokenServiceKeyName: xsuaa-key
- Name: SERVICE-API-FESR
Description: FESR Integration destination for service
URL: ~{service-api/url}
Authentication: OAuth2UserTokenExchange
TokenServiceInstanceName: xsuaa-srv
TokenServiceKeyName: xsuaa-key

So that’s a bit of a mouthful, but the key take-away here is that you’ll need a destination that points directly at your service URL root. If you do this anyway for destinations, and then handle the sub-routes on your approuter then that’s also fine.
I do however like to keep these separate to make sure that I always know that the FESR integration is there and available.

But this part is of course only half the battle. We’ve made it known to your cloud environment now that the integration endpoint exists, and that it can be accessed through this particular destination. But how do we then make sure that the UI5 application knows this?

In order to ensure this, we’ll need to go to the xs-app.json file defined for our UI. This can either be in your approuter directory or in your UI app’s root, depending on your setup (standalone vs. FLP).

Within the xs-app.json we just need to make an additional pointer to our integration destination as such:

{
...
"routes": [
...
{
"source": "^/fesr$",
"target": "/fesr",
"destination": "SERVICE-API-FESR",
"csrfProtection": false,
"authenticationType": "xsuaa"
}
...
]
}

And there we have it, the two applications should now be able to interact with each other across your cloud environment! It is as simple as that.

Acknowledgements

All of this is based on the documentation provided by SAP for their Cloud ALM integration, and it wouldn’t have been possible for me to do this without it. With that said, I still wanted to write this article to give a bit of insight into how I went about configuring it myself.

I hope this learned you something new or helped you on your journey to improve your Cloud ALM journey.

Don’t forget to checkout the previous part of this journey, and if you have any questions feel free to reach out.

--

--