From e7db68587e085a67865d4111c60646c2b973cdab Mon Sep 17 00:00:00 2001 From: tokebe <43009413+tokebe@users.noreply.github.com> Date: Fri, 10 Nov 2023 10:04:36 -0500 Subject: [PATCH] fix: Sentry config --- src/config/index.js | 51 ++++++++------- src/controllers/async/asyncquery.js | 4 +- src/controllers/threading/taskHandler.js | 80 ++++++++++++++++-------- src/middlewares/error.js | 53 +++++++++------- 4 files changed, 111 insertions(+), 77 deletions(-) diff --git a/src/config/index.js b/src/config/index.js index 1d37cc67..ba2b7ad6 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -80,7 +80,7 @@ module.exports = class Config { windowMs: 1 * 60 * 1000, //1min max: process.env.MAX_QUERIES_PER_MIN || 6000, }); - this.app.use("/", fastLimiter) + this.app.use("/", fastLimiter); this.app.use("/v1/query", slowLimiter); this.app.use("/v1/team/:team_name/query", medLimiter); this.app.use("/v1/team/:smartapiID/query", medLimiter); @@ -90,33 +90,36 @@ module.exports = class Config { this.app.use("/v1/asyncquery", fastLimiter); this.app.use("/v1/team/:teamName/asyncquery", fastLimiter); this.app.use("/v1/smartapi/:smartapiID/asyncquery", fastLimiter); - this.app.use("/queues", fastLimiter) + this.app.use("/queues", fastLimiter); } setSentry() { // use SENTRY_DSN environment variable - Sentry.init({ - // dsn: "https://5297933ef0f6487c9fd66532bb1fcefe@o4505444772806656.ingest.sentry.io/4505449737420800", - integrations: [ - // enable HTTP calls tracing - new Sentry.Integrations.Http({ tracing: true }), - // enable Express.js middleware tracing - new Sentry.Integrations.Express({ app: this.app }), - // Automatically instrument Node.js libraries and frameworks - ...Sentry.autoDiscoverNodePerformanceMonitoringIntegrations(), - ], + try { + Sentry.init({ + integrations: [ + // enable HTTP calls tracing + new Sentry.Integrations.Http({ tracing: true }), + // enable Express.js middleware tracing + new Sentry.Integrations.Express({ app: this.app }), + // Automatically instrument Node.js libraries and frameworks + ...Sentry.autoDiscoverNodePerformanceMonitoringIntegrations(), + ], - // Set tracesSampleRate to 1.0 to capture 100% - // of transactions for performance monitoring. - // We recommend adjusting this value in production - tracesSampleRate: process.env.EXPRESS_SAMPLE_RATE ? parseFloat(process.env.EXPRESS_SAMPLE_RATE) : 1.0, - environment: process.env.INSTANCE_ENV, - }); - - // RequestHandler creates a separate execution context, so that all - // transactions/spans/breadcrumbs are isolated across requests - this.app.use(Sentry.Handlers.requestHandler({ user: false })); - // TracingHandler creates a trace for every incoming request - this.app.use(Sentry.Handlers.tracingHandler()); + // Set tracesSampleRate to 1.0 to capture 100% + // of transactions for performance monitoring. + // We recommend adjusting this value in production + tracesSampleRate: process.env.EXPRESS_SAMPLE_RATE ? parseFloat(process.env.EXPRESS_SAMPLE_RATE) : 1.0, + environment: process.env.INSTANCE_ENV, + }); + // RequestHandler creates a separate execution context, so that all + // transactions/spans/breadcrumbs are isolated across requests + this.app.use(Sentry.Handlers.requestHandler({ user: false })); + // TracingHandler creates a trace for every incoming request + this.app.use(Sentry.Handlers.tracingHandler()); + } catch (error) { + debug("Sentry init error. This does not affect execution."); + debug(error); + } } }; diff --git a/src/controllers/async/asyncquery.js b/src/controllers/async/asyncquery.js index 4d471fb0..850ca344 100644 --- a/src/controllers/async/asyncquery.js +++ b/src/controllers/async/asyncquery.js @@ -8,7 +8,7 @@ const { Readable } = require("stream"); const chunker = require("stream-chunker"); const { parser } = require("stream-json"); const Assembler = require("stream-json/Assembler"); -const Sentry = require("@sentry/node"); +const { Telemetry } = require("@biothings-explorer/utils"); const ErrorHandler = require("../../middlewares/error.js"); exports.asyncquery = async (req, res, next, queueData, queryQueue) => { @@ -157,7 +157,7 @@ exports.asyncqueryResponse = async (handler, callback_url, jobID = null, jobURL console.error(e); if (ErrorHandler.shouldHandleError(e)) { - Sentry.captureException(e); + Telemetry.captureException(e); } //shape error > will be handled below diff --git a/src/controllers/threading/taskHandler.js b/src/controllers/threading/taskHandler.js index 4c54a6ec..d27348aa 100644 --- a/src/controllers/threading/taskHandler.js +++ b/src/controllers/threading/taskHandler.js @@ -13,26 +13,32 @@ const Sentry = require("@sentry/node"); const { ProfilingIntegration } = require("@sentry/profiling-node"); // use SENTRY_DSN environment variable -Sentry.init({ - // dsn: "https://5297933ef0f6487c9fd66532bb1fcefe@o4505444772806656.ingest.sentry.io/4505449737420800", - integrations: [ - // Automatically instrument Node.js libraries and frameworks - ...Sentry.autoDiscoverNodePerformanceMonitoringIntegrations(), - new ProfilingIntegration(), - ], - environment: process.env.INSTANCE_ENV, - debug: true, - normalizeDepth: 6, - maxBreadcrumbs: 500, - // Set tracesSampleRate to 1.0 to capture 100% - // of transactions for performance monitoring. - // We recommend adjusting this value in production - tracesSampleRate: process.env.THREAD_SAMPLE_RATE ? parseFloat(process.env.THREAD_SAMPLE_RATE) : 1.0, - profilesSampleRate: process.env.THREAD_PROFILE_RATE ? parseFloat(process.env.THREAD_PROFILE_RATE) : 1.0, // Profiling sample rate is relative to tracesSampleRate, - _experiments: { - maxProfileDurationMs: 6 * 60 * 1000, // max profiling duration of 6 minutes (technically "beta" feature) - }, -}); +try { + Sentry.init({ + integrations: [ + // Automatically instrument Node.js libraries and frameworks + ...Sentry.autoDiscoverNodePerformanceMonitoringIntegrations(), + new ProfilingIntegration(), + // enable HTTP calls tracing + new Sentry.Integrations.Http({ tracing: true }), + ], + environment: process.env.INSTANCE_ENV, + debug: true, + normalizeDepth: 6, + maxBreadcrumbs: 500, + // Set tracesSampleRate to 1.0 to capture 100% + // of transactions for performance monitoring. + // We recommend adjusting this value in production + tracesSampleRate: process.env.THREAD_SAMPLE_RATE ? parseFloat(process.env.THREAD_SAMPLE_RATE) : 1.0, + profilesSampleRate: process.env.THREAD_PROFILE_RATE ? parseFloat(process.env.THREAD_PROFILE_RATE) : 1.0, // Profiling sample rate is relative to tracesSampleRate, + _experiments: { + maxProfileDurationMs: 6 * 60 * 1000, // max profiling duration of 6 minutes (technically "beta" feature) + }, + }); +} catch (error) { + debug("Sentry init error. This does not affect execution."); + debug(error); +} const runTask = async ({ req, route, port, job: { jobId, queueName } = {} }) => { debug(`Worker thread ${threadId} beginning ${workerData.queue} task.`); @@ -52,17 +58,37 @@ const runTask = async ({ req, route, port, job: { jobId, queueName } = {} }) => global.job = await queue.getJob(jobId); } - const transaction = Sentry.startTransaction({ name: route }); - transaction.setData("request", req.data.queryGraph); - Sentry.getCurrentHub().configureScope(scope => { - scope.clearBreadcrumbs(); - scope.setSpan(transaction); - }); + let transaction; + try { + const routeNames = { + query_v1: "EXEC /v1/query", + query_v1_by_api: "EXEC /v1/smartapi/:/query", + query_v1_by_team: "EXEC /v1/team/:/query", + asyncquery_status: "EXEC /v1/asyncquery_status", + asyncquery_v1: "EXEC /v1/asyncquery", + asyncquery_v1_by_api: "EXEC /v1/smartapi/:/asyncquery", + asyncquery_v1_by_team: "EXEC /v1/team/:/asyncquery", + }; + transaction = Sentry.startTransaction({ name: routeNames[route] }); + transaction.setData("request", req.data.queryGraph); + Sentry.getCurrentHub().configureScope(scope => { + scope.clearBreadcrumbs(); + scope.setSpan(transaction); + }); + } catch (error) { + debug("Sentry transaction start error. This does not affect execution."); + debug(error); + } const completedTask = await tasks[route](req); await Promise.all(global.cachingTasks); - transaction.finish(); + try { + transaction.finish(); + } catch (error) { + debug("Sentry transaction finish error. This does not affect execution."); + debug(error); + } debug(`Worker thread ${threadId} completed ${workerData.queue} task.`); diff --git a/src/middlewares/error.js b/src/middlewares/error.js index 15fa2536..66b67d6b 100644 --- a/src/middlewares/error.js +++ b/src/middlewares/error.js @@ -27,30 +27,35 @@ class ErrorHandler { setRoutes(app) { // first pass through sentry - app.use( - Sentry.Handlers.errorHandler({ - shouldHandleError(error) { - // Do not capture non-server errors - if (error.status && error.status < 500) { - return false; - } - if (error instanceof swaggerValidation.InputValidationError || error.name === "InputValidationError") { - return false; - } - if ( - error instanceof QueryGraphHandler.InvalidQueryGraphError || - error.stack.includes("InvalidQueryGraphError") || - error.name === "InvalidQueryGraphError" - ) { - return false; - } - if (error.name === "QueryAborted") { - return false; - } - return true; - }, - }), - ); + try { + app.use( + Sentry.Handlers.errorHandler({ + shouldHandleError(error) { + // Do not capture non-server errors + if (error.status && error.status < 500) { + return false; + } + if (error instanceof swaggerValidation.InputValidationError || error.name === "InputValidationError") { + return false; + } + if ( + error instanceof QueryGraphHandler.InvalidQueryGraphError || + error.stack.includes("InvalidQueryGraphError") || + error.name === "InvalidQueryGraphError" + ) { + return false; + } + if (error.name === "QueryAborted") { + return false; + } + return true; + }, + }), + ); + } catch (error) { + debug("Sentry express config error. This does not affect execution."); + debug(error); + } app.use((error, req, res, next) => { const json = {