@dasasian/firebase-structured-logger 1.1.0 → 1.2.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 +77 -0
- package/dist/client/timing.d.ts +20 -0
- package/dist/client/timing.d.ts.map +1 -0
- package/dist/client/timing.js +133 -0
- package/dist/client/timing.js.map +1 -0
- package/dist/functions/index.d.ts +2 -0
- package/dist/functions/index.d.ts.map +1 -1
- package/dist/functions/index.js +5 -1
- package/dist/functions/index.js.map +1 -1
- package/dist/functions/trace.d.ts +13 -0
- package/dist/functions/trace.d.ts.map +1 -0
- package/dist/functions/trace.js +68 -0
- package/dist/functions/trace.js.map +1 -0
- package/dist/shared/trace.d.ts +59 -0
- package/dist/shared/trace.d.ts.map +1 -0
- package/dist/shared/trace.js +131 -0
- package/dist/shared/trace.js.map +1 -0
- package/package.json +10 -3
- package/skills/query-logs/SKILL.md +4 -0
package/README.md
CHANGED
|
@@ -675,6 +675,77 @@ enableNavigation({ cleanPath: (path) => path.replace(/[^/]+@[^/]+/g, ':email') }
|
|
|
675
675
|
enableNavigation({ path: false })
|
|
676
676
|
```
|
|
677
677
|
|
|
678
|
+
## Timing: when something is too slow
|
|
679
|
+
|
|
680
|
+
Some failures never throw. A loading screen waits on eight pieces, one of them takes 30
|
|
681
|
+
seconds, and the logs say nothing — because nothing failed. Traces report exactly that
|
|
682
|
+
case, and only that case: something took longer than you said it should, or never
|
|
683
|
+
finished. A trace that finishes in time sends nothing at all.
|
|
684
|
+
|
|
685
|
+
```ts
|
|
686
|
+
import { trace, startTrace } from '@dasasian/firebase-structured-logger/client/timing'
|
|
687
|
+
|
|
688
|
+
await trace('app_boot', async (boot) => {
|
|
689
|
+
const user = await boot.step('sign-in', () => signIn())
|
|
690
|
+
await Promise.all([
|
|
691
|
+
boot.step('products', () => loadProducts(user)),
|
|
692
|
+
boot.step('places', () => loadPlaces(user)),
|
|
693
|
+
])
|
|
694
|
+
})
|
|
695
|
+
|
|
696
|
+
// A flow that spans functions — a screen that starts on mount and ends when its data is in
|
|
697
|
+
const open = startTrace('order_open')
|
|
698
|
+
await open.step('order', () => loadOrder(id))
|
|
699
|
+
open.end()
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
Your code holds names only. The limits live in one place, set once at startup:
|
|
703
|
+
|
|
704
|
+
```ts
|
|
705
|
+
import { configureTraces } from '@dasasian/firebase-structured-logger/client/timing'
|
|
706
|
+
|
|
707
|
+
configureTraces({
|
|
708
|
+
app_boot: { warnAfterMs: 8000, steps: { products: 3000 } },
|
|
709
|
+
order_open: { warnAfterMs: 2000 },
|
|
710
|
+
})
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
A trace with no limits configured is timed but never reported. Limits are read when a run
|
|
714
|
+
starts, so configure before the first one.
|
|
715
|
+
|
|
716
|
+
**What gets sent.** One `WARNING` per run, the moment the first limit is crossed — the
|
|
717
|
+
trace's, or a step's own — while it is still running, so a step that never finishes is
|
|
718
|
+
reported too. Nothing more is sent for that run afterwards.
|
|
719
|
+
|
|
720
|
+
```
|
|
721
|
+
WARNING app_boot slow: products passed 3000 ms, still waiting
|
|
722
|
+
labels.trace="app_boot" labels.run="<id>" labels.slow="step" labels.step="products"
|
|
723
|
+
timing: { elapsedMs: 3000, limitMs: 3000,
|
|
724
|
+
steps: { sign-in: 610, places: 410 }, waiting: ["products"] }
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
`labels.slow` is `"trace"` or `"step"`. Durations are in milliseconds, always in `timing`.
|
|
728
|
+
`labels.run` tells two overlapping runs of the same trace apart.
|
|
729
|
+
|
|
730
|
+
**What is never judged.** A browser pauses pages it is not showing: a hidden tab, a phone
|
|
731
|
+
switching apps, a laptop going to sleep. Time spent paused is not your app being slow, so
|
|
732
|
+
a run that was hidden at any point, or that was paused — a one-second check that arrives
|
|
733
|
+
five or more seconds late — is not reported. Laptop sleep often fires no event at all,
|
|
734
|
+
which is why the late check exists.
|
|
735
|
+
|
|
736
|
+
**Underneath** each step is a standard `performance.mark` and `performance.measure`, so
|
|
737
|
+
it shows in the browser's Performance panel and in other tools that read them. Ours are
|
|
738
|
+
cleared when the run ends. A `step` after `end()`, or a second `end()`, is ignored.
|
|
739
|
+
|
|
740
|
+
**On the server**, `trace` and `startTrace` come from `/functions`, with the same entry. A
|
|
741
|
+
server run is judged when a step or the trace ends — there is no timer, because Cloud
|
|
742
|
+
Functions and Cloud Run can throttle the CPU once a response is sent. A request that hangs
|
|
743
|
+
outright is ended by the platform's timeout, which logs it. Inside `withLogging`, the entry
|
|
744
|
+
carries that request's labels like any other.
|
|
745
|
+
|
|
746
|
+
Traces explain the slow case; they do not measure what is normal. There are no
|
|
747
|
+
percentiles here and no sampling — if you need those, that is a monitoring tool's job.
|
|
748
|
+
|
|
678
749
|
## User feedback
|
|
679
750
|
|
|
680
751
|
```ts
|
|
@@ -922,6 +993,7 @@ Narrow it when you need to:
|
|
|
922
993
|
| `labels.truncated="true"` | entries shortened to fit — the full copy is `fsl-overflow.json` |
|
|
923
994
|
| `labels.repeatKey="<key>" OR labels.repeatOf="<key>"` | one repeating error: its full copies and its summaries |
|
|
924
995
|
| `labels.sentLate="true"` | repeat summaries sent on a later visit |
|
|
996
|
+
| `labels.trace="app_boot"` | slow runs of one trace — `labels.slow` says whether the trace or a step was late |
|
|
925
997
|
|
|
926
998
|
Locally, the emulator's JSONL answers the same questions. Point
|
|
927
999
|
**[firebase-mcp-server](https://github.com/dasasian/firebase-mcp-server)** at either and ask
|
|
@@ -959,6 +1031,9 @@ Optional helpers are separate entry points, so an app ships only what it imports
|
|
|
959
1031
|
```ts
|
|
960
1032
|
import { enableNavigation } from '@dasasian/firebase-structured-logger/client/navigation'
|
|
961
1033
|
enableNavigation({ routeFor?, cleanPath?, path? }) // see "Navigation, automatically"
|
|
1034
|
+
|
|
1035
|
+
import { trace, startTrace, configureTraces } from '@dasasian/firebase-structured-logger/client/timing'
|
|
1036
|
+
configureTraces({ name: { warnAfterMs?, steps?: { step: ms } } }) // see "Timing"
|
|
962
1037
|
```
|
|
963
1038
|
|
|
964
1039
|
Also exported: `initLogger`, `getClientLogger`, `setupGlobalErrorHandler`, `handleReactError`,
|
|
@@ -993,6 +1068,8 @@ getLogger() // the current request's writer, or an anonymo
|
|
|
993
1068
|
logError / logWarn / logInfo / logDebug (message, labels?, context?, attachments?)
|
|
994
1069
|
|
|
995
1070
|
configureAttachments({ bucket?, prefix? }) // once, at module load — see Attachments
|
|
1071
|
+
configureTraces({ name: { warnAfterMs?, steps? } }) // once — see "Timing"
|
|
1072
|
+
trace(name, async (t) => { await t.step(name, fn) }) // or startTrace(name) … .end()
|
|
996
1073
|
|
|
997
1074
|
// Receiving client logs. All three take the same source-map config:
|
|
998
1075
|
// { bucket?, sourceMaps?: { bucket?, prefix? } }
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type TraceConfig } from '../shared/trace';
|
|
2
|
+
export type { TraceConfig, TraceLimits } from '../shared/trace';
|
|
3
|
+
/**
|
|
4
|
+
* `@dasasian/firebase-structured-logger/client/timing` — its own entry point
|
|
5
|
+
* (CLAUDE.md, "Optional client helpers are separate entry points"), so an app
|
|
6
|
+
* that never imports it ships none of this. Touches no browser global at module
|
|
7
|
+
* load — `performance`, `document` and the timers below are only read once a
|
|
8
|
+
* trace actually starts. See README, "Timing: when something is too slow".
|
|
9
|
+
*/
|
|
10
|
+
export interface Trace {
|
|
11
|
+
step<T>(name: string, fn: () => T | Promise<T>): Promise<T>;
|
|
12
|
+
end(): void;
|
|
13
|
+
}
|
|
14
|
+
/** Replaces every limit. Read when a run starts, so configure before the first one. */
|
|
15
|
+
export declare function configureTraces(newConfig: TraceConfig): void;
|
|
16
|
+
/** `startTrace(name)` returns the object; call `.end()` when the flow is over. */
|
|
17
|
+
export declare function startTrace(name: string): Trace;
|
|
18
|
+
/** Runs `fn` with a trace object and ends the trace when `fn` settles, either way. */
|
|
19
|
+
export declare function trace<T>(name: string, fn: (t: Trace) => T | Promise<T>): Promise<T>;
|
|
20
|
+
//# sourceMappingURL=timing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timing.d.ts","sourceRoot":"","sources":["../../src/client/timing.ts"],"names":[],"mappings":"AACA,OAAO,EAAqD,KAAK,WAAW,EAAoB,MAAM,iBAAiB,CAAA;AAEvH,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAM/D;;;;;;GAMG;AACH,MAAM,WAAW,KAAK;IACpB,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IAC3D,GAAG,IAAI,IAAI,CAAA;CACZ;AAID,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,SAAS,EAAE,WAAW,GAAG,IAAI,CAE5D;AAqHD,kFAAkF;AAClF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAE9C;AAED,sFAAsF;AACtF,wBAAsB,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAOzF"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.configureTraces = configureTraces;
|
|
4
|
+
exports.startTrace = startTrace;
|
|
5
|
+
exports.trace = trace;
|
|
6
|
+
const logger_1 = require("./logger");
|
|
7
|
+
const trace_1 = require("../shared/trace");
|
|
8
|
+
function newPageLocalRunId() {
|
|
9
|
+
return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
|
|
10
|
+
}
|
|
11
|
+
let config = {};
|
|
12
|
+
/** Replaces every limit. Read when a run starts, so configure before the first one. */
|
|
13
|
+
function configureTraces(newConfig) {
|
|
14
|
+
config = newConfig;
|
|
15
|
+
}
|
|
16
|
+
const WATCHDOG_INTERVAL_MS = 1000;
|
|
17
|
+
const LATE_THRESHOLD_MS = 5000;
|
|
18
|
+
const activeRuns = new Set();
|
|
19
|
+
let watchdogId;
|
|
20
|
+
let visibilityWired = false;
|
|
21
|
+
let lastTickAt;
|
|
22
|
+
function now() {
|
|
23
|
+
return performance.now();
|
|
24
|
+
}
|
|
25
|
+
function markName(run, step, kind) {
|
|
26
|
+
return `fsl:${run.name}:${run.run}:${step}:${kind}`;
|
|
27
|
+
}
|
|
28
|
+
function disqualifyActiveRuns() {
|
|
29
|
+
for (const active of activeRuns)
|
|
30
|
+
active.disqualified = true;
|
|
31
|
+
}
|
|
32
|
+
function onVisibilityChange() {
|
|
33
|
+
if (document.visibilityState === 'hidden')
|
|
34
|
+
disqualifyActiveRuns();
|
|
35
|
+
}
|
|
36
|
+
/** No-op outside a browser (Node, SSR) — nothing there can ever go hidden. */
|
|
37
|
+
function wireVisibility() {
|
|
38
|
+
if (visibilityWired)
|
|
39
|
+
return;
|
|
40
|
+
visibilityWired = true;
|
|
41
|
+
if (typeof document === 'undefined')
|
|
42
|
+
return;
|
|
43
|
+
document.addEventListener('visibilitychange', onVisibilityChange);
|
|
44
|
+
}
|
|
45
|
+
function reportRun(run, report) {
|
|
46
|
+
(0, logger_1.getClientLogger)().warning((0, trace_1.traceMessage)(run.name, report), (0, trace_1.traceLabels)(run.name, run.run, report), (0, trace_1.traceContext)(report));
|
|
47
|
+
}
|
|
48
|
+
function tick() {
|
|
49
|
+
const nowMs = Date.now();
|
|
50
|
+
const late = lastTickAt !== undefined && nowMs - lastTickAt - WATCHDOG_INTERVAL_MS >= LATE_THRESHOLD_MS;
|
|
51
|
+
lastTickAt = nowMs;
|
|
52
|
+
if (late)
|
|
53
|
+
disqualifyActiveRuns();
|
|
54
|
+
for (const active of activeRuns) {
|
|
55
|
+
if (active.disqualified || active.run.hasReported)
|
|
56
|
+
continue;
|
|
57
|
+
const report = active.run.check();
|
|
58
|
+
if (!report)
|
|
59
|
+
continue;
|
|
60
|
+
active.run.markReported();
|
|
61
|
+
reportRun(active.run, report);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function ensureWatchdog() {
|
|
65
|
+
wireVisibility();
|
|
66
|
+
if (watchdogId !== undefined)
|
|
67
|
+
return;
|
|
68
|
+
lastTickAt = Date.now();
|
|
69
|
+
watchdogId = setInterval(tick, WATCHDOG_INTERVAL_MS);
|
|
70
|
+
}
|
|
71
|
+
function stopWatchdogIfIdle() {
|
|
72
|
+
if (activeRuns.size > 0 || watchdogId === undefined)
|
|
73
|
+
return;
|
|
74
|
+
clearInterval(watchdogId);
|
|
75
|
+
watchdogId = undefined;
|
|
76
|
+
}
|
|
77
|
+
async function runStep(run, active, name, fn) {
|
|
78
|
+
if (run.isEnded)
|
|
79
|
+
return fn();
|
|
80
|
+
const startMark = markName(run, name, 'start');
|
|
81
|
+
const endMark = markName(run, name, 'end');
|
|
82
|
+
const measureName = markName(run, name, 'measure');
|
|
83
|
+
active.marks.push(startMark, endMark);
|
|
84
|
+
active.measures.push(measureName);
|
|
85
|
+
performance.mark(startMark);
|
|
86
|
+
run.startStep(name);
|
|
87
|
+
try {
|
|
88
|
+
return await fn();
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
run.endStep(name);
|
|
92
|
+
performance.mark(endMark);
|
|
93
|
+
performance.measure(measureName, startMark, endMark);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function endRun(run, active) {
|
|
97
|
+
if (run.isEnded)
|
|
98
|
+
return;
|
|
99
|
+
run.end();
|
|
100
|
+
for (const mark of active.marks)
|
|
101
|
+
performance.clearMarks(mark);
|
|
102
|
+
for (const measure of active.measures)
|
|
103
|
+
performance.clearMeasures(measure);
|
|
104
|
+
activeRuns.delete(active);
|
|
105
|
+
stopWatchdogIfIdle();
|
|
106
|
+
}
|
|
107
|
+
function startRun(name) {
|
|
108
|
+
const run = new trace_1.TraceRun(name, config[name], now, newPageLocalRunId());
|
|
109
|
+
const active = { run, disqualified: false, marks: [], measures: [] };
|
|
110
|
+
if (config[name]) {
|
|
111
|
+
activeRuns.add(active);
|
|
112
|
+
ensureWatchdog();
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
step: (stepName, fn) => runStep(run, active, stepName, fn),
|
|
116
|
+
end: () => endRun(run, active),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/** `startTrace(name)` returns the object; call `.end()` when the flow is over. */
|
|
120
|
+
function startTrace(name) {
|
|
121
|
+
return startRun(name);
|
|
122
|
+
}
|
|
123
|
+
/** Runs `fn` with a trace object and ends the trace when `fn` settles, either way. */
|
|
124
|
+
async function trace(name, fn) {
|
|
125
|
+
const handle = startRun(name);
|
|
126
|
+
try {
|
|
127
|
+
return await fn(handle);
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
handle.end();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=timing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timing.js","sourceRoot":"","sources":["../../src/client/timing.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,8 @@ export { initLogger } from './logger';
|
|
|
3
3
|
export type { LogWriter } from './logger';
|
|
4
4
|
export { withLogging, getLogger } from './requestLogger';
|
|
5
5
|
export { configureAttachments } from './sourceMapCache';
|
|
6
|
+
export { trace, startTrace, configureTraces } from './trace';
|
|
7
|
+
export type { Trace, TraceConfig, TraceLimits } from './trace';
|
|
6
8
|
export { createClientLogHandler, createClientLogFunction, ClientLogError } from './logHandler';
|
|
7
9
|
export { createHttpLogHandler } from './httpHandler';
|
|
8
10
|
export type { ClientLogHandlerConfig, ClientLogRequest, LogRequest } from './logHandler';
|
|
@@ -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,SAAS,EAAE,MAAM,iBAAiB,CAAA;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AACvD,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC9F,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AACpD,YAAY,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACxF,YAAY,EAAE,oBAAoB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC1F,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"}
|
|
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,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AACvD,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAC5D,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAC9D,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC9F,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AACpD,YAAY,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACxF,YAAY,EAAE,oBAAoB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC1F,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,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.logDebug = exports.logInfo = exports.logWarn = exports.logError = exports.createHttpLogHandler = exports.ClientLogError = exports.createClientLogFunction = exports.createClientLogHandler = exports.configureAttachments = exports.getLogger = exports.withLogging = exports.initLogger = void 0;
|
|
3
|
+
exports.logDebug = exports.logInfo = exports.logWarn = exports.logError = exports.createHttpLogHandler = exports.ClientLogError = exports.createClientLogFunction = exports.createClientLogHandler = exports.configureTraces = exports.startTrace = exports.trace = exports.configureAttachments = 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; } });
|
|
@@ -9,6 +9,10 @@ Object.defineProperty(exports, "withLogging", { enumerable: true, get: function
|
|
|
9
9
|
Object.defineProperty(exports, "getLogger", { enumerable: true, get: function () { return requestLogger_2.getLogger; } });
|
|
10
10
|
var sourceMapCache_1 = require("./sourceMapCache");
|
|
11
11
|
Object.defineProperty(exports, "configureAttachments", { enumerable: true, get: function () { return sourceMapCache_1.configureAttachments; } });
|
|
12
|
+
var trace_1 = require("./trace");
|
|
13
|
+
Object.defineProperty(exports, "trace", { enumerable: true, get: function () { return trace_1.trace; } });
|
|
14
|
+
Object.defineProperty(exports, "startTrace", { enumerable: true, get: function () { return trace_1.startTrace; } });
|
|
15
|
+
Object.defineProperty(exports, "configureTraces", { enumerable: true, get: function () { return trace_1.configureTraces; } });
|
|
12
16
|
var logHandler_1 = require("./logHandler");
|
|
13
17
|
Object.defineProperty(exports, "createClientLogHandler", { enumerable: true, get: function () { return logHandler_1.createClientLogHandler; } });
|
|
14
18
|
Object.defineProperty(exports, "createClientLogFunction", { enumerable: true, get: function () { return logHandler_1.createClientLogFunction; } });
|
|
@@ -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,iDAAwD;AAA/C,4GAAA,WAAW,OAAA;AAAE,0GAAA,SAAS,OAAA;AAC/B,mDAAuD;AAA9C,sHAAA,oBAAoB,OAAA;AAC7B,2CAA8F;AAArF,oHAAA,sBAAsB,OAAA;AAAE,qHAAA,uBAAuB,OAAA;AAAE,4GAAA,cAAc,OAAA;AACxE,6CAAoD;AAA3C,mHAAA,oBAAoB,OAAA;AAKtB,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
|
+
{"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,mDAAuD;AAA9C,sHAAA,oBAAoB,OAAA;AAC7B,iCAA4D;AAAnD,8FAAA,KAAK,OAAA;AAAE,mGAAA,UAAU,OAAA;AAAE,wGAAA,eAAe,OAAA;AAE3C,2CAA8F;AAArF,oHAAA,sBAAsB,OAAA;AAAE,qHAAA,uBAAuB,OAAA;AAAE,4GAAA,cAAc,OAAA;AACxE,6CAAoD;AAA3C,mHAAA,oBAAoB,OAAA;AAKtB,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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type TraceConfig } from '../shared/trace';
|
|
2
|
+
export type { TraceConfig, TraceLimits } from '../shared/trace';
|
|
3
|
+
export interface Trace {
|
|
4
|
+
step<T>(name: string, fn: () => T | Promise<T>): Promise<T>;
|
|
5
|
+
end(): void;
|
|
6
|
+
}
|
|
7
|
+
/** Replaces every limit. Read when a run starts, so configure before the first one. */
|
|
8
|
+
export declare function configureTraces(newConfig: TraceConfig): void;
|
|
9
|
+
/** `startTrace(name)` returns the object; call `.end()` when the flow is over. */
|
|
10
|
+
export declare function startTrace(name: string): Trace;
|
|
11
|
+
/** Runs `fn` with a trace object and ends the trace when `fn` settles, either way. */
|
|
12
|
+
export declare function trace<T>(name: string, fn: (t: Trace) => T | Promise<T>): Promise<T>;
|
|
13
|
+
//# sourceMappingURL=trace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../../src/functions/trace.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqD,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAErG,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAE/D,MAAM,WAAW,KAAK;IACpB,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IAC3D,GAAG,IAAI,IAAI,CAAA;CACZ;AAID,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,SAAS,EAAE,WAAW,GAAG,IAAI,CAE5D;AAyCD,kFAAkF;AAClF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAE9C;AAED,sFAAsF;AACtF,wBAAsB,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAOzF"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.configureTraces = configureTraces;
|
|
4
|
+
exports.startTrace = startTrace;
|
|
5
|
+
exports.trace = trace;
|
|
6
|
+
const ulid_1 = require("ulid");
|
|
7
|
+
const requestLogger_1 = require("./requestLogger");
|
|
8
|
+
const trace_1 = require("../shared/trace");
|
|
9
|
+
let config = {};
|
|
10
|
+
/** Replaces every limit. Read when a run starts, so configure before the first one. */
|
|
11
|
+
function configureTraces(newConfig) {
|
|
12
|
+
config = newConfig;
|
|
13
|
+
}
|
|
14
|
+
function now() {
|
|
15
|
+
return Date.now();
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Judged only when a step finishes or the trace ends — no timer, since Cloud
|
|
19
|
+
* Functions and Cloud Run can throttle the CPU once a response is sent (see
|
|
20
|
+
* CLAUDE.md, "Traces report misbehaviour, nothing else"). Sent through the
|
|
21
|
+
* request-scoped writer, so it carries that request's labels like any other entry.
|
|
22
|
+
*/
|
|
23
|
+
function report(run, found) {
|
|
24
|
+
if (!found)
|
|
25
|
+
return;
|
|
26
|
+
run.markReported();
|
|
27
|
+
(0, requestLogger_1.getLogger)().warning((0, trace_1.traceMessage)(run.name, found), (0, trace_1.traceLabels)(run.name, run.run, found), (0, trace_1.traceContext)(found));
|
|
28
|
+
}
|
|
29
|
+
function startRun(name) {
|
|
30
|
+
const run = new trace_1.TraceRun(name, config[name], now, (0, ulid_1.ulid)());
|
|
31
|
+
return {
|
|
32
|
+
async step(stepName, fn) {
|
|
33
|
+
if (run.isEnded)
|
|
34
|
+
return fn();
|
|
35
|
+
run.startStep(stepName);
|
|
36
|
+
try {
|
|
37
|
+
return await fn();
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
const ms = run.endStep(stepName);
|
|
41
|
+
if (ms !== undefined)
|
|
42
|
+
report(run, run.checkFinishedStep(stepName, ms));
|
|
43
|
+
report(run, run.check());
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
end() {
|
|
47
|
+
if (run.isEnded)
|
|
48
|
+
return;
|
|
49
|
+
report(run, run.check());
|
|
50
|
+
run.end();
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** `startTrace(name)` returns the object; call `.end()` when the flow is over. */
|
|
55
|
+
function startTrace(name) {
|
|
56
|
+
return startRun(name);
|
|
57
|
+
}
|
|
58
|
+
/** Runs `fn` with a trace object and ends the trace when `fn` settles, either way. */
|
|
59
|
+
async function trace(name, fn) {
|
|
60
|
+
const handle = startRun(name);
|
|
61
|
+
try {
|
|
62
|
+
return await fn(handle);
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
handle.end();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=trace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace.js","sourceRoot":"","sources":["../../src/functions/trace.ts"],"names":[],"mappings":";;AAcA,0CAEC;AA0CD,gCAEC;AAGD,sBAOC;AAtED,+BAA2B;AAC3B,mDAA2C;AAC3C,2CAAqG;AASrG,IAAI,MAAM,GAAgB,EAAE,CAAA;AAE5B,uFAAuF;AACvF,SAAgB,eAAe,CAAC,SAAsB;IACpD,MAAM,GAAG,SAAS,CAAA;AACpB,CAAC;AAED,SAAS,GAAG;IACV,OAAO,IAAI,CAAC,GAAG,EAAE,CAAA;AACnB,CAAC;AAED;;;;;GAKG;AACH,SAAS,MAAM,CAAC,GAAa,EAAE,KAAoC;IACjE,IAAI,CAAC,KAAK;QAAE,OAAM;IAClB,GAAG,CAAC,YAAY,EAAE,CAAA;IAClB,IAAA,yBAAS,GAAE,CAAC,OAAO,CAAC,IAAA,oBAAY,EAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,IAAA,mBAAW,EAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,IAAA,oBAAY,EAAC,KAAK,CAAC,CAAC,CAAA;AAChH,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,GAAG,GAAG,IAAI,gBAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAA,WAAI,GAAE,CAAC,CAAA;IAEzD,OAAO;QACL,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE;YACrB,IAAI,GAAG,CAAC,OAAO;gBAAE,OAAO,EAAE,EAAE,CAAA;YAC5B,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;YACvB,IAAI,CAAC;gBACH,OAAO,MAAM,EAAE,EAAE,CAAA;YACnB,CAAC;oBAAS,CAAC;gBACT,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;gBAChC,IAAI,EAAE,KAAK,SAAS;oBAAE,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,iBAAiB,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAA;gBACtE,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAA;YAC1B,CAAC;QACH,CAAC;QACD,GAAG;YACD,IAAI,GAAG,CAAC,OAAO;gBAAE,OAAM;YACvB,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAA;YACxB,GAAG,CAAC,GAAG,EAAE,CAAA;QACX,CAAC;KACF,CAAA;AACH,CAAC;AAED,kFAAkF;AAClF,SAAgB,UAAU,CAAC,IAAY;IACrC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAA;AACvB,CAAC;AAED,sFAAsF;AAC/E,KAAK,UAAU,KAAK,CAAI,IAAY,EAAE,EAAgC;IAC3E,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;IAC7B,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,CAAC,MAAM,CAAC,CAAA;IACzB,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,GAAG,EAAE,CAAA;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pure bookkeeping behind `/client/timing` and `/functions`' `trace`/`startTrace` —
|
|
3
|
+
* which steps are finished (with their ms) and which are still running, checked
|
|
4
|
+
* against configured limits through an injected monotonic clock so tests never wait
|
|
5
|
+
* for real time. The two entry points differ only in how often they call `check()`
|
|
6
|
+
* (a watchdog tick in the browser, a step/trace boundary on the server, per
|
|
7
|
+
* CLAUDE.md "Traces report misbehaviour, nothing else") and in what they do with
|
|
8
|
+
* the `TraceReport` it returns.
|
|
9
|
+
*/
|
|
10
|
+
export interface TraceLimits {
|
|
11
|
+
warnAfterMs?: number;
|
|
12
|
+
steps?: Record<string, number>;
|
|
13
|
+
}
|
|
14
|
+
/** `configureTraces({ name: { warnAfterMs?, steps? } })` — see README, "Timing". */
|
|
15
|
+
export type TraceConfig = Record<string, TraceLimits>;
|
|
16
|
+
export interface TraceReport {
|
|
17
|
+
slow: 'trace' | 'step';
|
|
18
|
+
step?: string;
|
|
19
|
+
elapsedMs: number;
|
|
20
|
+
limitMs: number;
|
|
21
|
+
steps: Record<string, number>;
|
|
22
|
+
waiting: string[];
|
|
23
|
+
}
|
|
24
|
+
export declare class TraceRun {
|
|
25
|
+
readonly name: string;
|
|
26
|
+
private readonly limits;
|
|
27
|
+
private readonly clock;
|
|
28
|
+
readonly run: string;
|
|
29
|
+
private readonly startedAt;
|
|
30
|
+
private readonly finishedSteps;
|
|
31
|
+
private readonly runningSteps;
|
|
32
|
+
private reported;
|
|
33
|
+
private ended;
|
|
34
|
+
constructor(name: string, limits: TraceLimits | undefined, clock: () => number, runId: string);
|
|
35
|
+
get isEnded(): boolean;
|
|
36
|
+
get hasReported(): boolean;
|
|
37
|
+
startStep(name: string): void;
|
|
38
|
+
endStep(name: string): number | undefined;
|
|
39
|
+
end(): void;
|
|
40
|
+
/**
|
|
41
|
+
* The first limit crossed right now, or undefined. Does not itself mark the
|
|
42
|
+
* run as reported — callers do that only once they have actually sent it.
|
|
43
|
+
*/
|
|
44
|
+
check(): TraceReport | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* The server's counterpart to `check()`'s step loop: a step that has already
|
|
47
|
+
* been moved into `finishedSteps` (so `endStep` has already run for it) is no
|
|
48
|
+
* longer in `runningSteps` for `check()` to see, yet the instant it finished
|
|
49
|
+
* is the only one the server — with no timer — gets to judge it against its
|
|
50
|
+
* own limit. `ms` is the duration `endStep` just returned for it.
|
|
51
|
+
*/
|
|
52
|
+
checkFinishedStep(name: string, ms: number): TraceReport | undefined;
|
|
53
|
+
markReported(): void;
|
|
54
|
+
}
|
|
55
|
+
/** The message README, "Timing" shows: `"app_boot slow: products passed 3000 ms, still waiting"`. */
|
|
56
|
+
export declare function traceMessage(name: string, report: TraceReport): string;
|
|
57
|
+
export declare function traceLabels(name: string, run: string, report: TraceReport): Record<string, string>;
|
|
58
|
+
export declare function traceContext(report: TraceReport): Record<string, unknown>;
|
|
59
|
+
//# sourceMappingURL=trace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../../src/shared/trace.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B;AAED,oFAAoF;AACpF,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;AAErD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,GAAG,MAAM,CAAA;IACtB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,qBAAa,QAAQ;IASjB,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAVxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA6B;IAC3D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA4B;IACzD,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,KAAK,CAAQ;gBAGV,IAAI,EAAE,MAAM,EACJ,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,KAAK,EAAE,MAAM,MAAM,EACpC,KAAK,EAAE,MAAM;IAMf,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAK7B,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IASzC,GAAG,IAAI,IAAI;IAIX;;;OAGG;IACH,KAAK,IAAI,WAAW,GAAG,SAAS;IAkChC;;;;;;OAMG;IACH,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAcpE,YAAY,IAAI,IAAI;CAGrB;AAED,qGAAqG;AACrG,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAGtE;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIlG;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASzE"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TraceRun = void 0;
|
|
4
|
+
exports.traceMessage = traceMessage;
|
|
5
|
+
exports.traceLabels = traceLabels;
|
|
6
|
+
exports.traceContext = traceContext;
|
|
7
|
+
class TraceRun {
|
|
8
|
+
name;
|
|
9
|
+
limits;
|
|
10
|
+
clock;
|
|
11
|
+
run;
|
|
12
|
+
startedAt;
|
|
13
|
+
finishedSteps = {};
|
|
14
|
+
runningSteps = new Map();
|
|
15
|
+
reported = false;
|
|
16
|
+
ended = false;
|
|
17
|
+
constructor(name, limits, clock, runId) {
|
|
18
|
+
this.name = name;
|
|
19
|
+
this.limits = limits;
|
|
20
|
+
this.clock = clock;
|
|
21
|
+
this.run = runId;
|
|
22
|
+
this.startedAt = clock();
|
|
23
|
+
}
|
|
24
|
+
get isEnded() {
|
|
25
|
+
return this.ended;
|
|
26
|
+
}
|
|
27
|
+
get hasReported() {
|
|
28
|
+
return this.reported;
|
|
29
|
+
}
|
|
30
|
+
startStep(name) {
|
|
31
|
+
if (this.ended)
|
|
32
|
+
return;
|
|
33
|
+
this.runningSteps.set(name, this.clock());
|
|
34
|
+
}
|
|
35
|
+
endStep(name) {
|
|
36
|
+
const startedAt = this.runningSteps.get(name);
|
|
37
|
+
if (startedAt === undefined)
|
|
38
|
+
return undefined;
|
|
39
|
+
this.runningSteps.delete(name);
|
|
40
|
+
const ms = this.clock() - startedAt;
|
|
41
|
+
this.finishedSteps[name] = ms;
|
|
42
|
+
return ms;
|
|
43
|
+
}
|
|
44
|
+
end() {
|
|
45
|
+
this.ended = true;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The first limit crossed right now, or undefined. Does not itself mark the
|
|
49
|
+
* run as reported — callers do that only once they have actually sent it.
|
|
50
|
+
*/
|
|
51
|
+
check() {
|
|
52
|
+
if (this.reported || !this.limits)
|
|
53
|
+
return undefined;
|
|
54
|
+
const limits = this.limits;
|
|
55
|
+
const elapsedMs = this.clock() - this.startedAt;
|
|
56
|
+
if (limits.warnAfterMs !== undefined && elapsedMs >= limits.warnAfterMs) {
|
|
57
|
+
return {
|
|
58
|
+
slow: 'trace',
|
|
59
|
+
elapsedMs,
|
|
60
|
+
limitMs: limits.warnAfterMs,
|
|
61
|
+
steps: { ...this.finishedSteps },
|
|
62
|
+
waiting: [...this.runningSteps.keys()],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
for (const [stepName, startedAt] of this.runningSteps) {
|
|
66
|
+
const limitMs = limits.steps?.[stepName];
|
|
67
|
+
if (limitMs === undefined)
|
|
68
|
+
continue;
|
|
69
|
+
const stepElapsedMs = this.clock() - startedAt;
|
|
70
|
+
if (stepElapsedMs >= limitMs) {
|
|
71
|
+
return {
|
|
72
|
+
slow: 'step',
|
|
73
|
+
step: stepName,
|
|
74
|
+
elapsedMs: stepElapsedMs,
|
|
75
|
+
limitMs,
|
|
76
|
+
steps: { ...this.finishedSteps },
|
|
77
|
+
waiting: [...this.runningSteps.keys()],
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The server's counterpart to `check()`'s step loop: a step that has already
|
|
85
|
+
* been moved into `finishedSteps` (so `endStep` has already run for it) is no
|
|
86
|
+
* longer in `runningSteps` for `check()` to see, yet the instant it finished
|
|
87
|
+
* is the only one the server — with no timer — gets to judge it against its
|
|
88
|
+
* own limit. `ms` is the duration `endStep` just returned for it.
|
|
89
|
+
*/
|
|
90
|
+
checkFinishedStep(name, ms) {
|
|
91
|
+
if (this.reported || !this.limits)
|
|
92
|
+
return undefined;
|
|
93
|
+
const limitMs = this.limits.steps?.[name];
|
|
94
|
+
if (limitMs === undefined || ms < limitMs)
|
|
95
|
+
return undefined;
|
|
96
|
+
return {
|
|
97
|
+
slow: 'step',
|
|
98
|
+
step: name,
|
|
99
|
+
elapsedMs: ms,
|
|
100
|
+
limitMs,
|
|
101
|
+
steps: { ...this.finishedSteps },
|
|
102
|
+
waiting: [...this.runningSteps.keys()],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
markReported() {
|
|
106
|
+
this.reported = true;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
exports.TraceRun = TraceRun;
|
|
110
|
+
/** The message README, "Timing" shows: `"app_boot slow: products passed 3000 ms, still waiting"`. */
|
|
111
|
+
function traceMessage(name, report) {
|
|
112
|
+
const subject = report.slow === 'step' ? `${report.step} ` : '';
|
|
113
|
+
return `${name} slow: ${subject}passed ${report.limitMs} ms, still waiting`;
|
|
114
|
+
}
|
|
115
|
+
function traceLabels(name, run, report) {
|
|
116
|
+
const labels = { trace: name, run, slow: report.slow };
|
|
117
|
+
if (report.step !== undefined)
|
|
118
|
+
labels.step = report.step;
|
|
119
|
+
return labels;
|
|
120
|
+
}
|
|
121
|
+
function traceContext(report) {
|
|
122
|
+
return {
|
|
123
|
+
timing: {
|
|
124
|
+
elapsedMs: report.elapsedMs,
|
|
125
|
+
limitMs: report.limitMs,
|
|
126
|
+
steps: report.steps,
|
|
127
|
+
waiting: report.waiting,
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=trace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace.js","sourceRoot":"","sources":["../../src/shared/trace.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dasasian/firebase-structured-logger",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Structured logging for Firebase and Google Cloud web apps — browser, backend, and tools",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Dasasian",
|
|
@@ -38,6 +38,10 @@
|
|
|
38
38
|
"types": "./dist/client/navigation.d.ts",
|
|
39
39
|
"default": "./dist/client/navigation.js"
|
|
40
40
|
},
|
|
41
|
+
"./client/timing": {
|
|
42
|
+
"types": "./dist/client/timing.d.ts",
|
|
43
|
+
"default": "./dist/client/timing.js"
|
|
44
|
+
},
|
|
41
45
|
"./functions": {
|
|
42
46
|
"types": "./dist/functions/index.d.ts",
|
|
43
47
|
"default": "./dist/functions/index.js"
|
|
@@ -55,6 +59,9 @@
|
|
|
55
59
|
"client/navigation": [
|
|
56
60
|
"dist/client/navigation.d.ts"
|
|
57
61
|
],
|
|
62
|
+
"client/timing": [
|
|
63
|
+
"dist/client/timing.d.ts"
|
|
64
|
+
],
|
|
58
65
|
"functions": [
|
|
59
66
|
"dist/functions/index.d.ts"
|
|
60
67
|
],
|
|
@@ -64,7 +71,7 @@
|
|
|
64
71
|
}
|
|
65
72
|
},
|
|
66
73
|
"bin": {
|
|
67
|
-
"fsl": "
|
|
74
|
+
"fsl": "dist/tools/index.js"
|
|
68
75
|
},
|
|
69
76
|
"files": [
|
|
70
77
|
"dist",
|
|
@@ -75,7 +82,7 @@
|
|
|
75
82
|
"build:watch": "tsc --watch",
|
|
76
83
|
"clean": "rm -rf dist",
|
|
77
84
|
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.check.json",
|
|
78
|
-
"test": "tsx tests/logger.ts && tsx tests/browserBundle.ts && tsx tests/breadcrumbs.ts && tsx tests/navigation.ts && tsx tests/rateLimiter.ts && tsx tests/errorHandler.ts && tsx tests/attachments.ts && tsx tests/feedback.ts && tsx tests/paths.ts && tsx tests/loadsWithoutOptionalPeers.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 && FUNCTIONS_EMULATOR=true tsx tests/deprecatedNames.ts && tsx tests/productionOutput.ts && tsx tests/storageChain.ts && tsx tests/entrySize.ts && tsx tests/handlerSymbolication.ts && tsx tests/releaseResolution.ts && tsx tests/sourceMapCacheBounds.ts && tsx tests/doctor.ts && tsx tests/doctorProcess.ts && tsx tests/uploadSourcemapsBackend.ts",
|
|
85
|
+
"test": "tsx tests/logger.ts && tsx tests/browserBundle.ts && tsx tests/breadcrumbs.ts && tsx tests/navigation.ts && tsx tests/timing.ts && tsx tests/rateLimiter.ts && tsx tests/errorHandler.ts && tsx tests/attachments.ts && tsx tests/feedback.ts && tsx tests/paths.ts && tsx tests/loadsWithoutOptionalPeers.ts && tsx tests/sourceMapStripping.ts && FUNCTIONS_EMULATOR=true tsx tests/errorPayload.ts && FUNCTIONS_EMULATOR=true tsx tests/requestLogger.ts && FUNCTIONS_EMULATOR=true tsx tests/serverTrace.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 && FUNCTIONS_EMULATOR=true tsx tests/deprecatedNames.ts && tsx tests/productionOutput.ts && tsx tests/storageChain.ts && tsx tests/entrySize.ts && tsx tests/handlerSymbolication.ts && tsx tests/releaseResolution.ts && tsx tests/sourceMapCacheBounds.ts && tsx tests/doctor.ts && tsx tests/doctorProcess.ts && tsx tests/uploadSourcemapsBackend.ts",
|
|
79
86
|
"prepublishOnly": "npm run clean && npm run build",
|
|
80
87
|
"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)",
|
|
81
88
|
"smoke:deploy:run": "bash smoke/cloudrun/deploy.sh",
|
|
@@ -34,6 +34,10 @@ All entries written by firebase-structured-logger include these labels:
|
|
|
34
34
|
| `route` | Route pattern, e.g. `/orders/:id/items` — group by this (apps using `enableNavigation`) |
|
|
35
35
|
| `path` | Real path, e.g. `/orders/1042/items` — one specific page or record; query string never stored |
|
|
36
36
|
| `routeSource` | `router` (the app named the route) or `pattern` (ids replaced by rule) |
|
|
37
|
+
| `trace` | On a slow-trace WARNING: the trace's name, e.g. `app_boot` |
|
|
38
|
+
| `run` | On a slow-trace WARNING: tells overlapping runs of one trace apart |
|
|
39
|
+
| `slow` | `trace` (the whole run passed its limit) or `step` (one step passed its own) |
|
|
40
|
+
| `step` | When `slow="step"`: the step that was late. `jsonPayload.timing` has every step's ms and what was still waiting |
|
|
37
41
|
| `releaseId` | Git short hash or explicit release ID |
|
|
38
42
|
| `platform` | `ios`, `android`, `macos`, `windows`, `web` |
|
|
39
43
|
| `browser` | `chrome`, `firefox`, `safari`, `edge` |
|