Source maps
A production error says app-4f2a.js:1:98432. With the maps uploaded at deploy, the same error says Checkout.tsx:42:9.
The browser never gets your source maps, and Cloud Logging cannot use them. So fsl applies them in the one place that can: the log function every browser error already passes through on its way to your project. You do three things. Build with maps, tag the build with a release id, and upload the maps when you deploy.
Build with source maps
Section titled “Build with source maps”fsl’s upload tool reads a Vite build. Turn maps on in the Vite config:
import { defineConfig } from 'vite'
export default defineConfig({ build: { sourcemap: true, },})Every bundle in dist/ now has a .map file next to it. Those files are your source code, so the deploy step below moves them out of dist/ before hosting can serve them.
Tag the build with a release id
Section titled “Tag the build with a release id”A stack trace is only useful against the maps for the build that threw it. The release id is what joins the two. Every entry the client sends has it, and the upload stores maps under it.
import { initLogger } from '@dasasian/firebase-structured-logger/client'import { httpsCallable } from 'firebase/functions'import { functions } from './config/firebase'
export const logger = initLogger({ appId: 'my-app', releaseId: import.meta.env.VITE_RELEASE_ID ?? 'dev', logFunction: httpsCallable(functions, 'logFrontendEvent'),})Use the git commit for the value. A fixed string, such as '1.0', makes every build look like the same release, and an old error resolves against new maps. Locally it falls back to 'dev', and nothing is uploaded: development does not need it.
Upload the maps at deploy
Section titled “Upload the maps at deploy”Add the upload to your deploy script, between the build and firebase deploy. Keep any flags you already pass, such as --project.
"deploy": "export VITE_RELEASE_ID=$(git rev-parse --short HEAD) && npm run build && npx fsl upload-sourcemaps --backend=./functions --embed-sourcemaps && firebase deploy"The script sets the release id once, so the build and the upload cannot disagree. Then fsl upload-sourcemaps does three things:
- It uploads each map to
gs://<bucket>/sourcemaps/<releaseId>/in Cloud Storage. It reads the bucket fromVITE_FIREBASE_STORAGE_BUCKET, after loading.env.local. - It copies the current release’s maps into
functions/sourcemaps/current/, so the newest errors resolve without a Storage read. - It deletes the maps from
dist/.
Before it stores a map, the tool removes sourcesContent, the full text of your source files. Resolving a stack does not need it. So your source code does not go to the bucket or into the deploy, and the maps are smaller: 2.23 MB became 580 KB on one Vite build we measured.
If the upload to Storage fails, the command exits with code 3, and a deploy script joined with && stops there.
Check that it worked
Section titled “Check that it worked”Send the test entries from the browser after a deploy:
import { sendTestLog } from '@dasasian/firebase-structured-logger/client'
sendTestLog()It sends an error, a warning and an info. Find them in the Logs Explorer with labels.errorType="fsl-verify". The error’s stack names .tsx or .ts files with their lines. If it still names app-4f2a.js, the maps were not found. The next two sections cover the usual reasons.
A different bucket or prefix
Section titled “A different bucket or prefix”The upload and the log function are two halves of one contract, and nothing checks one against the other. If you change the bucket or the prefix, change both:
npx fsl upload-sourcemaps --bucket=my-maps --prefix=fsl-maps --backend=./functions --embed-sourcemapsimport { createClientLogFunction } from '@dasasian/firebase-structured-logger/functions'
export const logFrontendEvent = createClientLogFunction({ sourceMaps: { bucket: 'my-maps', prefix: 'fsl-maps' },})When the two disagree, the stacks stay minified, which looks the same as never uploading. The log function writes one warning for each release that resolves nothing, and it names the exact object it looked for.
Without a Storage bucket
Section titled “Without a Storage bucket”You can skip the bucket. Run the upload with an empty --bucket=, and it uploads nothing: the current release’s maps go out inside the backend deploy.
npx fsl upload-sourcemaps --bucket= --backend=./functions --embed-sourcemapsThe cost is older releases. Only the release that is deployed now can be resolved, so an error from a tab that has been open since yesterday’s release arrives minified. The entry is still written, and the log says once that it has no bucket to look in.
Made by Dasasian