@arkv/logger 0.1.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.
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defaultTestConfig = void 0;
4
+ exports.parseLogOutput = parseLogOutput;
5
+ const colors_1 = require("@arkv/colors");
6
+ const types_js_1 = require("./types.js");
7
+ function parseLogOutput(logCall) {
8
+ const cleanLog = (0, colors_1.strip)(logCall);
9
+ return JSON.parse(cleanLog);
10
+ }
11
+ exports.defaultTestConfig = {
12
+ name: 'test-app',
13
+ version: '1.0.0',
14
+ env: 'local',
15
+ isDevelopment: true,
16
+ level: types_js_1.LogLevel.DEBUG,
17
+ maskFields: [
18
+ 'password',
19
+ 'token',
20
+ 'apiKey',
21
+ 'apiSecret',
22
+ 'apiPass',
23
+ ],
24
+ filterEvents: ['/health'],
25
+ maxArrayLength: 1,
26
+ };
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_MASK_FIELDS = exports.LOG_LEVELS = exports.LogLevel = void 0;
4
+ var LogLevel;
5
+ (function (LogLevel) {
6
+ LogLevel["VERBOSE"] = "verbose";
7
+ LogLevel["DEBUG"] = "debug";
8
+ LogLevel["LOG"] = "log";
9
+ LogLevel["WARN"] = "warn";
10
+ LogLevel["ERROR"] = "error";
11
+ LogLevel["FATAL"] = "fatal";
12
+ })(LogLevel || (exports.LogLevel = LogLevel = {}));
13
+ exports.LOG_LEVELS = [
14
+ LogLevel.VERBOSE,
15
+ LogLevel.DEBUG,
16
+ LogLevel.LOG,
17
+ LogLevel.WARN,
18
+ LogLevel.ERROR,
19
+ LogLevel.FATAL,
20
+ ];
21
+ exports.DEFAULT_MASK_FIELDS = [
22
+ 'password',
23
+ 'secret',
24
+ 'token',
25
+ 'authorization',
26
+ 'cookie',
27
+ 'apiKey',
28
+ 'apiSecret',
29
+ 'apiPass',
30
+ ];
@@ -0,0 +1,20 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ export class ContextStore {
3
+ asyncLocalStorage = new AsyncLocalStorage();
4
+ getContext() {
5
+ const context = this.asyncLocalStorage.getStore();
6
+ if (!context) {
7
+ return {};
8
+ }
9
+ return { ...context };
10
+ }
11
+ updateContext(obj) {
12
+ const context = this.asyncLocalStorage.getStore();
13
+ if (context) {
14
+ Object.assign(context, obj);
15
+ }
16
+ }
17
+ runWithContext(context, callback) {
18
+ return this.asyncLocalStorage.run(context, callback);
19
+ }
20
+ }
@@ -0,0 +1,29 @@
1
+ import { brightBlue, brightCyan, brightGreen, brightMagenta, brightYellow, cyan, getLevelColorFn, getValueColor, gray, green, magenta, red, yellow, } from '@arkv/colors';
2
+ import { safeStringify } from '@arkv/shared';
3
+ export function formatColoredJson(obj, level) {
4
+ const jsonString = safeStringify(obj);
5
+ const colorMap = {
6
+ level: getLevelColorFn(level),
7
+ message: green,
8
+ timestamp: magenta,
9
+ requestId: brightGreen,
10
+ userId: brightBlue,
11
+ context: brightCyan,
12
+ duration: yellow,
13
+ event: brightMagenta,
14
+ error: red,
15
+ exception: red,
16
+ flow: brightGreen,
17
+ method: brightBlue,
18
+ stack: gray,
19
+ status: brightYellow,
20
+ elapsed: brightYellow,
21
+ };
22
+ return jsonString.replace(/(".*?":\s*)(.*?)(?=,|\n|$)/g, (_, key, value) => {
23
+ const keyWithoutQuotes = key
24
+ .replace(/"/g, '')
25
+ .slice(0, -1);
26
+ const colorizer = colorMap[keyWithoutQuotes] || getValueColor(value);
27
+ return `${cyan(key)}${colorizer(value)}`;
28
+ });
29
+ }
@@ -0,0 +1,3 @@
1
+ export { ContextStore } from './context.js';
2
+ export { Logger } from './logger.js';
3
+ export { DEFAULT_MASK_FIELDS, LOG_LEVELS, LogLevel, } from './types.js';
@@ -0,0 +1,216 @@
1
+ import { isPlainObject, safeStringify } from '@arkv/shared';
2
+ import { formatColoredJson } from './format.js';
3
+ import { findNestedError, sanitizeLogEntry, } from './sanitize.js';
4
+ import { DEFAULT_MASK_FIELDS, LOG_LEVELS, LogLevel, } from './types.js';
5
+ export class Logger {
6
+ logLevel;
7
+ #isDevelopment;
8
+ #maskFields;
9
+ #maxArrayLength;
10
+ #filterEvents;
11
+ #context;
12
+ #appName;
13
+ #appVersion;
14
+ #appEnv;
15
+ constructor(config, context) {
16
+ const cfg = config ?? {};
17
+ this.logLevel = cfg.level ?? LogLevel.DEBUG;
18
+ this.#isDevelopment =
19
+ cfg.isDevelopment ??
20
+ process.env.NODE_ENV !== 'production';
21
+ this.#maskFields =
22
+ cfg.maskFields && cfg.maskFields.length > 0
23
+ ? Array.from(new Set([
24
+ ...DEFAULT_MASK_FIELDS,
25
+ ...cfg.maskFields,
26
+ ]))
27
+ : [...DEFAULT_MASK_FIELDS];
28
+ this.#maxArrayLength = cfg.maxArrayLength ?? 100;
29
+ this.#filterEvents = cfg.filterEvents ?? [];
30
+ this.#context = context;
31
+ this.#appName = cfg.name;
32
+ this.#appVersion = cfg.version;
33
+ this.#appEnv = cfg.env;
34
+ }
35
+ get appId() {
36
+ if (this.#appName && this.#appVersion && this.#appEnv) {
37
+ return `${this.#appName}-${this.#appVersion}-${this.#appEnv}`;
38
+ }
39
+ return undefined;
40
+ }
41
+ log(message, ...optionalParams) {
42
+ this.#writeLog(LogLevel.LOG, message, optionalParams);
43
+ }
44
+ error(message, ...optionalParams) {
45
+ this.#writeLog(LogLevel.ERROR, message, optionalParams);
46
+ }
47
+ warn(message, ...optionalParams) {
48
+ this.#writeLog(LogLevel.WARN, message, optionalParams);
49
+ }
50
+ debug(message, ...optionalParams) {
51
+ this.#writeLog(LogLevel.DEBUG, message, optionalParams);
52
+ }
53
+ verbose(message, ...optionalParams) {
54
+ this.#writeLog(LogLevel.VERBOSE, message, optionalParams);
55
+ }
56
+ fatal(message, ...optionalParams) {
57
+ this.#writeLog(LogLevel.FATAL, message, optionalParams);
58
+ }
59
+ #writeLog(level, message, optionalParams) {
60
+ if (!this.#shouldLog(level)) {
61
+ return;
62
+ }
63
+ const { preparedMessage, invalidMessageInfo, messageError, messageExtra, } = this.#prepareMessage(message);
64
+ const { error, extra } = this.#extractErrorAndExtra(optionalParams, level);
65
+ const finalError = messageError || error;
66
+ const finalExtra = {
67
+ ...messageExtra,
68
+ ...extra,
69
+ };
70
+ const logEntry = this.#createLogEntry(level, preparedMessage, finalExtra, finalError, invalidMessageInfo);
71
+ const sanitizedLogEntry = sanitizeLogEntry(logEntry, {
72
+ maskFields: this.#maskFields,
73
+ maxArrayLength: this.#maxArrayLength,
74
+ });
75
+ const output = this.#isDevelopment
76
+ ? formatColoredJson(sanitizedLogEntry, level)
77
+ : safeStringify(sanitizedLogEntry);
78
+ console.log(output);
79
+ }
80
+ #prepareMessage(message) {
81
+ if (typeof message === 'string') {
82
+ return { preparedMessage: message };
83
+ }
84
+ if (message instanceof Error) {
85
+ return {
86
+ preparedMessage: message.message,
87
+ messageError: message,
88
+ };
89
+ }
90
+ if (isPlainObject(message)) {
91
+ const foundError = findNestedError(message);
92
+ if (foundError) {
93
+ return {
94
+ preparedMessage: foundError.message,
95
+ messageError: foundError,
96
+ messageExtra: message,
97
+ };
98
+ }
99
+ return {
100
+ preparedMessage: 'Object logged',
101
+ messageExtra: message,
102
+ };
103
+ }
104
+ const stack = new Error().stack
105
+ ?.split('\n')
106
+ .slice(2, 7)
107
+ .join('\n');
108
+ const preparedMessage = message === null || message === undefined
109
+ ? `[${String(message)}]`
110
+ : `[OBJECT]: ${safeStringify(message)}`;
111
+ const invalidMessageInfo = {
112
+ invalidMessageWarning: 'Logger called with non-string message parameter',
113
+ invalidMessageCallstack: stack,
114
+ originalMessageType: typeof message,
115
+ originalMessage: safeStringify(message),
116
+ };
117
+ return {
118
+ preparedMessage,
119
+ invalidMessageInfo,
120
+ };
121
+ }
122
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: handles multiple error extraction patterns
123
+ #extractErrorAndExtra(params, level) {
124
+ let error = null;
125
+ const extra = {};
126
+ for (const param of params) {
127
+ if (param instanceof Error) {
128
+ error = param;
129
+ }
130
+ else if (typeof param === 'string') {
131
+ const isErrorLevel = level === LogLevel.WARN ||
132
+ level === LogLevel.ERROR ||
133
+ level === LogLevel.FATAL;
134
+ if (isErrorLevel) {
135
+ error = new Error(param);
136
+ }
137
+ else {
138
+ extra.context = param;
139
+ }
140
+ }
141
+ else if (isPlainObject(param)) {
142
+ const isErrorLevel = level === LogLevel.WARN ||
143
+ level === LogLevel.ERROR ||
144
+ level === LogLevel.FATAL;
145
+ if (param.err instanceof Error) {
146
+ error = param.err;
147
+ const { err: _, ...rest } = param;
148
+ Object.assign(extra, rest);
149
+ }
150
+ else if (param.error instanceof Error) {
151
+ error = param.error;
152
+ const { error: _, ...rest } = param;
153
+ Object.assign(extra, rest);
154
+ }
155
+ else if (isErrorLevel &&
156
+ typeof param.err === 'string') {
157
+ error = new Error(param.err);
158
+ const { err: _, ...rest } = param;
159
+ Object.assign(extra, rest);
160
+ }
161
+ else if (isErrorLevel &&
162
+ typeof param.error === 'string') {
163
+ error = new Error(param.error);
164
+ const { error: _, ...rest } = param;
165
+ Object.assign(extra, rest);
166
+ }
167
+ else {
168
+ const foundError = findNestedError(param);
169
+ if (foundError) {
170
+ error = foundError;
171
+ }
172
+ Object.assign(extra, param);
173
+ }
174
+ }
175
+ }
176
+ return { error, extra };
177
+ }
178
+ #createLogEntry(level, message, extra, error, invalidMessageInfo) {
179
+ const ctx = this.#context
180
+ ? this.#context.getContext()
181
+ : {};
182
+ const logEntry = {
183
+ level,
184
+ timestamp: new Date().toISOString(),
185
+ pid: process.pid,
186
+ message,
187
+ ...(this.appId ? { appId: this.appId } : {}),
188
+ ...ctx,
189
+ ...extra,
190
+ ...(invalidMessageInfo || {}),
191
+ };
192
+ if (error) {
193
+ logEntry.error = {
194
+ name: error.name,
195
+ message: error.message,
196
+ stack: error.stack?.replace(/\n(\s+)?/g, ','),
197
+ };
198
+ }
199
+ return logEntry;
200
+ }
201
+ #shouldLog(level) {
202
+ const configuredIdx = LOG_LEVELS.indexOf(this.logLevel);
203
+ const messageIdx = LOG_LEVELS.indexOf(level);
204
+ if (messageIdx < configuredIdx) {
205
+ return false;
206
+ }
207
+ if (this.#context) {
208
+ const ctx = this.#context.getContext();
209
+ if (ctx.event &&
210
+ this.#filterEvents.includes(ctx.event)) {
211
+ return false;
212
+ }
213
+ }
214
+ return true;
215
+ }
216
+ }
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,183 @@
1
+ import { isPlainObject } from '@arkv/shared';
2
+ export function sanitizeLogEntry(obj, options, visited = new WeakSet()) {
3
+ if (visited.has(obj)) {
4
+ return {
5
+ '[Circular]': 'circular reference detected',
6
+ };
7
+ }
8
+ visited.add(obj);
9
+ const cleaned = {};
10
+ for (const [key, value] of Object.entries(obj)) {
11
+ if (value === undefined || value === null) {
12
+ continue;
13
+ }
14
+ const shouldMask = options.maskFields.some(field => key.toLowerCase().includes(field.toLowerCase()));
15
+ if (shouldMask) {
16
+ cleaned[key] = '[MASKED]';
17
+ }
18
+ else {
19
+ const safeValue = makeSafeForJson(value, options);
20
+ if (safeValue !== undefined) {
21
+ if (Array.isArray(safeValue)) {
22
+ cleaned[key] = sanitizeArray(safeValue, options, visited);
23
+ }
24
+ else if (isPlainObject(safeValue)) {
25
+ cleaned[key] = sanitizeLogEntry(safeValue, options, visited);
26
+ }
27
+ else {
28
+ cleaned[key] = safeValue;
29
+ }
30
+ }
31
+ }
32
+ }
33
+ return cleaned;
34
+ }
35
+ function sanitizeArray(array, options, visited) {
36
+ return array.map(item => {
37
+ if (isPlainObject(item)) {
38
+ return sanitizeLogEntry(item, options, visited);
39
+ }
40
+ if (Array.isArray(item)) {
41
+ return sanitizeArray(item, options, visited);
42
+ }
43
+ return makeSafeForJson(item, options);
44
+ });
45
+ }
46
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: recursive error search
47
+ export function findNestedError(obj, visited = new WeakSet()) {
48
+ if (!obj || typeof obj !== 'object') {
49
+ return null;
50
+ }
51
+ if (visited.has(obj)) {
52
+ return null;
53
+ }
54
+ visited.add(obj);
55
+ for (const value of Object.values(obj)) {
56
+ if (value instanceof Error) {
57
+ return value;
58
+ }
59
+ if (isPlainObject(value)) {
60
+ const nestedError = findNestedError(value, visited);
61
+ if (nestedError) {
62
+ return nestedError;
63
+ }
64
+ }
65
+ if (Array.isArray(value)) {
66
+ for (const item of value) {
67
+ if (item instanceof Error) {
68
+ return item;
69
+ }
70
+ if (isPlainObject(item)) {
71
+ const nestedError = findNestedError(item, visited);
72
+ if (nestedError) {
73
+ return nestedError;
74
+ }
75
+ }
76
+ }
77
+ }
78
+ }
79
+ return null;
80
+ }
81
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: handles many type serialization cases
82
+ function makeSafeForJson(value, options) {
83
+ if (value === null || value === undefined) {
84
+ return value;
85
+ }
86
+ const valueType = typeof value;
87
+ if (valueType === 'function') {
88
+ return `[Function: ${value.name || 'anonymous'}]`;
89
+ }
90
+ if (valueType === 'symbol') {
91
+ return `[Symbol: ${value.toString()}]`;
92
+ }
93
+ if (valueType === 'bigint') {
94
+ return `[BigInt: ${value.toString()}]`;
95
+ }
96
+ if (valueType !== 'object') {
97
+ return value;
98
+ }
99
+ if (value instanceof Date) {
100
+ return value.toISOString();
101
+ }
102
+ if (value instanceof RegExp) {
103
+ return `[RegExp: ${value.toString()}]`;
104
+ }
105
+ if (value instanceof Error) {
106
+ return {
107
+ name: value.name,
108
+ message: value.message,
109
+ stack: value.stack?.replace(/\n(\s+)?/g, ','),
110
+ };
111
+ }
112
+ if (typeof FormData !== 'undefined' &&
113
+ value instanceof FormData) {
114
+ const entries = {};
115
+ try {
116
+ for (const [key, val] of value.entries()) {
117
+ if (val &&
118
+ typeof val === 'object' &&
119
+ 'name' in val &&
120
+ 'size' in val &&
121
+ 'type' in val) {
122
+ const file = val;
123
+ entries[key] =
124
+ `[File: ${file.name} (${file.size} bytes, ${file.type})]`;
125
+ }
126
+ else {
127
+ entries[key] = val;
128
+ }
129
+ }
130
+ return { '[FormData]': entries };
131
+ }
132
+ catch {
133
+ return '[FormData: unable to read entries]';
134
+ }
135
+ }
136
+ if (value &&
137
+ typeof value === 'object' &&
138
+ 'name' in value &&
139
+ 'size' in value &&
140
+ 'type' in value &&
141
+ typeof value
142
+ .arrayBuffer === 'function') {
143
+ const file = value;
144
+ return `[File: ${file.name} (${file.size} bytes, ${file.type})]`;
145
+ }
146
+ if (typeof Blob !== 'undefined' &&
147
+ value instanceof Blob) {
148
+ return `[Blob: ${value.size} bytes, ${value.type}]`;
149
+ }
150
+ if (typeof ArrayBuffer !== 'undefined' &&
151
+ value instanceof ArrayBuffer) {
152
+ return `[ArrayBuffer: ${value.byteLength} bytes]`;
153
+ }
154
+ if (Array.isArray(value)) {
155
+ return sliceArray(value, options);
156
+ }
157
+ if (isPlainObject(value)) {
158
+ return value;
159
+ }
160
+ try {
161
+ JSON.stringify(value);
162
+ return value;
163
+ }
164
+ catch {
165
+ if (value
166
+ .constructor?.name) {
167
+ return `[${value.constructor.name}: object not serializable]`;
168
+ }
169
+ return '[Object: not serializable]';
170
+ }
171
+ }
172
+ function sliceArray(array, options) {
173
+ if (array.length <= options.maxArrayLength) {
174
+ return array.map(item => makeSafeForJson(item, options));
175
+ }
176
+ const slicedArray = array
177
+ .slice(0, options.maxArrayLength)
178
+ .map(item => makeSafeForJson(item, options));
179
+ return [
180
+ ...slicedArray,
181
+ `[TRUNCATED: ${array.length - options.maxArrayLength} more items]`,
182
+ ];
183
+ }
@@ -0,0 +1,22 @@
1
+ import { strip } from '@arkv/colors';
2
+ import { LogLevel } from './types.js';
3
+ export function parseLogOutput(logCall) {
4
+ const cleanLog = strip(logCall);
5
+ return JSON.parse(cleanLog);
6
+ }
7
+ export const defaultTestConfig = {
8
+ name: 'test-app',
9
+ version: '1.0.0',
10
+ env: 'local',
11
+ isDevelopment: true,
12
+ level: LogLevel.DEBUG,
13
+ maskFields: [
14
+ 'password',
15
+ 'token',
16
+ 'apiKey',
17
+ 'apiSecret',
18
+ 'apiPass',
19
+ ],
20
+ filterEvents: ['/health'],
21
+ maxArrayLength: 1,
22
+ };
@@ -0,0 +1,27 @@
1
+ export var LogLevel;
2
+ (function (LogLevel) {
3
+ LogLevel["VERBOSE"] = "verbose";
4
+ LogLevel["DEBUG"] = "debug";
5
+ LogLevel["LOG"] = "log";
6
+ LogLevel["WARN"] = "warn";
7
+ LogLevel["ERROR"] = "error";
8
+ LogLevel["FATAL"] = "fatal";
9
+ })(LogLevel || (LogLevel = {}));
10
+ export const LOG_LEVELS = [
11
+ LogLevel.VERBOSE,
12
+ LogLevel.DEBUG,
13
+ LogLevel.LOG,
14
+ LogLevel.WARN,
15
+ LogLevel.ERROR,
16
+ LogLevel.FATAL,
17
+ ];
18
+ export const DEFAULT_MASK_FIELDS = [
19
+ 'password',
20
+ 'secret',
21
+ 'token',
22
+ 'authorization',
23
+ 'cookie',
24
+ 'apiKey',
25
+ 'apiSecret',
26
+ 'apiPass',
27
+ ];
@@ -0,0 +1,7 @@
1
+ import type { AsyncContext } from './types.js';
2
+ export declare class ContextStore {
3
+ private readonly asyncLocalStorage;
4
+ getContext(): AsyncContext;
5
+ updateContext(obj: Partial<AsyncContext>): void;
6
+ runWithContext<T>(context: AsyncContext, callback: () => T): T;
7
+ }
@@ -0,0 +1,2 @@
1
+ import type { LogEntry, LogLevel } from './types.js';
2
+ export declare function formatColoredJson(obj: LogEntry, level: LogLevel): string;
@@ -0,0 +1,3 @@
1
+ export { ContextStore } from './context.js';
2
+ export { Logger } from './logger.js';
3
+ export { type AsyncContext, DEFAULT_MASK_FIELDS, LOG_LEVELS, type LogEntry, type LoggerConfig, LogLevel, } from './types.js';
@@ -0,0 +1,26 @@
1
+ import type { ContextStore } from './context.js';
2
+ import { type LoggerConfig, LogLevel } from './types.js';
3
+ export declare class Logger {
4
+ #private;
5
+ readonly logLevel: LogLevel;
6
+ constructor(config?: LoggerConfig, context?: ContextStore);
7
+ get appId(): string | undefined;
8
+ log(message: string, ...optionalParams: unknown[]): void;
9
+ log(message: Record<string, unknown>): void;
10
+ log(message: Error): void;
11
+ error(message: string, ...optionalParams: unknown[]): void;
12
+ error(message: Record<string, unknown>): void;
13
+ error(message: Error): void;
14
+ warn(message: string, ...optionalParams: unknown[]): void;
15
+ warn(message: Record<string, unknown>): void;
16
+ warn(message: Error): void;
17
+ debug(message: string, ...optionalParams: unknown[]): void;
18
+ debug(message: Record<string, unknown>): void;
19
+ debug(message: Error): void;
20
+ verbose(message: string, ...optionalParams: unknown[]): void;
21
+ verbose(message: Record<string, unknown>): void;
22
+ verbose(message: Error): void;
23
+ fatal(message: string, ...optionalParams: unknown[]): void;
24
+ fatal(message: Record<string, unknown>): void;
25
+ fatal(message: Error): void;
26
+ }
@@ -0,0 +1,8 @@
1
+ import type { LogEntry } from './types.js';
2
+ interface SanitizeOptions {
3
+ maskFields: string[];
4
+ maxArrayLength: number;
5
+ }
6
+ export declare function sanitizeLogEntry(obj: LogEntry, options: SanitizeOptions, visited?: WeakSet<WeakKey>): LogEntry;
7
+ export declare function findNestedError(obj: Record<string, unknown>, visited?: WeakSet<WeakKey>): Error | null;
8
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { LoggerConfig } from './types.js';
2
+ export declare function parseLogOutput(logCall: string): any;
3
+ export declare const defaultTestConfig: LoggerConfig;
@@ -0,0 +1,37 @@
1
+ export declare enum LogLevel {
2
+ VERBOSE = "verbose",
3
+ DEBUG = "debug",
4
+ LOG = "log",
5
+ WARN = "warn",
6
+ ERROR = "error",
7
+ FATAL = "fatal"
8
+ }
9
+ export declare const LOG_LEVELS: LogLevel[];
10
+ export interface AsyncContext {
11
+ requestId?: string;
12
+ userId?: string;
13
+ orgId?: string;
14
+ method?: string;
15
+ event?: string;
16
+ context?: string;
17
+ flow?: string;
18
+ [key: string]: unknown;
19
+ }
20
+ export interface LoggerConfig {
21
+ name?: string;
22
+ version?: string;
23
+ env?: string;
24
+ level?: LogLevel;
25
+ /** Defaults to `process.env.NODE_ENV !== 'production'` */
26
+ isDevelopment?: boolean;
27
+ /** Merged with DEFAULT_MASK_FIELDS */
28
+ maskFields?: string[];
29
+ /** Events to skip logging for */
30
+ filterEvents?: string[];
31
+ /** Truncate arrays beyond this length */
32
+ maxArrayLength?: number;
33
+ }
34
+ export type LogEntry = Record<string, unknown> & {
35
+ error?: Error;
36
+ };
37
+ export declare const DEFAULT_MASK_FIELDS: string[];