Get started
Four steps take a browser error from app-4f2a.js:1:98432 to Checkout.tsx:42:9 in your own Google Cloud project.
A browser cannot write to Cloud Logging, so the setup has two halves: a client logger in your app, and a log function in your Cloud Functions that the client calls. The function holds your source maps and writes the entry. Both halves ship in one package.
Prerequisites
Section titled “Prerequisites”- Node 22 or newer in the backend.
- A
functions/directory (firebase init functions, TypeScript) referenced byfirebase.json. - Firebase Storage enabled, because source maps are uploaded there. In the Firebase console, open Storage and choose Get started.
- A frontend build that emits source maps. The upload tool assumes Vite.
Backend on Cloud Run or another Node server instead of Cloud Functions? The browser half is the same. See Cloud Run for the server side.
Install
Section titled “Install”# frontend (project root)npm install @dasasian/firebase-structured-logger
# Cloud Functionscd functions && npm install @dasasian/firebase-structured-loggerThe package ships CommonJS with TypeScript types. You import from three entry points, /client for the browser, /functions for the backend and /testing for your tests, and you run the fsl command. firebase, firebase-admin and firebase-functions are optional peer dependencies, so bring your own versions.
1. Initialize the client
Section titled “1. Initialize the client”At your app entry, before any logging:
import { initLogger, setupGlobalErrorHandler } 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'),})
setupGlobalErrorHandler()setupGlobalErrorHandler() captures uncaught errors and unhandled rejections. logFunction is any function of the form (payload) => Promise<unknown>. httpsCallable() fits it, and so does a fetch to your own endpoint.
2. Add the log function
Section titled “2. Add the log function”import { initLogger, createClientLogFunction } from '@dasasian/firebase-structured-logger/functions'
initLogger({ appId: 'my-app' })
export const logFrontendEvent = createClientLogFunction({ bucket: 'my-app.firebasestorage.app',})The bucket holds source maps and attachments, under two default prefixes: sourcemaps/{releaseId}/ and logAttachments/{logId}/. Leave bucket out and both fall back to your project’s default bucket.
3. Wire the deploy script
Section titled “3. Wire the deploy script”Add the map upload 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. Locally the id falls back to 'dev' and nothing is uploaded. Source maps covers what the upload does and how to change the bucket.
4. Verify it works
Section titled “4. Verify it works”Send three test entries from the browser after a deploy:
import { sendTestLog } from '@dasasian/firebase-structured-logger/client'
sendTestLog()It sends one error, one warning and one info. In production only WARNING and above are sent by default, so the info is dropped. Open the Logs Explorer in the Google Cloud console and paste this filter:
labels.errorType="fsl-verify"The error’s stack should name a .tsx or .ts file with its line. If it still names a minified bundle, the maps were not found. If nothing arrives, nothing else here will work either, so fix this first. Wire sendTestLog() to a dev-only button.
- Source maps: release ids, buckets, and keeping
.mapfiles off hosting. - React or Vue: errors your framework catches, and router adapters.
- Cloud Run: a backend that is not Cloud Functions.
Made by Dasasian