-
Notifications
You must be signed in to change notification settings - Fork 2
Add sentry middleware #89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ff83f00
084e0a1
4bf972d
ca09ee0
35867d6
9345072
ff672d1
393a1fc
3138f40
803af4d
2c66a27
c8ec62a
0bafb08
9e25652
531b8ca
e725c0d
f89edb5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| require_relative 'integration' | ||
|
|
||
| # Auto configure eventboss to use sentry | ||
|
|
||
| Eventboss.configure do |config| | ||
| config.server_middleware.add Eventboss::Sentry::ServerMiddleware | ||
| config.error_handlers << Eventboss::Sentry::ErrorHandler.new | ||
| end | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| module Eventboss | ||
| module Sentry | ||
| class Context | ||
| # since sentry has env selector, we can remove it from queue names | ||
| QUEUES_WITHOUT_ENV = Hash.new do |hash, key| | ||
| hash[key] = key | ||
| .gsub(/-#{Eventboss.env}-deadletter$/, '-ENV-deadletter') | ||
| .gsub(/-#{Eventboss.env}$/, '-ENV') | ||
| end | ||
|
|
||
| def self.queue_name_for_sentry(queue_name) | ||
| QUEUES_WITHOUT_ENV[queue_name] | ||
| end | ||
|
|
||
| # Constructs SNS message attributes for Sentry trace propagation. | ||
| def self.build_sns_message_attributes | ||
| attributes = ::Sentry.get_trace_propagation_headers | ||
| .slice('sentry-trace', 'baggage') | ||
| .transform_values do |header_value| | ||
| { string_value: header_value, data_type: 'String' } | ||
| end | ||
|
|
||
| user = ::Sentry.get_current_scope&.user | ||
| if user && !user.empty? | ||
| attributes['sentry_user'] = { | ||
| string_value: user.to_json, | ||
| data_type: 'String' | ||
| } | ||
| end | ||
|
|
||
| attributes | ||
| end | ||
| end | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,15 @@ | ||||||||||||||||||||||||
| module Eventboss | ||||||||||||||||||||||||
| module Sentry | ||||||||||||||||||||||||
| class ErrorHandler | ||||||||||||||||||||||||
| def call(exception, _context = {}) | ||||||||||||||||||||||||
| return unless ::Sentry.initialized? | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Eventboss::Sentry::Integration.capture_exception( | ||||||||||||||||||||||||
| exception, | ||||||||||||||||||||||||
| contexts: { eventboss: { } }, | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
| contexts: { eventboss: { } }, | |
| contexts: { eventboss: _context }, |
Copilot
AI
Aug 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The empty hash in the eventboss context provides no useful information. Consider adding relevant context data like queue name, message ID, or listener information to help with debugging.
| contexts: { eventboss: { } }, | |
| eventboss_context = {} | |
| eventboss_context[:queue_name] = _context[:queue_name] if _context.key?(:queue_name) | |
| eventboss_context[:message_id] = _context[:message_id] if _context.key?(:message_id) | |
| eventboss_context[:listener] = _context[:listener] if _context.key?(:listener) | |
| Eventboss::Sentry::Integration.capture_exception( | |
| exception, | |
| contexts: { eventboss: eventboss_context }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already have that in the middleware
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| require 'sentry-ruby' | ||
| require 'sentry/integrable' | ||
| require_relative 'error_handler' | ||
| require_relative 'context' | ||
| require_relative 'server_middleware' | ||
|
|
||
| module Eventboss | ||
| module Sentry | ||
| class Integration | ||
| extend ::Sentry::Integrable | ||
|
|
||
| register_integration name: "eventboss", version: Eventboss::VERSION | ||
| end | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,95 @@ | ||||||||||||||
| module Eventboss | ||||||||||||||
| module Sentry | ||||||||||||||
| class ServerMiddleware < Eventboss::Middleware::Base | ||||||||||||||
| OP_NAME = 'queue.process' | ||||||||||||||
| SPAN_ORIGIN = 'auto.queue.eventboss' | ||||||||||||||
|
|
||||||||||||||
| def call(work) | ||||||||||||||
| return yield unless ::Sentry.initialized? | ||||||||||||||
|
|
||||||||||||||
| ::Sentry.clone_hub_to_current_thread | ||||||||||||||
| scope = ::Sentry.get_current_scope | ||||||||||||||
| scope.clear | ||||||||||||||
| if (user = extract_sentry_user(work)) | ||||||||||||||
| scope.set_user(user) | ||||||||||||||
| end | ||||||||||||||
| scope.set_tags(queue: extract_queue_name(work), message_id: work.message.message_id) | ||||||||||||||
| scope.set_transaction_name(extract_transaction_name(work), source: :task) | ||||||||||||||
| transaction = start_transaction(scope, work) | ||||||||||||||
|
|
||||||||||||||
| if transaction | ||||||||||||||
| scope.set_span(transaction) | ||||||||||||||
| transaction.set_data(::Sentry::Span::DataConventions::MESSAGING_MESSAGE_ID, work.message.message_id) | ||||||||||||||
| transaction.set_data(::Sentry::Span::DataConventions::MESSAGING_DESTINATION_NAME, extract_queue_name(work)) | ||||||||||||||
|
|
||||||||||||||
| if (latency = extract_latency(work.message)) | ||||||||||||||
| transaction.set_data(::Sentry::Span::DataConventions::MESSAGING_MESSAGE_RECEIVE_LATENCY, latency) | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| if (retry_count = extract_receive_count(work.message)) | ||||||||||||||
| transaction.set_data(::Sentry::Span::DataConventions::MESSAGING_MESSAGE_RETRY_COUNT, retry_count) | ||||||||||||||
| end | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| begin | ||||||||||||||
| yield | ||||||||||||||
| rescue StandardError | ||||||||||||||
| finish_transaction(transaction, 500) | ||||||||||||||
| raise | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| finish_transaction(transaction, 200) | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| def start_transaction(scope, work) | ||||||||||||||
| options = { | ||||||||||||||
| name: scope.transaction_name, | ||||||||||||||
| source: scope.transaction_source, | ||||||||||||||
| op: OP_NAME, | ||||||||||||||
| origin: SPAN_ORIGIN | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| env = { | ||||||||||||||
| 'sentry-trace' => work.message.message_attributes['sentry-trace']&.string_value, | ||||||||||||||
| 'baggage' => work.message.message_attributes['baggage']&.string_value | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| transaction = ::Sentry.continue_trace(env, **options) | ||||||||||||||
| ::Sentry.start_transaction(transaction: transaction, **options) | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| def finish_transaction(transaction, status) | ||||||||||||||
| return unless transaction | ||||||||||||||
|
|
||||||||||||||
| transaction.set_http_status(status) | ||||||||||||||
| transaction.finish | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| def extract_sentry_user(work) | ||||||||||||||
| if (value = work.message.message_attributes["sentry_user"]&.string_value) | ||||||||||||||
| JSON.parse(value) | ||||||||||||||
|
||||||||||||||
| JSON.parse(value) | |
| begin | |
| JSON.parse(value) | |
| rescue JSON::ParserError | |
| nil | |
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,3 @@ | ||
| module Eventboss | ||
| VERSION = "1.9.5" | ||
| VERSION = "1.9.6" | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We would need to implement client middleware to support it cleanly. But that was never needed and never requested. That can be refactored then to support the current and old needs. Right now i don't want to crystal ball a future use-case