@zudojs/observability 1.0.0 → 1.0.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/README.md
CHANGED
|
@@ -98,9 +98,11 @@ obs.logger.info("login", {
|
|
|
98
98
|
```
|
|
99
99
|
|
|
100
100
|
Traversal handles the shapes secrets actually arrive in: arrays, nested
|
|
101
|
-
objects,
|
|
102
|
-
|
|
103
|
-
`
|
|
101
|
+
objects, instances of your own classes (a DTO carrying a `password` field is
|
|
102
|
+
redacted and keeps its prototype), and cyclic graphs (a request object in a log
|
|
103
|
+
context becomes `[CIRCULAR]` rather than a stack overflow). Built-ins —
|
|
104
|
+
`Error`, `Date`, `Map`, `Set`, typed arrays, `URL` — are left intact instead of
|
|
105
|
+
being flattened to `{}`.
|
|
104
106
|
|
|
105
107
|
`redactObject`, `redactValue` and `createStructureRedactor` are exported for
|
|
106
108
|
use outside the logger.
|
|
@@ -8,8 +8,11 @@
|
|
|
8
8
|
* (`headers: [{ authorization: "Bearer …" }]`);
|
|
9
9
|
* - traversal is cycle-aware and depth-capped, because a request object in
|
|
10
10
|
* a log context is a graph, not a tree;
|
|
11
|
-
* -
|
|
12
|
-
*
|
|
11
|
+
* - built-ins such as `Error`, `Date`, `Map` and `Set` are left intact
|
|
12
|
+
* instead of collapsing to `{}`, while an instance of a user-defined
|
|
13
|
+
* class is walked like a plain object — its own fields are what an
|
|
14
|
+
* exporter serializes, so a DTO carrying a `password` must not slip
|
|
15
|
+
* past the field list;
|
|
13
16
|
* - matching is substring-based by default, so `userPassword` and
|
|
14
17
|
* `x-api-key` are caught, not just the exact names in the list.
|
|
15
18
|
*/
|
|
@@ -8,8 +8,11 @@
|
|
|
8
8
|
* (`headers: [{ authorization: "Bearer …" }]`);
|
|
9
9
|
* - traversal is cycle-aware and depth-capped, because a request object in
|
|
10
10
|
* a log context is a graph, not a tree;
|
|
11
|
-
* -
|
|
12
|
-
*
|
|
11
|
+
* - built-ins such as `Error`, `Date`, `Map` and `Set` are left intact
|
|
12
|
+
* instead of collapsing to `{}`, while an instance of a user-defined
|
|
13
|
+
* class is walked like a plain object — its own fields are what an
|
|
14
|
+
* exporter serializes, so a DTO carrying a `password` must not slip
|
|
15
|
+
* past the field list;
|
|
13
16
|
* - matching is substring-based by default, so `userPassword` and
|
|
14
17
|
* `x-api-key` are caught, not just the exact names in the list.
|
|
15
18
|
*/
|
|
@@ -145,10 +148,48 @@ function isPlainContainer(value) {
|
|
|
145
148
|
if (Array.isArray(value))
|
|
146
149
|
return false;
|
|
147
150
|
const prototype = Object.getPrototypeOf(value);
|
|
148
|
-
// Walk plain objects and null-prototype bags; leave Error, Date, Map, Set,
|
|
149
|
-
// Buffer and every other class instance intact.
|
|
150
151
|
return prototype === Object.prototype || prototype === null;
|
|
151
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* Built-ins whose contents are not key/value fields: they are left intact
|
|
155
|
+
* rather than walked, because rebuilding them field by field would collapse
|
|
156
|
+
* a `Date` or a `Map` to `{}`.
|
|
157
|
+
*/
|
|
158
|
+
function isOpaqueBuiltin(value) {
|
|
159
|
+
return (value instanceof Date ||
|
|
160
|
+
value instanceof RegExp ||
|
|
161
|
+
value instanceof Error ||
|
|
162
|
+
value instanceof Map ||
|
|
163
|
+
value instanceof Set ||
|
|
164
|
+
value instanceof WeakMap ||
|
|
165
|
+
value instanceof WeakSet ||
|
|
166
|
+
value instanceof Promise ||
|
|
167
|
+
value instanceof ArrayBuffer ||
|
|
168
|
+
ArrayBuffer.isView(value) ||
|
|
169
|
+
value instanceof Number ||
|
|
170
|
+
value instanceof String ||
|
|
171
|
+
value instanceof Boolean ||
|
|
172
|
+
(typeof URL !== "undefined" && value instanceof URL) ||
|
|
173
|
+
(typeof URLSearchParams !== "undefined" && value instanceof URLSearchParams));
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* True for an instance of a user-defined class (a DTO, a request model).
|
|
177
|
+
*
|
|
178
|
+
* Its own enumerable fields are exactly what `JSON.stringify` — and so every
|
|
179
|
+
* exporter — writes out, so a `password` field on a class instance leaked
|
|
180
|
+
* through a redactor that only walked plain objects. Such an instance is
|
|
181
|
+
* walked like a plain object and rebuilt on the same prototype, so
|
|
182
|
+
* `instanceof` and any `toJSON` it defines survive.
|
|
183
|
+
*/
|
|
184
|
+
function isWalkableInstance(value) {
|
|
185
|
+
if (value === null || typeof value !== "object")
|
|
186
|
+
return false;
|
|
187
|
+
if (Array.isArray(value) || isPlainContainer(value))
|
|
188
|
+
return false;
|
|
189
|
+
if (isOpaqueBuiltin(value))
|
|
190
|
+
return false;
|
|
191
|
+
return Object.keys(value).length > 0;
|
|
192
|
+
}
|
|
152
193
|
function walk(value, compiled, depth, seen) {
|
|
153
194
|
if (depth > compiled.maxDepth)
|
|
154
195
|
return MAX_DEPTH_MARKER;
|
|
@@ -160,11 +201,11 @@ function walk(value, compiled, depth, seen) {
|
|
|
160
201
|
seen.delete(value);
|
|
161
202
|
return result;
|
|
162
203
|
}
|
|
163
|
-
if (isPlainContainer(value)) {
|
|
204
|
+
if (isPlainContainer(value) || isWalkableInstance(value)) {
|
|
164
205
|
if (seen.has(value))
|
|
165
206
|
return CIRCULAR_MARKER;
|
|
166
207
|
seen.add(value);
|
|
167
|
-
const result =
|
|
208
|
+
const result = Object.create(Object.getPrototypeOf(value));
|
|
168
209
|
for (const [key, entry] of Object.entries(value)) {
|
|
169
210
|
const redacted = redactField(key, entry, compiled);
|
|
170
211
|
// A field the matcher replaced is done — never walk into it, or a
|
package/package.json
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zudojs/observability",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Structured logging, metrics, tracing, context propagation, and exporters for Zudojs applications.",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Oluwayemi Oyinlola",
|
|
8
|
+
"url": "https://github.com/oyinlola-tech"
|
|
9
|
+
},
|
|
6
10
|
"type": "module",
|
|
7
11
|
"main": "./dist/index.js",
|
|
8
12
|
"module": "./dist/index.js",
|
|
@@ -22,7 +26,7 @@
|
|
|
22
26
|
"!dist/.tsbuildinfo"
|
|
23
27
|
],
|
|
24
28
|
"dependencies": {
|
|
25
|
-
"@zudojs/errors": "1.0.
|
|
29
|
+
"@zudojs/errors": "1.0.1"
|
|
26
30
|
},
|
|
27
31
|
"devDependencies": {
|
|
28
32
|
"typescript": "7.0.2",
|