@jaypie/logger 1.2.17 → 1.2.19

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.
Files changed (39) hide show
  1. package/dist/cjs/index.cjs +302 -7
  2. package/dist/cjs/index.cjs.map +1 -1
  3. package/dist/cjs/index.d.cts +142 -0
  4. package/dist/cjs/{JaypieLogger.d.ts → src/JaypieLogger.d.ts} +10 -2
  5. package/dist/cjs/{Logger.d.ts → src/Logger.d.ts} +9 -2
  6. package/dist/cjs/{constants.d.ts → src/constants.d.ts} +6 -0
  7. package/dist/cjs/{index.d.ts → src/index.d.ts} +1 -0
  8. package/dist/cjs/src/limits.d.ts +55 -0
  9. package/dist/esm/index.d.ts +142 -8
  10. package/dist/esm/index.js +302 -7
  11. package/dist/esm/index.js.map +1 -1
  12. package/dist/esm/{JaypieLogger.d.ts → src/JaypieLogger.d.ts} +10 -2
  13. package/dist/esm/{Logger.d.ts → src/Logger.d.ts} +9 -2
  14. package/dist/esm/src/__tests__/limits.spec.d.ts +1 -0
  15. package/dist/esm/src/__tests__/sanitizeAuth.spec.d.ts +1 -0
  16. package/dist/esm/{constants.d.ts → src/constants.d.ts} +6 -0
  17. package/dist/esm/src/index.d.ts +9 -0
  18. package/dist/esm/src/limits.d.ts +55 -0
  19. package/package.json +10 -4
  20. /package/dist/cjs/{__tests__ → src/__tests__}/datadogTransport.spec.d.ts +0 -0
  21. /package/dist/cjs/{__tests__ → src/__tests__}/index.spec.d.ts +0 -0
  22. /package/dist/cjs/{__tests__/sanitizeAuth.spec.d.ts → src/__tests__/limits.spec.d.ts} +0 -0
  23. /package/dist/{esm → cjs/src}/__tests__/sanitizeAuth.spec.d.ts +0 -0
  24. /package/dist/cjs/{datadogTransport.d.ts → src/datadogTransport.d.ts} +0 -0
  25. /package/dist/cjs/{forceVar.d.ts → src/forceVar.d.ts} +0 -0
  26. /package/dist/cjs/{logTags.d.ts → src/logTags.d.ts} +0 -0
  27. /package/dist/cjs/{logVar.d.ts → src/logVar.d.ts} +0 -0
  28. /package/dist/cjs/{pipelines.d.ts → src/pipelines.d.ts} +0 -0
  29. /package/dist/cjs/{sanitizeAuth.d.ts → src/sanitizeAuth.d.ts} +0 -0
  30. /package/dist/cjs/{utils.d.ts → src/utils.d.ts} +0 -0
  31. /package/dist/esm/{__tests__ → src/__tests__}/datadogTransport.spec.d.ts +0 -0
  32. /package/dist/esm/{__tests__ → src/__tests__}/index.spec.d.ts +0 -0
  33. /package/dist/esm/{datadogTransport.d.ts → src/datadogTransport.d.ts} +0 -0
  34. /package/dist/esm/{forceVar.d.ts → src/forceVar.d.ts} +0 -0
  35. /package/dist/esm/{logTags.d.ts → src/logTags.d.ts} +0 -0
  36. /package/dist/esm/{logVar.d.ts → src/logVar.d.ts} +0 -0
  37. /package/dist/esm/{pipelines.d.ts → src/pipelines.d.ts} +0 -0
  38. /package/dist/esm/{sanitizeAuth.d.ts → src/sanitizeAuth.d.ts} +0 -0
  39. /package/dist/esm/{utils.d.ts → src/utils.d.ts} +0 -0
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Caller-facing limit options. `false` explicitly disables a limit;
3
+ * `undefined` resolves from env vars, then defaults.
4
+ */
5
+ export interface SerializationLimitOptions {
6
+ maxDepth?: number | false;
7
+ maxEntryBytes?: number | false;
8
+ maxStringLength?: number | false;
9
+ }
10
+ /**
11
+ * Resolved limits. `undefined` means the limit is off.
12
+ */
13
+ export interface SerializationLimits {
14
+ maxDepth?: number;
15
+ maxEntryBytes?: number;
16
+ maxStringLength?: number;
17
+ }
18
+ /** Characters preserved when an oversized entry truncates an attribute */
19
+ export declare const ENTRY_PREVIEW_LENGTH = 72;
20
+ /**
21
+ * Resolve limits from explicit options, env vars, then defaults.
22
+ * `maxEntryBytes` defaults on (payloads must fit the log pipeline);
23
+ * `maxDepth` and `maxStringLength` default off.
24
+ */
25
+ export declare function resolveSerializationLimits(options?: SerializationLimitOptions): SerializationLimits;
26
+ export declare function hasValueLimits(limits: SerializationLimits): boolean;
27
+ export declare function byteLength(value: unknown): number;
28
+ /**
29
+ * Keep the first `maxLength` characters and append a visible marker
30
+ * preserving the dropped size
31
+ */
32
+ export declare function truncateString(value: string, maxLength: number): string;
33
+ /**
34
+ * Fit a string to a byte budget, reserving room for the marker and
35
+ * never cutting below the preview length
36
+ */
37
+ export declare function truncateToBudget(value: string, budgetBytes: number): string;
38
+ /**
39
+ * Walk a value applying maxStringLength and maxDepth. Returns a new value;
40
+ * never mutates the input. Only plain objects and arrays are traversed so
41
+ * class instances (Error, Date, ...) keep their serialization behavior.
42
+ */
43
+ export declare function applyValueLimits(value: unknown, limits: SerializationLimits, depth?: number, seen?: WeakSet<object>): unknown;
44
+ /**
45
+ * Fit a serialized log entry under maxEntryBytes. Truncates the top-level
46
+ * attributes of `data` largest-first to short previews until the entry fits,
47
+ * collapsing `data` to a byte-count marker only as a last resort. When
48
+ * `syncMessageToData` is set (var entries and single-object messages, where
49
+ * `message` mirrors `data`), the message is rebuilt from the truncated data.
50
+ * Returns a new entry; never mutates the input.
51
+ */
52
+ export declare function enforceEntryLimit(entry: Record<string, unknown>, { maxEntryBytes, syncMessageToData, }: {
53
+ maxEntryBytes: number;
54
+ syncMessageToData?: boolean;
55
+ }): Record<string, unknown>;
@@ -1,8 +1,142 @@
1
- import Logger from "./Logger";
2
- import { createLogger } from "./JaypieLogger";
3
- import { FORMAT, LEVEL } from "./constants";
4
- import { _resetDatadogTransport, getDatadogTransport, isDatadogForwardingEnabled } from "./datadogTransport";
5
- import { redactAuth, sanitizeAuth } from "./sanitizeAuth";
6
- export { FORMAT, LEVEL, Logger, _resetDatadogTransport, createLogger, getDatadogTransport, isDatadogForwardingEnabled, redactAuth, sanitizeAuth, };
7
- export declare const log: import("./JaypieLogger").default;
8
- export default log;
1
+ /**
2
+ * Caller-facing limit options. `false` explicitly disables a limit;
3
+ * `undefined` resolves from env vars, then defaults.
4
+ */
5
+ interface SerializationLimitOptions {
6
+ maxDepth?: number | false;
7
+ maxEntryBytes?: number | false;
8
+ maxStringLength?: number | false;
9
+ }
10
+ /**
11
+ * Resolved limits. `undefined` means the limit is off.
12
+ */
13
+ interface SerializationLimits {
14
+ maxDepth?: number;
15
+ maxEntryBytes?: number;
16
+ maxStringLength?: number;
17
+ }
18
+
19
+ type LogLevel = string;
20
+ type LogFormat = "json" | "text";
21
+ type Tags = Record<string, string>;
22
+ interface LoggerOptions extends SerializationLimitOptions {
23
+ format?: LogFormat;
24
+ level?: LogLevel;
25
+ levelField?: boolean | string;
26
+ tags?: Tags;
27
+ varLevel?: LogLevel;
28
+ }
29
+ type LogMethod = {
30
+ (...messages: unknown[]): void;
31
+ var: (messageObject: unknown, messageValue?: unknown) => void;
32
+ };
33
+ declare class Logger {
34
+ debug: LogMethod;
35
+ error: LogMethod;
36
+ fatal: LogMethod;
37
+ info: LogMethod;
38
+ options: LoggerOptions;
39
+ tags: Tags;
40
+ trace: LogMethod;
41
+ var: (messageObject: unknown, messageValue?: unknown) => void;
42
+ warn: LogMethod;
43
+ private levelField;
44
+ private limits;
45
+ constructor({ format, level, levelField, maxDepth, maxEntryBytes, maxStringLength, tags, varLevel, }?: LoggerOptions);
46
+ private createLogMethod;
47
+ /**
48
+ * Update serialization limits at runtime. Pass a number to set a limit,
49
+ * `false` to disable one; omitted keys are unchanged.
50
+ */
51
+ config(options?: SerializationLimitOptions): void;
52
+ tag(key: unknown, value?: unknown): void;
53
+ untag(key: unknown): void;
54
+ with(key: unknown, value?: unknown): Logger;
55
+ }
56
+
57
+ interface JaypieLoggerOptions extends SerializationLimitOptions {
58
+ level?: string;
59
+ tags?: Record<string, string>;
60
+ }
61
+ declare class JaypieLogger {
62
+ debug: Logger["debug"];
63
+ error: Logger["error"];
64
+ fatal: Logger["fatal"];
65
+ info: Logger["info"];
66
+ level: string;
67
+ trace: Logger["trace"];
68
+ var: Logger["var"];
69
+ warn: Logger["warn"];
70
+ private _errorCount;
71
+ private _logger;
72
+ private _loggers;
73
+ private _params;
74
+ private _report;
75
+ private _sessionActive;
76
+ private _tags;
77
+ private _warnCount;
78
+ private _withLoggers;
79
+ constructor({ level, maxDepth, maxEntryBytes, maxStringLength, tags, }?: JaypieLoggerOptions);
80
+ /**
81
+ * Update serialization limits at runtime for this logger and all loggers
82
+ * derived from it (lib, with, flag). Pass a number to set a limit,
83
+ * `false` to disable one; omitted keys are unchanged. Persists across
84
+ * init().
85
+ */
86
+ config(options?: SerializationLimitOptions): void;
87
+ flag(flag?: string): JaypieLogger;
88
+ init(): void;
89
+ lib({ level, lib, tags, }?: {
90
+ level?: string;
91
+ lib?: string;
92
+ tags?: Record<string, string>;
93
+ }): JaypieLogger;
94
+ report(data: Record<string, unknown>): void;
95
+ setup(tags?: Record<string, unknown>): void;
96
+ tag(tags: Record<string, unknown>): void;
97
+ teardown(): void;
98
+ untag(key: unknown): void;
99
+ with(key: unknown, value?: unknown): JaypieLogger;
100
+ }
101
+ declare function createLogger(tags?: Record<string, string>): JaypieLogger;
102
+
103
+ declare const FORMAT: {
104
+ readonly JSON: "json";
105
+ readonly TEXT: "text";
106
+ };
107
+ declare const LEVEL: {
108
+ readonly ALL: "all";
109
+ readonly DEBUG: "debug";
110
+ readonly ERROR: "error";
111
+ readonly FATAL: "fatal";
112
+ readonly INFO: "info";
113
+ readonly SILENT: "silent";
114
+ readonly TRACE: "trace";
115
+ readonly WARN: "warn";
116
+ };
117
+
118
+ declare function isDatadogForwardingEnabled(): boolean;
119
+ declare class DatadogLogTransport {
120
+ private _beforeExitHandler;
121
+ private _buffer;
122
+ private _flushTimer;
123
+ private _ddsource;
124
+ private _env;
125
+ private _hostname;
126
+ private _service;
127
+ private _site;
128
+ constructor();
129
+ send(line: string, level: string): void;
130
+ flush(): void;
131
+ destroy(): void;
132
+ }
133
+ declare function getDatadogTransport(): DatadogLogTransport | null;
134
+ declare function _resetDatadogTransport(): void;
135
+
136
+ declare function redactAuth(value: unknown): string;
137
+ declare function sanitizeAuth(value: unknown): unknown;
138
+
139
+ declare const log: JaypieLogger;
140
+
141
+ export { FORMAT, LEVEL, Logger, _resetDatadogTransport, createLogger, log as default, getDatadogTransport, isDatadogForwardingEnabled, log, redactAuth, sanitizeAuth };
142
+ export type { SerializationLimitOptions, SerializationLimits };
package/dist/esm/index.js CHANGED
@@ -4,8 +4,16 @@ import { request } from 'node:https';
4
4
 
