@oconde/radar 0.3.0 → 0.4.1
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/CHANGELOG.md +17 -0
- package/README.md +14 -1
- package/dist/{client-BB-to8cr.d.cts → client-DRZ7htMJ.d.cts} +16 -2
- package/dist/{client-BS798K3S.d.ts → client-FkXWx8sJ.d.ts} +16 -2
- package/dist/index.cjs +49 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +49 -15
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +49 -15
- package/dist/nest/index.cjs.map +1 -1
- package/dist/nest/index.d.cts +1 -1
- package/dist/nest/index.d.ts +1 -1
- package/dist/nest/index.js +49 -15
- package/dist/nest/index.js.map +1 -1
- package/dist/sveltekit/index.cjs +49 -15
- package/dist/sveltekit/index.cjs.map +1 -1
- package/dist/sveltekit/index.d.cts +1 -1
- package/dist/sveltekit/index.d.ts +1 -1
- package/dist/sveltekit/index.js +49 -15
- package/dist/sveltekit/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.1
|
|
4
|
+
|
|
5
|
+
### Correções
|
|
6
|
+
|
|
7
|
+
- `logRequests: { sample: 0 }` não registra nenhuma requisição. Antes, 0 era tratado como valor inválido e virava 1, guardando todas.
|
|
8
|
+
|
|
9
|
+
## 0.4.0
|
|
10
|
+
|
|
11
|
+
### Novo
|
|
12
|
+
|
|
13
|
+
- `logRequests` aceita filtros e amostragem: `{ include, exclude, sample }`. `include` e `exclude` recebem caminhos ou rotas (`'/dashboard'` vale para `/dashboard` e tudo abaixo dele) ou RegExp, testados no caminho e na rota do framework. `sample` (de 0 a 1) guarda só essa fração das requisições, sorteada de forma uniforme, e cada log guardado leva `sampleRate`, que o Radar usa para estimar os totais. `logRequests: true` e `false` continuam valendo.
|
|
14
|
+
- `traceFetch.ignore`: hosts (o nome exato ou `.dominio`) ou RegExp testadas em `host + caminho` que não viram log `http.client`. As chamadas continuam saindo normalmente.
|
|
15
|
+
|
|
16
|
+
### Correções
|
|
17
|
+
|
|
18
|
+
- Uma RegExp com a flag `g` em `propagateTo` deixava de mandar o `x-request-id` em chamadas alternadas.
|
|
19
|
+
|
|
3
20
|
## 0.3.0
|
|
4
21
|
|
|
5
22
|
### Novo
|
package/README.md
CHANGED
|
@@ -104,7 +104,20 @@ await radar.cron('nightly-report', () => buildReport());
|
|
|
104
104
|
radar.init({ key: process.env.RADAR_KEY, traceFetch: { propagateTo: ['api.spinlab.dev', '.oconde.dev'] } });
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
-
Com `traceFetch`, cada `fetch` que o app faz vira um log `http.client` com método, host, caminho (sem a query, e com trechos que parecem token mascarados), status e duração, ou o erro quando a chamada nem chega a responder. Dentro de uma requisição, o log entra na linha do tempo dela. Para os hosts de `propagateTo` (o nome exato, `.dominio` para os subdomínios ou uma RegExp), o SDK manda o `x-request-id` da requisição atual, e o Radar mostra a mesma requisição nos dois projetos. `traceFetch: true` registra as chamadas sem mandar o id para ninguém. Chamadas para o próprio Radar ficam de fora.
|
|
107
|
+
Com `traceFetch`, cada `fetch` que o app faz vira um log `http.client` com método, host, caminho (sem a query, e com trechos que parecem token mascarados), status e duração, ou o erro quando a chamada nem chega a responder. Dentro de uma requisição, o log entra na linha do tempo dela. Para os hosts de `propagateTo` (o nome exato, `.dominio` para os subdomínios ou uma RegExp), o SDK manda o `x-request-id` da requisição atual, e o Radar mostra a mesma requisição nos dois projetos. `traceFetch: true` registra as chamadas sem mandar o id para ninguém. Chamadas para o próprio Radar ficam de fora. Para não registrar chamadas barulhentas, como polling, use `ignore` com hosts ou RegExp testadas em `host + caminho`: `traceFetch: { ignore: ['api.telegram.org', /\/getUpdates$/] }`.
|
|
108
|
+
|
|
109
|
+
## Quais requisições viram log
|
|
110
|
+
|
|
111
|
+
Por padrão, cada requisição vira um log `http.request`, que alimenta o Desempenho e os limites do Radar. Para guardar só uma parte:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
radar.init({
|
|
115
|
+
key: process.env.RADAR_KEY,
|
|
116
|
+
logRequests: { include: ['/dashboard', /^\/api\//], exclude: ['/dashboard/ping'], sample: 0.5 },
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`include` e `exclude` recebem caminhos ou rotas (`'/dashboard'` vale para `/dashboard` e tudo abaixo dele) ou RegExp. `sample` guarda só essa fração, sorteada de forma uniforme, e o Radar compensa a amostragem nos totais. `logRequests: false` desliga.
|
|
108
121
|
|
|
109
122
|
## Erros com o seu código
|
|
110
123
|
|
|
@@ -38,10 +38,24 @@ type RequestDetail = 'full' | 'route';
|
|
|
38
38
|
type FetchTraceTarget = string | RegExp;
|
|
39
39
|
type TraceFetchOption = boolean | {
|
|
40
40
|
propagateTo?: FetchTraceTarget[];
|
|
41
|
+
ignore?: FetchTraceTarget[];
|
|
41
42
|
};
|
|
42
43
|
type ResolvedTraceFetch = {
|
|
43
44
|
enabled: boolean;
|
|
44
45
|
propagateTo: FetchTraceTarget[];
|
|
46
|
+
ignore: FetchTraceTarget[];
|
|
47
|
+
};
|
|
48
|
+
type RequestPattern = string | RegExp;
|
|
49
|
+
type LogRequestsOption = boolean | {
|
|
50
|
+
include?: RequestPattern[];
|
|
51
|
+
exclude?: RequestPattern[];
|
|
52
|
+
sample?: number;
|
|
53
|
+
};
|
|
54
|
+
type ResolvedLogRequests = {
|
|
55
|
+
enabled: boolean;
|
|
56
|
+
include: RequestPattern[];
|
|
57
|
+
exclude: RequestPattern[];
|
|
58
|
+
sample: number;
|
|
45
59
|
};
|
|
46
60
|
type RadarOptions = {
|
|
47
61
|
key?: string;
|
|
@@ -53,7 +67,7 @@ type RadarOptions = {
|
|
|
53
67
|
minLevel?: LogLevel;
|
|
54
68
|
redact?: RedactMode;
|
|
55
69
|
requestDetail?: RequestDetail;
|
|
56
|
-
logRequests?:
|
|
70
|
+
logRequests?: LogRequestsOption;
|
|
57
71
|
ignorePaths?: string[];
|
|
58
72
|
captureUnhandled?: boolean;
|
|
59
73
|
traceFetch?: TraceFetchOption;
|
|
@@ -69,7 +83,7 @@ type ResolvedOptions = {
|
|
|
69
83
|
minLevel: LogLevel;
|
|
70
84
|
redact: RedactMode;
|
|
71
85
|
requestDetail: RequestDetail;
|
|
72
|
-
logRequests:
|
|
86
|
+
logRequests: ResolvedLogRequests;
|
|
73
87
|
ignorePaths: string[];
|
|
74
88
|
captureUnhandled: boolean;
|
|
75
89
|
traceFetch: ResolvedTraceFetch;
|
|
@@ -38,10 +38,24 @@ type RequestDetail = 'full' | 'route';
|
|
|
38
38
|
type FetchTraceTarget = string | RegExp;
|
|
39
39
|
type TraceFetchOption = boolean | {
|
|
40
40
|
propagateTo?: FetchTraceTarget[];
|
|
41
|
+
ignore?: FetchTraceTarget[];
|
|
41
42
|
};
|
|
42
43
|
type ResolvedTraceFetch = {
|
|
43
44
|
enabled: boolean;
|
|
44
45
|
propagateTo: FetchTraceTarget[];
|
|
46
|
+
ignore: FetchTraceTarget[];
|
|
47
|
+
};
|
|
48
|
+
type RequestPattern = string | RegExp;
|
|
49
|
+
type LogRequestsOption = boolean | {
|
|
50
|
+
include?: RequestPattern[];
|
|
51
|
+
exclude?: RequestPattern[];
|
|
52
|
+
sample?: number;
|
|
53
|
+
};
|
|
54
|
+
type ResolvedLogRequests = {
|
|
55
|
+
enabled: boolean;
|
|
56
|
+
include: RequestPattern[];
|
|
57
|
+
exclude: RequestPattern[];
|
|
58
|
+
sample: number;
|
|
45
59
|
};
|
|
46
60
|
type RadarOptions = {
|
|
47
61
|
key?: string;
|
|
@@ -53,7 +67,7 @@ type RadarOptions = {
|
|
|
53
67
|
minLevel?: LogLevel;
|
|
54
68
|
redact?: RedactMode;
|
|
55
69
|
requestDetail?: RequestDetail;
|
|
56
|
-
logRequests?:
|
|
70
|
+
logRequests?: LogRequestsOption;
|
|
57
71
|
ignorePaths?: string[];
|
|
58
72
|
captureUnhandled?: boolean;
|
|
59
73
|
traceFetch?: TraceFetchOption;
|
|
@@ -69,7 +83,7 @@ type ResolvedOptions = {
|
|
|
69
83
|
minLevel: LogLevel;
|
|
70
84
|
redact: RedactMode;
|
|
71
85
|
requestDetail: RequestDetail;
|
|
72
|
-
logRequests:
|
|
86
|
+
logRequests: ResolvedLogRequests;
|
|
73
87
|
ignorePaths: string[];
|
|
74
88
|
captureUnhandled: boolean;
|
|
75
89
|
traceFetch: ResolvedTraceFetch;
|
package/dist/index.cjs
CHANGED
|
@@ -280,6 +280,12 @@ function requestPath(req) {
|
|
|
280
280
|
const end = url.indexOf("?");
|
|
281
281
|
return end >= 0 ? url.slice(0, end) : url;
|
|
282
282
|
}
|
|
283
|
+
function matchesRequest(pattern, path, route) {
|
|
284
|
+
const candidates = route === void 0 ? [path] : [path, route];
|
|
285
|
+
if (pattern instanceof RegExp) return candidates.some((value) => value.search(pattern) !== -1);
|
|
286
|
+
const prefix = pattern.endsWith("/") ? pattern : `${pattern}/`;
|
|
287
|
+
return candidates.some((value) => value === pattern || value.startsWith(prefix));
|
|
288
|
+
}
|
|
283
289
|
function requestRoute(req, route) {
|
|
284
290
|
if (route) return route;
|
|
285
291
|
if (typeof req.route?.path !== "string") return void 0;
|
|
@@ -419,11 +425,24 @@ function createMiddleware(logger) {
|
|
|
419
425
|
// src/options.ts
|
|
420
426
|
var DEFAULT_ENDPOINT = "https://radar-ingest.oconde.dev";
|
|
421
427
|
var LEVELS = ["debug", "info", "warn", "error"];
|
|
428
|
+
function patterns(value) {
|
|
429
|
+
if (!Array.isArray(value)) return [];
|
|
430
|
+
return value.flatMap((item) => {
|
|
431
|
+
if (item instanceof RegExp) return [item];
|
|
432
|
+
if (typeof item === "string" && item.trim() !== "") return [item.trim()];
|
|
433
|
+
return [];
|
|
434
|
+
});
|
|
435
|
+
}
|
|
422
436
|
function resolveTraceFetch(value) {
|
|
423
|
-
if (!value) return { enabled: false, propagateTo: [] };
|
|
424
|
-
if (value === true) return { enabled: true, propagateTo: [] };
|
|
425
|
-
|
|
426
|
-
|
|
437
|
+
if (!value) return { enabled: false, propagateTo: [], ignore: [] };
|
|
438
|
+
if (value === true) return { enabled: true, propagateTo: [], ignore: [] };
|
|
439
|
+
return { enabled: true, propagateTo: patterns(value.propagateTo), ignore: patterns(value.ignore) };
|
|
440
|
+
}
|
|
441
|
+
function resolveLogRequests(value) {
|
|
442
|
+
if (value === false) return { enabled: false, include: [], exclude: [], sample: 1 };
|
|
443
|
+
if (value === void 0 || value === true || typeof value !== "object" || value === null) return { enabled: true, include: [], exclude: [], sample: 1 };
|
|
444
|
+
const sample = typeof value.sample === "number" && Number.isFinite(value.sample) && value.sample >= 0 && value.sample <= 1 ? value.sample : 1;
|
|
445
|
+
return { enabled: true, include: patterns(value.include), exclude: patterns(value.exclude), sample };
|
|
427
446
|
}
|
|
428
447
|
function resolveOptions(options = {}, env = process.env) {
|
|
429
448
|
const key = options.key?.trim() || void 0;
|
|
@@ -437,7 +456,7 @@ function resolveOptions(options = {}, env = process.env) {
|
|
|
437
456
|
minLevel: options.minLevel && LEVELS.includes(options.minLevel) ? options.minLevel : "info",
|
|
438
457
|
redact: options.redact === "none" ? "none" : "mask",
|
|
439
458
|
requestDetail: options.requestDetail === "route" ? "route" : "full",
|
|
440
|
-
logRequests: options.logRequests
|
|
459
|
+
logRequests: resolveLogRequests(options.logRequests),
|
|
441
460
|
ignorePaths: options.ignorePaths ?? ["/healthz", "/health"],
|
|
442
461
|
captureUnhandled: options.captureUnhandled ?? true,
|
|
443
462
|
traceFetch: resolveTraceFetch(options.traceFetch),
|
|
@@ -591,8 +610,8 @@ function headerOf(code, index, prefix) {
|
|
|
591
610
|
return header;
|
|
592
611
|
}
|
|
593
612
|
function nameIn(header) {
|
|
594
|
-
const
|
|
595
|
-
for (const pattern of
|
|
613
|
+
const patterns2 = ANONYMOUS_FUNCTION.test(header) ? NAME_PATTERNS : [...NAME_PATTERNS, METHOD_PATTERN];
|
|
614
|
+
for (const pattern of patterns2) {
|
|
596
615
|
const name = pattern.exec(header)?.[1];
|
|
597
616
|
if (name && !KEYWORDS.has(name)) return name.slice(0, MAX_NAME_LENGTH);
|
|
598
617
|
}
|
|
@@ -912,12 +931,19 @@ function originOf(value) {
|
|
|
912
931
|
return null;
|
|
913
932
|
}
|
|
914
933
|
}
|
|
915
|
-
function
|
|
916
|
-
|
|
917
|
-
const wanted = target.trim().toLowerCase();
|
|
934
|
+
function hostMatches(url, host) {
|
|
935
|
+
const wanted = host.trim().toLowerCase();
|
|
918
936
|
if (wanted.startsWith(".")) return url.hostname.toLowerCase().endsWith(wanted);
|
|
919
937
|
return url.host.toLowerCase() === wanted || url.hostname.toLowerCase() === wanted;
|
|
920
938
|
}
|
|
939
|
+
function targets(url, target) {
|
|
940
|
+
if (target instanceof RegExp) return url.host.search(target) !== -1;
|
|
941
|
+
return hostMatches(url, target);
|
|
942
|
+
}
|
|
943
|
+
function ignored(url, target) {
|
|
944
|
+
if (target instanceof RegExp) return `${url.host}${url.pathname}`.search(target) !== -1;
|
|
945
|
+
return hostMatches(url, target);
|
|
946
|
+
}
|
|
921
947
|
function isPlainObject(value) {
|
|
922
948
|
if (value === null || typeof value !== "object") return false;
|
|
923
949
|
const prototype = Object.getPrototypeOf(value);
|
|
@@ -941,6 +967,7 @@ function traceFetch(config) {
|
|
|
941
967
|
const method = (init?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase();
|
|
942
968
|
const context = currentContext();
|
|
943
969
|
const [nextInput, nextInit] = context && config.propagateTo.some((target) => targets(url, target)) ? withRequestId(input, init, context.requestId) : [input, init];
|
|
970
|
+
if (config.ignore?.some((target) => ignored(url, target))) return original(nextInput, nextInit);
|
|
944
971
|
const attrs = { method, host: url.host, path: outboundPath(url.pathname, config.redact) };
|
|
945
972
|
const started = performance.now();
|
|
946
973
|
try {
|
|
@@ -1189,7 +1216,7 @@ function withTimeout(promise, ms) {
|
|
|
1189
1216
|
|
|
1190
1217
|
// src/version.ts
|
|
1191
1218
|
var SDK_NAME = "@oconde/radar";
|
|
1192
|
-
var SDK_VERSION = true ? "0.
|
|
1219
|
+
var SDK_VERSION = true ? "0.4.1" : "0.0.0-dev";
|
|
1193
1220
|
|
|
1194
1221
|
// src/client.ts
|
|
1195
1222
|
var MAX_CHECK_IN_DURATION_MS = 7 * 864e5;
|
|
@@ -1233,6 +1260,7 @@ var RadarClient = class {
|
|
|
1233
1260
|
this.restoreFetch = traceFetch({
|
|
1234
1261
|
endpoint: this.options.endpoint,
|
|
1235
1262
|
propagateTo: this.options.traceFetch.propagateTo,
|
|
1263
|
+
ignore: this.options.traceFetch.ignore,
|
|
1236
1264
|
redact: this.options.redact,
|
|
1237
1265
|
record: (level, attrs) => this.safely(() => this.emitLog(level, "http.client", attrs))
|
|
1238
1266
|
});
|
|
@@ -1343,22 +1371,28 @@ var RadarClient = class {
|
|
|
1343
1371
|
return createMiddleware(this);
|
|
1344
1372
|
}
|
|
1345
1373
|
shouldLogRequest(req) {
|
|
1346
|
-
return this.options.logRequests && !this.options.ignorePaths.includes(requestPath(req));
|
|
1374
|
+
return this.options.logRequests.enabled && !this.options.ignorePaths.includes(requestPath(req));
|
|
1347
1375
|
}
|
|
1348
1376
|
logHttpRequest(context, status) {
|
|
1349
1377
|
this.safely(() => {
|
|
1350
1378
|
const req = context.req;
|
|
1351
1379
|
if (!req) return;
|
|
1352
|
-
const level = status >= 500 ? "error" : status >= 400 ? "warn" : "info";
|
|
1353
1380
|
const route = requestRoute(req, context.route);
|
|
1381
|
+
const path = requestPath(req);
|
|
1382
|
+
const { include, exclude, sample } = this.options.logRequests;
|
|
1383
|
+
if (include.length > 0 && !include.some((pattern) => matchesRequest(pattern, path, route))) return;
|
|
1384
|
+
if (exclude.some((pattern) => matchesRequest(pattern, path, route))) return;
|
|
1385
|
+
if (sample < 1 && Math.random() >= sample) return;
|
|
1386
|
+
const level = status >= 500 ? "error" : status >= 400 ? "warn" : "info";
|
|
1354
1387
|
runWithContext(
|
|
1355
1388
|
context,
|
|
1356
1389
|
() => this.emitLog(level, "http.request", {
|
|
1357
1390
|
method: (req.method ?? "GET").toUpperCase(),
|
|
1358
1391
|
...route ? { route } : {},
|
|
1359
|
-
...this.options.requestDetail === "full" ? { path: redactPath(
|
|
1392
|
+
...this.options.requestDetail === "full" ? { path: redactPath(path, pathParams(req, path), this.options.redact) } : {},
|
|
1360
1393
|
status,
|
|
1361
|
-
durationMs: elapsedSince(context.startedAt)
|
|
1394
|
+
durationMs: elapsedSince(context.startedAt),
|
|
1395
|
+
...sample < 1 ? { sampleRate: sample } : {}
|
|
1362
1396
|
})
|
|
1363
1397
|
);
|
|
1364
1398
|
});
|