Observability support / otel integration
According to the docs,
TanStack Start integrates seamlessly with observability tools to provide comprehensive insights into how your application behaves in production, helping you ensure that everything runs smoothly.
Can anyone give more details around this? Looking at the reccomended sentry integration, I dont understand how its tracing anything or how to use this as a guide for writing my own integration (I would like to use datadog / otel). From what I can tell, its simply just creating a middleware that does nothing.
I would love some more information on how tanstack start integrates semlessly with observability.5 Replies
absent-sapphire•2mo ago
Did you manage to install otel? I'm on that road right now!
implicit-lime•2mo ago
i think you create a
server.ts
file :
absent-sapphire•2mo ago
anybody tried? does it work or have gotchas?
absent-sapphire•2w ago
I have went down a deep rabbit hole with this.
First lets take a look at what Sentry does.
If you follow the tanstack start sentry guide, they give you two options
https://docs.sentry.io/platforms/javascript/guides/tanstackstart-react/
1. Either Sentry registers ESM loader hooks and manages Otel SDK or
2. You manage ESM loader hooks and otel SDK
So either way you will be using ESM loader hooks
They are a massive pain.
You see, when you use ESM loader hooks, you need the hooks to run before you import the packages that you want to instrument.
As far as I can tell you have two options to do this
1. Using
--import=path-to-otel-setup.mjs
or NODE_OPTIONS=--import=path-to-otel-setup.mjs
This is the one truly blessed way by Otel to use esm. Everything else you try they say it may or may not work.
2. Try to just make sure you run otelSdk.start() before you import anything you want to isntrument.
Option 2 is pretty much impossible as far as I can tell because when you bundle your app, rollup will move esm imports to wherever it feels like. So even if you run otelSdk.start() at the top of your server entry file (or import a file that does that) at the very top of the file, it won't matter. Imports will moved.
So that brings us back to option 1. To do that, you really need 2 different server entry points. One for the main server, and another to bootstrap otel before your packages have been imported.
I tried and succeeded to do this by combining a vite plugin and a nitro hook.
vite plugin
nitro.config.ts:
You see, when you build your tanstack app with vite, it produces a client and a server bundle.
When vite makes the server bundle, it does not write it to disk, instead it saves it in memory then passes that virtual bundle to nitro to make the final server bundle.
The vite plugin ensures that your bootstrap file has a stable module name (without a hash in the name) so we can target it as an entry point within nitro
Then within nitro, we tell it to output 2 entry points.
I finally got all this working and thought I had everything right, however after doing this and deploying to aws lambda, the response type from my API was the untransformed response object (aws lambda should transform the response object to a response but that wasn't working)
After prompting AI, I am told that nitro expects and requires that there is only 1 entry point for its build presets to work. So now I am back at square one. PLEASE SOMEONE HELP.
Okay I made some progress in case it helps anyone. I have found that changing anything in the nitro rollup hook breaks the aws-lambda preset. So I deleted those changes and I am only using this vite plugin
You can then do a async import at the start of your server entry like so
This is enough to make the bootstrap a seperate chunk in the output without a hash in the filename. It should land in this this location
Then you can add this to your deployed environment variables
NODE_OPTIONS=--import=./chunks/_/bootstrap.mjs
And one more final note. The esm hook pattern is supposed to use this code
The import.meta.url is replaced by an unenv import.meta.url pollyfill that breaks the behavior. So I replaced it with file://${process.cwd()}/chunks/_/bootstrap.mjs
After all that I finally got otel to load before the rest of my app imports. I really hope we can update the docs to help others through this process.
This solution still does not work in local dev environment but I think that should be a relatively easy fixabsent-sapphire•7d ago
Thanks for all these experiments 🙏