Skip to content

[v6] utils Logging #2526

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions docs/utils/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,44 @@ import firebase from '@react-native-firebase/app';
// Access the device pictures directory
const picturesDir = firebase.utils.FilePath.PICTURES_DIRECTORY;
```

### Enable logging

You can enable logging for native method calls and event streams.

```js
import firebase from '@react-native-firebase/app';

firebase.utils().enableLogger({
enableMethodLogging: true,
enableEventLogging: true,
});

// It also returns back config

const config = firebase.utils().enableLogger({
enableMethodLogging: true,
enableEventLogging: true,
});

// config = { enableMethodLogging: true, enableEventLogging: true }
```

### Log out custom information

You can also use our built in logger to log your custom information.

```js
import firebase from '@react-native-firebase/app';

firebase.utils.info('Custom Log');
```

Info logger also takes in second argument that can be object or array, this is usefull
when you need to log some additional data.

```js
import firebase from '@react-native-firebase/app';

firebase.utils.info('Custom Log', { uid: '123' });
```
84 changes: 84 additions & 0 deletions packages/app/e2e/utils.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,88 @@ describe('utils()', () => {
should.equal(firebase.utils().isRunningInTestLab, false);
});
});

describe('logger', () => {
it('throws if config is not an object', () => {
try {
firebase.utils().enableLogger(5);
return Promise.reject(new Error('Did not throw Error.'));
} catch (e) {
e.message.should.containEql('Invalid config passed to enableLogger');
return Promise.resolve();
}
});

it("throws if config object doesn't contain at least one valid option", () => {
try {
firebase.utils().enableLogger({ invalidOption: true });
return Promise.reject(new Error('Did not throw Error.'));
} catch (e) {
e.message.should.containEql(
'enableLogger expects at least one option: enableMethodLogging or enableEventLogging',
);
return Promise.resolve();
}
});

it('enables logging correctly', () => {
const config = firebase
.utils()
.enableLogger({ enableMethodLogging: true, enableEventLogging: true });
config.enableMethodLogging.should.eql(true);
config.enableEventLogging.should.eql(true);
});

it('throws if non string text was passed to .info', () => {
try {
firebase
.utils()
.logger()
.info(123);
return Promise.reject(new Error('Did not throw Error.'));
} catch (e) {
e.message.should.containEql(
'Invalid text passed to logger. Expected string, but got number',
);
return Promise.resolve();
}
});

it('logs correctly', () => {
try {
firebase
.utils()
.logger()
.info('Custom Log');
} catch (e) {
Promise.reject(new Error(e.message));
}
});

it('throws if incorrect params were passed', () => {
try {
firebase
.utils()
.logger()
.info('Custom Log', 123);
return Promise.reject(new Error('Did not throw Error.'));
} catch (e) {
e.message.should.containEql(
'Invalid params passed to logger. Expected array or object, but got number',
);
return Promise.resolve();
}
});

it('logs correctly with params', () => {
try {
firebase
.utils()
.logger()
.info('Custom Log', { uid: 123 });
} catch (e) {
Promise.reject(new Error(e.message));
}
});
});
});
16 changes: 16 additions & 0 deletions packages/app/lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,12 @@ export namespace ReactNativeFirebase {
export namespace Utils {
import FirebaseModule = ReactNativeFirebase.FirebaseModule;

/**
* Logger configuration object you can use to enable internal RNFB logging
* for methods and events.
*/
type LoggerConfig = { enableMethodLogging: boolean; enableEventLogging: boolean };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will get added to the docs so worth having a quick description above it.


/**
* A collection of native device file paths to aid in the usage of file path based methods.
*
Expand Down Expand Up @@ -379,6 +385,16 @@ export namespace Utils {
* @android
*/
isRunningInTestLab: boolean;

/**
* Enables logging based on configuration that was passed in
*/
enableLogger: (config: LoggerConfig) => LoggerConfig;

/**
* Returns logger instance
*/
logger: { config: LoggerConfig; info: <T>(text: string, params?: object | T[] | null) => void };
}
}

Expand Down
22 changes: 20 additions & 2 deletions packages/app/lib/internal/registry/nativeModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/

import { NativeModules, Platform } from 'react-native';
import Logger from '../../utils/logger';
import { APP_NATIVE_MODULE } from '../constants';
import NativeFirebaseError from '../NativeFirebaseError';
import RNFBNativeEventEmitter from '../RNFBNativeEventEmitter';
Expand All @@ -37,8 +38,12 @@ function nativeModuleKey(module) {
* @param argToPrepend
* @returns {Function}
*/
function nativeModuleMethodWrapped(namespace, method, argToPrepend) {
function nativeModuleMethodWrapped(namespace, method, argToPrepend, methodName) {
return (...args) => {
if (Logger.config.enableMethodLogging) {
Logger.info(`METHOD:${namespace}::${methodName}`, args);
}

const possiblePromise = method(...[...argToPrepend, ...args]);

if (possiblePromise && possiblePromise.then) {
Expand Down Expand Up @@ -70,7 +75,12 @@ function nativeModuleWrapped(namespace, NativeModule, argToPrepend) {
for (let i = 0, len = properties.length; i < len; i++) {
const property = properties[i];
if (typeof NativeModule[property] === 'function') {
native[property] = nativeModuleMethodWrapped(namespace, NativeModule[property], argToPrepend);
native[property] = nativeModuleMethodWrapped(
namespace,
NativeModule[property],
argToPrepend,
property,
);
} else {
native[property] = NativeModule[property];
}
Expand Down Expand Up @@ -150,11 +160,19 @@ function initialiseNativeModule(module) {
*/
function subscribeToNativeModuleEvent(eventName) {
if (!NATIVE_MODULE_EVENT_SUBSCRIPTIONS[eventName]) {
const eventLoggingEnabled = Logger.config.enableEventLogging;

RNFBNativeEventEmitter.addListener(eventName, event => {
if (event.appName) {
if (eventLoggingEnabled) {
Logger.info(`EVENT:${eventName}::${appName}`, args);
}
// native event has an appName property - auto prefix and internally emit
SharedEventEmitter.emit(`${event.appName}-${eventName}`, event);
} else {
if (eventLoggingEnabled) {
Logger.info(`EVENT:${eventName}`, args);
}
// standard event - no need to prefix
SharedEventEmitter.emit(eventName, event);
}
Expand Down
26 changes: 23 additions & 3 deletions packages/app/lib/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
*
*/

import { isIOS } from '../../lib/common';
import { hasOwnProperty, isIOS, isObject } from '../../lib/common';
import { createModuleNamespace, FirebaseModule } from '../../lib/internal';
import Logger from './logger';
import UtilsStatics from './UtilsStatics';

const namespace = 'utils';
Expand All @@ -31,8 +32,27 @@ class FirebaseUtilsModule extends FirebaseModule {
return this.native.isRunningInTestLab;
}

logInfo(...args) {
return logger.logInfo(...args);
enableLogger(config) {
if (!isObject(config)) {
throw new Error('Invalid config passed to enableLogger');
}

if (
!hasOwnProperty(config, 'enableMethodLogging') &&
!hasOwnProperty(config, 'enableEventLogging')
) {
throw new Error(
'enableLogger expects at least one option: enableMethodLogging or enableEventLogging',
);
}

Logger.config = { ...Logger.config, ...config };

return Logger.config;
}

logger() {
return Logger;
}
}

Expand Down
47 changes: 47 additions & 0 deletions packages/app/lib/utils/logger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* eslint-disable no-console */
/**
* List of ansi colors
* @url https://github.com/shiena/ansicolor/blob/master/README.md
*/

import { isArray, isNull, isObject, isString } from '@react-native-firebase/app/lib/common';

const config = {
enableMethodLogging: false,
enableEventLogging: false,
};

/**
* Resets terminal to default color
*/
function resetTerminalColor() {
console.log('\x1b[0m');
}

/**
* Info level log
* @param {String} text
* @param {Array} params
*/
function info(text, params = null) {
if (!isString(text)) {
throw new Error(`Invalid text passed to logger. Expected string, but got ${typeof text}`);
}
console.log('\x1b[35m', text);

if (!isArray(params) && !isObject(params) && !isNull(params)) {
throw new Error(
`Invalid params passed to logger. Expected array or object, but got ${typeof params}`,
);
}
if (params) {
console.log('\x1b[94m', JSON.stringify(params, null, 2));
}

resetTerminalColor();
}

export default {
config,
info,
};
4 changes: 4 additions & 0 deletions packages/app/type-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ console.log(utils.FilePath.CACHES_DIRECTORY);
console.log(firebase.utils.FilePath.CACHES_DIRECTORY);
console.log(utils.FilePath.CACHES_DIRECTORY);

// checks logger
console.log(firebase.utils().logger);
console.log(firebase.utils().enableLogger);

// checks root exists
console.log(firebase.SDK_VERSION);

Expand Down