humanbehavior-js 0.0.8 → 0.1.0

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/src/api.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { logError, logInfo } from './utils/logger';
2
+
1
3
  export const MAX_CHUNK_SIZE_BYTES = 1024 * 1024 * 10; // 10MB chunk size
2
4
 
3
5
  export function isChunkSizeExceeded(currentChunk: any[], newEvent: any, sessionId: string): boolean {
@@ -147,7 +149,7 @@ export class HumanBehaviorAPI {
147
149
 
148
150
  return results.flat();
149
151
  } catch (error) {
150
- console.error('Error sending events:', error);
152
+ logError('Error sending events:', error);
151
153
  throw error;
152
154
  }
153
155
  }
@@ -173,7 +175,7 @@ export class HumanBehaviorAPI {
173
175
 
174
176
  return await response.json();
175
177
  } catch (error) {
176
- console.error('Error sending user data:', error);
178
+ logError('Error sending user data:', error);
177
179
  throw error;
178
180
  }
179
181
  }
@@ -200,111 +202,22 @@ export class HumanBehaviorAPI {
200
202
 
201
203
  return await response.json();
202
204
  } catch (error) {
203
- console.error('Error authenticating user:', error);
205
+ logError('Error authenticating user:', error);
204
206
  throw error;
205
207
  }
206
208
  }
207
209
 
208
- async sendSessionComplete(sessionId: string) {
209
- const response = await fetch(`${this.baseUrl}/api/ingestion/sessionComplete`, {
210
- method: 'POST',
211
- headers: {
212
- 'Content-Type': 'application/json',
213
- 'Authorization': `Bearer ${this.apiKey}`
214
- },
215
- body: JSON.stringify({ sessionId })
216
- });
217
-
218
- if (!response.ok) {
219
- throw new Error(`Failed to send session complete: ${response.statusText}`);
220
- }
221
- }
222
210
 
223
- async sendCustomEvent(eventName: string, eventProperties: Record<string, any>, sessionId: string) {
224
- const maxRetries = 3;
225
- let retryCount = 0;
226
-
227
- while (retryCount < maxRetries) {
228
- try {
229
- const response = await fetch(`${this.baseUrl}/api/ingestion/customEvent`, {
230
- method: 'POST',
231
- headers: {
232
- 'Content-Type': 'application/json',
233
- 'Authorization': `Bearer ${this.apiKey}`
234
- },
235
- body: JSON.stringify({
236
- name: eventName,
237
- properties: eventProperties,
238
- sessionId: sessionId,
239
- timestamp: new Date().toISOString()
240
- })
241
- });
242
-
243
- if (!response.ok) {
244
- throw new Error(`Failed to send custom event: ${response.statusText}`);
245
- }
246
-
247
- return await response.json();
248
- } catch (error) {
249
- retryCount++;
250
- if (retryCount === maxRetries) {
251
- console.error('Error sending custom event after max retries:', error);
252
- throw error;
253
- }
254
- // Exponential backoff
255
- await new Promise(resolve => setTimeout(resolve, Math.pow(2, retryCount) * 1000));
256
- }
257
- }
258
- }
259
211
 
260
- async sendCustomEvents(events: any[], sessionId: string) {
261
- const maxRetries = 3;
262
- let retryCount = 0;
263
-
264
- while (retryCount < maxRetries) {
265
- try {
266
- const response = await fetch(`${this.baseUrl}/api/ingestion/customEvent/batch`, {
267
- method: 'POST',
268
- headers: {
269
- 'Content-Type': 'application/json',
270
- 'Authorization': `Bearer ${this.apiKey}`
271
- },
272
- body: JSON.stringify({
273
- events: events.map(event => ({
274
- ...event,
275
- sessionId: sessionId
276
- }))
277
- })
278
- });
279
-
280
- if (!response.ok) {
281
- throw new Error(`Failed to send custom events: ${response.statusText}`);
282
- }
283
-
284
- return await response.json();
285
- } catch (error) {
286
- retryCount++;
287
- if (retryCount === maxRetries) {
288
- console.error('Error sending custom events after max retries:', error);
289
- throw error;
290
- }
291
- // Exponential backoff
292
- await new Promise(resolve => setTimeout(resolve, Math.pow(2, retryCount) * 1000));
293
- }
294
- }
295
- }
296
212
 
