DO NOT USE THIS. THIS PROJECT IS EXPERIMENTAL AND NOT READY FOR PRODUCTION USE
This package provides helpers for using Remix with the Elastic APM Node.js agent and the Elastic APM Real User Monitoring JavaScript agent.
While the Elastic APM agents are completely suitable for use with Remix in both Node.js and browser environments, if you use it with Remix out of the box the transaction names won't match what you would expect. This package allows you to patch Remix's server runtime to improve the experience of using Remix with Elastic APM.
This project was the result of some exploratory work on instrumenting Remix, and hopefully can be used as a starting point for adding official support in the future.
It should work in all Node.js-based environments, but I've only used it with Express.
You'll want to have an existing Remix project started with Express Server as the deployment target.
You'll need an Elastic APM server URL and secret token. The easiest way to get this set up is by creating a deployment on Elastic Cloud.
Once you have a deployment, you can copy the endpoint and secret token from the APM & Fleet page.
You'll want to store these as environment variables. See the Remix environment variables documentation on how to set this up.
You can also use direnv locally to manage these variables.
ELASTIC_APM_SERVER_URL="https://my-apm-server-url.es.io"
ELASTIC_APM_SECRET_TOKEN=abcdefghijklmnopqr
npm install @elastic/apm-rum elastic-apm-node https://github.com/smith/remix-elastic-apm --save
(We're just following the Node.js setup instructions here.)
In your server/index.js add the following to the top of the file:
const apm = require("elastic-apm-node").start({
serviceName: "my-remix-app-server",
secretToken: process.env.ELASTIC_APM_SECRET_TOKEN,
serverUrl: process.env.ELASTIC_APM_SERVER_URL,
frameworkName: "remix",
});
At this point, if you run npm run dev
to build the Remix bundle, and npm start
to run Express, then go to http://localhost:3000, transactions will be sent to your Elastic cluster.
If you open Kibana from your deployment and go to Observability > APM, "my-remix-app-server" should show up in the list of services.
You'll see transactions in the service overview, but they all will show up looking like "GET /*" for the transaction name. That's not very helpful, so let's fix it.
The addPatch
method on the APM agent allows us to wrap loaded modules to change their behavior.
After the const apm...
code we've added, add:
apm.addPatch(
"@remix-run/server-runtime",
require("remix-elastic-apm").patchHandler
);
Now in Kibana your transactions will have names like "GET /demos/params/$id", "action /demos/actions", and "loader /index". These should correspond with your routes, actions, and loaders.
(it would be nice if this were easier to do, but this gets the job done. I've opened remix-run/remix#1029 requesting a better way to do this.)
In order to do correlation with the RUM agent we'll need to get some context about the current transaction on the server to send to the client setup.
This will make it so that on page load the agent on the client will be able to correlate its transactions with the server transactions.
Where we currently have createRequestHandler({ build: require("./build") })
, add:
createRequestHandler({
getLoadContext: () => {
return {
elasticApmRumAgentConfig:
require("remix-elastic-apm").getElasticApmClientConfig(
{
serviceName: "my-remix-app-client",
serverUrl: process.env.ELASTIC_APM_SERVER_URL,
},
apm
),
};
},
build: require("./build"),
});
(Note that in the default Express setup there are two calls to createRequestHandler
so you'll need to update both.)
When getLoadContext
is called it will return something like:
{
serviceName: 'my-remix-app-client',
serverUrl: 'https://my-cloud-cluster.es.io',
pageLoadSpanId: '7f61a915f84d2caa',
pageLoadTraceId: 'bf51f30595b60e2b5c23f19007ac2c60',
pageLoadSampled: true
}
Return the context from the root loader by adding the following to app/root.tsx:
export const loader: LoaderFunction = async ({ context }) => {
return { elasticApmRumAgentConfig: context.elasticApmRumAgentConfig };
};
Add this to app/entry.client.tsx before the call to hydrate
:
import { init as initApm } from "@elastic/apm-rum";
initApm(__remixContext.routeData.root.elasticApmRumAgentConfig);
This will initialize the APM agent with the data from the loader context.
Now after loading the app you should see "my-remix-app-client" in your list of services in Kibana.
On the client we record "page-load", "route-change", and "http-request" transactions.
In addition to the transaction data in APM, you can use the User Experience dashboard to see and filter useful data about client-side interactions in your Remix app.