@datadog/mobile-react-native
Version:
A client-side React Native module to interact with Datadog
142 lines (140 loc) • 4.86 kB
JavaScript
/*
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
* This product includes software developed at Datadog (https://www.datadoghq.com/).
* Copyright 2016-Present Datadog, Inc.
*/
import { InternalLog } from '../../../InternalLog';
import { SdkVerbosity } from '../../../SdkVerbosity';
import { DdSdk } from '../../../sdk/DdSdk';
import { getErrorStackTrace } from '../../../utils/errorUtils';
import { DatadogBuffer } from './DatadogBuffer';
const DEFAULT_BUFFER_SIZE = 100;
export class BoundedBuffer extends DatadogBuffer {
buffer = [];
idTable = {};
telemetryBuffer = {};
constructor(bufferSize = DEFAULT_BUFFER_SIZE) {
super();
this.bufferSize = bufferSize;
}
addCallback = callback => {
if (this.buffer.length < this.bufferSize) {
this.buffer.push({
callback,
_type: 'VOID'
});
} else {
this.logBufferOverflow();
}
return new Promise(resolve => resolve(undefined));
};
addCallbackReturningId = callback => {
try {
const bufferId = this.generateRandomBufferId();
if (this.buffer.length < this.bufferSize) {
this.buffer.push({
callback,
returnedBufferId: bufferId,
_type: 'RETURNING_ID'
});
this.idTable[bufferId] = null;
} else {
this.logBufferOverflow();
}
return new Promise(resolve => resolve(bufferId));
} catch (error) {
this.logRandomIdGenerationError(error);
return new Promise(resolve => resolve(''));
}
};
addCallbackWithId = (callback, bufferId) => {
if (this.idTable[bufferId] !== undefined) {
this.buffer.push({
callback,
withBufferId: bufferId,
_type: 'WITH_ID'
});
} else {
this.logBufferOverflow();
}
return new Promise(resolve => resolve(undefined));
};
drain = async () => {
for (let bufferIndex = 0; bufferIndex < this.buffer.length; bufferIndex++) {
try {
const item = this.buffer[bufferIndex];
if (item._type === 'RETURNING_ID') {
try {
// Here we want to await the callback result to make sure that it has registered the id returned
// by the callback before executing the callback needing this id.
// eslint-disable-next-line no-await-in-loop
const callbackId = await item.callback();
this.idTable[item.returnedBufferId] = callbackId;
} catch (error) {
InternalLog.log(`Error running a callback returning an id in Buffer: ${error}`, SdkVerbosity.WARN);
}
continue;
}
if (item._type === 'WITH_ID') {
const callbackId = this.idTable[item.withBufferId];
// callbackId can be `null` if the callback supposed to return the id errored. In this case, let's ignore the next callback.
if (callbackId !== null && callbackId !== undefined) {
item.callback(callbackId);
delete this.idTable[item.withBufferId];
} else {
InternalLog.log(`1 event was not sent as callback id was ${callbackId === null ? 'not set' : 'already unset'}`, SdkVerbosity.WARN);
}
continue;
}
item.callback();
} catch (error) {
InternalLog.log(`Error while draining Datadog Buffer: ${error}`, SdkVerbosity.WARN);
}
}
this.buffer = [];
this.drainTelemetry();
};
logBufferOverflow() {
this.addTelemetryEvent('Buffer overflow', '', 'BufferOverflow');
}
logRandomIdGenerationError = error => {
this.addTelemetryEvent('Could not generate enough random numbers', getErrorStackTrace(error), 'RandomIdGenerationError');
// Not using InternalLog here as it is not yet instantiated
console.warn(`[Datadog] Could not generate enough random numbers for RUM buffer. Please check that Math.random is not overwritten. Math.random returns: ${Math.random()}`);
};
generateRandomBufferId = () => {
let tries = 0;
while (tries < 20) {
const hash = Math.random().toString(36).slice(2);
if (this.idTable[hash] !== undefined) {
tries++;
} else {
return hash;
}
}
throw new Error('Could not generate random Buffer id');
};
addTelemetryEvent = (message, stack, kind) => {
if (this.telemetryBuffer[kind]) {
this.telemetryBuffer[kind].occurrences++;
} else {
this.telemetryBuffer[kind] = {
message,
stack,
kind,
occurrences: 1
};
}
};
drainTelemetry = () => {
Object.values(this.telemetryBuffer).forEach(({
message,
stack,
kind,
occurrences
}) => {
DdSdk.telemetryError(`${message} happened ${occurrences} times.`, stack, kind);
});
};
}
//# sourceMappingURL=BoundedBuffer.js.map