fboxrec 0.1.0 → 0.3.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 +4 -0
- package/dist/{chunk-HCJF2ASP.mjs → chunk-4DHWBXXW.mjs} +123 -37
- package/dist/chunk-4DHWBXXW.mjs.map +1 -0
- package/dist/cli/main.cjs +1 -1
- package/dist/index.cjs +122 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.mjs +1 -1
- package/dist/register.cjs +73 -36
- package/dist/register.cjs.map +1 -1
- package/dist/register.mjs +1 -1
- package/package.json +2 -2
- package/dist/chunk-HCJF2ASP.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -32,6 +32,10 @@ Everything is correlated by request via AsyncLocalStorage, timestamped with
|
|
|
32
32
|
the monotonic clock, and held in a fixed 64MB ring (configurable) — the
|
|
33
33
|
recorded window is a consequence of memory and traffic, not a setting.
|
|
34
34
|
|
|
35
|
+
`flightbox.stop()` disarms recording and restores the patched modules
|
|
36
|
+
(console, http server/client, fetch, pg) to their originals, so tests and
|
|
37
|
+
clean shutdowns leave no trace; `start()` can be called again afterwards.
|
|
38
|
+
|
|
35
39
|
## When something breaks
|
|
36
40
|
|
|
37
41
|
Triggers snapshot the ring into a gzipped `.fbox` incident file:
|
|
@@ -653,7 +653,7 @@ var FORMAT_VERSION = 1;
|
|
|
653
653
|
var MAX_DECOMPRESSED_BYTES = 1024 * 1024 * 1024;
|
|
654
654
|
|
|
655
655
|
// src/dump/serializer.ts
|
|
656
|
-
var FLIGHTBOX_VERSION = "0.
|
|
656
|
+
var FLIGHTBOX_VERSION = "0.3.0";
|
|
657
657
|
var DUMP_GZIP_LEVEL = 1;
|
|
658
658
|
var dumpCounter = 0;
|
|
659
659
|
function buildIncident(opts) {
|
|
@@ -1444,8 +1444,9 @@ function startWatchdog(agent) {
|
|
|
1444
1444
|
// src/instrumentations/http-server.ts
|
|
1445
1445
|
import * as http from "http";
|
|
1446
1446
|
import { createHash as createHash2, timingSafeEqual } from "crypto";
|
|
1447
|
-
var patched = false;
|
|
1448
1447
|
var agentRef;
|
|
1448
|
+
var active2 = false;
|
|
1449
|
+
var saved = null;
|
|
1449
1450
|
function tokenMatches(supplied, expected) {
|
|
1450
1451
|
const a = createHash2("sha256").update(supplied).digest();
|
|
1451
1452
|
const b = createHash2("sha256").update(expected).digest();
|
|
@@ -1487,12 +1488,12 @@ function handleFlightboxEndpoint(agent, req, res) {
|
|
|
1487
1488
|
}
|
|
1488
1489
|
function instrumentHttpServer(agent) {
|
|
1489
1490
|
agentRef = agent;
|
|
1490
|
-
|
|
1491
|
-
|
|
1491
|
+
active2 = true;
|
|
1492
|
+
if (saved) return;
|
|
1492
1493
|
const orig = http.Server.prototype.emit;
|
|
1493
1494
|
const flightboxEmit = function(event) {
|
|
1494
1495
|
const args = arguments;
|
|
1495
|
-
if (event !== "request") {
|
|
1496
|
+
if (!active2 || event !== "request") {
|
|
1496
1497
|
return orig.apply(this, args);
|
|
1497
1498
|
}
|
|
1498
1499
|
const agent2 = agentRef;
|
|
@@ -1545,13 +1546,22 @@ function instrumentHttpServer(agent) {
|
|
|
1545
1546
|
}
|
|
1546
1547
|
return requestStorage.run(ctx, () => orig.apply(this, args));
|
|
1547
1548
|
};
|
|
1548
|
-
|
|
1549
|
+
saved = { orig, wrapper: flightboxEmit };
|
|
1550
|
+
http.Server.prototype.emit = saved.wrapper;
|
|
1551
|
+
}
|
|
1552
|
+
function restoreHttpServer() {
|
|
1553
|
+
active2 = false;
|
|
1554
|
+
if (saved && http.Server.prototype.emit === saved.wrapper) {
|
|
1555
|
+
http.Server.prototype.emit = saved.orig;
|
|
1556
|
+
saved = null;
|
|
1557
|
+
}
|
|
1549
1558
|
}
|
|
1550
1559
|
|
|
1551
1560
|
// src/instrumentations/http-client.ts
|
|
1552
1561
|
import { createRequire } from "module";
|
|
1553
|
-
var patched2 = false;
|
|
1554
1562
|
var agentRef2;
|
|
1563
|
+
var active3 = false;
|
|
1564
|
+
var liveWraps = /* @__PURE__ */ new Map();
|
|
1555
1565
|
function extractTarget(args) {
|
|
1556
1566
|
let method = "GET";
|
|
1557
1567
|
let host = "";
|
|
@@ -1583,10 +1593,12 @@ function extractTarget(args) {
|
|
|
1583
1593
|
return { method: String(method).toUpperCase(), host: String(host), path: String(path8) };
|
|
1584
1594
|
}
|
|
1585
1595
|
var nodeRequire = createRequire(process.cwd() + "/noop.js");
|
|
1586
|
-
function wrapModule(mod) {
|
|
1587
|
-
const
|
|
1596
|
+
function wrapModule(key, mod) {
|
|
1597
|
+
const rawRequest = mod.request;
|
|
1598
|
+
const rawGet = mod.get;
|
|
1599
|
+
const origRequest = rawRequest.bind(mod);
|
|
1588
1600
|
const tracedRequest = function(...args) {
|
|
1589
|
-
if (isShedding()) return origRequest(...args);
|
|
1601
|
+
if (!active3 || isShedding()) return origRequest(...args);
|
|
1590
1602
|
const agent = agentRef2;
|
|
1591
1603
|
let spanId = 0n;
|
|
1592
1604
|
let requestId = 0n;
|
|
@@ -1633,18 +1645,27 @@ function wrapModule(mod) {
|
|
|
1633
1645
|
}
|
|
1634
1646
|
return req;
|
|
1635
1647
|
};
|
|
1636
|
-
|
|
1637
|
-
mod.get = function(...args) {
|
|
1648
|
+
const tracedGet = function(...args) {
|
|
1638
1649
|
const req = tracedRequest(...args);
|
|
1639
1650
|
req.end();
|
|
1640
1651
|
return req;
|
|
1641
1652
|
};
|
|
1653
|
+
mod.request = tracedRequest;
|
|
1654
|
+
mod.get = tracedGet;
|
|
1655
|
+
liveWraps.set(key, () => {
|
|
1656
|
+
let clear = true;
|
|
1657
|
+
if (mod.request === tracedRequest) mod.request = rawRequest;
|
|
1658
|
+
else clear = false;
|
|
1659
|
+
if (mod.get === tracedGet) mod.get = rawGet;
|
|
1660
|
+
else clear = false;
|
|
1661
|
+
return clear;
|
|
1662
|
+
});
|
|
1642
1663
|
}
|
|
1643
1664
|
function wrapFetch() {
|
|
1644
1665
|
const origFetch = globalThis.fetch;
|
|
1645
1666
|
if (typeof origFetch !== "function") return;
|
|
1646
|
-
|
|
1647
|
-
if (isShedding()) return origFetch(input, init);
|
|
1667
|
+
const wrapper = async function flightboxFetch(input, init) {
|
|
1668
|
+
if (!active3 || isShedding()) return origFetch(input, init);
|
|
1648
1669
|
const agent = agentRef2;
|
|
1649
1670
|
let spanId = 0n;
|
|
1650
1671
|
let requestId = 0n;
|
|
@@ -1697,22 +1718,41 @@ function wrapFetch() {
|
|
|
1697
1718
|
throw err;
|
|
1698
1719
|
}
|
|
1699
1720
|
};
|
|
1721
|
+
globalThis.fetch = wrapper;
|
|
1722
|
+
liveWraps.set("fetch", () => {
|
|
1723
|
+
if (globalThis.fetch === wrapper) {
|
|
1724
|
+
globalThis.fetch = origFetch;
|
|
1725
|
+
return true;
|
|
1726
|
+
}
|
|
1727
|
+
return false;
|
|
1728
|
+
});
|
|
1700
1729
|
}
|
|
1701
1730
|
function instrumentHttpClient(agent) {
|
|
1702
1731
|
agentRef2 = agent;
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1732
|
+
active3 = true;
|
|
1733
|
+
if (!liveWraps.has("http")) {
|
|
1734
|
+
try {
|
|
1735
|
+
wrapModule("http", nodeRequire("node:http"));
|
|
1736
|
+
} catch {
|
|
1737
|
+
}
|
|
1708
1738
|
}
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1739
|
+
if (!liveWraps.has("https")) {
|
|
1740
|
+
try {
|
|
1741
|
+
wrapModule("https", nodeRequire("node:https"));
|
|
1742
|
+
} catch {
|
|
1743
|
+
}
|
|
1712
1744
|
}
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1745
|
+
if (!liveWraps.has("fetch")) {
|
|
1746
|
+
try {
|
|
1747
|
+
wrapFetch();
|
|
1748
|
+
} catch {
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
function restoreHttpClient() {
|
|
1753
|
+
active3 = false;
|
|
1754
|
+
for (const [key, undo] of liveWraps) {
|
|
1755
|
+
if (undo()) liveWraps.delete(key);
|
|
1716
1756
|
}
|
|
1717
1757
|
}
|
|
1718
1758
|
|
|
@@ -1732,17 +1772,21 @@ function paramShapes(values) {
|
|
|
1732
1772
|
return values.slice(0, 32).map((v) => v === null ? "null" : Array.isArray(v) ? "array" : typeof v);
|
|
1733
1773
|
}
|
|
1734
1774
|
var agentRef3;
|
|
1775
|
+
var active4 = false;
|
|
1776
|
+
var queryPatch = null;
|
|
1777
|
+
var connectPatch = null;
|
|
1735
1778
|
function instrumentPg(agent) {
|
|
1736
1779
|
agentRef3 = agent;
|
|
1737
1780
|
const recorder = {
|
|
1738
1781
|
record: ((...args) => agentRef3.recorder.record(...args))
|
|
1739
1782
|
};
|
|
1783
|
+
active4 = true;
|
|
1740
1784
|
const pg = loadPg();
|
|
1741
1785
|
if (!pg?.Client?.prototype?.query) return false;
|
|
1742
|
-
if (!pg.Client.prototype.query.__flightbox) {
|
|
1786
|
+
if (!queryPatch && !pg.Client.prototype.query.__flightbox) {
|
|
1743
1787
|
const origQuery = pg.Client.prototype.query;
|
|
1744
1788
|
const wrappedQuery = function(...args) {
|
|
1745
|
-
if (isShedding()) return origQuery.apply(this, args);
|
|
1789
|
+
if (!active4 || isShedding()) return origQuery.apply(this, args);
|
|
1746
1790
|
let spanId = 0n;
|
|
1747
1791
|
let requestId = 0n;
|
|
1748
1792
|
let tStart = 0n;
|
|
@@ -1815,13 +1859,14 @@ function instrumentPg(agent) {
|
|
|
1815
1859
|
return out;
|
|
1816
1860
|
};
|
|
1817
1861
|
wrappedQuery.__flightbox = true;
|
|
1862
|
+
queryPatch = { proto: pg.Client.prototype, orig: origQuery, wrapper: wrappedQuery };
|
|
1818
1863
|
pg.Client.prototype.query = wrappedQuery;
|
|
1819
1864
|
}
|
|
1820
1865
|
const Pool = pg.Pool;
|
|
1821
|
-
if (Pool?.prototype?.connect && !Pool.prototype.connect.__flightbox) {
|
|
1866
|
+
if (Pool?.prototype?.connect && !connectPatch && !Pool.prototype.connect.__flightbox) {
|
|
1822
1867
|
const origConnect = Pool.prototype.connect;
|
|
1823
1868
|
const wrappedConnect = function(cb) {
|
|
1824
|
-
if (isShedding()) return origConnect.call(this, cb);
|
|
1869
|
+
if (!active4 || isShedding()) return origConnect.call(this, cb);
|
|
1825
1870
|
const tStart = nowMono();
|
|
1826
1871
|
const requestId = currentRequestId();
|
|
1827
1872
|
const record = () => {
|
|
@@ -1848,33 +1893,58 @@ function instrumentPg(agent) {
|
|
|
1848
1893
|
return out;
|
|
1849
1894
|
};
|
|
1850
1895
|
wrappedConnect.__flightbox = true;
|
|
1896
|
+
connectPatch = { proto: Pool.prototype, orig: origConnect, wrapper: wrappedConnect };
|
|
1851
1897
|
Pool.prototype.connect = wrappedConnect;
|
|
1852
1898
|
}
|
|
1853
1899
|
return true;
|
|
1854
1900
|
}
|
|
1901
|
+
function restorePg() {
|
|
1902
|
+
active4 = false;
|
|
1903
|
+
if (queryPatch && queryPatch.proto.query === queryPatch.wrapper) {
|
|
1904
|
+
queryPatch.proto.query = queryPatch.orig;
|
|
1905
|
+
queryPatch = null;
|
|
1906
|
+
}
|
|
1907
|
+
if (connectPatch && connectPatch.proto.connect === connectPatch.wrapper) {
|
|
1908
|
+
connectPatch.proto.connect = connectPatch.orig;
|
|
1909
|
+
connectPatch = null;
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1855
1912
|
|
|
1856
1913
|
// src/instrumentations/console.ts
|
|
1857
1914
|
import { formatWithOptions } from "util";
|
|
1858
1915
|
var LEVELS = ["log", "info", "warn", "error", "debug"];
|
|
1859
1916
|
var FORMAT_OPTS = { depth: 2, maxArrayLength: 20, maxStringLength: LIMITS.log };
|
|
1860
|
-
var patched3 = false;
|
|
1861
1917
|
var agentRef4;
|
|
1918
|
+
var active5 = false;
|
|
1919
|
+
var patches = /* @__PURE__ */ new Map();
|
|
1862
1920
|
function instrumentConsole(agent) {
|
|
1863
1921
|
agentRef4 = agent;
|
|
1864
|
-
|
|
1865
|
-
patched3 = true;
|
|
1922
|
+
active5 = true;
|
|
1866
1923
|
for (const level of LEVELS) {
|
|
1867
|
-
|
|
1868
|
-
console[level]
|
|
1924
|
+
if (patches.has(level)) continue;
|
|
1925
|
+
const orig = console[level];
|
|
1926
|
+
const bound = orig.bind(console);
|
|
1927
|
+
const wrapper = function flightboxConsole(...args) {
|
|
1869
1928
|
try {
|
|
1870
|
-
if (!isShedding()) {
|
|
1929
|
+
if (active5 && !isShedding()) {
|
|
1871
1930
|
const msg = capScrub(formatWithOptions(FORMAT_OPTS, ...args), LIMITS.log);
|
|
1872
1931
|
agentRef4.recorder.record(8 /* Log */, { level, msg });
|
|
1873
1932
|
}
|
|
1874
1933
|
} catch {
|
|
1875
1934
|
}
|
|
1876
|
-
|
|
1935
|
+
bound(...args);
|
|
1877
1936
|
};
|
|
1937
|
+
patches.set(level, { orig, wrapper });
|
|
1938
|
+
console[level] = wrapper;
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
function restoreConsole() {
|
|
1942
|
+
active5 = false;
|
|
1943
|
+
for (const [level, p] of patches) {
|
|
1944
|
+
if (console[level] === p.wrapper) {
|
|
1945
|
+
console[level] = p.orig;
|
|
1946
|
+
patches.delete(level);
|
|
1947
|
+
}
|
|
1878
1948
|
}
|
|
1879
1949
|
}
|
|
1880
1950
|
|
|
@@ -1896,6 +1966,21 @@ function applyInstrumentations(agent) {
|
|
|
1896
1966
|
}
|
|
1897
1967
|
}
|
|
1898
1968
|
}
|
|
1969
|
+
var RESTORATIONS = [
|
|
1970
|
+
["http-server", restoreHttpServer],
|
|
1971
|
+
["http-client", restoreHttpClient],
|
|
1972
|
+
["pg", restorePg],
|
|
1973
|
+
["console", restoreConsole]
|
|
1974
|
+
];
|
|
1975
|
+
function removeInstrumentations(log) {
|
|
1976
|
+
for (const [name, restore] of RESTORATIONS) {
|
|
1977
|
+
try {
|
|
1978
|
+
restore();
|
|
1979
|
+
} catch (err) {
|
|
1980
|
+
log(`instrumentation ${name} failed to restore: ${err.message}`);
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1899
1984
|
|
|
1900
1985
|
// src/instrumentations/vitals.ts
|
|
1901
1986
|
import { monitorEventLoopDelay } from "perf_hooks";
|
|
@@ -2177,6 +2262,7 @@ function stop() {
|
|
|
2177
2262
|
} catch {
|
|
2178
2263
|
}
|
|
2179
2264
|
}
|
|
2265
|
+
removeInstrumentations(instance.config.log);
|
|
2180
2266
|
instance.panicWriter.disarm();
|
|
2181
2267
|
setShedding(false);
|
|
2182
2268
|
instance = null;
|
|
@@ -2191,4 +2277,4 @@ export {
|
|
|
2191
2277
|
stop,
|
|
2192
2278
|
index_default
|
|
2193
2279
|
};
|
|
2194
|
-
//# sourceMappingURL=chunk-
|
|
2280
|
+
//# sourceMappingURL=chunk-4DHWBXXW.mjs.map
|