UNPKG

@sentry/node

Version:

Sentry Node SDK using OpenTelemetry for performance instrumentation

131 lines (128 loc) • 5.98 kB
import { MESSAGING_DESTINATION_NAME, MESSAGING_OPERATION_NAME, MESSAGING_MESSAGE_ID, MESSAGING_MESSAGE_RETRY_COUNT, MESSAGING_BATCH_MESSAGE_COUNT, MESSAGING_CLIENT_ID, MESSAGING_BULLMQ_JOB_STATE, MESSAGING_BULLMQ_FLOW_NAME, MESSAGING_BULLMQ_JOB_ATTEMPT_FINISHED_TIMESTAMP, MESSAGING_BULLMQ_JOB_BULK_NAMES, MESSAGING_BULLMQ_JOB_DEDUPLICATION_KEY, MESSAGING_BULLMQ_JOB_IDS, MESSAGING_BULLMQ_JOB_KEY, MESSAGING_BULLMQ_JOB_NAME, MESSAGING_BULLMQ_JOB_PROCESSED_TIMESTAMP, MESSAGING_BULLMQ_JOB_SCHEDULER_ID, MESSAGING_BULLMQ_JOB_TYPE, MESSAGING_BULLMQ_QUEUE_CLEAN_COUNT, MESSAGING_BULLMQ_QUEUE_CLEAN_LIMIT, MESSAGING_BULLMQ_QUEUE_DRAIN_DELAY, MESSAGING_BULLMQ_QUEUE_EVENT_MAX_LENGTH, MESSAGING_BULLMQ_QUEUE_GRACE, MESSAGING_BULLMQ_QUEUE_JOBS_STATE, MESSAGING_BULLMQ_QUEUE_RATE_LIMIT, MESSAGING_BULLMQ_WORKER_DO_NOT_WAIT_ACTIVE, MESSAGING_BULLMQ_WORKER_FORCE_CLOSE, MESSAGING_BULLMQ_WORKER_JOBS_TO_EXTEND_LOCKS, MESSAGING_BULLMQ_WORKER_NAME, MESSAGING_BULLMQ_WORKER_RATE_LIMIT, MESSAGING_BULLMQ_WORKER_STALLED_JOBS } from '@sentry/conventions/attributes'; import { SPAN_STATUS_ERROR, captureException } from '@sentry/core'; const DROPPED_ATTRIBUTES = /* @__PURE__ */ new Set([ // BullMQ sets these to `JSON.stringify()` output. They are left out until Sentry attributes support objects. "bullmq.job.options", "bullmq.job.progress", "bullmq.queue.options", "bullmq.worker.options", // BullMQ deprecated this for `bullmq.job.attempt_finished_timestamp`, which it sets on the same spans. "bullmq.job.finished.timestamp" ]); const MESSAGING_BULLMQ_KEYS = /* @__PURE__ */ new Map([ ["bullmq.flow.name", MESSAGING_BULLMQ_FLOW_NAME], ["bullmq.job.attempt_finished_timestamp", MESSAGING_BULLMQ_JOB_ATTEMPT_FINISHED_TIMESTAMP], ["bullmq.job.bulk.names", MESSAGING_BULLMQ_JOB_BULK_NAMES], ["bullmq.job.deduplication.key", MESSAGING_BULLMQ_JOB_DEDUPLICATION_KEY], ["bullmq.job.ids", MESSAGING_BULLMQ_JOB_IDS], ["bullmq.job.key", MESSAGING_BULLMQ_JOB_KEY], ["bullmq.job.name", MESSAGING_BULLMQ_JOB_NAME], ["bullmq.job.processed.timestamp", MESSAGING_BULLMQ_JOB_PROCESSED_TIMESTAMP], ["bullmq.job.scheduler.id", MESSAGING_BULLMQ_JOB_SCHEDULER_ID], ["bullmq.job.type", MESSAGING_BULLMQ_JOB_TYPE], ["bullmq.queue.clean.count", MESSAGING_BULLMQ_QUEUE_CLEAN_COUNT], ["bullmq.queue.clean.limit", MESSAGING_BULLMQ_QUEUE_CLEAN_LIMIT], ["bullmq.queue.drain.delay", MESSAGING_BULLMQ_QUEUE_DRAIN_DELAY], ["bullmq.queue.event.max.length", MESSAGING_BULLMQ_QUEUE_EVENT_MAX_LENGTH], ["bullmq.queue.grace", MESSAGING_BULLMQ_QUEUE_GRACE], ["bullmq.queue.jobs.state", MESSAGING_BULLMQ_QUEUE_JOBS_STATE], ["bullmq.queue.rate.limit", MESSAGING_BULLMQ_QUEUE_RATE_LIMIT], ["bullmq.worker.do.not.wait.active", MESSAGING_BULLMQ_WORKER_DO_NOT_WAIT_ACTIVE], ["bullmq.worker.force.close", MESSAGING_BULLMQ_WORKER_FORCE_CLOSE], ["bullmq.worker.jobs.to.extend.locks", MESSAGING_BULLMQ_WORKER_JOBS_TO_EXTEND_LOCKS], ["bullmq.worker.name", MESSAGING_BULLMQ_WORKER_NAME], ["bullmq.worker.rate.limit", MESSAGING_BULLMQ_WORKER_RATE_LIMIT], ["bullmq.worker.stalled.jobs", MESSAGING_BULLMQ_WORKER_STALLED_JOBS] ]); function toMessagingKey(key) { return MESSAGING_BULLMQ_KEYS.get(key) ?? (key.startsWith("bullmq.") ? `messaging.${key}` : key); } function toSentryAttributes(attributes) { const { "bullmq.queue.name": queueName, "bullmq.queue.operation": operation, "bullmq.job.id": jobId, "bullmq.job.attempts.made": attemptsMade, "bullmq.job.bulk.count": bulkCount, "bullmq.worker.id": workerId, "bullmq.job.state": jobState, "bullmq.job.status": jobStatus, ...rest } = attributes; const result = {}; for (const [key, value] of Object.entries(rest)) { if (!DROPPED_ATTRIBUTES.has(key)) { result[toMessagingKey(key)] = value; } } if (typeof queueName === "string") { result[MESSAGING_DESTINATION_NAME] = queueName; } if (typeof operation === "string") { result[MESSAGING_OPERATION_NAME] = operation; } if (jobId !== void 0) { result[MESSAGING_MESSAGE_ID] = String(jobId); } if (typeof attemptsMade === "number") { result[MESSAGING_MESSAGE_RETRY_COUNT] = Math.max(attemptsMade - 1, 0); } if (typeof bulkCount === "number") { result[MESSAGING_BATCH_MESSAGE_COUNT] = bulkCount; } if (typeof workerId === "string") { result[MESSAGING_CLIENT_ID] = workerId; } const state = jobState ?? jobStatus; if (typeof state === "string") { result[MESSAGING_BULLMQ_JOB_STATE] = state; } return result; } class SentryBullMQSpan { constructor(span, scope) { this._span = span; this._scope = scope; } setAttribute(key, value) { this.setAttributes({ [key]: value }); } setAttributes(attributes) { this._span.setAttributes(toSentryAttributes(attributes)); } addEvent(name, attributes) { this._span.addEvent(name, attributes); if (name === "job failed") { this._span.setStatus({ code: SPAN_STATUS_ERROR, message: "internal_error" }); const reason = attributes?.["bullmq.job.failed.reason"]; const error = new Error(String(reason || "Unknown error")); error.stack = void 0; captureException(error, { mechanism: { handled: false, type: "auto.queue.bullmq" } }); } } /** * Does not capture to Sentry. BullMQ calls this for Redis, lock and serialization errors that it * retries itself, often several times for one fault. Job failures are captured in `addEvent`. */ recordException(exception) { const error = exception instanceof Error ? exception : new Error(typeof exception === "string" ? exception : exception.message || "Unknown error"); this._span.recordException(error); } setSpanOnContext(context) { return { ...context, span: this._span, scope: this._scope }; } end() { this._span.end(); } } export { SentryBullMQSpan, toMessagingKey, toSentryAttributes }; //# sourceMappingURL=span.js.map