-
-
Notifications
You must be signed in to change notification settings - Fork 348
/
Copy pathjson.ts
133 lines (113 loc) Β· 3.81 KB
/
json.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import {LodestarError, mapValues, toHexString} from "@lodestar/utils";
const MAX_DEPTH = 0;
type LogDataBasic = string | number | bigint | boolean | null | undefined;
export type LogData =
| LogDataBasic
| LogDataBasic[]
| Record<string, LogDataBasic>
| Record<string, LogDataBasic>[]
| Record<string, LogDataBasic[]>
| Record<string, LogDataBasic[]>[];
/**
* Renders any log Context to JSON up to one level of depth.
*
* By limiting recursiveness, it renders limited content while ensuring safer logging.
* Consumers of the logger should ensure to send pre-formated data if they require nesting.
*/
export function logCtxToJson(arg: unknown, depth = 0, fromError = false): LogData {
switch (typeof arg) {
case "bigint":
case "symbol":
case "function":
return arg.toString();
case "object":
if (arg === null) return "null";
if (arg instanceof Uint8Array) {
return toHexString(arg);
}
// For any type that may include recursiveness break early at the first level
// - Prevent recursive loops
// - Ensures Error with deep complex metadata won't leak into the logs and cause bugs
if (depth > MAX_DEPTH) {
return "[object]";
}
if (arg instanceof Error) {
let metadata: Record<string, unknown>;
if (arg instanceof LodestarError) {
if (fromError) {
return "[LodestarErrorCircular]";
} else {
// Allow one extra depth level for LodestarError
metadata = logCtxToJson(arg.getMetadata(), depth - 1, true) as Record<string, unknown>;
}
} else {
metadata = {message: arg.message};
}
if (arg.stack) metadata.stack = arg.stack;
return metadata as LogData;
}
if (Array.isArray(arg)) {
return arg.map((item) => logCtxToJson(item, depth + 1)) as LogData;
}
return mapValues(arg as Record<string, unknown>, (item) => logCtxToJson(item, depth + 1)) as LogData;
// Already valid JSON
case "number":
case "string":
case "undefined":
case "boolean":
return arg;
default:
return String(arg);
}
}
/**
* Renders any log Context to a string up to one level of depth.
*
* By limiting recursiveness, it renders limited content while ensuring safer logging.
* Consumers of the logger should ensure to send pre-formated data if they require nesting.
*/
export function logCtxToString(arg: unknown, depth = 0, fromError = false): string {
switch (typeof arg) {
case "bigint":
case "symbol":
case "function":
return arg.toString();
case "object":
if (arg === null) return "null";
if (arg instanceof Uint8Array) {
return toHexString(arg);
}
// For any type that may include recursiveness break early at the first level
// - Prevent recursive loops
// - Ensures Error with deep complex metadata won't leak into the logs and cause bugs
if (depth > MAX_DEPTH) {
return "[object]";
}
if (arg instanceof Error) {
let metadata: string;
if (arg instanceof LodestarError) {
if (fromError) {
return "[LodestarErrorCircular]";
} else {
// Allow one extra depth level for LodestarError
metadata = logCtxToString(arg.getMetadata(), depth - 1, true);
}
} else {
metadata = arg.message;
}
return `${metadata}\n${arg.stack || ""}`;
}
if (Array.isArray(arg)) {
return arg.map((item) => logCtxToString(item, depth + 1)).join(", ");
}
return Object.entries(arg)
.map(([key, value]) => `${key}=${logCtxToString(value, depth + 1)}`)
.join(", ");
case "number":
case "string":
case "undefined":
case "boolean":
default:
return String(arg);
}
}