posthog-js
Version:
Posthog-js allows you to automatically capture usage and send events to PostHog.
156 lines • 7.82 kB
JavaScript
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExceptionObserver = void 0;
var globals_1 = require("@posthog/browser-common/utils/globals");
var globals_2 = require("../../utils/globals");
var logger_1 = require("@posthog/browser-common/utils/logger");
var constants_1 = require("../../constants");
var core_1 = require("@posthog/core");
var logger = (0, logger_1.createLogger)('[ExceptionAutocapture]');
var ExceptionObserver = /** @class */ (function () {
function ExceptionObserver(instance) {
var _this = this;
var _a;
this._startCapturing = function () {
var _a;
if (!globals_1.window || !_this.isEnabled || !((_a = globals_2.assignableWindow.__PosthogExtensions__) === null || _a === void 0 ? void 0 : _a.errorWrappingFunctions)) {
return;
}
var wrapOnError = globals_2.assignableWindow.__PosthogExtensions__.errorWrappingFunctions.wrapOnError;
var wrapUnhandledRejection = globals_2.assignableWindow.__PosthogExtensions__.errorWrappingFunctions.wrapUnhandledRejection;
var wrapConsoleError = globals_2.assignableWindow.__PosthogExtensions__.errorWrappingFunctions.wrapConsoleError;
try {
if (!_this._unwrapOnError && _this._config.capture_unhandled_errors) {
_this._unwrapOnError = wrapOnError(_this.captureException.bind(_this));
}
if (!_this._unwrapUnhandledRejection && _this._config.capture_unhandled_rejections) {
_this._unwrapUnhandledRejection = wrapUnhandledRejection(_this.captureException.bind(_this));
}
if (!_this._unwrapConsoleError && _this._config.capture_console_errors) {
_this._unwrapConsoleError = wrapConsoleError(_this.captureException.bind(_this));
}
}
catch (e) {
logger.error('failed to start', e);
_this._stopCapturing();
}
};
this._instance = instance;
this._remoteEnabled = !!((_a = this._instance.persistence) === null || _a === void 0 ? void 0 : _a.props[constants_1.EXCEPTION_CAPTURE_ENABLED_SERVER_SIDE]);
// Burst protection is scoped per exception type: captures ten exceptions before rate
// limiting, then refills at a rate of one token / 10 second period (e.g. captures 1
// rate-limited exception of that type every 10 seconds until the burst ends). The bucket
// size and refill rate can be tuned via the `exceptionRateLimiterBucketSize` and
// `exceptionRateLimiterRefillRate` config options.
this._rateLimiter = new core_1.BucketedRateLimiter(__assign(__assign({}, (0, core_1.resolveExceptionRateLimiterConfig)(this._instance.config.error_tracking)), { refillInterval: 10000, _logger: logger }));
this._config = this._requiredConfig();
this.startIfEnabledOrStop();
}
ExceptionObserver.prototype._requiredConfig = function () {
var providedConfig = this._instance.config.capture_exceptions;
var config = {
capture_unhandled_errors: false,
capture_unhandled_rejections: false,
capture_console_errors: false,
};
if ((0, core_1.isObject)(providedConfig)) {
config = __assign(__assign({}, config), providedConfig);
}
else if ((0, core_1.isUndefined)(providedConfig) ? this._remoteEnabled : providedConfig) {
config = __assign(__assign({}, config), { capture_unhandled_errors: true, capture_unhandled_rejections: true });
}
return config;
};
Object.defineProperty(ExceptionObserver.prototype, "isEnabled", {
get: function () {
return (this._config.capture_console_errors ||
this._config.capture_unhandled_errors ||
this._config.capture_unhandled_rejections);
},
enumerable: false,
configurable: true
});
ExceptionObserver.prototype.startIfEnabledOrStop = function () {
if (this.isEnabled) {
logger.info('enabled');
this._stopCapturing();
this._loadScript(this._startCapturing);
}
else {
this._stopCapturing();
}
};
ExceptionObserver.prototype._loadScript = function (cb) {
var _a, _b, _c;
if ((_a = globals_2.assignableWindow.__PosthogExtensions__) === null || _a === void 0 ? void 0 : _a.errorWrappingFunctions) {
// already loaded
cb();
return;
}
(_c = (_b = globals_2.assignableWindow.__PosthogExtensions__) === null || _b === void 0 ? void 0 : _b.loadExternalDependency) === null || _c === void 0 ? void 0 : _c.call(_b, this._instance, 'exception-autocapture', function (err) {
if (err) {
return logger.error('failed to load script', err);
}
cb();
});
};
ExceptionObserver.prototype._stopCapturing = function () {
var _a, _b, _c;
(_a = this._unwrapOnError) === null || _a === void 0 ? void 0 : _a.call(this);
this._unwrapOnError = undefined;
(_b = this._unwrapUnhandledRejection) === null || _b === void 0 ? void 0 : _b.call(this);
this._unwrapUnhandledRejection = undefined;
(_c = this._unwrapConsoleError) === null || _c === void 0 ? void 0 : _c.call(this);
this._unwrapConsoleError = undefined;
};
ExceptionObserver.prototype.onRemoteConfig = function (result) {
var _a;
if (!result.ok) {
// Failure behaves like a response without an autocaptureExceptions key.
return;
}
var response = result.config;
if (!('autocaptureExceptions' in response)) {
return;
}
var autocaptureExceptionsResponse = response.autocaptureExceptions;
// store this in-memory in case persistence is disabled
this._remoteEnabled = !!autocaptureExceptionsResponse || false;
if (this._instance.persistence) {
this._instance.persistence.register((_a = {},
_a[constants_1.EXCEPTION_CAPTURE_ENABLED_SERVER_SIDE] = this._remoteEnabled,
_a));
}
this._config = this._requiredConfig();
this.startIfEnabledOrStop();
};
ExceptionObserver.prototype.onConfigChange = function () {
this._config = this._requiredConfig();
};
ExceptionObserver.prototype.captureException = function (errorProperties) {
var _a, _b, _c, _d;
var exceptionType = (_c = (_b = (_a = errorProperties === null || errorProperties === void 0 ? void 0 : errorProperties.$exception_list) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.type) !== null && _c !== void 0 ? _c : 'Exception';
var isRateLimited = this._rateLimiter.consumeRateLimit(exceptionType);
if (isRateLimited) {
logger.info('Skipping exception capture because of client rate limiting.', {
exception: exceptionType,
});
return;
}
(_d = this._instance.exceptions) === null || _d === void 0 ? void 0 : _d.sendExceptionEvent(errorProperties);
};
return ExceptionObserver;
}());
exports.ExceptionObserver = ExceptionObserver;
//# sourceMappingURL=index.js.map