Screenshots and files
A 5 MB screenshot costs the log line two labels. The file waits in Cloud Storage under the entry’s logId.
A log entry is not the place for a screenshot, a request body or a serialised store. Attachments hold those, and a Cloud Logging entry is capped at 256 KB, so a big payload in the entry fails to write. They are uploaded to Cloud Storage and stripped before the entry is written, so a 5 MB screenshot costs the log line two labels.
Attach files to a log
Section titled “Attach files to a log”Any log method takes a final attachments argument. On the client it is Record<string, Blob | File | string>. On the backend it is Record<string, string | Buffer>.
import { logger } from './main'
logger.error(err, { orderId }, undefined, { photo: blob, state: JSON.stringify(cart) })logger is the one that initLogger returned in Get started.
sendFeedback takes the same thing as { attachments }. Use attachments for anything too big for the entry: screenshots, request bodies, a serialised store, a captured frame.
Find the files
Section titled “Find the files”They are stored at:
gs://my-app.firebasestorage.app/logAttachments/{logId}/{name}logId is a ULID on the entry itself, so the log line tells you where its files are. This filter returns the entries that have files:
labels.hasAttachments="true"And this one returns the entry whose files you are looking for:
labels.logId="01J..."By default they share the bucket passed to createClientLogFunction({ bucket }), the same one the source maps live in, falling back to the project’s default bucket. fsl logs can download an entry’s files too. See reading logs.
Use another bucket
Section titled “Use another bucket”Call configureAttachments once, in your functions entry point:
import { configureAttachments } from '@dasasian/firebase-structured-logger/functions'
configureAttachments({ bucket: 'my-app-user-content', prefix: 'evidence' })It is global on purpose. The upload happens on every log call, including ones inside your own handlers that never touch createClientLogFunction, so there is no per-handler setting. Fields you leave out keep their defaults. If you do not call it, nothing changes.
It is worth doing when user content needs its own region for residency, its own retention policy, or different IAM from your source maps. None of those can be arranged with a prefix.
With no bucket and no firebase-admin installed, attachments are dropped and the log says so once. Name a bucket on the handler.
Big entries
Section titled “Big entries”Cloud Functions and Cloud Run cut a stdout or stderr log line at exactly 102,400 bytes (100 KiB). Past that, the entry does not arrive truncated but valid. It arrives as broken plain text, with no severity, no labels, and nothing for Error Reporting to group.
The backend logger watches for this. An entry over 90 KiB is shortened before it is written, in this order:
- breadcrumb data
- other context
- the tail of a long stack, so the top frames that Error Reporting groups on survive longest
- long text fields
- label values, only as a last resort
severity, labels, the trace and serviceContext are never touched.
The full entry is saved to Cloud Storage as fsl-overflow.json, at the same logAttachments/{logId}/ path, whenever a bucket is available. A shortened entry has labels.truncated="true", and labels.hasAttachments="true" when the full entry was saved. With no Storage configured the entry is still shortened, the original is lost, and the process warns once, not per entry.
Made by Dasasian