297
- public sendBeaconEvents(events: any[], sessionId: string, isSessionComplete: boolean = false) {
213
+
214
+ public sendBeaconEvents(events: any[], sessionId: string) {
298
215
  const data = new URLSearchParams()
299
216
  data.append('events', encodeURIComponent(JSON.stringify(events)))
300
217
  data.append('sessionId', encodeURIComponent(sessionId))
301
218
  data.append('timestamp', encodeURIComponent(Date.now().toString()))
302
219
  data.append('apiKey', encodeURIComponent(this.apiKey))
303
- if (isSessionComplete) {
304
- console.log('Session complete beacon sending');
305
- localStorage.setItem('koalaware_session_complete', Date.now().toString());
306
- data.append('sessionComplete', encodeURIComponent('true'))
307
- }
220
+
308
221
 
309
222
  const success = navigator.sendBeacon(
310
223
  `${this.baseUrl}/api/ingestion/events`,
@@ -314,47 +227,6 @@ export class HumanBehaviorAPI {
314
227
  // KoalawareTracker.logToStorage(`Sending events beacon: ${this.baseUrl}/api/ingestion/events`);
315
228
  // KoalawareTracker.logToStorage(`Events beacon success: ${success}`);
316
229
  }
230
+
317
231
 
318
- public sendBeaconSessionComplete(sessionId: string) {
319
- const data = new URLSearchParams()
320
- data.append('sessionId', sessionId)
321
- data.append('apiKey', this.apiKey)
322
- data.append('sessionComplete', 'true')
323
-
324
- const success = navigator.sendBeacon(
325
- `${this.baseUrl}/api/ingestion/sessionComplete`,
326
- data
327
- );
328
-
329
- // KoalawareTracker.logToStorage(`Sending completion beacon: ${this.baseUrl}/api/ingestion/sessionComplete`);
330
- // KoalawareTracker.logToStorage(`Complete beacon success: ${success}`);
331
- }
332
-
333
- public sendBeaconCustomEvent(eventName: string, eventProperties: Record<string, any>, sessionId: string) {
334
- const data = new URLSearchParams()
335
- data.append('name', encodeURIComponent(eventName))
336
- data.append('properties', encodeURIComponent(JSON.stringify(eventProperties)))
337
- data.append('sessionId', encodeURIComponent(sessionId))
338
- data.append('timestamp', encodeURIComponent(new Date().toISOString()))
339
- data.append('apiKey', encodeURIComponent(this.apiKey))
340
-
341
- return navigator.sendBeacon(
342
- `${this.baseUrl}/api/ingestion/customEvent`,
343
- data
344
- );
345
- }
346
-
347
- public sendBeaconCustomEvents(events: any[], sessionId: string) {
348
- const data = new URLSearchParams()
349
- data.append('events', encodeURIComponent(JSON.stringify(events.map(event => ({
350
- ...event,
351
- sessionId: sessionId
352
- })))))
353
- data.append('apiKey', encodeURIComponent(this.apiKey))
354
-
355
- return navigator.sendBeacon(
356
- `${this.baseUrl}/api/ingestion/customEvent/batch`,
357
- data
358
- );
359
- }
360
232
  }
package/src/index.ts CHANGED
@@ -13,6 +13,9 @@ export * from './api';
13
13
  // Export redaction functionality
14
14
  export * from './redact';
15
15
 
16
+ // Export logger functionality
17
+ export * from './utils/logger';
18
+
16
19
  // Also export the tracker as the default export
17
20
  export { HumanBehaviorTracker as default } from './tracker';
18
21
 
@@ -1,5 +1,6 @@
1
- import React, { useEffect, useState, createContext, useContext, ReactNode } from "react";
1
+ import React, { useEffect, useState, createContext, useContext, ReactNode, useCallback, useMemo, useRef } from "react";
2
2
  import { HumanBehaviorTracker } from "..";
3
+ import { logError, logWarn, logDebug } from "../utils/logger";
3
4
 
4
5
  // Check if we're in a browser environment
5
6
  const isBrowser = () => typeof window !== 'undefined';
@@ -23,8 +24,14 @@ interface HumanBehaviorProviderProps {
23
24
  apiKey?: string;
24
25
  client?: HumanBehaviorTracker;
25
26
  children: ReactNode;
27
+ options?: {
28
+ ingestionUrl?: string;
29
+ logLevel?: 'none' | 'error' | 'warn' | 'info' | 'debug';
30
+ redactFields?: string[];
31
+ };
26
32
  }
27
33
 
34
+ // Default context to prevent unnecessary re-renders
28
35
  const defaultContext: HumanBehaviorContextType = {
29
36
  humanBehavior: null,
30
37
  queueEvent: (event: any) => {
@@ -32,22 +39,38 @@ const defaultContext: HumanBehaviorContextType = {
32
39
  if (typeof window === 'undefined') {
33
40
  return;
34
41
  }
35
- console.warn('HumanBehavior not initialized yet, event queued:', event);
42
+ logWarn('HumanBehavior not initialized yet, event queued:', event);
36
43
  }
37
44
  };
38
45
 
39
- const HumanBehaviorContext = createContext<HumanBehaviorContextType>(defaultContext);
46
+ const HumanBehaviorContext = createContext<HumanBehaviorTracker | null>(null);
40
47
 
41
- export const HumanBehaviorProvider = ({ apiKey, client, children }: HumanBehaviorProviderProps) => {
48
+ export const HumanBehaviorProvider = ({ apiKey, client, children, options }: HumanBehaviorProviderProps) => {
42
49
  const [humanBehavior, setHumanBehavior] = useState<HumanBehaviorTracker | null>(client || null);
43
50
  const [eventQueue, setEventQueue] = useState<any[]>([]);
44
51
  const [isMounted, setIsMounted] = useState(false);
45
52
  const [isInitialized, setIsInitialized] = useState(false);
53
+
54
+ // Use refs to avoid dependency issues in useEffect
55
+ const apiKeyRef = useRef(apiKey);
56
+ const clientRef = useRef(client);
57
+ const eventQueueRef = useRef(eventQueue);
58
+
59
+ // Update refs when props change
60
+ useEffect(() => {
61
+ apiKeyRef.current = apiKey;
62
+ clientRef.current = client;
63
+ }, [apiKey, client]);
64
+
65
+ // Update eventQueue ref when queue changes
66
+ useEffect(() => {
67
+ eventQueueRef.current = eventQueue;
68
+ }, [eventQueue]);
46
69
 
47
- // Function to queue events before initialization
48
- const queueEvent = (event: any) => {
70
+ // Memoized queueEvent function to prevent unnecessary re-renders
71
+ const queueEvent = useCallback((event: any) => {
49
72
  setEventQueue(prev => [...prev, event]);
50
- };
73
+ }, []);
51
74
 
52
75
  // Handle mounting state
53
76
  useEffect(() => {
@@ -66,15 +89,15 @@ export const HumanBehaviorProvider = ({ apiKey, client, children }: HumanBehavio
66
89
  }
67
90
 
68
91
  // If client is provided, use that
69
- if (client) {
70
- setHumanBehavior(client);
92
+ if (clientRef.current) {
93
+ setHumanBehavior(clientRef.current);
71
94
  setIsInitialized(true);
72
95
  return;
73
96
  }
74
97
 
75
98
  // If no client is provided, apiKey is required
76
- if (!apiKey || apiKey.trim() === '') {
77
- console.error("An apiKey is required when no client is provided");
99
+ if (!apiKeyRef.current || apiKeyRef.current.trim() === '') {
100
+ logError("An apiKey is required when no client is provided");
78
101
  return;
79
102
  }
80
103
 
@@ -83,7 +106,10 @@ export const HumanBehaviorProvider = ({ apiKey, client, children }: HumanBehavio
83
106
  }
84
107
 
85
108
  // Create new tracker instance with the validated apiKey
86
- const tracker = new HumanBehaviorTracker(apiKey.trim(), process.env.NEXT_PUBLIC_INGESTION_URL || 'https://ingestion.humanbehavior.ai');
109
+ const tracker = new HumanBehaviorTracker(
110
+ apiKeyRef.current.trim(),
111
+ process.env.NEXT_PUBLIC_INGESTION_URL || 'https://ingestion.humanbehavior.ai'
112
+ );
87
113
  setHumanBehavior(tracker);
88
114
 
89
115
  // Wait for initialization to complete
@@ -91,10 +117,11 @@ export const HumanBehaviorProvider = ({ apiKey, client, children }: HumanBehavio
91
117
  setIsInitialized(true);
92
118
 
93
119
  // Process any queued events
94
- if (eventQueue.length > 0) {
95
- eventQueue.forEach(event => {
120
+ const currentQueue = eventQueueRef.current;
121
+ if (currentQueue.length > 0) {
122
+ currentQueue.forEach(event => {
96
123
  if (event.type === 'identify') {
97
- console.log('Processing queued identify event', event.userProperties);
124
+ logDebug('Processing queued identify event', event.userProperties);
98
125
  tracker.addUserInfo(event.userProperties);
99
126
  } else {
100
127
  tracker.addEvent(event);
@@ -103,87 +130,116 @@ export const HumanBehaviorProvider = ({ apiKey, client, children }: HumanBehavio
103
130
  setEventQueue([]); // Clear the queue
104
131
  }
105
132
  }).catch(error => {
106
- console.error('Failed to initialize HumanBehaviorTracker:', error);
133
+ logError('Failed to initialize HumanBehaviorTracker:', error);
107
134
  });
108
- }, [apiKey, client, eventQueue, isMounted]);
135
+ }, [isMounted, humanBehavior]); // Removed apiKey, client, eventQueue from dependencies
136
+
137
+ // Memoized context value to prevent unnecessary re-renders
138
+ const contextValue = useMemo(() => {
139
+ if (!isMounted) {
140
+ return null;
141
+ }
142
+
143
+ if (!isInitialized) {
144
+ return null;
145
+ }
146
+
147
+ return humanBehavior;
148
+ }, [isMounted, isInitialized, humanBehavior]);
109
149
 
110
150
  // If not in browser, render children without context
111
151
  if (!(isBrowser())) {
112
152
  return <>{children}</>;
113
153
  }
114
154
 
115
- // If not mounted yet, render children with queuing context
116
- if (!isMounted) {
117
- return (
118
- <HumanBehaviorContext.Provider value={{ humanBehavior: null, queueEvent }}>
119
- {children}
120
- </HumanBehaviorContext.Provider>
121
- );
122
- }
123
-
124
- // If not initialized yet, render children with queuing context
125
- if (!isInitialized) {
126
- return (
127
- <HumanBehaviorContext.Provider value={{ humanBehavior: null, queueEvent }}>
128
- {children}
129
- </HumanBehaviorContext.Provider>
130
- );
131
- }
132
-
133
155
  return (
134
- <HumanBehaviorContext.Provider value={{ humanBehavior, queueEvent }}>
156
+ <HumanBehaviorContext.Provider value={contextValue}>
157
+ <HumanBehaviorPageView />
135
158
  {children}
136
159
  </HumanBehaviorContext.Provider>
137
160
  );
138
161
  };
139
162
 
140
- export const useHumanBehavior = (): HumanBehaviorInterface => {
141
- const context = useContext(HumanBehaviorContext);
142
-
143
- // Only throw if the hook is used outside of a provider entirely
144
- if (context === null) {
145
- throw new Error("useHumanBehavior must be used within a HumanBehaviorProvider");
146
- }
163
+ // No-op implementation for server-side
164
+ const serverSideImplementation: HumanBehaviorInterface = {
165
+ addEvent: () => {},
166
+ addUserInfo: async () => {},
167
+ start: () => {},
168
+ stop: () => {},
169
+ viewLogs: () => {},
170
+ };
147
171
 
148
- // If we're in the server-side, return a no-op implementation
149
- if (typeof window === 'undefined') {
150
- console.warn('HumanBehavior is being used before being initialized');
151
- return {
152
- addEvent: () => {},
153
- addUserInfo: async () => {},
154
- start: () => {},
155
- stop: () => {},
156
- viewLogs: () => {},
157
- };
172
+ // Memoized queuing implementation for initialization period
173
+ const createQueuingImplementation = (queueEvent: (event: any) => void): HumanBehaviorInterface => ({
174
+ addEvent: (event: any) => {
175
+ queueEvent(event);
176
+ },
177
+ addUserInfo: async (userProperties: Record<string, any>) => {
178
+ queueEvent({
179
+ type: 'identify',
180
+ userProperties,
181
+ });
182
+ },
183
+ start: () => {
184
+ // Start will be called automatically when initialized
185
+ },
186
+ stop: () => {
187
+ // Stop is a no-op when not initialized
188
+ },
189
+ viewLogs: () => {
190
+ logWarn('Logs are not available until HumanBehaviorTracker is initialized');
158
191
  }
159
-
160
- // If we have an initialized tracker, return it as the interface
161
- if (context.humanBehavior) {
162
- return context.humanBehavior;
192
+ });
193
+
194
+ export const useHumanBehavior = () => {
195
+ const tracker = useContext(HumanBehaviorContext);
196
+ if (!tracker) {
197
+ throw new Error('useHumanBehavior must be used within a HumanBehaviorProvider');
163
198
  }
199
+ return tracker;
200
+ };
201
+
202
+ // Automatic page tracking component (similar to PostHog's PostHogPageView)
203
+ function HumanBehaviorPageView() {
204
+ const tracker = useContext(HumanBehaviorContext);
164
205
 
165
- // If we reach here, we're in the initialization period where:
166
- // - context.humanBehavior is null
167
- // - context.queueEvent is available
168
- // - we need to queue all operations until initialization completes
169
- return {
170
- addEvent: (event: any) => {
171
- context.queueEvent(event);
172
- },
173
- addUserInfo: async (userProperties: Record<string, any>) => {
174
- context.queueEvent({
175
- type: 'identify',
176
- userProperties,
177
- });
178
- },
179
- start: () => {
180
- // Start will be called automatically when initialized
181
- },
182
- stop: () => {
183
- // Stop is a no-op when not initialized
184
- },
185
- viewLogs: () => {
186
- console.warn('Logs are not available until HumanBehaviorTracker is initialized');
206
+ useEffect(() => {
207
+ if (tracker && typeof window !== 'undefined') {
208
+ // Track initial page load
209
+ tracker.trackPageView();
210
+
211
+ // Listen for route changes (for SPAs)
212
+ const handleRouteChange = () => {
213
+ tracker.trackPageView();
214
+ };
215
+
216
+ // Listen for popstate (back/forward navigation)
217
+ window.addEventListener('popstate', handleRouteChange);
218
+
219
+ // Listen for pushstate/replacestate (programmatic navigation)
220
+ const originalPushState = history.pushState;
221
+ const originalReplaceState = history.replaceState;
222
+
223
+ history.pushState = function(...args) {
224
+ originalPushState.apply(this, args);
225
+ handleRouteChange();
226
+ };
227
+
228
+ history.replaceState = function(...args) {
229
+ originalReplaceState.apply(this, args);
230
+ handleRouteChange();
231
+ };
232
+
233
+ return () => {
234
+ window.removeEventListener('popstate', handleRouteChange);
235
+ history.pushState = originalPushState;
236
+ history.replaceState = originalReplaceState;
237
+ };
187
238
  }
188
- };
189
- };
239
+ }, [tracker]);
240
+
241
+ return null;
242
+ }
243
+
244
+ // Export the tracker class for direct use
245
+ export { HumanBehaviorTracker };
package/src/redact.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  // Redaction functionality for sensitive input fields
2
2
  // This module provides methods to redact sensitive input fields in event recordings
3
3
 
4
+ import { logDebug, logWarn } from './utils/logger';
5
+
4
6
  // Check if we're in a browser environment
5
7
  const isBrowser = typeof window !== 'undefined';
6
8
 
@@ -39,18 +41,18 @@ export class RedactionManager {
39
41
  fields.forEach(field => this.userSelectedFields.add(field));
40
42
 
41
43
  if (fields.length > 0) {
42
- console.log(`Redaction: Active for ${fields.length} field(s):`, fields);
44
+ logDebug(`Redaction: Active for ${fields.length} field(s):`, fields);
43
45
 
44
46
  // Debug: Check if elements exist
45
47
  fields.forEach(selector => {
46
48
  const elements = document.querySelectorAll(selector);
47
- console.log(`Redaction: Found ${elements.length} element(s) for selector '${selector}'`);
49
+ logDebug(`Redaction: Found ${elements.length} element(s) for selector '${selector}'`);
48
50
  elements.forEach((el, index) => {
49
- console.log(`Redaction: Element ${index} for '${selector}':`, el);
51
+ logDebug(`Redaction: Element ${index} for '${selector}':`, el);
50
52
  });
51
53
  });
52
54
  } else {
53
- console.log('Redaction: Disabled - no fields selected');
55
+ logDebug('Redaction: Disabled - no fields selected');
54
56
  }
55
57
  }
56
58
 
@@ -85,7 +87,7 @@ export class RedactionManager {
85
87
  if (processedEvent.data.source === 5) { // Input event
86
88
  const shouldRedact = this.isFieldSelected(processedEvent.data);
87
89
  if (shouldRedact) {
88
- console.log('Redaction: Processing input event for redaction');
90
+ logDebug('Redaction: Processing input event for redaction');
89
91
  this.redactInputEvent(processedEvent.data);
90
92
  }
91
93
  }
@@ -114,14 +116,14 @@ export class RedactionManager {
114
116
  return;
115
117
  }
116
118
 
117
- console.log('Redaction: Redacting input event with text:', inputData.text);
119
+ logDebug('Redaction: Redacting input event with text:', inputData.text);
118
120
 
119
121
  // Redact all text-related properties that could contain input data
120
122
  const textProperties = ['text', 'value', 'content', 'data', 'input', 'textContent'];
121
123
  textProperties.forEach(prop => {
122
124
  if (inputData[prop] !== undefined && typeof inputData[prop] === 'string') {
123
125
  inputData[prop] = this.redactedText;
124
- console.log(`Redaction: Redacted property '${prop}'`);
126
+ logDebug(`Redaction: Redacted property '${prop}'`);
125
127
  }
126
128
  });
127
129
 
@@ -129,7 +131,7 @@ export class RedactionManager {
129
131
  Object.keys(inputData).forEach(key => {
130
132
  if (typeof inputData[key] === 'string' && inputData[key].length > 0) {
131
133
  inputData[key] = this.redactedText;
132
- console.log(`Redaction: Redacted additional property '${key}'`);
134
+ logDebug(`Redaction: Redacted additional property '${key}'`);
133
135
  }
134
136
  });
135
137
 
@@ -137,11 +139,11 @@ export class RedactionManager {
137
139
  if (inputData.attributes && typeof inputData.attributes === 'object') {
138
140
  if (inputData.attributes.value && typeof inputData.attributes.value === 'string') {
139
141
  inputData.attributes.value = this.redactedText;
140
- console.log('Redaction: Redacted nested value attribute');
142
+ logDebug('Redaction: Redacted nested value attribute');
141
143
  }
142
144
  }
143
145
 
144
- console.log('Redaction: Input event redaction complete');
146
+ logDebug('Redaction: Input event redaction complete');
145
147
  }
146
148
 
147
149
  /**
@@ -209,7 +211,7 @@ export class RedactionManager {
209
211
 
210
212
  return false;
211
213
  } catch (e) {
212
- console.warn('Error checking if DOM change should be redacted:', e);
214
+ logWarn('Error checking if DOM change should be redacted:', e);
213
215
  return false;
214
216
  }
215
217
  }
@@ -373,10 +375,10 @@ export class RedactionManager {
373
375
  if (matchingElements.length > 0) {
374
376
  // Check if any of these elements are currently focused
375
377
  for (const el of matchingElements) {
376
- if (el === document.activeElement) {
377
- console.log('Redaction: Found focused element matching selector:', selector);
378
- return true;
379
- }
378
+ if (el === document.activeElement) {
379
+ logDebug('Redaction: Found focused element matching selector:', selector);
380
+ return true;
381
+ }
380
382
  }
381
383
  }
382
384
  }
@@ -385,7 +387,7 @@ export class RedactionManager {
385
387
  // Look for any input element that might be the active one
386
388
  const activeElement = document.activeElement;
387
389
  if (activeElement && this.shouldRedactElement(activeElement as HTMLElement)) {
388
- console.log('Redaction: Active element should be redacted');
390
+ logDebug('Redaction: Active element should be redacted');
389
391
  return true;
390
392
  }
391
393
 
@@ -423,7 +425,7 @@ export class RedactionManager {
423
425
 
424
426
  return false;
425
427
  } catch (e) {
426
- console.warn('Error checking if field should be redacted:', e);
428
+ logWarn('Error checking if field should be redacted:', e);
427
429
  return false;
428
430
  }
429
431
  }