5
5
  const DEFAULT = {
6
6
  LEVEL: "debug",
7
+ // CloudWatch Logs caps events at 256KB and fronts Datadog in Lambda;
8
+ // Datadog's own per-log cap is 1MB. Truncate deliberately below both.
9
+ MAX_ENTRY_BYTES: 262144,
7
10
  VAR_LEVEL: "debug",
8
11
  };
12
+ const LIMIT_ENV = {
13
+ MAX_DEPTH: "LOG_MAX_DEPTH",
14
+ MAX_ENTRY_BYTES: "LOG_MAX_ENTRY_BYTES",
15
+ MAX_STRING: "LOG_MAX_STRING",
16
+ };
9
17
  const ERROR_PREFIX = "[logger]";
10
18
  const ERROR = {
11
19
  VAR: {
@@ -59,6 +67,207 @@ const DATADOG_TRANSPORT = {
59
67
  MAX_BATCH_SIZE: 100,
60
68
  };
61
69
 
70
+ //
71
+ //
72
+ // Constants
73
+ //
74
+ const CIRCULAR_PLACEHOLDER = "[Circular]";
75
+ const DISABLED_ENV_VALUES = ["", "0", "false", "none", "off"];
76
+ const ELLIPSIS = "…";
77
+ // Reserve headroom for the truncation marker when fitting a string to a
78
+ // byte budget
79
+ const MARKER_RESERVE_BYTES = 64;
80
+ /** Characters preserved when an oversized entry truncates an attribute */
81
+ const ENTRY_PREVIEW_LENGTH = 72;
82
+ //
83
+ //
84
+ // Helpers
85
+ //
86
+ function isPlainObject(value) {
87
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
88
+ return false;
89
+ }
90
+ const proto = Object.getPrototypeOf(value);
91
+ return proto === Object.prototype || proto === null;
92
+ }
93
+ function normalizeLimit(option) {
94
+ if (option === false)
95
+ return undefined;
96
+ if (typeof option === "number" && Number.isFinite(option) && option > 0) {
97
+ return Math.floor(option);
98
+ }
99
+ return undefined;
100
+ }
101
+ function resolveLimit(option, envKey, defaultValue) {
102
+ if (option !== undefined) {
103
+ return normalizeLimit(option);
104
+ }
105
+ const raw = process.env[envKey];
106
+ if (raw !== undefined) {
107
+ if (DISABLED_ENV_VALUES.includes(raw.toLowerCase()))
108
+ return undefined;
109
+ const parsed = Number.parseInt(raw, 10);
110
+ if (Number.isFinite(parsed) && parsed > 0)
111
+ return parsed;
112
+ }
113
+ return defaultValue;
114
+ }
115
+ function truncationMarker(droppedChars) {
116
+ return `${ELLIPSIS} [truncated ${droppedChars.toLocaleString("en-US")} chars]`;
117
+ }
118
+ //
119
+ //
120
+ // Main
121
+ //
122
+ /**
123
+ * Resolve limits from explicit options, env vars, then defaults.
124
+ * `maxEntryBytes` defaults on (payloads must fit the log pipeline);
125
+ * `maxDepth` and `maxStringLength` default off.
126
+ */
127
+ function resolveSerializationLimits(options = {}) {
128
+ return {
129
+ maxDepth: resolveLimit(options.maxDepth, LIMIT_ENV.MAX_DEPTH),
130
+ maxEntryBytes: resolveLimit(options.maxEntryBytes, LIMIT_ENV.MAX_ENTRY_BYTES, DEFAULT.MAX_ENTRY_BYTES),
131
+ maxStringLength: resolveLimit(options.maxStringLength, LIMIT_ENV.MAX_STRING),
132
+ };
133
+ }
134
+ function hasValueLimits(limits) {
135
+ return limits.maxDepth !== undefined || limits.maxStringLength !== undefined;
136
+ }
137
+ function byteLength(value) {
138
+ try {
139
+ const str = typeof value === "string" ? value : JSON.stringify(value);
140
+ return Buffer.byteLength(str ?? "", "utf8");
141
+ }
142
+ catch {
143
+ return 0;
144
+ }
145
+ }
146
+ /**
147
+ * Keep the first `maxLength` characters and append a visible marker
148
+ * preserving the dropped size
149
+ */
150
+ function truncateString(value, maxLength) {
151
+ if (value.length <= maxLength)
152
+ return value;
153
+ return value.slice(0, maxLength) + truncationMarker(value.length - maxLength);
154
+ }
155
+ /**
156
+ * Fit a string to a byte budget, reserving room for the marker and
157
+ * never cutting below the preview length
158
+ */
159
+ function truncateToBudget(value, budgetBytes) {
160
+ if (Buffer.byteLength(value, "utf8") <= budgetBytes)
161
+ return value;
162
+ const maxLength = Math.max(ENTRY_PREVIEW_LENGTH, budgetBytes - MARKER_RESERVE_BYTES);
163
+ return truncateString(value, maxLength);
164
+ }
165
+ /**
166
+ * Walk a value applying maxStringLength and maxDepth. Returns a new value;
167
+ * never mutates the input. Only plain objects and arrays are traversed so
168
+ * class instances (Error, Date, ...) keep their serialization behavior.
169
+ */
170
+ function applyValueLimits(value, limits, depth = 0, seen = new WeakSet()) {
171
+ const { maxDepth, maxStringLength } = limits;
172
+ if (typeof value === "string") {
173
+ return maxStringLength !== undefined
174
+ ? truncateString(value, maxStringLength)
175
+ : value;
176
+ }
177
+ if (Array.isArray(value)) {
178
+ if (seen.has(value))
179
+ return CIRCULAR_PLACEHOLDER;
180
+ if (maxDepth !== undefined && depth > maxDepth) {
181
+ return `[Array(${value.length})]`;
182
+ }
183
+ seen.add(value);
184
+ const result = value.map((item) => applyValueLimits(item, limits, depth + 1, seen));
185
+ seen.delete(value);
186
+ return result;
187
+ }
188
+ if (isPlainObject(value)) {
189
+ if (seen.has(value))
190
+ return CIRCULAR_PLACEHOLDER;
191
+ if (maxDepth !== undefined && depth > maxDepth) {
192
+ return "[Object]";
193
+ }
194
+ seen.add(value);
195
+ const result = {};
196
+ for (const key of Object.keys(value)) {
197
+ result[key] = applyValueLimits(value[key], limits, depth + 1, seen);
198
+ }
199
+ seen.delete(value);
200
+ return result;
201
+ }
202
+ return value;
203
+ }
204
+ function truncateToPreview(value) {
205
+ const str = typeof value === "string"
206
+ ? value
207
+ : (JSON.stringify(value) ?? String(value));
208
+ if (str.length <= ENTRY_PREVIEW_LENGTH)
209
+ return value;
210
+ return truncateString(str, ENTRY_PREVIEW_LENGTH);
211
+ }
212
+ /**
213
+ * Fit a serialized log entry under maxEntryBytes. Truncates the top-level
214
+ * attributes of `data` largest-first to short previews until the entry fits,
215
+ * collapsing `data` to a byte-count marker only as a last resort. When
216
+ * `syncMessageToData` is set (var entries and single-object messages, where
217
+ * `message` mirrors `data`), the message is rebuilt from the truncated data.
218
+ * Returns a new entry; never mutates the input.
219
+ */
220
+ function enforceEntryLimit(entry, { maxEntryBytes, syncMessageToData = false, }) {
221
+ const originalBytes = byteLength(entry);
222
+ if (originalBytes <= maxEntryBytes)
223
+ return entry;
224
+ const result = { ...entry };
225
+ const sync = () => {
226
+ if (syncMessageToData) {
227
+ result.message =
228
+ typeof result.data === "string"
229
+ ? result.data
230
+ : (JSON.stringify(result.data) ?? String(result.data));
231
+ }
232
+ };
233
+ const data = result.data;
234
+ if (typeof data === "string") {
235
+ result.data = truncateToPreview(data);
236
+ sync();
237
+ }
238
+ else if (Array.isArray(data) || isPlainObject(data)) {
239
+ const container = Array.isArray(data)
240
+ ? [...data]
241
+ : { ...data };
242
+ result.data = container;
243
+ const keys = Object.keys(container).sort((a, b) => byteLength(container[b]) - byteLength(container[a]));
244
+ for (const key of keys) {
245
+ container[key] = truncateToPreview(container[key]);
246
+ sync();
247
+ if (byteLength(result) <= maxEntryBytes)
248
+ return result;
249
+ }
250
+ }
251
+ else if (typeof result.message === "string") {
252
+ // No structured data: the message itself is oversized
253
+ const overhead = originalBytes - byteLength(result.message);
254
+ result.message = truncateToBudget(result.message, Math.max(ENTRY_PREVIEW_LENGTH, maxEntryBytes - overhead));
255
+ }
256
+ if (byteLength(result) <= maxEntryBytes)
257
+ return result;
258
+ // Last resort: entry is still oversized after attribute-level truncation
259
+ const marker = `[truncated ${originalBytes.toLocaleString("en-US")} bytes]`;
260
+ if ("data" in result) {
261
+ result.data = marker;
262
+ if (syncMessageToData)
263
+ result.message = marker;
264
+ }
265
+ else if (typeof result.message === "string") {
266
+ result.message = marker;
267
+ }
268
+ return result;
269
+ }
270
+
62
271
  //
63
272
  // Key-based pipelines (match on var key name)
64
273
  //
@@ -650,12 +859,22 @@ function resolveLevelField(value) {
650
859
  return value;
651
860
  }
652
861
  class Logger {
653
- constructor({ format = process.env.LOG_FORMAT || DEFAULT.LEVEL, level = process.env.LOG_LEVEL || DEFAULT.LEVEL, levelField, tags = {}, varLevel = process.env.LOG_VAR_LEVEL || DEFAULT.VAR_LEVEL, } = {}) {
862
+ constructor({ format = process.env.LOG_FORMAT || DEFAULT.LEVEL, level = process.env.LOG_LEVEL || DEFAULT.LEVEL, levelField, maxDepth, maxEntryBytes, maxStringLength, tags = {}, varLevel = process.env.LOG_VAR_LEVEL || DEFAULT.VAR_LEVEL, } = {}) {
654
863
  this.levelField = resolveLevelField(levelField);
864
+ this.limits = resolveSerializationLimits({
865
+ maxDepth,
866
+ maxEntryBytes,
867
+ maxStringLength,
868
+ });
655
869
  this.options = {
656
870
  format,
657
871
  level,
658
872
  levelField: this.levelField || undefined,
873
+ // Pin resolved limits (false = explicitly off) so child loggers
874
+ // created via with() inherit this config instead of re-resolving
875
+ maxDepth: this.limits.maxDepth ?? false,
876
+ maxEntryBytes: this.limits.maxEntryBytes ?? false,
877
+ maxStringLength: this.limits.maxStringLength ?? false,
659
878
  varLevel,
660
879
  };
661
880
  this.tags = {};
@@ -674,10 +893,16 @@ class Logger {
674
893
  createLogMethod(logLevel, format, checkLevel) {
675
894
  const logFn = (...messages) => {
676
895
  if (LEVEL_VALUES[logLevel] <= LEVEL_VALUES[checkLevel]) {
677
- const sanitized = messages.map(sanitizeAuth);
896
+ let sanitized = messages.map(sanitizeAuth);
897
+ if (hasValueLimits(this.limits)) {
898
+ sanitized = sanitized.map((item) => applyValueLimits(item, this.limits));
899
+ }
678
900
  if (format === FORMAT.JSON) {
679
901
  let message = stringify(...sanitized);
680
902
  let parses = parsesTo(message);
903
+ // When data comes from the full message they mirror each other;
904
+ // entry-limit truncation must keep them in sync
905
+ let syncMessageToData = parses.parses;
681
906
  const last = sanitized[sanitized.length - 1];
682
907
  if (sanitized.length > 1 &&
683
908
  typeof last === "object" &&
@@ -689,6 +914,7 @@ class Logger {
689
914
  if (lastParses.parses) {
690
915
  message = stringify(...sanitized.slice(0, -1));
691
916
  parses = lastParses;
917
+ syncMessageToData = false;
692
918
  }
693
919
  }
694
920
  const json = {
@@ -701,10 +927,20 @@ class Logger {
701
927
  if (this.levelField) {
702
928
  json[this.levelField] = logLevel;
703
929
  }
704
- out(json, { level: logLevel });
930
+ let entry = json;
931
+ if (this.limits.maxEntryBytes !== undefined) {
932
+ entry = enforceEntryLimit(json, {
933
+ maxEntryBytes: this.limits.maxEntryBytes,
934
+ syncMessageToData,
935
+ });
936
+ }
937
+ out(entry, { level: logLevel });
705
938
  }
706
939
  else {
707
- const message = stringify(...sanitized);
940
+ let message = stringify(...sanitized);
941
+ if (this.limits.maxEntryBytes !== undefined) {
942
+ message = truncateToBudget(message, this.limits.maxEntryBytes);
943
+ }
708
944
  out(message, { level: logLevel });
709
945
  }
710
946
  }
@@ -746,6 +982,9 @@ class Logger {
746
982
  }
747
983
  }
748
984
  messageVal = filterByType(messageVal);
985
+ if (hasValueLimits(this.limits)) {
986
+ messageVal = applyValueLimits(messageVal, this.limits);
987
+ }
749
988
  const json = {
750
989
  data: parse(messageVal),
751
990
  dataType: typeof messageVal,
@@ -757,7 +996,14 @@ class Logger {
757
996
  json[this.levelField] = logLevel;
758
997
  }
759
998
  if (LEVEL_VALUES[logLevel] <= LEVEL_VALUES[checkLevel]) {
760
- out(json, { level: logLevel });
999
+ let entry = json;
1000
+ if (this.limits.maxEntryBytes !== undefined) {
1001
+ entry = enforceEntryLimit(json, {
1002
+ maxEntryBytes: this.limits.maxEntryBytes,
1003
+ syncMessageToData: true,
1004
+ });
1005
+ }
1006
+ out(entry, { level: logLevel });
761
1007
  }
762
1008
  }
763
1009
  else {
@@ -766,6 +1012,20 @@ class Logger {
766
1012
  };
767
1013
  return logFn;
768
1014
  }
1015
+ /**
1016
+ * Update serialization limits at runtime. Pass a number to set a limit,
1017
+ * `false` to disable one; omitted keys are unchanged.
1018
+ */
1019
+ config(options = {}) {
1020
+ this.limits = resolveSerializationLimits({
1021
+ maxDepth: options.maxDepth ?? this.limits.maxDepth ?? false,
1022
+ maxEntryBytes: options.maxEntryBytes ?? this.limits.maxEntryBytes ?? false,
1023
+ maxStringLength: options.maxStringLength ?? this.limits.maxStringLength ?? false,
1024
+ });
1025
+ this.options.maxDepth = this.limits.maxDepth ?? false;
1026
+ this.options.maxEntryBytes = this.limits.maxEntryBytes ?? false;
1027
+ this.options.maxStringLength = this.limits.maxStringLength ?? false;
1028
+ }
769
1029
  tag(key, value) {
770
1030
  if (value) {
771
1031
  this.tags[forceString(key)] = forceString(value);
@@ -895,12 +1155,12 @@ function envBoolean(key, { defaultValue }) {
895
1155
  lower === "no");
896
1156
  }
897
1157
  class JaypieLogger {
898
- constructor({ level = process.env.LOG_LEVEL, tags = {}, } = {}) {
1158
+ constructor({ level = process.env.LOG_LEVEL, maxDepth, maxEntryBytes, maxStringLength, tags = {}, } = {}) {
899
1159
  this._errorCount = 0;
900
1160
  this._report = {};
901
1161
  this._sessionActive = false;
902
1162
  this._warnCount = 0;
903
- this._params = { level, tags };
1163
+ this._params = { level, maxDepth, maxEntryBytes, maxStringLength, tags };
904
1164
  this._loggers = [];
905
1165
  this._tags = {};
906
1166
  this._withLoggers = {};
@@ -909,6 +1169,9 @@ class JaypieLogger {
909
1169
  this._logger = new Logger({
910
1170
  format: FORMAT.JSON,
911
1171
  level: this.level,
1172
+ maxDepth,
1173
+ maxEntryBytes,
1174
+ maxStringLength,
912
1175
  tags: this._tags,
913
1176
  });
914
1177
  this._loggers = [this._logger];
@@ -950,6 +1213,29 @@ class JaypieLogger {
950
1213
  };
951
1214
  this.var = (messageObject, messageValue) => this._logger.var(logVar(messageObject, messageValue));
952
1215
  }
1216
+ /**
1217
+ * Update serialization limits at runtime for this logger and all loggers
1218
+ * derived from it (lib, with, flag). Pass a number to set a limit,
1219
+ * `false` to disable one; omitted keys are unchanged. Persists across
1220
+ * init().
1221
+ */
1222
+ config(options = {}) {
1223
+ if (options.maxDepth !== undefined) {
1224
+ this._params.maxDepth = options.maxDepth;
1225
+ }
1226
+ if (options.maxEntryBytes !== undefined) {
1227
+ this._params.maxEntryBytes = options.maxEntryBytes;
1228
+ }
1229
+ if (options.maxStringLength !== undefined) {
1230
+ this._params.maxStringLength = options.maxStringLength;
1231
+ }
1232
+ for (const logger of this._loggers) {
1233
+ logger.config(options);
1234
+ }
1235
+ for (const key of Object.keys(this._withLoggers)) {
1236
+ this._withLoggers[key].config(options);
1237
+ }
1238
+ }
953
1239
  flag(flag) {
954
1240
  if (typeof flag !== "string" || flag === "") {
955
1241
  return this;
@@ -970,6 +1256,9 @@ class JaypieLogger {
970
1256
  this._logger = new Logger({
971
1257
  format: FORMAT.JSON,
972
1258
  level: this.level,
1259
+ maxDepth: this._params.maxDepth,
1260
+ maxEntryBytes: this._params.maxEntryBytes,
1261
+ maxStringLength: this._params.maxStringLength,
973
1262
  tags: this._tags,
974
1263
  });
975
1264
  this._loggers = [this._logger];
@@ -1052,6 +1341,9 @@ class JaypieLogger {
1052
1341
  }
1053
1342
  return LEVEL.SILENT;
1054
1343
  })(),
1344
+ maxDepth: this._params.maxDepth,
1345
+ maxEntryBytes: this._params.maxEntryBytes,
1346
+ maxStringLength: this._params.maxStringLength,
1055
1347
  tags: newTags,
1056
1348
  });
1057
1349
  this._loggers.push(logger._logger);
@@ -1134,6 +1426,9 @@ class JaypieLogger {
1134
1426
  }
1135
1427
  const logger = new JaypieLogger({
1136
1428
  level: this.level,
1429
+ maxDepth: this._params.maxDepth,
1430
+ maxEntryBytes: this._params.maxEntryBytes,
1431
+ maxStringLength: this._params.maxStringLength,
1137
1432
  tags: { ...this._tags },
1138
1433
  });
1139
1434
  logger._logger = this._logger.with(key, value);