UNPKG

4.46 kBJavaScriptView Raw
1import Constants from 'expo-constants';
2import { EventEmitter } from 'fbemitter';
3import invariant from 'invariant';
4import uuidv4 from 'uuid/v4';
5import LogSerialization from './LogSerialization';
6const _sessionId = uuidv4();
7const _logQueue = [];
8const _transportEventEmitter = new EventEmitter();
9let _logCounter = 0;
10let _isSendingLogs = false;
11let _completionPromise = null;
12let _resolveCompletion = null;
13async function enqueueRemoteLogAsync(level, additionalFields, data) {
14 if (_isReactNativeWarning(data)) {
15 // Remove the stack trace from the warning message since we'll capture our own
16 if (data.length === 0) {
17 throw new Error(`Warnings must include log arguments`);
18 }
19 const warning = data[0];
20 if (typeof warning !== 'string') {
21 throw new TypeError(`The log argument for a warning must be a string`);
22 }
23 const lines = warning.split('\n');
24 if (lines.length > 1 && /^\s+in /.test(lines[1])) {
25 data[0] = lines[0];
26 }
27 }
28 const { body, includesStack } = await LogSerialization.serializeLogDataAsync(data, level);
29 _logQueue.push({
30 count: _logCounter++,
31 level,
32 body,
33 includesStack,
34 ...additionalFields,
35 });
36 // Send the logs asynchronously (system errors are emitted with transport error events) and throw an uncaught error
37 _sendRemoteLogsAsync().catch(error => {
38 setImmediate(() => {
39 throw error;
40 });
41 });
42}
43async function _sendRemoteLogsAsync() {
44 if (_isSendingLogs || !_logQueue.length) {
45 return;
46 }
47 // Our current transport policy is to send all of the pending log messages in one batch. If we opt
48 // for another policy (ex: throttling) this is where to to implement it.
49 const batch = _logQueue.splice(0);
50 const { logUrl } = Constants.manifest;
51 if (typeof logUrl !== 'string') {
52 throw new Error('The Expo project manifest must specify `logUrl`');
53 }
54 _isSendingLogs = true;
55 try {
56 await _sendNextLogBatchAsync(batch, logUrl);
57 }
58 finally {
59 _isSendingLogs = false;
60 }
61 if (_logQueue.length) {
62 return _sendRemoteLogsAsync();
63 }
64 else if (_resolveCompletion) {
65 _resolveCompletion();
66 }
67}
68async function _sendNextLogBatchAsync(batch, logUrl) {
69 let response;
70 const headers = {
71 'Content-Type': 'application/json',
72 Connection: 'keep-alive',
73 'Proxy-Connection': 'keep-alive',
74 Accept: 'application/json',
75 'Device-Id': Constants.installationId,
76 'Session-Id': _sessionId,
77 };
78 if (Constants.deviceName) {
79 headers['Device-Name'] = Constants.deviceName;
80 }
81 try {
82 response = await fetch(logUrl, {
83 method: 'POST',
84 headers,
85 body: JSON.stringify(batch),
86 });
87 }
88 catch (error) {
89 _transportEventEmitter.emit('error', { error });
90 return;
91 }
92 const success = response.status >= 200 && response.status < 300;
93 if (!success) {
94 _transportEventEmitter.emit('error', {
95 error: new Error(`An HTTP error occurred when sending remote logs`),
96 response,
97 });
98 }
99}
100function addTransportErrorListener(listener) {
101 return _transportEventEmitter.addListener('error', listener);
102}
103function _isReactNativeWarning(data) {
104 // NOTE: RN does the same thing internally for YellowBox
105 const message = data[0];
106 return data.length === 1 && typeof message === 'string' && message.startsWith('Warning: ');
107}
108export default {
109 enqueueRemoteLogAsync,
110 addTransportErrorListener,
111};
112/**
113 * Returns a promise that resolves when all entries in the log queue have been sent. This method is
114 * intended for testing only.
115 */
116export function __waitForEmptyLogQueueAsync() {
117 if (_completionPromise) {
118 return _completionPromise;
119 }
120 if (!_isSendingLogs && !_logQueue.length) {
121 return Promise.resolve();
122 }
123 _completionPromise = new Promise(resolve => {
124 _resolveCompletion = () => {
125 invariant(!_isSendingLogs, `Must not be sending logs at completion`);
126 invariant(!_logQueue.length, `Log queue must be empty at completion`);
127 _completionPromise = null;
128 _resolveCompletion = null;
129 resolve();
130 };
131 });
132 return _completionPromise;
133}
134//# sourceMappingURL=RemoteLogging.js.map
\No newline at end of file