@dasasian/firebase-structured-logger 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +652 -91
- package/dist/client/breadcrumbs.d.ts +14 -2
- package/dist/client/breadcrumbs.d.ts.map +1 -1
- package/dist/client/breadcrumbs.js +39 -21
- package/dist/client/breadcrumbs.js.map +1 -1
- package/dist/client/index.d.ts +3 -3
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -2
- package/dist/client/index.js.map +1 -1
- package/dist/client/logger.d.ts +29 -0
- package/dist/client/logger.d.ts.map +1 -1
- package/dist/client/logger.js +39 -5
- package/dist/client/logger.js.map +1 -1
- package/dist/client/rateLimiter.d.ts +8 -3
- package/dist/client/rateLimiter.d.ts.map +1 -1
- package/dist/client/rateLimiter.js +9 -4
- package/dist/client/rateLimiter.js.map +1 -1
- package/dist/functions/httpHandler.d.ts +89 -0
- package/dist/functions/httpHandler.d.ts.map +1 -0
- package/dist/functions/httpHandler.js +112 -0
- package/dist/functions/httpHandler.js.map +1 -0
- package/dist/functions/index.d.ts +6 -3
- package/dist/functions/index.d.ts.map +1 -1
- package/dist/functions/index.js +6 -2
- package/dist/functions/index.js.map +1 -1
- package/dist/functions/logHandler.d.ts +53 -2
- package/dist/functions/logHandler.d.ts.map +1 -1
- package/dist/functions/logHandler.js +38 -7
- package/dist/functions/logHandler.js.map +1 -1
- package/dist/functions/logger.d.ts.map +1 -1
- package/dist/functions/logger.js +95 -7
- package/dist/functions/logger.js.map +1 -1
- package/dist/functions/requestLogger.d.ts +2 -20
- package/dist/functions/requestLogger.d.ts.map +1 -1
- package/dist/functions/requestLogger.js +2 -39
- package/dist/functions/requestLogger.js.map +1 -1
- package/dist/functions/sourceMapCache.d.ts +27 -1
- package/dist/functions/sourceMapCache.d.ts.map +1 -1
- package/dist/functions/sourceMapCache.js +84 -9
- package/dist/functions/sourceMapCache.js.map +1 -1
- package/dist/functions/traceContext.d.ts +40 -0
- package/dist/functions/traceContext.d.ts.map +1 -0
- package/dist/functions/traceContext.js +93 -0
- package/dist/functions/traceContext.js.map +1 -0
- package/dist/shared/paths.d.ts +59 -0
- package/dist/shared/paths.d.ts.map +1 -0
- package/dist/shared/paths.js +126 -0
- package/dist/shared/paths.js.map +1 -0
- package/dist/shared/severity.d.ts +21 -0
- package/dist/shared/severity.d.ts.map +1 -1
- package/dist/shared/severity.js +31 -3
- package/dist/shared/severity.js.map +1 -1
- package/dist/shared/types.d.ts +1 -2
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/tools/index.js +13 -4
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/uploadSourceMaps.d.ts +24 -1
- package/dist/tools/uploadSourceMaps.d.ts.map +1 -1
- package/dist/tools/uploadSourceMaps.js +31 -4
- package/dist/tools/uploadSourceMaps.js.map +1 -1
- package/package.json +4 -4
- package/skills/logs/SKILL.md +32 -9
- package/skills/query-logs/SKILL.md +14 -1
package/dist/tools/index.js
CHANGED
|
@@ -79,9 +79,13 @@ async function main() {
|
|
|
79
79
|
switch (command) {
|
|
80
80
|
case 'upload-sourcemaps': {
|
|
81
81
|
const bucket = args.bucket ?? process.env.VITE_FIREBASE_STORAGE_BUCKET ?? process.env.FIREBASE_STORAGE_BUCKET;
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
const embed = args['embed-sourcemaps'] === 'true';
|
|
83
|
+
// A bucket is only required when something is going to be uploaded.
|
|
84
|
+
// Embed-only is the whole flow for a backend that has no bucket — see #34.
|
|
85
|
+
if (!bucket && !embed) {
|
|
86
|
+
console.error('Usage: fsl upload-sourcemaps [--bucket=<name>] [--functions=<path>] [--embed-sourcemaps] [--release=<id>] [--dist=<path>] [--prefix=<path>]');
|
|
84
87
|
console.error('Bucket can also be set via VITE_FIREBASE_STORAGE_BUCKET or FIREBASE_STORAGE_BUCKET env var (loaded from .env.local automatically).');
|
|
88
|
+
console.error('Omit the bucket only with --embed-sourcemaps, to embed the current release without uploading.');
|
|
85
89
|
process.exit(1);
|
|
86
90
|
}
|
|
87
91
|
const result = await (0, uploadSourceMaps_1.uploadSourceMaps)({
|
|
@@ -89,7 +93,8 @@ async function main() {
|
|
|
89
93
|
release: args.release,
|
|
90
94
|
distDir: args.dist,
|
|
91
95
|
functionsDir: args.functions,
|
|
92
|
-
embedSourcemaps:
|
|
96
|
+
embedSourcemaps: embed,
|
|
97
|
+
prefix: args.prefix,
|
|
93
98
|
});
|
|
94
99
|
// Distinct code so a deploy chain can choose to continue:
|
|
95
100
|
// npx fsl upload-sourcemaps … || [ $? -eq 3 ]
|
|
@@ -106,11 +111,15 @@ async function main() {
|
|
|
106
111
|
firebase-structured-logger (fsl)
|
|
107
112
|
|
|
108
113
|
Commands:
|
|
109
|
-
fsl upload-sourcemaps [--bucket=<name>] [--functions=<path>] [--embed-sourcemaps] [--release=<id>] [--dist=<path>]
|
|
114
|
+
fsl upload-sourcemaps [--bucket=<name>] [--functions=<path>] [--embed-sourcemaps] [--release=<id>] [--dist=<path>] [--prefix=<path>]
|
|
110
115
|
Upload .map files from dist/ to Cloud Storage and delete them locally.
|
|
111
116
|
--bucket defaults to VITE_FIREBASE_STORAGE_BUCKET or FIREBASE_STORAGE_BUCKET (loaded from .env.local automatically).
|
|
112
117
|
--functions path to Cloud Functions directory (e.g. ./functions or ./backend).
|
|
113
118
|
--embed-sourcemaps copies maps to {functions}/sourcemaps/current/ for fast lookup of current release.
|
|
119
|
+
Given without --bucket, embeds only and uploads nothing — for a backend with no
|
|
120
|
+
bucket. Only the deployed release can then be symbolicated.
|
|
121
|
+
--prefix Cloud Storage prefix to upload under (default sourcemaps/). Must match
|
|
122
|
+
createClientLogHandler({ sourceMaps: { prefix } }) or maps are not found.
|
|
114
123
|
Authenticates via FIREBASE_SERVICE_ACCOUNT_PATH if set, otherwise uses ADC.
|
|
115
124
|
Release ID defaults to git rev-parse --short HEAD.
|
|
116
125
|
|
package/dist/tools/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,uCAAwB;AACxB,2CAA4B;AAC5B,yDAAsF;AACtF,mDAA+C;AAE/C,MAAM,CAAC,EAAE,AAAD,EAAG,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAA;AAE9C,SAAS,SAAS,CAAC,IAAc;IAC/B,MAAM,MAAM,GAA2B,EAAE,CAAA;IACzC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAA;QAC3C,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QAC7B,CAAC;aAAM,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAChC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAA;QAC/B,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,WAAW,CAAC,QAAgB;IACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAA;IACtD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAM;IACpC,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAClE,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;QAC3B,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAQ;QACjD,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAC/B,IAAI,EAAE,KAAK,CAAC,CAAC;YAAE,SAAQ;QACvB,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;QACvC,IAAI,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;QACxC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACrG,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;QAC5B,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;IACjD,CAAC;AACH,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,CAAA;IAE/B,2CAA2C;IAC3C,WAAW,CAAC,YAAY,CAAC,CAAA;IAEzB,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,mBAAmB,CAAC,CAAC,CAAC;YACzB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAA;YAC7G,IAAI,CAAC,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,11 +17,34 @@ export declare const EXIT_UPLOAD_FAILED_BUT_EMBEDDED = 3;
|
|
|
17
17
|
*/
|
|
18
18
|
export declare const EMBEDDED_RELEASE_MARKER = ".release";
|
|
19
19
|
export interface UploadOptions {
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Cloud Storage bucket to upload to. Resolved by the caller — the CLI falls
|
|
22
|
+
* back to env vars.
|
|
23
|
+
*
|
|
24
|
+
* Optional, but only alongside `embedSourcemaps`: a run that neither uploads
|
|
25
|
+
* nor embeds does nothing except delete maps, which is not a thing to let
|
|
26
|
+
* someone ask for by accident. Omitting it is how a backend with no bucket —
|
|
27
|
+
* Cloud Run, say — gets the embedded copy without inventing a bucket name to
|
|
28
|
+
* satisfy the tool (#34).
|
|
29
|
+
*/
|
|
30
|
+
bucket?: string;
|
|
21
31
|
release?: string;
|
|
22
32
|
distDir?: string;
|
|
23
33
|
functionsDir?: string;
|
|
24
34
|
embedSourcemaps?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Cloud Storage prefix to upload under. Defaults to `sourcemaps/`.
|
|
37
|
+
*
|
|
38
|
+
* The reader must be told the same value, via
|
|
39
|
+
* `createClientLogHandler({ sourceMaps: { prefix } })`. These are the two ends
|
|
40
|
+
* of one contract and nothing checks them against each other — when they
|
|
41
|
+
* disagree the maps are simply not found and stacks stay minified, which
|
|
42
|
+
* looks identical to never having uploaded them (#35).
|
|
43
|
+
*
|
|
44
|
+
* Does not affect the embedded copy: that directory ships inside the deploy
|
|
45
|
+
* artifact and nothing outside reads it, so it stays fixed.
|
|
46
|
+
*/
|
|
47
|
+
prefix?: string;
|
|
25
48
|
}
|
|
26
49
|
/**
|
|
27
50
|
* Drop `sourcesContent` from a source map, returning the JSON to store.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploadSourceMaps.d.ts","sourceRoot":"","sources":["../../src/tools/uploadSourceMaps.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"uploadSourceMaps.d.ts","sourceRoot":"","sources":["../../src/tools/uploadSourceMaps.ts"],"names":[],"mappings":"AAKA;;;;;;;GAOG;AACH,eAAO,MAAM,+BAA+B,IAAI,CAAA;AAEhD;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,aAAiB,CAAA;AAErD,MAAM,WAAW,aAAa;IAC5B;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CASpF;AA8BD,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,CAkH7F"}
|
|
@@ -39,6 +39,7 @@ exports.uploadSourceMaps = uploadSourceMaps;
|
|
|
39
39
|
const fs = __importStar(require("fs"));
|
|
40
40
|
const path = __importStar(require("path"));
|
|
41
41
|
const storage_1 = require("@google-cloud/storage");
|
|
42
|
+
const paths_js_1 = require("../shared/paths.js");
|
|
42
43
|
/**
|
|
43
44
|
* Exit code used when maps were embedded but the Storage upload failed.
|
|
44
45
|
*
|
|
@@ -56,7 +57,7 @@ exports.EXIT_UPLOAD_FAILED_BUT_EMBEDDED = 3;
|
|
|
56
57
|
* by filename, so an old stack naming a bundle that still exists would be
|
|
57
58
|
* resolved with the current map, giving confidently wrong line numbers.
|
|
58
59
|
*/
|
|
59
|
-
exports.EMBEDDED_RELEASE_MARKER =
|
|
60
|
+
exports.EMBEDDED_RELEASE_MARKER = paths_js_1.RELEASE_MARKER;
|
|
60
61
|
/**
|
|
61
62
|
* Drop `sourcesContent` from a source map, returning the JSON to store.
|
|
62
63
|
*
|
|
@@ -118,14 +119,19 @@ async function uploadSourceMaps(options) {
|
|
|
118
119
|
}
|
|
119
120
|
const distDir = options.distDir ?? path.join(process.cwd(), 'dist');
|
|
120
121
|
const mapFiles = findMapFiles(distDir);
|
|
122
|
+
if (!options.bucket && !options.embedSourcemaps) {
|
|
123
|
+
throw new Error('[fsl] Nothing to do: no --bucket to upload to and no --embed-sourcemaps. ' +
|
|
124
|
+
'Running would only delete the maps from dist/.');
|
|
125
|
+
}
|
|
121
126
|
if (mapFiles.length === 0) {
|
|
122
127
|
console.log('[fsl] No .map files found in', distDir);
|
|
123
128
|
return { uploaded: true };
|
|
124
129
|
}
|
|
125
|
-
|
|
130
|
+
const verb = options.bucket ? 'Uploading' : 'Embedding';
|
|
131
|
+
console.log(`[fsl] ${verb} ${mapFiles.length} source map(s) for release ${releaseId}...`);
|
|
126
132
|
// Embed maps into functions directory before uploading (for fast lookup of current release)
|
|
127
133
|
if (options.embedSourcemaps && options.functionsDir) {
|
|
128
|
-
const embedDir = path.join(process.cwd(), options.functionsDir
|
|
134
|
+
const embedDir = (0, paths_js_1.embeddedDir)(path.join(process.cwd(), options.functionsDir));
|
|
129
135
|
fs.mkdirSync(embedDir, { recursive: true });
|
|
130
136
|
for (const f of fs.readdirSync(embedDir)) {
|
|
131
137
|
fs.unlinkSync(path.join(embedDir, f));
|
|
@@ -139,6 +145,27 @@ async function uploadSourceMaps(options) {
|
|
|
139
145
|
const saved = before > after ? ` (${kb(before)} → ${kb(after)})` : '';
|
|
140
146
|
console.log(` ✓ embedded ${path.basename(localPath)} → ${options.functionsDir}/sourcemaps/current/${saved}`);
|
|
141
147
|
}
|
|
148
|
+
// Record which release these maps are for. Without it the runtime cannot tell
|
|
149
|
+
// a stack from the deployed release from a stack from an older one, and
|
|
150
|
+
// resolves both with whatever is embedded — see the marker's own doc above.
|
|
151
|
+
// Written after the loop, which has already cleared any stale marker.
|
|
152
|
+
fs.writeFileSync(path.join(embedDir, paths_js_1.RELEASE_MARKER), releaseId);
|
|
153
|
+
console.log(` ✓ marked ${options.functionsDir}/sourcemaps/current/ as release ${releaseId}`);
|
|
154
|
+
}
|
|
155
|
+
// Embed-only: nothing to authenticate against, so do not construct a client.
|
|
156
|
+
// The maps still leave dist/ — they are embedded, and leaving them behind
|
|
157
|
+
// would serve source maps to browsers, which is the worse outcome.
|
|
158
|
+
if (!options.bucket) {
|
|
159
|
+
for (const localPath of mapFiles) {
|
|
160
|
+
if (fs.existsSync(localPath)) {
|
|
161
|
+
fs.unlinkSync(localPath);
|
|
162
|
+
console.log(` ✗ deleted ${path.relative(process.cwd(), localPath)}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
console.log('[fsl] Embedded only — no bucket given, nothing uploaded.');
|
|
166
|
+
console.log(`[fsl] Stacks from ${releaseId} symbolicate while it is the deployed release.`);
|
|
167
|
+
console.log('[fsl] Older releases cannot be symbolicated without a bucket to read from.');
|
|
168
|
+
return { uploaded: true };
|
|
142
169
|
}
|
|
143
170
|
// Authenticate via service account key if available, otherwise fall back to ADC
|
|
144
171
|
const serviceAccountPath = process.env.FIREBASE_SERVICE_ACCOUNT_PATH;
|
|
@@ -150,7 +177,7 @@ async function uploadSourceMaps(options) {
|
|
|
150
177
|
try {
|
|
151
178
|
for (const localPath of mapFiles) {
|
|
152
179
|
const fileName = path.basename(localPath);
|
|
153
|
-
const destination =
|
|
180
|
+
const destination = (0, paths_js_1.storageMapPath)(releaseId, (0, paths_js_1.bundleNameOf)(fileName), options.prefix);
|
|
154
181
|
const { json, before, after } = readStrippedMap(localPath);
|
|
155
182
|
await bucket.file(destination).save(json, { contentType: 'application/json' });
|
|
156
183
|
const saved = before > after ? ` (${kb(before)} → ${kb(after)}, -${Math.round(100 - (after / before) * 100)}%)` : '';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploadSourceMaps.js","sourceRoot":"","sources":["../../src/tools/uploadSourceMaps.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"uploadSourceMaps.js","sourceRoot":"","sources":["../../src/tools/uploadSourceMaps.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dasasian/firebase-structured-logger",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Structured logging for Firebase apps
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "Structured logging for Firebase apps — client, functions, and tools",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Dasasian",
|
|
7
7
|
"keywords": [
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
"build": "tsc",
|
|
65
65
|
"build:watch": "tsc --watch",
|
|
66
66
|
"clean": "rm -rf dist",
|
|
67
|
-
"typecheck": "tsc --noEmit",
|
|
68
|
-
"test": "tsx tests/logger.ts && tsx tests/breadcrumbs.ts && tsx tests/rateLimiter.ts && tsx tests/errorHandler.ts && tsx tests/attachments.ts && tsx tests/sourceMapStripping.ts && FUNCTIONS_EMULATOR=true tsx tests/errorPayload.ts && FUNCTIONS_EMULATOR=true tsx tests/requestLogger.ts && FUNCTIONS_EMULATOR=true tsx tests/handler.ts && FUNCTIONS_EMULATOR=true tsx tests/symbolication.ts && FUNCTIONS_EMULATOR=true tsx tests/publicApi.ts && FUNCTIONS_EMULATOR=true tsx tests/configureTwice.ts && tsx tests/productionOutput.ts && tsx tests/handlerSymbolication.ts && tsx tests/releaseResolution.ts && tsx tests/sourceMapCacheBounds.ts",
|
|
67
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.check.json",
|
|
68
|
+
"test": "tsx tests/logger.ts && tsx tests/breadcrumbs.ts && tsx tests/rateLimiter.ts && tsx tests/errorHandler.ts && tsx tests/attachments.ts && tsx tests/feedback.ts && tsx tests/paths.ts && tsx tests/sourceMapStripping.ts && FUNCTIONS_EMULATOR=true tsx tests/errorPayload.ts && FUNCTIONS_EMULATOR=true tsx tests/requestLogger.ts && FUNCTIONS_EMULATOR=true tsx tests/handler.ts && FUNCTIONS_EMULATOR=true tsx tests/httpHandler.ts && FUNCTIONS_EMULATOR=true tsx tests/symbolication.ts && FUNCTIONS_EMULATOR=true tsx tests/publicApi.ts && FUNCTIONS_EMULATOR=true tsx tests/configureTwice.ts && tsx tests/productionOutput.ts && tsx tests/handlerSymbolication.ts && tsx tests/releaseResolution.ts && tsx tests/sourceMapCacheBounds.ts",
|
|
69
69
|
"prepublishOnly": "npm run clean && npm run build",
|
|
70
70
|
"smoke:deploy": "cd smoke/functions && npm install && npm run build && cd .. && firebase deploy --only functions --project $(grep '^FSL_SMOKE_PROJECT=' .env.local | cut -d= -f2)",
|
|
71
71
|
"smoke": "tsx smoke/run.ts"
|
package/skills/logs/SKILL.md
CHANGED
|
@@ -9,7 +9,7 @@ Validate frontend and backend logging implementation for a given file.
|
|
|
9
9
|
Identify whether the target file is frontend or backend:
|
|
10
10
|
|
|
11
11
|
**Frontend:** path starts with `src/`, imports from `firebase-structured-logger/client`, uses `logError/logWarn/logInfo/logDebug` or `bc.*`
|
|
12
|
-
**Backend:** path starts with `functions/src/`, imports from `firebase-structured-logger/functions`, uses `
|
|
12
|
+
**Backend:** path starts with `functions/src/`, imports from `firebase-structured-logger/functions`, uses `withLogging/getLogger/logError/logWarn/logInfo/logDebug`
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
@@ -96,11 +96,16 @@ API:
|
|
|
96
96
|
- Must import from `firebase-structured-logger/functions`
|
|
97
97
|
- Use `logError/logWarn/logInfo/logDebug` — never `console.log/console.error` directly
|
|
98
98
|
|
|
99
|
-
**`
|
|
100
|
-
- Every `onCall` handler must
|
|
99
|
+
**`withLogging` — mandatory wrapper on every handler**
|
|
100
|
+
- Every `onCall` handler must be wrapped: `onCall(withLogging<AppLabels>({ functionName, labels }, handler))`
|
|
101
101
|
- Auto-seeds `userId` and `functionName` — do not pass these manually in labels
|
|
102
|
-
- Any `AppLabels` fields present in `request.data`
|
|
103
|
-
- Flag
|
|
102
|
+
- Any `AppLabels` fields present in `request.data` need the callback form, which is evaluated per request: `withLogging<AppLabels>((request) => ({ functionName, labels: { orgId: request.data.orgId } }), handler)`
|
|
103
|
+
- Flag an unwrapped handler as a critical violation
|
|
104
|
+
- Flag `initRequestLogger` as a critical violation — it was removed in 0.6.0. It bound the
|
|
105
|
+
scope with `enterWith()`, which never unwinds, so a request's labels outlived the request
|
|
106
|
+
and any later handler that did *not* call it inherited whichever user last touched the
|
|
107
|
+
warm instance. `withLogging` uses `run()`, which restores the previous scope when the
|
|
108
|
+
handler settles.
|
|
104
109
|
|
|
105
110
|
**Error paths**
|
|
106
111
|
- Every `catch` block must call `logError`
|
|
@@ -109,7 +114,7 @@ API:
|
|
|
109
114
|
|
|
110
115
|
**Label completeness**
|
|
111
116
|
- For each log call, check which `AppLabels` fields are in scope
|
|
112
|
-
- Fields already seeded via `
|
|
117
|
+
- Fields already seeded via `withLogging` labels do not need repeating
|
|
113
118
|
- Flag any in-scope fields not seeded at entry or passed on the log call
|
|
114
119
|
|
|
115
120
|
**PII**
|
|
@@ -165,11 +170,28 @@ bc.nav(screen: string): void // on navigation
|
|
|
165
170
|
bc.error(type: string, data?: Record<string, unknown>): void // on errors
|
|
166
171
|
```
|
|
167
172
|
|
|
173
|
+
Import `sendFeedback` from `firebase-structured-logger/client`:
|
|
174
|
+
```ts
|
|
175
|
+
sendFeedback(text: string, extras?: { labels?: Partial<AppLabels>; attachments?: Record<string, Blob | File | string> }): void
|
|
176
|
+
```
|
|
177
|
+
- For what a user reports, not what the code catches — a wrong total, a button that did
|
|
178
|
+
nothing. Wire it to a feedback form, never to a `catch` block.
|
|
179
|
+
- Writes at `NOTICE` with `labels.feedback="true"`, and carries the current breadcrumbs,
|
|
180
|
+
screen and user labels automatically.
|
|
181
|
+
- Exempt from the severity floor and the rate limiter. Returns nothing — tell the user
|
|
182
|
+
thanks; if the app needs correlation, pass its own id in `labels`.
|
|
183
|
+
|
|
168
184
|
### Backend
|
|
169
185
|
Import from `firebase-structured-logger/functions`:
|
|
170
186
|
```ts
|
|
171
|
-
//
|
|
172
|
-
|
|
187
|
+
// Wrap every onCall handler — auto-seeds userId and functionName, and unwinds
|
|
188
|
+
// the scope when the handler settles
|
|
189
|
+
export const myFunc = onCall(
|
|
190
|
+
withLogging<AppLabels>(
|
|
191
|
+
(request) => ({ functionName: 'myFunc', labels: { organizationId: request.data.organizationId } }),
|
|
192
|
+
async (request) => { /* logError/logWarn/logInfo/logDebug work in here */ },
|
|
193
|
+
),
|
|
194
|
+
)
|
|
173
195
|
|
|
174
196
|
logError(raw: unknown, labels?: Record<string, string | undefined>, context?: Record<string, unknown>, attachments?: Record<string, string | Buffer>): void
|
|
175
197
|
logWarn(message: string, labels?: Record<string, string | undefined>, context?: Record<string, unknown>, attachments?: Record<string, string | Buffer>): void
|
|
@@ -180,6 +202,7 @@ logDebug(message: string, labels?: Record<string, string | undefined>, context?:
|
|
|
180
202
|
### Key behaviours
|
|
181
203
|
- `logError` auto-derives `errorType` from `error.name` — only pass explicitly to override (e.g. `{ errorType: 'DatabaseError' }`)
|
|
182
204
|
- `logError` accepts `unknown` — never cast with `as Error`
|
|
183
|
-
- `
|
|
205
|
+
- `withLogging` auto-seeds `userId` (from `request.auth.uid`) and `functionName` — do not pass these in labels
|
|
206
|
+
- `getLogger()` outside a wrapped handler returns an anonymous writer, not stale labels
|
|
184
207
|
- Every log entry includes `labels.logId` (ULID) — if `attachments` are passed, they are uploaded to GCS at `logAttachments/{logId}/{name}` fire-and-forget; upload failure never blocks the log entry
|
|
185
208
|
- When catching an error, check if there are attachments in scope (images, file snapshots, captured data) that would help reproduce or diagnose it — if so, pass them via `attachments`
|
|
@@ -30,7 +30,20 @@ App-specific labels are defined in each app's `AppLabels` type.
|
|
|
30
30
|
|
|
31
31
|
## Common Queries
|
|
32
32
|
|
|
33
|
-
###
|
|
33
|
+
### Everything one user did, in order
|
|
34
|
+
```
|
|
35
|
+
labels.userId="<uid>"
|
|
36
|
+
```
|
|
37
|
+
No severity filter. Frontend and backend entries share the `userId` label, so this
|
|
38
|
+
returns both halves interleaved in time order — the clicks and screen changes that
|
|
39
|
+
led up to a failure, the backend call that failed, and the error the browser saw.
|
|
40
|
+
Filtering to `severity=ERROR` keeps the crash and throws away the story; reach for
|
|
41
|
+
it only when you already know what you are looking for.
|
|
42
|
+
|
|
43
|
+
Add `labels.platform:*` for client entries only, or `labels.functionName:*` for
|
|
44
|
+
server entries only.
|
|
45
|
+
|
|
46
|
+
### Only the errors for a user
|
|
34
47
|
```
|
|
35
48
|
labels.userId="<uid>" severity=ERROR
|
|
36
49
|
```
|