@sentry/google-cloud-serverless
Version:
Official Sentry SDK for Google Cloud Functions
86 lines (75 loc) • 3.17 kB
JavaScript
import { _optionalChain } from '@sentry/utils';
import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, setHttpStatus, handleCallbackErrors } from '@sentry/core';
import { continueTrace, startSpanManual, getCurrentScope, flush, captureException } from '@sentry/node';
import { stripUrlQueryAndFragment, isString, logger } from '@sentry/utils';
import { DEBUG_BUILD } from '../debug-build.js';
import { proxyFunction, domainify, markEventUnhandled } from '../utils.js';
/**
* Wraps an HTTP function handler adding it error capture and tracing capabilities.
*
* @param fn HTTP Handler
* @param options Options
* @returns HTTP handler
*/
function wrapHttpFunction(fn, wrapOptions = {}) {
const wrap = (f) => domainify(_wrapHttpFunction(f, wrapOptions));
let overrides;
// Functions emulator from firebase-tools has a hack-ish workaround that saves the actual function
// passed to `onRequest(...)` and in fact runs it so we need to wrap it too.
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
const emulatorFunc = (fn ).__emulator_func ;
if (emulatorFunc) {
overrides = { __emulator_func: proxyFunction(emulatorFunc, wrap) };
}
return proxyFunction(fn, wrap, overrides);
}
/** */
function _wrapHttpFunction(fn, options) {
const flushTimeout = options.flushTimeout || 2000;
return (req, res) => {
const reqMethod = (req.method || '').toUpperCase();
const reqUrl = stripUrlQueryAndFragment(req.originalUrl || req.url || '');
const sentryTrace = req.headers && isString(req.headers['sentry-trace']) ? req.headers['sentry-trace'] : undefined;
const baggage = _optionalChain([req, 'access', _ => _.headers, 'optionalAccess', _2 => _2.baggage]);
return continueTrace({ sentryTrace, baggage }, () => {
return startSpanManual(
{
name: `${reqMethod} ${reqUrl}`,
op: 'function.gcp.http',
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.serverless.gcp_http',
},
},
span => {
getCurrentScope().setSDKProcessingMetadata({
request: req,
});
// eslint-disable-next-line @typescript-eslint/unbound-method
const _end = res.end;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
res.end = function (chunk, encoding, cb) {
setHttpStatus(span, res.statusCode);
span.end();
// eslint-disable-next-line @typescript-eslint/no-floating-promises
flush(flushTimeout)
.then(null, e => {
DEBUG_BUILD && logger.error(e);
})
.then(() => {
_end.call(this, chunk, encoding, cb);
});
};
return handleCallbackErrors(
() => fn(req, res),
err => {
captureException(err, scope => markEventUnhandled(scope));
},
);
},
);
});
};
}
export { wrapHttpFunction };
//# sourceMappingURL=http.js.map