diff --git a/CHANGELOG.md b/CHANGELOG.md index f53e24293..10dc53d40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ - Bump Instabug iOS SDK to v14.0.0 ([#1312](https://github.com/Instabug/Instabug-React-Native/pull/1312)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/14.0.0). - Bump Instabug Android SDK to v14.0.0 ([#1312](https://github.com/Instabug/Instabug-React-Native/pull/1312)). [See release notes](https://github.com/Instabug/Instabug-Android/releases/tag/v14.0.0). +### Added + +- Exclude DEV server from network logs ([#1307](https://github.com/Instabug/Instabug-React-Native/pull/1307)). + ### Fixed - Replace thrown errors with logs ([#1220](https://github.com/Instabug/Instabug-React-Native/pull/1220)) diff --git a/src/modules/CrashReporting.ts b/src/modules/CrashReporting.ts index c374c7d77..858e035a3 100644 --- a/src/modules/CrashReporting.ts +++ b/src/modules/CrashReporting.ts @@ -5,6 +5,7 @@ import InstabugUtils from '../utils/InstabugUtils'; import { Platform } from 'react-native'; import type { NonFatalOptions } from '../models/NonFatalOptions'; import { NonFatalErrorLevel } from '../utils/Enums'; +import { Logger } from '../utils/logger'; /** * Enables and disables everything related to crash reporting including intercepting @@ -35,7 +36,7 @@ export const reportError = (error: ExtendedError, nonFatalOptions: NonFatalOptio ), ); } else { - console.warn( + Logger.warn( `IBG-RN: The error ${error} has been omitted because only error type is supported.`, ); return; diff --git a/src/modules/Instabug.ts b/src/modules/Instabug.ts index 1d528fba9..51f1f6adf 100644 --- a/src/modules/Instabug.ts +++ b/src/modules/Instabug.ts @@ -26,6 +26,8 @@ import { captureUnhandledRejections } from '../utils/UnhandledRejectionTracking' import type { ReproConfig } from '../models/ReproConfig'; import type { FeatureFlag } from '../models/FeatureFlag'; import InstabugConstants from '../utils/InstabugConstants'; +import { InstabugRNConfig } from '../utils/config'; +import { Logger } from '../utils/logger'; let _currentScreen: string | null = null; let _lastScreen: string | null = null; @@ -88,6 +90,8 @@ export const init = (config: InstabugConfig) => { _isFirstScreen = true; _currentScreen = firstScreen; + InstabugRNConfig.debugLogsLevel = config.debugLogsLevel ?? LogLevel.error; + reportCurrentViewForAndroid(firstScreen); setTimeout(() => { if (_currentScreen === firstScreen) { @@ -382,9 +386,10 @@ export const setReproStepsConfig = (config: ReproConfig) => { */ export const setUserAttribute = (key: string, value: string) => { if (!key || typeof key !== 'string' || typeof value !== 'string') { - console.error(InstabugConstants.SET_USER_ATTRIBUTES_ERROR_TYPE_MESSAGE); + Logger.error(InstabugConstants.SET_USER_ATTRIBUTES_ERROR_TYPE_MESSAGE); return; } + NativeInstabug.setUserAttribute(key, value); }; @@ -406,7 +411,7 @@ export const getUserAttribute = async (key: string): Promise => { */ export const removeUserAttribute = (key: string) => { if (!key || typeof key !== 'string') { - console.error(InstabugConstants.REMOVE_USER_ATTRIBUTES_ERROR_TYPE_MESSAGE); + Logger.error(InstabugConstants.REMOVE_USER_ATTRIBUTES_ERROR_TYPE_MESSAGE); return; } @@ -633,6 +638,13 @@ export const willRedirectToStore = () => { NativeInstabug.willRedirectToStore(); }; +/** + * This API has be called when changing the default Metro server port (8081) to exclude the DEV URL from network logging. + */ +export const setMetroDevServerPort = (port: number) => { + InstabugRNConfig.metroDevServerPort = port.toString(); +}; + export const componentDidAppearListener = (event: ComponentDidAppearEvent) => { if (_isFirstScreen) { _lastScreen = event.componentName; diff --git a/src/modules/NetworkLogger.ts b/src/modules/NetworkLogger.ts index 67f3a54cc..1e40b9fa9 100644 --- a/src/modules/NetworkLogger.ts +++ b/src/modules/NetworkLogger.ts @@ -2,7 +2,9 @@ import type { RequestHandler } from '@apollo/client'; import InstabugConstants from '../utils/InstabugConstants'; import xhr, { NetworkData, ProgressCallback } from '../utils/XhrNetworkInterceptor'; -import { reportNetworkLog, isContentTypeNotAllowed } from '../utils/InstabugUtils'; +import { isContentTypeNotAllowed, reportNetworkLog } from '../utils/InstabugUtils'; +import { InstabugRNConfig } from '../utils/config'; +import { Logger } from '../utils/logger'; export type { NetworkData }; @@ -10,6 +12,11 @@ export type NetworkDataObfuscationHandler = (data: NetworkData) => Promise { network = await _networkDataObfuscationHandler(network); } + if (__DEV__) { + const urlPort = getPortFromUrl(network.url); + if (urlPort === InstabugRNConfig.metroDevServerPort) { + return; + } + } if (network.requestBodySize > InstabugConstants.MAX_NETWORK_BODY_SIZE_IN_BYTES) { network.requestBody = InstabugConstants.MAX_REQUEST_BODY_SIZE_EXCEEDED_MESSAGE; - console.warn('IBG-RN:', InstabugConstants.MAX_REQUEST_BODY_SIZE_EXCEEDED_MESSAGE); + Logger.warn('IBG-RN:', InstabugConstants.MAX_REQUEST_BODY_SIZE_EXCEEDED_MESSAGE); } if (network.responseBodySize > InstabugConstants.MAX_NETWORK_BODY_SIZE_IN_BYTES) { network.responseBody = InstabugConstants.MAX_RESPONSE_BODY_SIZE_EXCEEDED_MESSAGE; - console.warn('IBG-RN:', InstabugConstants.MAX_RESPONSE_BODY_SIZE_EXCEEDED_MESSAGE); + Logger.warn('IBG-RN:', InstabugConstants.MAX_RESPONSE_BODY_SIZE_EXCEEDED_MESSAGE); } if (network.requestBody && isContentTypeNotAllowed(network.requestContentType)) { network.requestBody = `Body is omitted because content type ${network.requestContentType} isn't supported`; - console.warn( + Logger.warn( `IBG-RN: The request body for the network request with URL ${network.url} has been omitted because the content type ${network.requestContentType} isn't supported.`, ); } if (network.responseBody && isContentTypeNotAllowed(network.contentType)) { network.responseBody = `Body is omitted because content type ${network.contentType} isn't supported`; - console.warn( + Logger.warn( `IBG-RN: The response body for the network request with URL ${network.url} has been omitted because the content type ${network.contentType} isn't supported.`, ); } reportNetworkLog(network); } catch (e) { - console.error(e); + Logger.error(e); } } }); @@ -96,7 +109,7 @@ export const apolloLinkRequestHandler: RequestHandler = (operation, forward) => return { headers: newHeaders }; }); } catch (e) { - console.error(e); + Logger.error(e); } return forward(operation); diff --git a/src/utils/InstabugConstants.ts b/src/utils/InstabugConstants.ts index 6d117d987..aedc84070 100644 --- a/src/utils/InstabugConstants.ts +++ b/src/utils/InstabugConstants.ts @@ -11,6 +11,7 @@ const InstabugConstants = { 'IBG-RN: Expected key and value passed to setUserAttribute to be of type string', REMOVE_USER_ATTRIBUTES_ERROR_TYPE_MESSAGE: 'IBG-RN: Expected key and value passed to removeUserAttribute to be of type string', + DEFAULT_METRO_PORT: '8081', }; export default InstabugConstants; diff --git a/src/utils/UnhandledRejectionTracking.ts b/src/utils/UnhandledRejectionTracking.ts index d8049a9b0..9cbe0dc91 100644 --- a/src/utils/UnhandledRejectionTracking.ts +++ b/src/utils/UnhandledRejectionTracking.ts @@ -2,6 +2,7 @@ import tracking, { RejectionTrackingOptions } from 'promise/setimmediate/rejecti import { sendCrashReport } from './InstabugUtils'; import { NativeCrashReporting } from '../native/NativeCrashReporting'; import { NonFatalErrorLevel } from './Enums'; +import { Logger } from './logger'; export interface HermesInternalType { enablePromiseRejectionTracker?: (options?: RejectionTrackingOptions) => void; @@ -113,5 +114,5 @@ function _originalOnUnhandled(id: number, rejection: unknown = {}) { `Possible Unhandled Promise Rejection (id: ${id}):\n` + `${message ?? ''}\n` + (stack == null ? '' : stack); - console.warn(warning); + Logger.warn(warning); } diff --git a/src/utils/config.ts b/src/utils/config.ts new file mode 100644 index 000000000..de7073d93 --- /dev/null +++ b/src/utils/config.ts @@ -0,0 +1,7 @@ +import InstabugConstants from './InstabugConstants'; +import { LogLevel } from './Enums'; + +export const InstabugRNConfig = { + metroDevServerPort: InstabugConstants.DEFAULT_METRO_PORT, + debugLogsLevel: LogLevel.error, +}; diff --git a/src/utils/logger.ts b/src/utils/logger.ts new file mode 100644 index 000000000..e43d740d0 --- /dev/null +++ b/src/utils/logger.ts @@ -0,0 +1,54 @@ +import { InstabugRNConfig } from './config'; +import { LogLevel } from './Enums'; + +export class Logger { + private static shouldLog(level: LogLevel): boolean { + const currentLevel = InstabugRNConfig.debugLogsLevel; + + // Return true if the current log level is equal to or more verbose than the requested level + const logLevelHierarchy: Record = { + [LogLevel.verbose]: 3, + [LogLevel.debug]: 2, + [LogLevel.error]: 1, + [LogLevel.none]: 0, + }; + + return logLevelHierarchy[currentLevel] >= logLevelHierarchy[level]; + } + + // General logging method that takes a logging function as an argument + private static logMessage( + level: LogLevel, + logMethod: (...args: any[]) => void, + message?: any, + ...optionalParams: any[] + ): void { + if (this.shouldLog(level)) { + logMethod(message, ...optionalParams); + } + } + + static error(message?: any, ...optionalParams: any[]) { + this.logMessage(LogLevel.error, console.error, message, ...optionalParams); // Pass console.error for errors + } + + static info(message?: any, ...optionalParams: any[]) { + this.logMessage(LogLevel.verbose, console.info, message, ...optionalParams); // Pass console.info for info + } + + static log(message?: any, ...optionalParams: any[]) { + this.logMessage(LogLevel.verbose, console.log, message, ...optionalParams); // Default log method + } + + static warn(message?: any, ...optionalParams: any[]) { + this.logMessage(LogLevel.debug, console.warn, message, ...optionalParams); // Use console.warn for debug + } + + static trace(message?: any, ...optionalParams: any[]) { + this.logMessage(LogLevel.debug, console.trace, message, ...optionalParams); // Use console.trace for debugging + } + + static debug(message?: any, ...optionalParams: any[]) { + this.logMessage(LogLevel.debug, console.debug, message, ...optionalParams); // Use console.debug for debug logs + } +} diff --git a/test/modules/Instabug.spec.ts b/test/modules/Instabug.spec.ts index 46b4b208e..fc7bc72a0 100644 --- a/test/modules/Instabug.spec.ts +++ b/test/modules/Instabug.spec.ts @@ -24,6 +24,7 @@ import { import InstabugUtils from '../../src/utils/InstabugUtils'; import type { FeatureFlag } from '../../src/models/FeatureFlag'; import InstabugConstants from '../../src/utils/InstabugConstants'; +import { Logger } from '../../src/utils/logger'; describe('Instabug Module', () => { beforeEach(() => { @@ -641,7 +642,7 @@ describe('Instabug Module', () => { [{}, 'value'], ['key', []], ])("should fail if key and value aren't strings when calling setUserAttribute", (key, value) => { - const logSpy = jest.spyOn(console, 'error'); + const logSpy = jest.spyOn(Logger, 'error'); // @ts-ignore Instabug.setUserAttribute(key, value); diff --git a/test/modules/NetworkLogger.spec.ts b/test/modules/NetworkLogger.spec.ts index 71dd2dd77..c7ecd7248 100644 --- a/test/modules/NetworkLogger.spec.ts +++ b/test/modules/NetworkLogger.spec.ts @@ -7,6 +7,7 @@ import * as NetworkLogger from '../../src/modules/NetworkLogger'; import Interceptor from '../../src/utils/XhrNetworkInterceptor'; import { isContentTypeNotAllowed, reportNetworkLog } from '../../src/utils/InstabugUtils'; import InstabugConstants from '../../src/utils/InstabugConstants'; +import { Logger } from '../../src/utils/logger'; const clone = (obj: T): T => { return JSON.parse(JSON.stringify(obj)); @@ -89,7 +90,7 @@ describe('NetworkLogger Module', () => { it('should not break if network data obfuscation fails', async () => { // Avoid the console.error to clutter the test log - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const consoleSpy = jest.spyOn(Logger, 'error').mockImplementation(() => {}); // Make a circular object, this should make JSON.stringify fail const handler = jest.fn(() => { @@ -133,7 +134,7 @@ describe('NetworkLogger Module', () => { }); it('should not break if apollo handler throws an error', async () => { - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const consoleSpy = jest.spyOn(Logger, 'error').mockImplementation(() => {}); const operation = { setContext: jest.fn(() => { @@ -150,7 +151,7 @@ describe('NetworkLogger Module', () => { }); it('should omit request body if its content type is not allowed', () => { - const consoleWarn = jest.spyOn(console, 'warn').mockImplementation(); + const consoleWarn = jest.spyOn(Logger, 'warn').mockImplementation(); jest.mocked(isContentTypeNotAllowed).mockReturnValueOnce(true); const networkData = { @@ -175,7 +176,7 @@ describe('NetworkLogger Module', () => { }); it('should omit response body if its content type is not allowed', () => { - const consoleWarn = jest.spyOn(console, 'warn').mockImplementation(); + const consoleWarn = jest.spyOn(Logger, 'warn').mockImplementation(); jest.mocked(isContentTypeNotAllowed).mockReturnValueOnce(true); const networkData = { @@ -200,7 +201,7 @@ describe('NetworkLogger Module', () => { }); it('should omit request body if its size exceeds the maximum allowed size', () => { - const consoleWarn = jest.spyOn(console, 'warn').mockImplementation(); + const consoleWarn = jest.spyOn(Logger, 'warn').mockImplementation(); const networkData = { ...network, @@ -239,7 +240,7 @@ describe('NetworkLogger Module', () => { }); it('should omit response body if its size exceeds the maximum allowed size', () => { - const consoleWarn = jest.spyOn(console, 'warn').mockImplementation(); + const consoleWarn = jest.spyOn(Logger, 'warn').mockImplementation(); const networkData = { ...network,