@dasasian/firebase-structured-logger 0.5.0 → 0.6.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 +71 -6
- 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/index.d.ts +1 -1
- package/dist/functions/index.d.ts.map +1 -1
- package/dist/functions/index.js +1 -2
- package/dist/functions/index.js.map +1 -1
- package/dist/functions/logger.d.ts.map +1 -1
- package/dist/functions/logger.js +41 -4
- 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/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/package.json +3 -3
- package/skills/logs/SKILL.md +32 -9
package/README.md
CHANGED
|
@@ -160,6 +160,68 @@ logger.addBreadcrumb(type, name, data?)
|
|
|
160
160
|
|
|
161
161
|
**Attachments** (images, snapshots, captured data) upload to GCS at `logAttachments/{logId}/{name}`; the same `logId` is on the entry's `labels.logId`. Add a lifecycle rule to auto-delete `logAttachments/` after N days.
|
|
162
162
|
|
|
163
|
+
### Breadcrumbs
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
import { bc } from '@dasasian/firebase-structured-logger/client'
|
|
167
|
+
|
|
168
|
+
bc.nav('Checkout') // also sets labels.screen
|
|
169
|
+
bc.action('apply_discount', { code: 'SAVE10' })
|
|
170
|
+
bc.state('total_recalculated', { total: 42.00 })
|
|
171
|
+
bc.error('ValidationError', { field: 'price' })
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
A stack trace tells you where the code broke. It cannot tell you what the person did to
|
|
175
|
+
get there, which is usually the part you need to reproduce it. Breadcrumbs are that trail:
|
|
176
|
+
a rolling record of the last steps, attached automatically to every error and every piece
|
|
177
|
+
of feedback, with no correlation work on your side.
|
|
178
|
+
|
|
179
|
+
Drop a `bc.action` before anything that can fail and a `bc.nav` on every screen change, and
|
|
180
|
+
`total is wrong` arrives as `navigate_Checkout · apply_discount · total_recalculated ·
|
|
181
|
+
tap_place_order`.
|
|
182
|
+
|
|
183
|
+
The trail is capped at **50 entries** and **5 minutes** — old enough to cover the steps that
|
|
184
|
+
led here, short enough that it stays the current attempt rather than the whole session, and
|
|
185
|
+
bounded so a long-lived tab cannot grow it without limit. It lives in memory only, so it
|
|
186
|
+
never touches storage and never leaves the device except attached to a log you send.
|
|
187
|
+
|
|
188
|
+
Breadcrumbs are session-global by design: one user, one path. `bc.nav()` also sets the
|
|
189
|
+
current screen, so `labels.screen` stays correct without a second call.
|
|
190
|
+
|
|
191
|
+
> Record the step, not the data. Breadcrumb `data` is written to your logs verbatim — keep
|
|
192
|
+
> PII, tokens and card numbers out of it, the same as you would for any label.
|
|
193
|
+
|
|
194
|
+
### User feedback
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
import { sendFeedback } from '@dasasian/firebase-structured-logger/client'
|
|
198
|
+
|
|
199
|
+
sendFeedback("the discount didn't apply")
|
|
200
|
+
sendFeedback(text, { attachments: { screenshot }, labels: { orderId } })
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Error tracking only sees things that **throw**. A button that does nothing, a total that
|
|
204
|
+
comes out wrong, the wrong data rendered — none of them throw, so none are captured, and
|
|
205
|
+
you hear about them weeks later. `sendFeedback` captures that class, and it is actionable
|
|
206
|
+
because the breadcrumb trail is already in memory when the user hits send: *"the discount
|
|
207
|
+
didn't apply"* is a complaint; the same sentence plus `nav→Checkout · apply_discount ·
|
|
208
|
+
total_recalculated · tap_place_order` is a reproduction.
|
|
209
|
+
|
|
210
|
+
It carries everything a log carries — breadcrumbs, `screen`, `userId`, `releaseId`,
|
|
211
|
+
`platform`, `browser`, and any labels seeded via `setUser`.
|
|
212
|
+
|
|
213
|
+
Headless: the package renders nothing, so the UI is yours. It returns nothing either —
|
|
214
|
+
a reference number is meaningless to a user with no portal to check it against. Say thank
|
|
215
|
+
you and move on; if the app wants correlation, pass its own id as a label.
|
|
216
|
+
|
|
217
|
+
Entries are written at **`NOTICE`**, which ranks between `INFO` and `WARNING`: feedback
|
|
218
|
+
from a person outranks routine status, and is not a warning about system health. Find it
|
|
219
|
+
with the severity dropdown, or `labels.feedback="true"`. Any existing `severity >= WARNING`
|
|
220
|
+
alert ignores it with no configuration.
|
|
221
|
+
|
|
222
|
+
Feedback is exempt from the severity floor and the rate limiter — those are volume controls
|
|
223
|
+
for events the system emits, and someone hitting send twice is not a duplicate to throttle.
|
|
224
|
+
|
|
163
225
|
## Functions API
|
|
164
226
|
|
|
165
227
|
```ts
|
|
@@ -185,11 +247,14 @@ withLogging(
|
|
|
185
247
|
)
|
|
186
248
|
```
|
|
187
249
|
|
|
188
|
-
>
|
|
189
|
-
> `
|
|
190
|
-
>
|
|
191
|
-
>
|
|
192
|
-
>
|
|
250
|
+
> **Migrating from `initRequestLogger`?** It was removed in 0.6.0 — wrap the handler in
|
|
251
|
+
> `withLogging` instead of calling it as the first line. It bound the scope with
|
|
252
|
+
> `enterWith()`, which is never unwound, so the labels outlived the request and a later
|
|
253
|
+
> handler that did *not* call it (a scheduled function, a Firestore trigger, or anything
|
|
254
|
+
> relying on `getLogger()`'s anonymous fallback) inherited whichever user last touched the
|
|
255
|
+
> warm instance. Codebases where every handler called it were unaffected; the hazard was
|
|
256
|
+
> the ones that didn't. `withLogging` uses `run()`, which restores the previous scope when
|
|
257
|
+
> the handler settles.
|
|
193
258
|
|
|
194
259
|
Backend log methods also accept an optional `attachments` (`Record<string, string | Buffer>`).
|
|
195
260
|
|
|
@@ -214,7 +279,7 @@ npx fsl install-skills
|
|
|
214
279
|
|
|
215
280
|
| Skill | Description |
|
|
216
281
|
|-------|-------------|
|
|
217
|
-
| `/logs` | Validate logging in a file — error paths, labels, PII,
|
|
282
|
+
| `/logs` | Validate logging in a file — error paths, labels, PII, unwrapped handlers, breadcrumbs |
|
|
218
283
|
| `/query-logs` | Query Cloud Logging or local JSONL via [firebase-mcp-server](https://github.com/dasasian/firebase-mcp-server) |
|
|
219
284
|
|
|
220
285
|
## Local log rotation
|
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import type { BreadcrumbEntry } from '../shared/types';
|
|
2
|
+
/**
|
|
3
|
+
* How many breadcrumbs are retained — and therefore how many are sent.
|
|
4
|
+
*
|
|
5
|
+
* These were two numbers once: the trail kept 50 and the send path asked for
|
|
6
|
+
* 20, so 30 were retained that nothing could ever read. Exported for the send
|
|
7
|
+
* path to import rather than restate, because the two drifting apart is silent
|
|
8
|
+
* — the extra entries simply never appear in any log, and nothing fails.
|
|
9
|
+
*
|
|
10
|
+
* MAX_AGE_MS is the filter that actually matters; this is the safety net that
|
|
11
|
+
* bounds a long-lived tab. Sending the whole retained trail costs ~5 KB, which
|
|
12
|
+
* is 2% of Cloud Logging's 256 KB entry limit, against losing the steps that
|
|
13
|
+
* would have reproduced the bug.
|
|
14
|
+
*/
|
|
15
|
+
export declare const MAX_BREADCRUMBS = 50;
|
|
2
16
|
export declare function setCurrentScreen(screen: string): void;
|
|
3
|
-
export declare function setActiveActivity(activity: string | undefined): void;
|
|
4
17
|
export declare function getCurrentScreen(): string | undefined;
|
|
5
|
-
export declare function getActiveActivity(): string | undefined;
|
|
6
18
|
export declare function addBreadcrumb(type: BreadcrumbEntry['type'], name: string, data?: Record<string, unknown>): void;
|
|
7
19
|
export declare function getLastBreadcrumbs(count: number): BreadcrumbEntry[];
|
|
8
20
|
export declare function clearBreadcrumbs(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"breadcrumbs.d.ts","sourceRoot":"","sources":["../../src/client/breadcrumbs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"breadcrumbs.d.ts","sourceRoot":"","sources":["../../src/client/breadcrumbs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,eAAe,KAAK,CAAA;AAMjC,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAGrD;AAED,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,SAAS,CAErD;AAoBD,wBAAgB,aAAa,CAC3B,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,EAC7B,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,IAAI,CAQN;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,EAAE,CAKnE;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAGvC;AAED,eAAO,MAAM,EAAE;mBACE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;kBACtC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;kBACpC,MAAM;kBACR,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CACtD,CAAA"}
|
|
@@ -1,52 +1,70 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.bc = void 0;
|
|
3
|
+
exports.bc = exports.MAX_BREADCRUMBS = void 0;
|
|
4
4
|
exports.setCurrentScreen = setCurrentScreen;
|
|
5
|
-
exports.setActiveActivity = setActiveActivity;
|
|
6
5
|
exports.getCurrentScreen = getCurrentScreen;
|
|
7
|
-
exports.getActiveActivity = getActiveActivity;
|
|
8
6
|
exports.addBreadcrumb = addBreadcrumb;
|
|
9
7
|
exports.getLastBreadcrumbs = getLastBreadcrumbs;
|
|
10
8
|
exports.clearBreadcrumbs = clearBreadcrumbs;
|
|
11
|
-
|
|
9
|
+
/**
|
|
10
|
+
* How many breadcrumbs are retained — and therefore how many are sent.
|
|
11
|
+
*
|
|
12
|
+
* These were two numbers once: the trail kept 50 and the send path asked for
|
|
13
|
+
* 20, so 30 were retained that nothing could ever read. Exported for the send
|
|
14
|
+
* path to import rather than restate, because the two drifting apart is silent
|
|
15
|
+
* — the extra entries simply never appear in any log, and nothing fails.
|
|
16
|
+
*
|
|
17
|
+
* MAX_AGE_MS is the filter that actually matters; this is the safety net that
|
|
18
|
+
* bounds a long-lived tab. Sending the whole retained trail costs ~5 KB, which
|
|
19
|
+
* is 2% of Cloud Logging's 256 KB entry limit, against losing the steps that
|
|
20
|
+
* would have reproduced the bug.
|
|
21
|
+
*/
|
|
22
|
+
exports.MAX_BREADCRUMBS = 50;
|
|
12
23
|
const MAX_AGE_MS = 5 * 60 * 1000; // 5 minutes
|
|
13
24
|
let currentScreen;
|
|
14
|
-
let activeActivity;
|
|
15
25
|
let breadcrumbs = [];
|
|
16
26
|
function setCurrentScreen(screen) {
|
|
17
27
|
currentScreen = screen;
|
|
18
28
|
addBreadcrumb('nav', `navigate_${screen}`);
|
|
19
29
|
}
|
|
20
|
-
function setActiveActivity(activity) {
|
|
21
|
-
activeActivity = activity;
|
|
22
|
-
}
|
|
23
30
|
function getCurrentScreen() {
|
|
24
31
|
return currentScreen;
|
|
25
32
|
}
|
|
26
|
-
|
|
27
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Drop anything past the age cutoff.
|
|
35
|
+
*
|
|
36
|
+
* Applied on READ as well as on write. Expiring only on write means the cutoff
|
|
37
|
+
* lapses exactly when nothing is happening — a user who goes idle for ten
|
|
38
|
+
* minutes and then hits an error sends a trail of ten-minute-old steps
|
|
39
|
+
* presented as the path that led there. The steps before a pause are rarely
|
|
40
|
+
* the ones that explain what happened after it.
|
|
41
|
+
*
|
|
42
|
+
* Entries are appended in timestamp order, so the array only needs rebuilding
|
|
43
|
+
* when the oldest one has actually aged out.
|
|
44
|
+
*/
|
|
45
|
+
function unexpired(entries, now) {
|
|
46
|
+
const cutoff = now - MAX_AGE_MS;
|
|
47
|
+
if (entries.length === 0 || entries[0].timestamp > cutoff)
|
|
48
|
+
return entries;
|
|
49
|
+
return entries.filter((bc) => bc.timestamp > cutoff);
|
|
28
50
|
}
|
|
29
51
|
function addBreadcrumb(type, name, data) {
|
|
30
52
|
const now = Date.now();
|
|
31
|
-
|
|
32
|
-
breadcrumbs
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const cutoff = now - MAX_AGE_MS;
|
|
36
|
-
if (breadcrumbs[0].timestamp <= cutoff) {
|
|
37
|
-
breadcrumbs = breadcrumbs.filter((bc) => bc.timestamp > cutoff);
|
|
38
|
-
}
|
|
39
|
-
if (breadcrumbs.length > MAX_BREADCRUMBS) {
|
|
40
|
-
breadcrumbs = breadcrumbs.slice(breadcrumbs.length - MAX_BREADCRUMBS);
|
|
53
|
+
breadcrumbs.push({ timestamp: now, type, name, data });
|
|
54
|
+
breadcrumbs = unexpired(breadcrumbs, now);
|
|
55
|
+
if (breadcrumbs.length > exports.MAX_BREADCRUMBS) {
|
|
56
|
+
breadcrumbs = breadcrumbs.slice(breadcrumbs.length - exports.MAX_BREADCRUMBS);
|
|
41
57
|
}
|
|
42
58
|
}
|
|
43
59
|
function getLastBreadcrumbs(count) {
|
|
60
|
+
// Prune the stored trail too, so an idle tab does not hold expired entries
|
|
61
|
+
// alive until the next write.
|
|
62
|
+
breadcrumbs = unexpired(breadcrumbs, Date.now());
|
|
44
63
|
return breadcrumbs.slice(Math.max(0, breadcrumbs.length - count));
|
|
45
64
|
}
|
|
46
65
|
function clearBreadcrumbs() {
|
|
47
66
|
breadcrumbs = [];
|
|
48
67
|
currentScreen = undefined;
|
|
49
|
-
activeActivity = undefined;
|
|
50
68
|
}
|
|
51
69
|
exports.bc = {
|
|
52
70
|
action: (name, data) => addBreadcrumb('action', name, data),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"breadcrumbs.js","sourceRoot":"","sources":["../../src/client/breadcrumbs.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"breadcrumbs.js","sourceRoot":"","sources":["../../src/client/breadcrumbs.ts"],"names":[],"mappings":";;;AAqBA,4CAGC;AAED,4CAEC;AAoBD,sCAYC;AAED,gDAKC;AAED,4CAGC;AAtED;;;;;;;;;;;;GAYG;AACU,QAAA,eAAe,GAAG,EAAE,CAAA;AACjC,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAA,CAAC,YAAY;AAE7C,IAAI,aAAiC,CAAA;AACrC,IAAI,WAAW,GAAsB,EAAE,CAAA;AAEvC,SAAgB,gBAAgB,CAAC,MAAc;IAC7C,aAAa,GAAG,MAAM,CAAA;IACtB,aAAa,CAAC,KAAK,EAAE,YAAY,MAAM,EAAE,CAAC,CAAA;AAC5C,CAAC;AAED,SAAgB,gBAAgB;IAC9B,OAAO,aAAa,CAAA;AACtB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAS,SAAS,CAAC,OAA0B,EAAE,GAAW;IACxD,MAAM,MAAM,GAAG,GAAG,GAAG,UAAU,CAAA;IAC/B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,MAAM;QAAE,OAAO,OAAO,CAAA;IACzE,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,SAAS,GAAG,MAAM,CAAC,CAAA;AACtD,CAAC;AAED,SAAgB,aAAa,CAC3B,IAA6B,EAC7B,IAAY,EACZ,IAA8B;IAE9B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IACtB,WAAW,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;IACtD,WAAW,GAAG,SAAS,CAAC,WAAW,EAAE,GAAG,CAAC,CAAA;IAEzC,IAAI,WAAW,CAAC,MAAM,GAAG,uBAAe,EAAE,CAAC;QACzC,WAAW,GAAG,WAAW,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,GAAG,uBAAe,CAAC,CAAA;IACvE,CAAC;AACH,CAAC;AAED,SAAgB,kBAAkB,CAAC,KAAa;IAC9C,2EAA2E;IAC3E,8BAA8B;IAC9B,WAAW,GAAG,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;IAChD,OAAO,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAA;AACnE,CAAC;AAED,SAAgB,gBAAgB;IAC9B,WAAW,GAAG,EAAE,CAAA;IAChB,aAAa,GAAG,SAAS,CAAA;AAC3B,CAAC;AAEY,QAAA,EAAE,GAAG;IAChB,MAAM,EAAE,CAAC,IAAY,EAAE,IAA8B,EAAE,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC;IAC7F,KAAK,EAAG,CAAC,IAAY,EAAE,IAA8B,EAAE,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC;IAC5F,GAAG,EAAK,CAAC,MAAc,EAAE,EAAE,CAAC,gBAAgB,CAAC,MAAM,CAAC;IACpD,KAAK,EAAG,CAAC,IAAY,EAAE,IAA8B,EAAE,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC;CAC7F,CAAA"}
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { initLogger, getClientLogger, triggerTestLog } from './logger';
|
|
2
|
-
export type { Logger, InitLoggerConfig, RateLimitConfig } from './logger';
|
|
1
|
+
export { initLogger, getClientLogger, sendFeedback, triggerTestLog } from './logger';
|
|
2
|
+
export type { Logger, InitLoggerConfig, FeedbackOptions, RateLimitConfig } from './logger';
|
|
3
3
|
export { setupGlobalErrorHandler, handleReactError } from './errorHandler';
|
|
4
|
-
export { addBreadcrumb,
|
|
4
|
+
export { addBreadcrumb, bc } from './breadcrumbs';
|
|
5
5
|
export type { LogSeverity, LogPayload, BreadcrumbEntry, BaseLabels } from '../shared/types';
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAMpF,YAAY,EAAE,MAAM,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1F,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAC1E,OAAO,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,eAAe,CAAA;AACjD,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA"}
|
package/dist/client/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.bc = exports.
|
|
3
|
+
exports.bc = exports.addBreadcrumb = exports.handleReactError = exports.setupGlobalErrorHandler = exports.triggerTestLog = exports.sendFeedback = exports.getClientLogger = exports.initLogger = void 0;
|
|
4
4
|
var logger_1 = require("./logger");
|
|
5
5
|
Object.defineProperty(exports, "initLogger", { enumerable: true, get: function () { return logger_1.initLogger; } });
|
|
6
6
|
Object.defineProperty(exports, "getClientLogger", { enumerable: true, get: function () { return logger_1.getClientLogger; } });
|
|
7
|
+
Object.defineProperty(exports, "sendFeedback", { enumerable: true, get: function () { return logger_1.sendFeedback; } });
|
|
7
8
|
Object.defineProperty(exports, "triggerTestLog", { enumerable: true, get: function () { return logger_1.triggerTestLog; } });
|
|
8
9
|
var errorHandler_1 = require("./errorHandler");
|
|
9
10
|
Object.defineProperty(exports, "setupGlobalErrorHandler", { enumerable: true, get: function () { return errorHandler_1.setupGlobalErrorHandler; } });
|
|
10
11
|
Object.defineProperty(exports, "handleReactError", { enumerable: true, get: function () { return errorHandler_1.handleReactError; } });
|
|
11
12
|
var breadcrumbs_1 = require("./breadcrumbs");
|
|
12
13
|
Object.defineProperty(exports, "addBreadcrumb", { enumerable: true, get: function () { return breadcrumbs_1.addBreadcrumb; } });
|
|
13
|
-
Object.defineProperty(exports, "setActiveActivity", { enumerable: true, get: function () { return breadcrumbs_1.setActiveActivity; } });
|
|
14
14
|
Object.defineProperty(exports, "bc", { enumerable: true, get: function () { return breadcrumbs_1.bc; } });
|
|
15
15
|
//# sourceMappingURL=index.js.map
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":";;;AAAA,mCAAoF;AAA3E,oGAAA,UAAU,OAAA;AAAE,yGAAA,eAAe,OAAA;AAAE,sGAAA,YAAY,OAAA;AAAE,wGAAA,cAAc,OAAA;AAOlE,+CAA0E;AAAjE,uHAAA,uBAAuB,OAAA;AAAE,gHAAA,gBAAgB,OAAA;AAClD,6CAAiD;AAAxC,4GAAA,aAAa,OAAA;AAAE,iGAAA,EAAE,OAAA"}
|
package/dist/client/logger.d.ts
CHANGED
|
@@ -2,6 +2,12 @@ import type { LogSeverity, LogPayload, BaseLabels } from '../shared/types';
|
|
|
2
2
|
import { type RateLimitConfig } from './rateLimiter';
|
|
3
3
|
export type { RateLimitConfig };
|
|
4
4
|
type LogCallable = (data: LogPayload) => Promise<unknown>;
|
|
5
|
+
export interface FeedbackOptions<AppLabels extends Record<string, string | undefined> = Record<string, string | undefined>> {
|
|
6
|
+
/** A screenshot or any file. Rides the same GCS path as error attachments. */
|
|
7
|
+
attachments?: Record<string, Blob | File | string>;
|
|
8
|
+
/** Anything the app wants to tag — which widget, which flow, its own ticket id. */
|
|
9
|
+
labels?: Partial<AppLabels & BaseLabels>;
|
|
10
|
+
}
|
|
5
11
|
export interface InitLoggerConfig<AppLabels extends Record<string, string | undefined> = Record<string, string | undefined>> {
|
|
6
12
|
appId: string;
|
|
7
13
|
releaseId: string;
|
|
@@ -23,8 +29,31 @@ export declare class Logger<AppLabels extends Record<string, string | undefined>
|
|
|
23
29
|
info(message: string, labels?: Partial<AppLabels & BaseLabels>, context?: Record<string, unknown>, attachments?: Record<string, Blob | File | string>): void;
|
|
24
30
|
warning(message: string, labels?: Partial<AppLabels & BaseLabels>, context?: Record<string, unknown>, attachments?: Record<string, Blob | File | string>): void;
|
|
25
31
|
debug(message: string, labels?: Partial<AppLabels & BaseLabels>, context?: Record<string, unknown>, attachments?: Record<string, Blob | File | string>): void;
|
|
32
|
+
/**
|
|
33
|
+
* Send feedback a user typed, carrying everything the logger already knows.
|
|
34
|
+
*
|
|
35
|
+
* This exists because error tracking only sees things that throw. A button
|
|
36
|
+
* that does nothing, a total that comes out wrong, the wrong data rendered —
|
|
37
|
+
* none of them throw, so none are captured, and you hear about them weeks
|
|
38
|
+
* later. Feedback is the capture mechanism for that whole class, and it is
|
|
39
|
+
* actionable only because the breadcrumb trail is already in memory when the
|
|
40
|
+
* user hits send: "the discount didn't apply" is a complaint, the same
|
|
41
|
+
* sentence plus the trail is a reproduction.
|
|
42
|
+
*
|
|
43
|
+
* Headless — the app owns the UI. Returns nothing: a reference number is
|
|
44
|
+
* meaningless to a user with no portal to check it against. An app wanting
|
|
45
|
+
* correlation passes its own id as a label, which it knows before sending.
|
|
46
|
+
*/
|
|
47
|
+
sendFeedback(text: string, extras?: FeedbackOptions<AppLabels>): void;
|
|
26
48
|
private send;
|
|
27
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Send user feedback through the configured logger.
|
|
52
|
+
*
|
|
53
|
+
* Module-level for the same reason as `addBreadcrumb` and `bc`: the app calls
|
|
54
|
+
* it from wherever its feedback UI lives, without threading a logger through.
|
|
55
|
+
*/
|
|
56
|
+
export declare function sendFeedback<AppLabels extends Record<string, string | undefined> = Record<string, string | undefined>>(text: string, extras?: FeedbackOptions<AppLabels>): void;
|
|
28
57
|
/**
|
|
29
58
|
* Trigger a test log entry to verify the logging pipeline is working end-to-end.
|
|
30
59
|
* Logs at all severities with errorType: 'fsl-verify'. Safe to call in dev only.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/client/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAgB,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAWxF,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,eAAe,CAAA;AAEtB,YAAY,EAAE,eAAe,EAAE,CAAA;AAE/B,KAAK,WAAW,GAAG,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;AAEzD,MAAM,WAAW,gBAAgB,CAC/B,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAEzF,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,WAAW,CAAA;IACxB,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,gBAAgB,CAAC,EAAE,eAAe,CAAA;CACnC;AAmDD,qBAAa,MAAM,CACjB,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAEzF,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6B;IACpD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;IACjC,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,UAAU,CAAyB;gBAE/B,MAAM,EAAE,gBAAgB,CAAC,SAAS,CAAC;IAQ/C,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI;IAK5D,SAAS,IAAI,IAAI;IAMjB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B,aAAa,CACX,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,KAAK,GAAG,OAAO,EAC1C,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,IAAI;IAIP,KAAK,CACH,GAAG,EAAE,OAAO,EACZ,MAAM,CAAC,EAAE,OAAO,CAAC,SAAS,GAAG,UAAU,CAAC,EACxC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC,GACjD,IAAI;IAmBP,IAAI,CACF,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,OAAO,CAAC,SAAS,GAAG,UAAU,CAAC,EACxC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC,GACjD,IAAI;IAIP,OAAO,CACL,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,OAAO,CAAC,SAAS,GAAG,UAAU,CAAC,EACxC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC,GACjD,IAAI;IAIP,KAAK,CACH,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,OAAO,CAAC,SAAS,GAAG,UAAU,CAAC,EACxC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC,GACjD,IAAI;
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/client/logger.ts"],"names":[],"mappings":"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"}
|
package/dist/client/logger.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Logger = void 0;
|
|
4
|
+
exports.sendFeedback = sendFeedback;
|
|
4
5
|
exports.triggerTestLog = triggerTestLog;
|
|
5
6
|
exports.initLogger = initLogger;
|
|
6
7
|
exports.getClientLogger = getClientLogger;
|
|
@@ -86,7 +87,7 @@ class Logger {
|
|
|
86
87
|
errorType: error.name || 'UnknownError',
|
|
87
88
|
...labels,
|
|
88
89
|
};
|
|
89
|
-
void this.send(error.message, 'ERROR', errorLabels, context, attachments, (0, error_1.toErrorPayload)(error), (0, rateLimiter_1.signatureFor)(error, (0, breadcrumbs_1.getCurrentScreen)()
|
|
90
|
+
void this.send(error.message, 'ERROR', errorLabels, context, attachments, (0, error_1.toErrorPayload)(error), (0, rateLimiter_1.signatureFor)(error, (0, breadcrumbs_1.getCurrentScreen)()));
|
|
90
91
|
}
|
|
91
92
|
info(message, labels, context, attachments) {
|
|
92
93
|
void this.send(message, 'INFO', labels, context, attachments);
|
|
@@ -97,13 +98,37 @@ class Logger {
|
|
|
97
98
|
debug(message, labels, context, attachments) {
|
|
98
99
|
void this.send(message, 'DEBUG', labels, context, attachments);
|
|
99
100
|
}
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Send feedback a user typed, carrying everything the logger already knows.
|
|
103
|
+
*
|
|
104
|
+
* This exists because error tracking only sees things that throw. A button
|
|
105
|
+
* that does nothing, a total that comes out wrong, the wrong data rendered —
|
|
106
|
+
* none of them throw, so none are captured, and you hear about them weeks
|
|
107
|
+
* later. Feedback is the capture mechanism for that whole class, and it is
|
|
108
|
+
* actionable only because the breadcrumb trail is already in memory when the
|
|
109
|
+
* user hits send: "the discount didn't apply" is a complaint, the same
|
|
110
|
+
* sentence plus the trail is a reproduction.
|
|
111
|
+
*
|
|
112
|
+
* Headless — the app owns the UI. Returns nothing: a reference number is
|
|
113
|
+
* meaningless to a user with no portal to check it against. An app wanting
|
|
114
|
+
* correlation passes its own id as a label, which it knows before sending.
|
|
115
|
+
*/
|
|
116
|
+
sendFeedback(text, extras) {
|
|
117
|
+
void this.send(text, 'NOTICE', { [severity_1.FEEDBACK_LABEL]: 'true', ...extras?.labels }, undefined, extras?.attachments, undefined, undefined, true);
|
|
118
|
+
}
|
|
119
|
+
async send(message, severity, labels, context, attachments, error, signature,
|
|
120
|
+
// Both gates below are volume and cost controls for events the SYSTEM
|
|
121
|
+
// emits. Feedback is a person sending a message that happens to travel the
|
|
122
|
+
// same pipe — it is rare by nature and there is nothing to throttle. The
|
|
123
|
+
// exemption keys on the record being feedback, not on its severity, so a
|
|
124
|
+
// NOTICE emitted for anything else still behaves normally.
|
|
125
|
+
bypassVolumeControls = false) {
|
|
126
|
+
if (!bypassVolumeControls && severity_1.SEVERITY_ORDER[severity] > this.minLevel)
|
|
102
127
|
return;
|
|
103
128
|
// One gate for every severity. Passing a signature opts this log into
|
|
104
129
|
// duplicate suppression; the check and the budget spend are one operation,
|
|
105
130
|
// so a log can neither be counted twice nor checked without being counted.
|
|
106
|
-
const decision = (0, rateLimiter_1.allow)(signature);
|
|
131
|
+
const decision = bypassVolumeControls ? { allowed: true } : (0, rateLimiter_1.allow)(signature);
|
|
107
132
|
if (!decision.allowed) {
|
|
108
133
|
if (decision.reason === 'duplicate') {
|
|
109
134
|
console.warn(`[fsl] Duplicate suppressed: ${decision.signature}`);
|
|
@@ -156,7 +181,7 @@ class Logger {
|
|
|
156
181
|
severity,
|
|
157
182
|
labels: allLabels,
|
|
158
183
|
jsonPayload: {
|
|
159
|
-
breadcrumbs: (0, breadcrumbs_1.getLastBreadcrumbs)(
|
|
184
|
+
breadcrumbs: (0, breadcrumbs_1.getLastBreadcrumbs)(breadcrumbs_1.MAX_BREADCRUMBS),
|
|
160
185
|
context,
|
|
161
186
|
error,
|
|
162
187
|
},
|
|
@@ -170,6 +195,15 @@ class Logger {
|
|
|
170
195
|
}
|
|
171
196
|
}
|
|
172
197
|
exports.Logger = Logger;
|
|
198
|
+
/**
|
|
199
|
+
* Send user feedback through the configured logger.
|
|
200
|
+
*
|
|
201
|
+
* Module-level for the same reason as `addBreadcrumb` and `bc`: the app calls
|
|
202
|
+
* it from wherever its feedback UI lives, without threading a logger through.
|
|
203
|
+
*/
|
|
204
|
+
function sendFeedback(text, extras) {
|
|
205
|
+
getClientLogger().sendFeedback(text, extras);
|
|
206
|
+
}
|
|
173
207
|
/**
|
|
174
208
|
* Trigger a test log entry to verify the logging pipeline is working end-to-end.
|
|
175
209
|
* Logs at all severities with errorType: 'fsl-verify'. Safe to call in dev only.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/client/logger.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/client/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,10 +6,15 @@ export interface RateLimitConfig {
|
|
|
6
6
|
export declare function configureRateLimiter(options: RateLimitConfig): void;
|
|
7
7
|
/**
|
|
8
8
|
* Build the key used to recognise a repeat of the same problem. Two occurrences
|
|
9
|
-
* count as duplicates only if the message
|
|
10
|
-
*
|
|
9
|
+
* count as duplicates only if the message and the screen both match, so the same
|
|
10
|
+
* error from two different screens is not collapsed into one.
|
|
11
|
+
*
|
|
12
|
+
* This is coarse: the same error reached by two different paths on ONE screen
|
|
13
|
+
* still collapses, and the second path can be suppressed before anyone sees it.
|
|
14
|
+
* See #29 — deriving the path from breadcrumbs would discriminate properly,
|
|
15
|
+
* without the staleness of a span someone has to remember to clear.
|
|
11
16
|
*/
|
|
12
|
-
export declare function signatureFor(error: Error | string, screen?: string
|
|
17
|
+
export declare function signatureFor(error: Error | string, screen?: string): string;
|
|
13
18
|
export type RateLimitDecision = {
|
|
14
19
|
allowed: true;
|
|
15
20
|
} | {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rateLimiter.d.ts","sourceRoot":"","sources":["../../src/client/rateLimiter.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,eAAe;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAeD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAEnE;AAmBD
|
|
1
|
+
{"version":3,"file":"rateLimiter.d.ts","sourceRoot":"","sources":["../../src/client/rateLimiter.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,eAAe;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAeD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAEnE;AAmBD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,KAAK,GAAG,MAAM,EACrB,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,CAGR;AAED,MAAM,MAAM,iBAAiB,GACzB;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GACjB;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,eAAe,GAAG,WAAW,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAEjF;;;;;;;;;;;GAWG;AACH,wBAAgB,KAAK,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAkB3D;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAMvC"}
|
|
@@ -36,12 +36,17 @@ function writeState(state) {
|
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
38
|
* Build the key used to recognise a repeat of the same problem. Two occurrences
|
|
39
|
-
* count as duplicates only if the message
|
|
40
|
-
*
|
|
39
|
+
* count as duplicates only if the message and the screen both match, so the same
|
|
40
|
+
* error from two different screens is not collapsed into one.
|
|
41
|
+
*
|
|
42
|
+
* This is coarse: the same error reached by two different paths on ONE screen
|
|
43
|
+
* still collapses, and the second path can be suppressed before anyone sees it.
|
|
44
|
+
* See #29 — deriving the path from breadcrumbs would discriminate properly,
|
|
45
|
+
* without the staleness of a span someone has to remember to clear.
|
|
41
46
|
*/
|
|
42
|
-
function signatureFor(error, screen
|
|
47
|
+
function signatureFor(error, screen) {
|
|
43
48
|
const str = error instanceof Error ? `${error.name}:${error.message}` : String(error);
|
|
44
|
-
return `${str}|${screen ?? ''}
|
|
49
|
+
return `${str}|${screen ?? ''}`;
|
|
45
50
|
}
|
|
46
51
|
/**
|
|
47
52
|
* Decide whether one log may be sent, and consume its budget if so.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rateLimiter.js","sourceRoot":"","sources":["../../src/client/rateLimiter.ts"],"names":[],"mappings":";;AAuBA,oDAEC;
|
|
1
|
+
{"version":3,"file":"rateLimiter.js","sourceRoot":"","sources":["../../src/client/rateLimiter.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { LogWriter } from './logger';
|
|
2
2
|
export { initLogger } from './logger';
|
|
3
3
|
export type { LogWriter } from './logger';
|
|
4
|
-
export { withLogging,
|
|
4
|
+
export { withLogging, getLogger } from './requestLogger';
|
|
5
5
|
export { createClientLogHandler, createClientLogFunction } from './logHandler';
|
|
6
6
|
export type { ClientLogHandlerConfig } from './logHandler';
|
|
7
7
|
export type { LogSeverity, LogPayload, BaseLabels } from '../shared/types';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/functions/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEzC,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACrC,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/functions/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEzC,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACrC,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AACxD,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAC9E,YAAY,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AAC1D,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE1E,eAAO,MAAM,QAAQ,GAAI,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,KAAG,IAAkC,CAAA;AACrG,eAAO,MAAM,OAAO,GAAI,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,KAAG,IAAoC,CAAA;AACxG,eAAO,MAAM,OAAO,GAAI,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,KAAG,IAAiC,CAAA;AAClG,eAAO,MAAM,QAAQ,GAAI,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,KAAG,IAAkC,CAAA"}
|
package/dist/functions/index.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.logDebug = exports.logInfo = exports.logWarn = exports.logError = exports.createClientLogFunction = exports.createClientLogHandler = exports.getLogger = exports.
|
|
3
|
+
exports.logDebug = exports.logInfo = exports.logWarn = exports.logError = exports.createClientLogFunction = exports.createClientLogHandler = exports.getLogger = exports.withLogging = exports.initLogger = void 0;
|
|
4
4
|
const requestLogger_1 = require("./requestLogger");
|
|
5
5
|
var logger_1 = require("./logger");
|
|
6
6
|
Object.defineProperty(exports, "initLogger", { enumerable: true, get: function () { return logger_1.initLogger; } });
|
|
7
7
|
var requestLogger_2 = require("./requestLogger");
|
|
8
8
|
Object.defineProperty(exports, "withLogging", { enumerable: true, get: function () { return requestLogger_2.withLogging; } });
|
|
9
|
-
Object.defineProperty(exports, "initRequestLogger", { enumerable: true, get: function () { return requestLogger_2.initRequestLogger; } });
|
|
10
9
|
Object.defineProperty(exports, "getLogger", { enumerable: true, get: function () { return requestLogger_2.getLogger; } });
|
|
11
10
|
var logHandler_1 = require("./logHandler");
|
|
12
11
|
Object.defineProperty(exports, "createClientLogHandler", { enumerable: true, get: function () { return logHandler_1.createClientLogHandler; } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/functions/index.ts"],"names":[],"mappings":";;;AAAA,mDAA2C;AAG3C,mCAAqC;AAA5B,oGAAA,UAAU,OAAA;AAEnB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/functions/index.ts"],"names":[],"mappings":";;;AAAA,mDAA2C;AAG3C,mCAAqC;AAA5B,oGAAA,UAAU,OAAA;AAEnB,iDAAwD;AAA/C,4GAAA,WAAW,OAAA;AAAE,0GAAA,SAAS,OAAA;AAC/B,2CAA8E;AAArE,oHAAA,sBAAsB,OAAA;AAAE,qHAAA,uBAAuB,OAAA;AAIjD,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAoC,EAAQ,EAAE,CAAC,IAAA,yBAAS,GAAE,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAA;AAAxF,QAAA,QAAQ,YAAgF;AAC9F,MAAM,OAAO,GAAG,CAAC,GAAG,IAAsC,EAAQ,EAAE,CAAC,IAAA,yBAAS,GAAE,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAA;AAA3F,QAAA,OAAO,WAAoF;AACjG,MAAM,OAAO,GAAG,CAAC,GAAG,IAAmC,EAAQ,EAAE,CAAC,IAAA,yBAAS,GAAE,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAA;AAArF,QAAA,OAAO,WAA8E;AAC3F,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAoC,EAAQ,EAAE,CAAC,IAAA,yBAAS,GAAE,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAA;AAAxF,QAAA,QAAQ,YAAgF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/functions/logger.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAM/D,eAAO,MAAM,YAAY,cAAc,CAAC;AAExC,UAAU,qBAAqB;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAKD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,qBAAqB,GAAG,IAAI,CAQ9D;
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/functions/logger.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAM/D,eAAO,MAAM,YAAY,cAAc,CAAC;AAExC,UAAU,qBAAqB;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAKD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,qBAAqB,GAAG,IAAI,CAQ9D;AAoFD;;GAEG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAC1C,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CASxB;AAED;;GAEG;AACH,wBAAgB,QAAQ,CACtB,OAAO,EAAE,UAAU,GAAG;IAAE,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAClE,IAAI,CAkGN;AAaD;;GAEG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;eAuBrC,OAAO,WACH,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,YACjC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,gBACnB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAC5C,IAAI;oBAWI,MAAM,WACN,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,YACjC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,gBACnB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KAC5C,IAAI;uBAEI,MAAM,WACN,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,YACjC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,gBACnB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KAC5C,IAAI;qBAEI,MAAM,WACN,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,YACjC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,gBACnB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KAC5C,IAAI;EAEV;AAED,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAG3D,YAAY,EAAE,WAAW,EAAE,CAAC"}
|
package/dist/functions/logger.js
CHANGED
|
@@ -108,9 +108,24 @@ async function uploadLogAttachments(logId, logAttachments) {
|
|
|
108
108
|
await file.save(Buffer.from(data, "base64"));
|
|
109
109
|
}));
|
|
110
110
|
}
|
|
111
|
+
// Warn once per bad value: writeLog is on the per-log path, and a caller with a
|
|
112
|
+
// broken severity will hit it every time.
|
|
113
|
+
const warnedSeverities = new Set();
|
|
114
|
+
function coerceUnknownSeverity(value) {
|
|
115
|
+
const shown = typeof value === "string" ? value : String(value);
|
|
116
|
+
if (!warnedSeverities.has(shown)) {
|
|
117
|
+
warnedSeverities.add(shown);
|
|
118
|
+
console.warn(`[fsl] Unknown severity ${JSON.stringify(shown)} — writing as ERROR. ` +
|
|
119
|
+
`Valid values: ${severity_1.SEVERITIES.join(", ")}.`);
|
|
120
|
+
}
|
|
121
|
+
return "ERROR";
|
|
122
|
+
}
|
|
111
123
|
const CONSOLE_FN = {
|
|
112
124
|
ERROR: console.error,
|
|
113
125
|
WARNING: console.warn,
|
|
126
|
+
// There is no console.notice. firebase-functions maps NOTICE to console.info
|
|
127
|
+
// on the production side, so match that rather than inventing a mapping.
|
|
128
|
+
NOTICE: console.info,
|
|
114
129
|
DEBUG: console.debug,
|
|
115
130
|
INFO: console.log,
|
|
116
131
|
};
|
|
@@ -132,9 +147,31 @@ function cleanLabels(labels) {
|
|
|
132
147
|
* Write a structured log entry. Transport depends on environment.
|
|
133
148
|
*/
|
|
134
149
|
function writeLog(payload) {
|
|
150
|
+
// An unrecognised severity is not merely mislabelled — it is fatal. Both
|
|
151
|
+
// dispatches look the value up in a fixed table: CONSOLE_FN below, and
|
|
152
|
+
// firebase-functions' own CONSOLE_SEVERITY inside ffWrite. A miss resolves to
|
|
153
|
+
// undefined and calling it throws, which Logger.send()'s catch then swallows,
|
|
154
|
+
// so the entry disappears with no useful diagnostic — and only in production,
|
|
155
|
+
// since the emulator takes the other branch.
|
|
156
|
+
//
|
|
157
|
+
// It also slips past the floor: SEVERITY_ORDER[unknown] is undefined, and
|
|
158
|
+
// `undefined > n` is false, so the check below would not have stopped it.
|
|
159
|
+
//
|
|
160
|
+
// writeLog is exported, so a caller can reach this with a value read from
|
|
161
|
+
// config, crossing a type boundary, or from plain JavaScript. Coerce to ERROR
|
|
162
|
+
// rather than drop: an entry arriving loud beats one vanishing quietly.
|
|
163
|
+
const severity = (0, severity_1.isLogSeverity)(payload.severity)
|
|
164
|
+
? payload.severity
|
|
165
|
+
: coerceUnknownSeverity(payload.severity);
|
|
166
|
+
// Feedback bypasses the floor. The client already bypassed its own, but the
|
|
167
|
+
// payload still passes through here on its way to Cloud Logging, and this
|
|
168
|
+
// floor defaults to WARNING in production — so without the exemption every
|
|
169
|
+
// report would be dropped here instead. Verified: it was.
|
|
135
170
|
const minSeverity = globalConfig?.minSeverity ?? (IS_EMULATOR ? "DEBUG" : "WARNING");
|
|
136
|
-
if (severity_1.
|
|
171
|
+
if (!(0, severity_1.isFeedback)(payload.labels) &&
|
|
172
|
+
severity_1.SEVERITY_ORDER[severity] > severity_1.SEVERITY_ORDER[minSeverity]) {
|
|
137
173
|
return;
|
|
174
|
+
}
|
|
138
175
|
const logId = (0, ulid_1.ulid)();
|
|
139
176
|
const hasAttachments = payload.attachments && Object.keys(payload.attachments).length > 0;
|
|
140
177
|
const labels = {
|
|
@@ -152,7 +189,7 @@ function writeLog(payload) {
|
|
|
152
189
|
try {
|
|
153
190
|
const entry = {
|
|
154
191
|
timestamp: new Date().toISOString(),
|
|
155
|
-
severity
|
|
192
|
+
severity,
|
|
156
193
|
message: payload.message,
|
|
157
194
|
labels,
|
|
158
195
|
jsonPayload: payload.jsonPayload,
|
|
@@ -176,7 +213,7 @@ function writeLog(payload) {
|
|
|
176
213
|
}
|
|
177
214
|
}
|
|
178
215
|
// Also write to console for immediate visibility
|
|
179
|
-
CONSOLE_FN[
|
|
216
|
+
CONSOLE_FN[severity](`[${severity}] ${payload.message}`, labels);
|
|
180
217
|
return;
|
|
181
218
|
}
|
|
182
219
|
// Production: use firebase-functions/logger write() directly, bypassing entryFromArgs.
|
|
@@ -190,7 +227,7 @@ function writeLog(payload) {
|
|
|
190
227
|
// filters match nothing. Verified live: the smoke run's entry labels contained only
|
|
191
228
|
// Cloud Functions' own platform labels until this changed.
|
|
192
229
|
(0, logger_1.write)({
|
|
193
|
-
severity
|
|
230
|
+
severity,
|
|
194
231
|
message: payload.message,
|
|
195
232
|
"logging.googleapis.com/labels": labels,
|
|
196
233
|
...payload.jsonPayload,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/functions/logger.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,gCAQC;
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/functions/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -28,30 +28,12 @@ interface RequestLoggerOptions<AppLabels extends Record<string, string | undefin
|
|
|
28
28
|
* )
|
|
29
29
|
*/
|
|
30
30
|
export declare function withLogging<AppLabels extends Record<string, string | undefined> = Record<string, string | undefined>, Req extends CallableRequest = CallableRequest, Res = unknown>(options: RequestLoggerOptions<AppLabels> | ((request: Req) => RequestLoggerOptions<AppLabels>), handler: (request: Req) => Res | Promise<Res>): (request: Req) => Promise<Res>;
|
|
31
|
-
/**
|
|
32
|
-
* Initialize a request-scoped logger and store it in AsyncLocalStorage.
|
|
33
|
-
*
|
|
34
|
-
* @deprecated Use {@link withLogging}. This leaks the request scope: it binds
|
|
35
|
-
* with `AsyncLocalStorage.enterWith()`, which persists for the remainder of the
|
|
36
|
-
* execution context and is never unwound. The labels therefore outlive the
|
|
37
|
-
* request, and any later handler that does NOT call this — a scheduled
|
|
38
|
-
* function, a Firestore or Storage trigger, or anything relying on
|
|
39
|
-
* `getLogger()`'s anonymous fallback — inherits whichever user last touched the
|
|
40
|
-
* warm instance.
|
|
41
|
-
*
|
|
42
|
-
* Handlers that all call this are unaffected: each overwrites the store with
|
|
43
|
-
* its own labels. The hazard is the handlers that do not.
|
|
44
|
-
*
|
|
45
|
-
* Removed in 0.6.0.
|
|
46
|
-
*/
|
|
47
|
-
export declare function initRequestLogger<AppLabels extends Record<string, string | undefined> = Record<string, string | undefined>>(request: CallableRequest, extra?: RequestLoggerOptions<AppLabels>): LogWriter;
|
|
48
31
|
/**
|
|
49
32
|
* Retrieve the request-scoped logger.
|
|
50
33
|
*
|
|
51
34
|
* Returns an anonymous writer (no request labels) when called outside a
|
|
52
|
-
* request.
|
|
53
|
-
*
|
|
54
|
-
* request's writer can still be in scope here.
|
|
35
|
+
* request. `withLogging` unwinds the scope when a handler settles, so this is
|
|
36
|
+
* genuinely anonymous rather than whatever ran last.
|
|
55
37
|
*/
|
|
56
38
|
export declare function getLogger(): LogWriter;
|
|
57
39
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"requestLogger.d.ts","sourceRoot":"","sources":["../../src/functions/requestLogger.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAClE,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,UAAU,CAAA;AAIvE,UAAU,oBAAoB,CAAC,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACjF,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,CAAA;CAC5B;AAgBD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,WAAW,CACzB,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACzF,GAAG,SAAS,eAAe,GAAG,eAAe,EAC7C,GAAG,GAAG,OAAO,EAEb,OAAO,EACH,oBAAoB,CAAC,SAAS,CAAC,GAC/B,CAAC,CAAC,OAAO,EAAE,GAAG,KAAK,oBAAoB,CAAC,SAAS,CAAC,CAAC,EACvD,OAAO,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,GAC5C,CAAC,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAMhC;AAED
|
|
1
|
+
{"version":3,"file":"requestLogger.d.ts","sourceRoot":"","sources":["../../src/functions/requestLogger.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAClE,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,UAAU,CAAA;AAIvE,UAAU,oBAAoB,CAAC,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACjF,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,CAAA;CAC5B;AAgBD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,WAAW,CACzB,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACzF,GAAG,SAAS,eAAe,GAAG,eAAe,EAC7C,GAAG,GAAG,OAAO,EAEb,OAAO,EACH,oBAAoB,CAAC,SAAS,CAAC,GAC/B,CAAC,CAAC,OAAO,EAAE,GAAG,KAAK,oBAAoB,CAAC,SAAS,CAAC,CAAC,EACvD,OAAO,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,GAC5C,CAAC,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAMhC;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,IAAI,SAAS,CAErC"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.withLogging = withLogging;
|
|
4
|
-
exports.initRequestLogger = initRequestLogger;
|
|
5
4
|
exports.getLogger = getLogger;
|
|
6
5
|
const async_hooks_1 = require("async_hooks");
|
|
7
6
|
const logger_1 = require("./logger");
|
|
@@ -43,48 +42,12 @@ function withLogging(options, handler) {
|
|
|
43
42
|
return storage.run(writer, async () => handler(request));
|
|
44
43
|
};
|
|
45
44
|
}
|
|
46
|
-
/**
|
|
47
|
-
* Initialize a request-scoped logger and store it in AsyncLocalStorage.
|
|
48
|
-
*
|
|
49
|
-
* @deprecated Use {@link withLogging}. This leaks the request scope: it binds
|
|
50
|
-
* with `AsyncLocalStorage.enterWith()`, which persists for the remainder of the
|
|
51
|
-
* execution context and is never unwound. The labels therefore outlive the
|
|
52
|
-
* request, and any later handler that does NOT call this — a scheduled
|
|
53
|
-
* function, a Firestore or Storage trigger, or anything relying on
|
|
54
|
-
* `getLogger()`'s anonymous fallback — inherits whichever user last touched the
|
|
55
|
-
* warm instance.
|
|
56
|
-
*
|
|
57
|
-
* Handlers that all call this are unaffected: each overwrites the store with
|
|
58
|
-
* its own labels. The hazard is the handlers that do not.
|
|
59
|
-
*
|
|
60
|
-
* Removed in 0.6.0.
|
|
61
|
-
*/
|
|
62
|
-
function initRequestLogger(request, extra) {
|
|
63
|
-
warnDeprecated(extra?.functionName);
|
|
64
|
-
const writer = writerFor(request, extra);
|
|
65
|
-
storage.enterWith(writer);
|
|
66
|
-
return writer;
|
|
67
|
-
}
|
|
68
|
-
// Warn once per function name rather than per invocation — a per-request warning
|
|
69
|
-
// on a hot path is noise that gets filtered out and then ignored.
|
|
70
|
-
const warned = new Set();
|
|
71
|
-
function warnDeprecated(functionName) {
|
|
72
|
-
const key = functionName ?? '(unnamed)';
|
|
73
|
-
if (warned.has(key))
|
|
74
|
-
return;
|
|
75
|
-
warned.add(key);
|
|
76
|
-
console.warn(`[fsl] initRequestLogger() is deprecated (${key}). It binds the request scope with ` +
|
|
77
|
-
'enterWith(), which is never unwound — the labels outlive the request, so a later ' +
|
|
78
|
-
'handler that does not call it inherits this request\'s userId. Wrap the handler ' +
|
|
79
|
-
'with withLogging() instead. Removed in 0.6.0.');
|
|
80
|
-
}
|
|
81
45
|
/**
|
|
82
46
|
* Retrieve the request-scoped logger.
|
|
83
47
|
*
|
|
84
48
|
* Returns an anonymous writer (no request labels) when called outside a
|
|
85
|
-
* request.
|
|
86
|
-
*
|
|
87
|
-
* request's writer can still be in scope here.
|
|
49
|
+
* request. `withLogging` unwinds the scope when a handler settles, so this is
|
|
50
|
+
* genuinely anonymous rather than whatever ran last.
|
|
88
51
|
*/
|
|
89
52
|
function getLogger() {
|
|
90
53
|
return storage.getStore() ?? (0, logger_1.createLogWriter)({});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"requestLogger.js","sourceRoot":"","sources":["../../src/functions/requestLogger.ts"],"names":[],"mappings":";;AAgDA,kCAeC;
|
|
1
|
+
{"version":3,"file":"requestLogger.js","sourceRoot":"","sources":["../../src/functions/requestLogger.ts"],"names":[],"mappings":";;AAgDA,kCAeC;AASD,8BAEC;AA1ED,6CAA+C;AAE/C,qCAAuE;AAEvE,MAAM,OAAO,GAAG,IAAI,+BAAiB,EAAa,CAAA;AAQlD,SAAS,SAAS,CAChB,OAAwB,EACxB,KAAuC;IAEvC,OAAO,IAAA,wBAAe,EACpB,IAAA,oBAAW,EAAC;QACV,YAAY,EAAE,KAAK,EAAE,YAAY;QACjC,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,GAAG;QACzB,KAAK,EAAE,KAAK,EAAE,KAAK;QACnB,GAAG,KAAK,EAAE,MAAM;KACjB,CAAC,CACH,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,SAAgB,WAAW,CAKzB,OAEuD,EACvD,OAA6C;IAE7C,OAAO,CAAC,OAAY,EAAE,EAAE;QACtB,MAAM,QAAQ,GAAG,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;QAC3E,MAAM,MAAM,GAAG,SAAS,CAAY,OAAO,EAAE,QAAQ,CAAC,CAAA;QACtD,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAA;IAC1D,CAAC,CAAA;AACH,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,SAAS;IACvB,OAAO,OAAO,CAAC,QAAQ,EAAE,IAAI,IAAA,wBAAe,EAAC,EAAE,CAAC,CAAA;AAClD,CAAC"}
|
|
@@ -1,9 +1,30 @@
|
|
|
1
1
|
import type { LogSeverity } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* Single source of truth for severity ranking. Lower number = more severe.
|
|
4
|
+
*
|
|
5
|
+
* NOTICE ranks between WARNING and INFO, matching Cloud Logging's own ordering
|
|
6
|
+
* (INFO 200, NOTICE 300, WARNING 400). Its official meaning — "normal but
|
|
7
|
+
* significant events" — is where user feedback belongs: more important than
|
|
8
|
+
* routine status, not a warning about system health.
|
|
4
9
|
* Both the client and functions loggers filter against this, so a change here
|
|
5
10
|
* keeps min-level behaviour consistent on both sides of the wire.
|
|
6
11
|
*/
|
|
7
12
|
export declare const SEVERITY_ORDER: Record<LogSeverity, number>;
|
|
8
13
|
export declare const SEVERITIES: LogSeverity[];
|
|
14
|
+
/** Is this a severity the loggers know how to dispatch? */
|
|
15
|
+
export declare function isLogSeverity(value: unknown): value is LogSeverity;
|
|
16
|
+
/**
|
|
17
|
+
* Label marking an entry as user feedback rather than a system event.
|
|
18
|
+
*
|
|
19
|
+
* The severity floors are volume and cost controls for events the system emits.
|
|
20
|
+
* Feedback is a person sending a message that happens to travel the same pipe,
|
|
21
|
+
* so it is exempt from them — on BOTH sides, since the client and the functions
|
|
22
|
+
* logger each have their own floor and either one would drop it silently.
|
|
23
|
+
*
|
|
24
|
+
* The exemption keys on this marker, not on the NOTICE severity. A NOTICE
|
|
25
|
+
* emitted for anything else still respects the floor, otherwise we would have
|
|
26
|
+
* built a severity level that silently defeats filtering.
|
|
27
|
+
*/
|
|
28
|
+
export declare const FEEDBACK_LABEL = "feedback";
|
|
29
|
+
export declare function isFeedback(labels: Record<string, unknown> | undefined): boolean;
|
|
9
30
|
//# sourceMappingURL=severity.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"severity.d.ts","sourceRoot":"","sources":["../../src/shared/severity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAE1C
|
|
1
|
+
{"version":3,"file":"severity.d.ts","sourceRoot":"","sources":["../../src/shared/severity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAE1C;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAMtD,CAAA;AAED,eAAO,MAAM,UAAU,EAAkC,WAAW,EAAE,CAAA;AAEtE,2DAA2D;AAC3D,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAElE;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,aAAa,CAAA;AAExC,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,OAAO,CAE/E"}
|
package/dist/shared/severity.js
CHANGED
|
@@ -1,16 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SEVERITIES = exports.SEVERITY_ORDER = void 0;
|
|
3
|
+
exports.FEEDBACK_LABEL = exports.SEVERITIES = exports.SEVERITY_ORDER = void 0;
|
|
4
|
+
exports.isLogSeverity = isLogSeverity;
|
|
5
|
+
exports.isFeedback = isFeedback;
|
|
4
6
|
/**
|
|
5
7
|
* Single source of truth for severity ranking. Lower number = more severe.
|
|
8
|
+
*
|
|
9
|
+
* NOTICE ranks between WARNING and INFO, matching Cloud Logging's own ordering
|
|
10
|
+
* (INFO 200, NOTICE 300, WARNING 400). Its official meaning — "normal but
|
|
11
|
+
* significant events" — is where user feedback belongs: more important than
|
|
12
|
+
* routine status, not a warning about system health.
|
|
6
13
|
* Both the client and functions loggers filter against this, so a change here
|
|
7
14
|
* keeps min-level behaviour consistent on both sides of the wire.
|
|
8
15
|
*/
|
|
9
16
|
exports.SEVERITY_ORDER = {
|
|
10
17
|
ERROR: 0,
|
|
11
18
|
WARNING: 1,
|
|
12
|
-
|
|
13
|
-
|
|
19
|
+
NOTICE: 2,
|
|
20
|
+
INFO: 3,
|
|
21
|
+
DEBUG: 4,
|
|
14
22
|
};
|
|
15
23
|
exports.SEVERITIES = Object.keys(exports.SEVERITY_ORDER);
|
|
24
|
+
/** Is this a severity the loggers know how to dispatch? */
|
|
25
|
+
function isLogSeverity(value) {
|
|
26
|
+
return typeof value === 'string' && Object.prototype.hasOwnProperty.call(exports.SEVERITY_ORDER, value);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Label marking an entry as user feedback rather than a system event.
|
|
30
|
+
*
|
|
31
|
+
* The severity floors are volume and cost controls for events the system emits.
|
|
32
|
+
* Feedback is a person sending a message that happens to travel the same pipe,
|
|
33
|
+
* so it is exempt from them — on BOTH sides, since the client and the functions
|
|
34
|
+
* logger each have their own floor and either one would drop it silently.
|
|
35
|
+
*
|
|
36
|
+
* The exemption keys on this marker, not on the NOTICE severity. A NOTICE
|
|
37
|
+
* emitted for anything else still respects the floor, otherwise we would have
|
|
38
|
+
* built a severity level that silently defeats filtering.
|
|
39
|
+
*/
|
|
40
|
+
exports.FEEDBACK_LABEL = 'feedback';
|
|
41
|
+
function isFeedback(labels) {
|
|
42
|
+
return labels?.[exports.FEEDBACK_LABEL] === 'true';
|
|
43
|
+
}
|
|
16
44
|
//# sourceMappingURL=severity.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"severity.js","sourceRoot":"","sources":["../../src/shared/severity.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"severity.js","sourceRoot":"","sources":["../../src/shared/severity.ts"],"names":[],"mappings":";;;AAuBA,sCAEC;AAgBD,gCAEC;AAzCD;;;;;;;;;GASG;AACU,QAAA,cAAc,GAAgC;IACzD,KAAK,EAAE,CAAC;IACR,OAAO,EAAE,CAAC;IACV,MAAM,EAAE,CAAC;IACT,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACT,CAAA;AAEY,QAAA,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,sBAAc,CAAkB,CAAA;AAEtE,2DAA2D;AAC3D,SAAgB,aAAa,CAAC,KAAc;IAC1C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,sBAAc,EAAE,KAAK,CAAC,CAAA;AACjG,CAAC;AAED;;;;;;;;;;;GAWG;AACU,QAAA,cAAc,GAAG,UAAU,CAAA;AAExC,SAAgB,UAAU,CAAC,MAA2C;IACpE,OAAO,MAAM,EAAE,CAAC,sBAAc,CAAC,KAAK,MAAM,CAAA;AAC5C,CAAC"}
|
package/dist/shared/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type LogSeverity = 'ERROR' | 'WARNING' | 'INFO' | 'DEBUG';
|
|
1
|
+
export type LogSeverity = 'ERROR' | 'WARNING' | 'NOTICE' | 'INFO' | 'DEBUG';
|
|
2
2
|
export interface BaseLabels {
|
|
3
3
|
appId: string;
|
|
4
4
|
userId?: string;
|
|
@@ -8,7 +8,6 @@ export interface BaseLabels {
|
|
|
8
8
|
releaseId?: string;
|
|
9
9
|
errorType?: string;
|
|
10
10
|
errorCategory?: string;
|
|
11
|
-
activity?: string;
|
|
12
11
|
}
|
|
13
12
|
export interface ErrorPayload {
|
|
14
13
|
message: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAA;AAE3E,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,WAAW,CAAA;IACrB,MAAM,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IACvD,WAAW,CAAC,EAAE;QACZ,WAAW,CAAC,EAAE,eAAe,EAAE,CAAA;QAC/B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QACjC,KAAK,CAAC,EAAE,YAAY,CAAA;KACrB,CAAA;IACD,2GAA2G;IAC3G,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACrC;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,KAAK,GAAG,OAAO,CAAA;IAC1C,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC/B"}
|
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.6.0",
|
|
4
|
+
"description": "Structured logging for Firebase apps — client, functions, and tools",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Dasasian",
|
|
7
7
|
"keywords": [
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"build:watch": "tsc --watch",
|
|
66
66
|
"clean": "rm -rf dist",
|
|
67
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",
|
|
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/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",
|
|
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`
|