Skip to content

Commit

Permalink
feat: Disable the singleton logger (#93)
Browse files Browse the repository at this point in the history
* Disable the singleton logger

* lint

* tests

* Update src/__tests__/index.spec.ts

* test

* added note
  • Loading branch information
jwallet committed Dec 23, 2023
1 parent 049fab8 commit 1191b61
Show file tree
Hide file tree
Showing 7 changed files with 286 additions and 28 deletions.
40 changes: 29 additions & 11 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useCallback, useLayoutEffect, useState } from 'react';
import {
StyleSheet,
Button,
Expand All @@ -13,6 +13,7 @@ import NetworkLogger, {
ThemeName,
getBackHandler,
startNetworkLogging,
stopNetworkLogging,
} from 'react-native-network-logger';
import { getRates } from './apolloClient';

Expand Down Expand Up @@ -47,29 +48,45 @@ export default function App() {
// fetch('https://failingrequest');
};

startNetworkLogging({
ignoredHosts: ['192.168.1.28', '127.0.0.1'],
maxRequests: 20,
ignoredUrls: ['https://httpstat.us/other'],
ignoredPatterns: [/^POST http:\/\/(192|10)/],
});
const start = useCallback(() => {
startNetworkLogging({
ignoredHosts: ['127.0.0.1'],
maxRequests: 20,
ignoredUrls: ['https://httpstat.us/other'],
ignoredPatterns: [/^POST http:\/\/(192|10)/, /\/logs$/, /\/symbolicate$/],
});
}, []);

const [unmountNetworkLogger, setUnmountNetworkLogger] = useState(false);

// note: Logger is a singleton so it starts on the first render
// useLayoutEffect is used to ensure the setup runs before the component mounts (useEffect is async)
useLayoutEffect(() => {
start();
return () => {
stopNetworkLogging();
};
}, [start, unmountNetworkLogger]);

const [theme, setTheme] = useState<ThemeName>('dark');
const isDark = theme === 'dark';

const styles = themedStyles(isDark);

const goBack = () => setUnmountNetworkLogger(true);

const [unmountNetworkLogger, setUnmountNetworkLogger] = useState(false);
const goBack = () => {
stopNetworkLogging();
setUnmountNetworkLogger(true);
};

const backHandler = getBackHandler(goBack);

const remountButton = (
<View>
<Button
title={'Re-open the network logger'}
onPress={() => setUnmountNetworkLogger(false)}
onPress={() => {
setUnmountNetworkLogger(false);
}}
/>
</View>
);
Expand All @@ -80,6 +97,7 @@ export default function App() {
<View style={styles.header}>
<TouchableOpacity
style={styles.navButton}
testID="backButton"
onPress={backHandler}
hitSlop={{ top: 20, left: 20, bottom: 20, right: 20 }}
>
Expand Down
2 changes: 2 additions & 0 deletions module.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Network/XHRInterceptor.js
declare module 'react-native/Libraries/Network/XHRInterceptor' {
export function isInterceptorEnabled(): boolean;
export function setOpenCallback(...props: any): void;
Expand All @@ -6,6 +7,7 @@ declare module 'react-native/Libraries/Network/XHRInterceptor' {
export function setHeaderReceivedCallback(...props: any): void;
export function setResponseCallback(...props: any): void;
export function enableInterception(): void;
export function disableInterception(): void;
}

declare module 'react-native/Libraries/Blob/FileReader' {
Expand Down
27 changes: 22 additions & 5 deletions src/Logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,12 +234,29 @@ export default class Logger {
this.debouncedCallback();
};

// dispose in tests
private dispose = () => {
this.enabled = false;
disableXHRInterception = () => {
if (!this.enabled) return;

this.clearRequests();

nextXHRId = 0;
this.requests = [];
this.callback(this.requests);
this.enabled = false;
this.paused = false;
this.xhrIdMap.clear();
this.maxRequests = LOGGER_MAX_REQUESTS;
this.refreshRate = LOGGER_REFRESH_RATE;
this.ignoredHosts = undefined;
this.ignoredUrls = undefined;
this.ignoredPatterns = undefined;

const noop = () => null;
// manually reset callbacks even if the XHRInterceptor lib does it for us with 'disableInterception'
XHRInterceptor.setOpenCallback(noop);
XHRInterceptor.setRequestHeaderCallback(noop);
XHRInterceptor.setHeaderReceivedCallback(noop);
XHRInterceptor.setSendCallback(noop);
XHRInterceptor.setResponseCallback(noop);

XHRInterceptor.disableInterception();
};
}
88 changes: 77 additions & 11 deletions src/__tests__/Logger.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import XHRInterceptor from 'react-native/Libraries/Network/XHRInterceptor';
import { warn } from '../utils/logger';
import Logger from '../Logger';
import { LOGGER_REFRESH_RATE } from '../constant';
import { LOGGER_MAX_REQUESTS, LOGGER_REFRESH_RATE } from '../constant';

jest.mock('react-native/Libraries/Blob/FileReader', () => ({}));
jest.mock('react-native/Libraries/Network/XHRInterceptor', () => ({
Expand All @@ -12,6 +12,7 @@ jest.mock('react-native/Libraries/Network/XHRInterceptor', () => ({
setHeaderReceivedCallback: jest.fn(),
setResponseCallback: jest.fn(),
enableInterception: jest.fn(),
disableInterception: jest.fn(),
}));

jest.mock('../utils/logger', () => ({
Expand Down Expand Up @@ -156,6 +157,76 @@ describe('enableXHRInterception', () => {
});
});

describe('disableXHRInterception', () => {
it('should do nothing if interceptor has not been enabled', () => {
const logger = new Logger();

(XHRInterceptor.isInterceptorEnabled as jest.Mock).mockReturnValueOnce(
false
);

const result = logger.disableXHRInterception();

expect(result).toBeUndefined();
expect(XHRInterceptor.isInterceptorEnabled).toHaveBeenCalledTimes(0);
expect(XHRInterceptor.disableInterception).toHaveBeenCalledTimes(0);
});

it('should call disableInterception and clear options', () => {
const logger = new Logger();

logger.enableXHRInterception();
logger.disableXHRInterception();

expect(XHRInterceptor.disableInterception).toHaveBeenCalledTimes(1);
expect(XHRInterceptor.disableInterception).toHaveBeenCalledWith();

// @ts-ignore
expect(logger.ignoredHosts).toBeUndefined();
// @ts-ignore
expect(logger.ignoredUrls).toBeUndefined();
// @ts-ignore
expect(logger.ignoredPatterns).toBeUndefined();
// @ts-ignore
expect(logger.maxRequests).toEqual(LOGGER_MAX_REQUESTS);
// @ts-ignore
expect(logger.refreshRate).toEqual(LOGGER_REFRESH_RATE);
// @ts-ignore
expect(logger.requests).toEqual([]);
// @ts-ignore
expect(logger.latestRequestUpdatedAt).toEqual(0);
// @ts-ignore
expect(logger.xhrIdMap).toEqual(new Map());
});

it('should set options after re-enabling', () => {
const logger = new Logger();

logger.enableXHRInterception({
ignoredHosts: ['example.com'],
ignoredUrls: ['http://example.com/'],
ignoredPatterns: [/^POST /],
maxRequests: 100,
});
logger.disableXHRInterception();
logger.enableXHRInterception({
ignoredHosts: ['test.com'],
ignoredUrls: ['http://test.com/'],
ignoredPatterns: [/^HEAD /],
maxRequests: 10,
});

// @ts-ignore
expect(logger.ignoredHosts).toStrictEqual(new Set(['test.com']));
// @ts-ignore
expect(logger.ignoredUrls).toStrictEqual(new Set(['http://test.com/']));
// @ts-ignore
expect(logger.ignoredPatterns).toStrictEqual([/^HEAD /]);
// @ts-ignore
expect(logger.maxRequests).toEqual(10);
});
});

describe('getRequests', () => {
it('should return the requests', () => {
const logger = new Logger();
Expand Down Expand Up @@ -249,8 +320,7 @@ describe('openCallback', () => {
expect(logger.getRequests()[0].url).toEqual(url2);
expect(logger.getRequests()[1].url).toEqual(url1);

// @ts-expect-error
logger.dispose();
logger.disableXHRInterception();
});

it('should ignore requests that have ignored hosts', () => {
Expand All @@ -268,8 +338,7 @@ describe('openCallback', () => {
expect(logger.getRequests()[0].url).toEqual(url1);
expect(logger.getRequests()).toHaveLength(1);

// @ts-expect-error
logger.dispose();
logger.disableXHRInterception();
});

it('should ignore requests that have ignored urls', () => {
Expand All @@ -286,8 +355,7 @@ describe('openCallback', () => {
expect(logger.getRequests()[0].url).toEqual(url1);
expect(logger.getRequests()).toHaveLength(1);

// @ts-expect-error
logger.dispose();
logger.disableXHRInterception();
});

it('should ignore requests that match pattern', () => {
Expand All @@ -314,8 +382,7 @@ describe('openCallback', () => {
expect(logger.getRequests()[0].method).toEqual('PUT');
expect(logger.getRequests()).toHaveLength(2);

// @ts-expect-error
logger.dispose();
logger.disableXHRInterception();
});

it('should retrieve requests when it is restricted by maxRequests', () => {
Expand Down Expand Up @@ -346,7 +413,6 @@ describe('openCallback', () => {
// @ts-expect-error
expect(logger.getRequest(3)?.method).toBe(first?.method);

// @ts-expect-error
logger.dispose();
logger.disableXHRInterception();
});
});

0 comments on commit 1191b61

Please sign in to comment.