From 6bd17d6cc5493f89055c05234a6190e6b1e93089 Mon Sep 17 00:00:00 2001 From: Peter Solnica Date: Thu, 27 Feb 2025 13:10:06 +0100 Subject: [PATCH 1/8] Add .devcontainer setup --- .devcontainer/Dockerfile | 50 ++++++++++++++++++++++++++++++++ .devcontainer/devcontainer.json | 16 ++++++++++ .devcontainer/docker-compose.yml | 22 ++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/docker-compose.yml diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000..76bc473bc --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,50 @@ +ARG IMAGE="bitnami/ruby" +ARG TAG="latest" + +FROM ${IMAGE}:${TAG} + +USER root +RUN apt-get update && apt-get install -y \ + gnupg \ + git \ + curl \ + wget \ + zsh \ + vim \ + build-essential \ + sudo \ + libssl-dev \ + libreadline-dev \ + zlib1g-dev \ + autoconf \ + bison \ + libyaml-dev \ + libncurses5-dev \ + libffi-dev \ + libgdbm-dev \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +RUN groupadd --gid 1000 sentry \ + && useradd --uid 1000 --gid sentry --shell /bin/zsh --create-home sentry + +# Add sentry to sudoers with NOPASSWD option +RUN echo "sentry ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/sentry \ + && chmod 0440 /etc/sudoers.d/sentry + +WORKDIR /workspace/sentry + +RUN chown -R sentry:sentry /workspace/sentry +RUN mkdir /workspace/gems && chown -R sentry:sentry /workspace/gems + +ARG TAG=latest + +ENV LANG=C.UTF-8 \ + BUNDLE_JOBS=4 \ + BUNDLE_RETRY=3 \ + GEM_HOME=/workspace/gems/${TAG} \ + REDIS_HOST=redis + +USER sentry + +CMD ["ruby", "--version"] diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..4e81ac81b --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,16 @@ +{ + "name": "sentry-ruby", + "dockerComposeFile": "docker-compose.yml", + "service": "app", + "workspaceFolder": "/workspace/sentry", + "customizations": { + "vscode": { + "extensions": [ + "sleistner.vscode-fileutils", + "Shopify.ruby-lsp" + ] + } + }, + "remoteUser": "sentry", + "postCreateCommand": "ruby --version" +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 000000000..484e24bc8 --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,22 @@ +services: + app: + build: + context: . + dockerfile: Dockerfile + args: + IMAGE: ${IMAGE} + TAG: ${TAG} + volumes: + - ..:/workspace/sentry:cached + command: sleep infinity + environment: + - REDIS_URL=${REDIS_URL:-redis://redis:6379/0} + depends_on: + - redis + + redis: + image: redis:latest + environment: + - ALLOW_EMPTY_PASSWORD=yes + ports: + - "6379:6379" From 082543605d8f413fe31dcec61815b521e119f2dd Mon Sep 17 00:00:00 2001 From: Peter Solnica Date: Thu, 27 Feb 2025 13:10:43 +0100 Subject: [PATCH 2/8] Update code workspace configuration --- sentry-ruby.code-workspace | 64 ++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/sentry-ruby.code-workspace b/sentry-ruby.code-workspace index 5c90507e0..0ec40e923 100644 --- a/sentry-ruby.code-workspace +++ b/sentry-ruby.code-workspace @@ -1,30 +1,40 @@ { - "folders": [ - { - "path": "sentry-ruby" - }, - { - "path": "sentry-rails" - }, - { - "path": "sentry-sidekiq" - }, - { - "path": "sentry-delayed_job" - }, - { - "path": "sentry-resque" - }, - { - "path": "sentry-opentelemetry" - }, - { - "path": ".github" - } - ], - "extensions": { - "recommendations": [ - "Shopify.ruby-lsp" - ] + "folders": [ + { + "path": ".devcontainer" + }, + { + "path": ".github", + }, + { + "path": ".", + "name": "root" + }, + { + "path": "sentry-ruby" + }, + { + "path": "sentry-rails" + }, + { + "path": "sentry-sidekiq" + }, + { + "path": "sentry-delayed_job" + }, + { + "path": "sentry-resque" + }, + { + "path": "sentry-opentelemetry" + }, + { + "path": "sentry-raven" } + ], + "extensions": { + "recommendations": [ + "Shopify.ruby-lsp" + ] + } } From 78907d1b7283093d95d1535d038bf9a30863a930 Mon Sep 17 00:00:00 2001 From: Peter Solnica Date: Thu, 27 Feb 2025 13:10:57 +0100 Subject: [PATCH 3/8] Use REDIS_HOST in specs --- .../spec/sentry/breadcrumb/redis_logger_spec.rb | 12 ++++++------ sentry-ruby/spec/sentry/redis_spec.rb | 4 ++-- sentry-ruby/spec/spec_helper.rb | 2 ++ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/sentry-ruby/spec/sentry/breadcrumb/redis_logger_spec.rb b/sentry-ruby/spec/sentry/breadcrumb/redis_logger_spec.rb index 4285a43f4..c6e863be3 100644 --- a/sentry-ruby/spec/sentry/breadcrumb/redis_logger_spec.rb +++ b/sentry-ruby/spec/sentry/breadcrumb/redis_logger_spec.rb @@ -3,7 +3,7 @@ require "spec_helper" RSpec.describe :redis_logger do - let(:redis) { Redis.new(host: "127.0.0.1") } + let(:redis) { Redis.new(host: REDIS_HOST) } before do perform_basic_setup do |config| @@ -20,7 +20,7 @@ expect(result).to eq("OK") expect(Sentry.get_current_scope.breadcrumbs.peek).to have_attributes( category: "db.redis", - data: { commands: [{ command: "SET", key: "key" }], server: "127.0.0.1:6379/0" } + data: { commands: [{ command: "SET", key: "key" }], server: "#{REDIS_HOST}:6379/0" } ) end end @@ -34,7 +34,7 @@ expect(result).to eq("OK") expect(Sentry.get_current_scope.breadcrumbs.peek).to have_attributes( category: "db.redis", - data: { commands: [{ command: "SET", key: "key", arguments: "value" }], server: "127.0.0.1:6379/0" } + data: { commands: [{ command: "SET", key: "key", arguments: "value" }], server: "#{REDIS_HOST}:6379/0" } ) end @@ -62,7 +62,7 @@ expect(result.key?("uptime_in_days")).to eq(true) expect(Sentry.get_current_scope.breadcrumbs.peek).to have_attributes( category: "db.redis", - data: { commands: [{ command: "INFO", key: nil }], server: "127.0.0.1:6379/0" } + data: { commands: [{ command: "INFO", key: nil }], server: "#{REDIS_HOST}:6379/0" } ) end end @@ -88,7 +88,7 @@ { command: "INCR", key: "counter" }, { command: "EXEC", key: nil } ], - server: "127.0.0.1:6379/0" + server: "#{REDIS_HOST}:6379/0" } ) end @@ -105,7 +105,7 @@ expect(result).to eq("OK") expect(Sentry.get_current_scope.breadcrumbs.peek).to have_attributes( category: "db.redis", - data: { commands: [{ command: "SET", key: "key" }], server: "127.0.0.1:6379/0" } + data: { commands: [{ command: "SET", key: "key" }], server: "#{REDIS_HOST}:6379/0" } ) end end diff --git a/sentry-ruby/spec/sentry/redis_spec.rb b/sentry-ruby/spec/sentry/redis_spec.rb index 50e86ac55..3130acff1 100644 --- a/sentry-ruby/spec/sentry/redis_spec.rb +++ b/sentry-ruby/spec/sentry/redis_spec.rb @@ -3,7 +3,7 @@ require "spec_helper" RSpec.describe Sentry::Redis do - let(:redis) { Redis.new(host: "127.0.0.1") } + let(:redis) { Redis.new(host: REDIS_HOST) } context "with tracing enabled" do before do @@ -33,7 +33,7 @@ expect(request_span.data).to eq({ "db.name" => 0, "db.system" => "redis", - "server.address" => "127.0.0.1", + "server.address" => REDIS_HOST, "server.port" => 6379 }) end diff --git a/sentry-ruby/spec/spec_helper.rb b/sentry-ruby/spec/spec_helper.rb index d830b6b0a..6fd9ee565 100644 --- a/sentry-ruby/spec/spec_helper.rb +++ b/sentry-ruby/spec/spec_helper.rb @@ -18,6 +18,8 @@ coverage_dir File.join(__FILE__, "../../coverage") end +REDIS_HOST = ENV.fetch("REDIS_HOST", "127.0.0.1") + if ENV["CI"] require 'simplecov-cobertura' SimpleCov.formatter = SimpleCov::Formatter::CoberturaFormatter From 8e976b0c520f01d873774dc33867ee7e092ed137 Mon Sep 17 00:00:00 2001 From: Peter Solnica Date: Fri, 28 Feb 2025 15:44:38 +0000 Subject: [PATCH 4/8] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4521171ca..eda1979e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Features - Add correct breadcrumb levels for 4xx/5xx response codes ([#2549](https://github.com/getsentry/sentry-ruby/pull/2549)) +- Update sentry-sidekiq to work correctly with Sidekiq 8.0 and its new timestamp format ([#2570](https://github.com/getsentry/sentry-ruby/pull/2570)) ### Bug Fixes From 468074a25064322b71b36f01648b85ea0b4a085c Mon Sep 17 00:00:00 2001 From: Peter Solnica Date: Fri, 28 Feb 2025 14:47:07 +0000 Subject: [PATCH 5/8] wip - define latency calc methods based on sidekiq ver --- .../sidekiq/sentry_context_middleware.rb | 13 +++++- .../spec/sentry/sidekiq/error_handler_spec.rb | 46 +++++++++++++------ sentry-sidekiq/spec/spec_helper.rb | 1 + 3 files changed, 45 insertions(+), 15 deletions(-) diff --git a/sentry-sidekiq/lib/sentry/sidekiq/sentry_context_middleware.rb b/sentry-sidekiq/lib/sentry/sidekiq/sentry_context_middleware.rb index 85c115e93..fd761b667 100644 --- a/sentry-sidekiq/lib/sentry/sidekiq/sentry_context_middleware.rb +++ b/sentry-sidekiq/lib/sentry/sidekiq/sentry_context_middleware.rb @@ -13,6 +13,16 @@ def set_span_data(span, id:, queue:, latency: nil, retry_count: nil) span.set_data(Span::DataConventions::MESSAGING_MESSAGE_RECEIVE_LATENCY, latency) if latency span.set_data(Span::DataConventions::MESSAGING_MESSAGE_RETRY_COUNT, retry_count) if retry_count end + + if ::Gem::Version.new(::Sidekiq::VERSION) >= ::Gem::Version.new("8.0.0.beta") + def calculate_latency(job) + (Time.now.to_f * 1000).to_i - job["enqueued_at"] if job["enqueued_at"] + end + else + def calculate_latency(job) + ((Time.now.to_f - job["enqueued_at"]) * 1000).to_i if job["enqueued_at"] + end + end end class SentryContextServerMiddleware @@ -40,7 +50,8 @@ def call(worker, job, queue) if transaction scope.set_span(transaction) - latency = ((Time.now.to_f - job["enqueued_at"]) * 1000).to_i if job["enqueued_at"] + latency = calculate_latency(job) + set_span_data( transaction, id: job["jid"], diff --git a/sentry-sidekiq/spec/sentry/sidekiq/error_handler_spec.rb b/sentry-sidekiq/spec/sentry/sidekiq/error_handler_spec.rb index 5e144bf9a..d1e835d6b 100644 --- a/sentry-sidekiq/spec/sentry/sidekiq/error_handler_spec.rb +++ b/sentry-sidekiq/spec/sentry/sidekiq/error_handler_spec.rb @@ -11,20 +11,38 @@ Sentry.get_current_client.transport end - let(:context) do - { - "args" => [true, true], - "class" => "HardWorker", - "created_at" => 1_474_922_824.910579, - "enqueued_at" => 1_474_922_824.910665, - "error_class" => "RuntimeError", - "error_message" => "a wild exception appeared", - "failed_at" => 1_474_922_825.158953, - "jid" => "701ed9cfa51c84a763d56bc4", - "queue" => "default", - "retry" => true, - "retry_count" => 0 - } + if WITH_SIDEKIQ_8 + let(:context) do + { + "args" => [true, true], + "class" => "HardWorker", + "created_at" => 1_474_922_824_910, + "enqueued_at" => 1_474_922_824_910, + "error_class" => "RuntimeError", + "error_message" => "a wild exception appeared", + "failed_at" => 1_474_922_825_158, + "jid" => "701ed9cfa51c84a763d56bc4", + "queue" => "default", + "retry" => true, + "retry_count" => 0 + } + end + else + let(:context) do + { + "args" => [true, true], + "class" => "HardWorker", + "created_at" => 1_474_922_824.910579, + "enqueued_at" => 1_474_922_824.910665, + "error_class" => "RuntimeError", + "error_message" => "a wild exception appeared", + "failed_at" => 1_474_922_825.158953, + "jid" => "701ed9cfa51c84a763d56bc4", + "queue" => "default", + "retry" => true, + "retry_count" => 0 + } + end end let(:processor) do diff --git a/sentry-sidekiq/spec/spec_helper.rb b/sentry-sidekiq/spec/spec_helper.rb index d2ce7ebfd..4cf60cb93 100644 --- a/sentry-sidekiq/spec/spec_helper.rb +++ b/sentry-sidekiq/spec/spec_helper.rb @@ -11,6 +11,7 @@ MIN_SIDEKIQ_6 = Gem::Version.new(Sidekiq::VERSION) >= Gem::Version.new("6.0") WITH_SIDEKIQ_7 = Gem::Version.new(Sidekiq::VERSION) >= Gem::Version.new("7.0") +WITH_SIDEKIQ_8 = Gem::Version.new(Sidekiq::VERSION) >= Gem::Version.new("8.0.0.beta") WITH_SIDEKIQ_6 = MIN_SIDEKIQ_6 && !WITH_SIDEKIQ_7 require "sidekiq/embedded" if WITH_SIDEKIQ_7 From a63e07a59f66b1d1a5efccf2a8824dc0e1dfeb93 Mon Sep 17 00:00:00 2001 From: Peter Solnica Date: Fri, 28 Feb 2025 14:47:30 +0000 Subject: [PATCH 6/8] tmp - diplay that we run specs against sidekiq 8 --- sentry-sidekiq/spec/spec_helper.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sentry-sidekiq/spec/spec_helper.rb b/sentry-sidekiq/spec/spec_helper.rb index 4cf60cb93..f1b88ec91 100644 --- a/sentry-sidekiq/spec/spec_helper.rb +++ b/sentry-sidekiq/spec/spec_helper.rb @@ -57,6 +57,14 @@ c.syntax = :expect end + config.before :suite do + if WITH_SIDEKIQ_8 + puts "*" * 100 + puts "Running with Sidekiq 8.0.0.beta" + puts "*" * 100 + end + end + config.before :each do # Make sure we reset the env in case something leaks in ENV.delete('SENTRY_DSN') From f46798b12ef49bd1f7728ece67021b49532a63d1 Mon Sep 17 00:00:00 2001 From: Peter Solnica Date: Fri, 28 Feb 2025 14:47:59 +0000 Subject: [PATCH 7/8] wip - set test msg depending on sidekiq ver --- sentry-sidekiq/spec/spec_helper.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sentry-sidekiq/spec/spec_helper.rb b/sentry-sidekiq/spec/spec_helper.rb index f1b88ec91..6e545a50a 100644 --- a/sentry-sidekiq/spec/spec_helper.rb +++ b/sentry-sidekiq/spec/spec_helper.rb @@ -259,7 +259,13 @@ def execute_worker(processor, klass, **options) jid = options.delete(:jid) || "123123" timecop_delay = options.delete(:timecop_delay) - msg = Sidekiq.dump_json(created_at: Time.now.to_f, enqueued_at: Time.now.to_f, jid: jid, class: klass, args: [], **options) + if WITH_SIDEKIQ_8 + current_time_ms = (Time.now.to_f * 1000).to_i + msg = Sidekiq.dump_json(created_at: current_time_ms, enqueued_at: current_time_ms, jid: jid, class: klass, args: [], **options) + else + msg = Sidekiq.dump_json(created_at: Time.now.to_f, enqueued_at: Time.now.to_f, jid: jid, class: klass, args: [], **options) + end + Timecop.freeze(timecop_delay) if timecop_delay work = Sidekiq::BasicFetch::UnitOfWork.new('queue:default', msg) process_work(processor, work) From 0138b2d892d0565ddcacd7701f9f903b289ee4ba Mon Sep 17 00:00:00 2001 From: Peter Solnica Date: Fri, 28 Feb 2025 15:42:57 +0000 Subject: [PATCH 8/8] fixup! wip - define latency calc methods based on sidekiq ver --- .../lib/sentry/sidekiq/sentry_context_middleware.rb | 6 +++++- .../spec/sentry/sidekiq/sentry_context_middleware_spec.rb | 7 ++++++- sentry-sidekiq/spec/spec_helper.rb | 6 +++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/sentry-sidekiq/lib/sentry/sidekiq/sentry_context_middleware.rb b/sentry-sidekiq/lib/sentry/sidekiq/sentry_context_middleware.rb index fd761b667..bf3978976 100644 --- a/sentry-sidekiq/lib/sentry/sidekiq/sentry_context_middleware.rb +++ b/sentry-sidekiq/lib/sentry/sidekiq/sentry_context_middleware.rb @@ -16,13 +16,17 @@ def set_span_data(span, id:, queue:, latency: nil, retry_count: nil) if ::Gem::Version.new(::Sidekiq::VERSION) >= ::Gem::Version.new("8.0.0.beta") def calculate_latency(job) - (Time.now.to_f * 1000).to_i - job["enqueued_at"] if job["enqueued_at"] + now_in_ms - job["enqueued_at"] if job["enqueued_at"] end else def calculate_latency(job) ((Time.now.to_f - job["enqueued_at"]) * 1000).to_i if job["enqueued_at"] end end + + def now_in_ms + ::Process.clock_gettime(::Process::CLOCK_REALTIME, :millisecond) + end end class SentryContextServerMiddleware diff --git a/sentry-sidekiq/spec/sentry/sidekiq/sentry_context_middleware_spec.rb b/sentry-sidekiq/spec/sentry/sidekiq/sentry_context_middleware_spec.rb index 45907adb4..13ce984e2 100644 --- a/sentry-sidekiq/spec/sentry/sidekiq/sentry_context_middleware_spec.rb +++ b/sentry-sidekiq/spec/sentry/sidekiq/sentry_context_middleware_spec.rb @@ -96,8 +96,13 @@ expect(transaction.spans.count).to eq(0) expect(transaction.contexts[:trace][:data]['messaging.message.id']).to eq('123456') # Explicitly set above. expect(transaction.contexts[:trace][:data]['messaging.destination.name']).to eq('default') - expect(transaction.contexts[:trace][:data]['messaging.message.receive.latency']).to eq(86400000) expect(transaction.contexts[:trace][:data]['messaging.message.retry.count']).to eq(0) + + if WITH_SIDEKIQ_8 + expect(transaction.contexts[:trace][:data]['messaging.message.receive.latency']).to eq(0) + else + expect(transaction.contexts[:trace][:data]['messaging.message.receive.latency']).to eq(86400000) + end end if MIN_SIDEKIQ_6 diff --git a/sentry-sidekiq/spec/spec_helper.rb b/sentry-sidekiq/spec/spec_helper.rb index 6e545a50a..0c45ee5d8 100644 --- a/sentry-sidekiq/spec/spec_helper.rb +++ b/sentry-sidekiq/spec/spec_helper.rb @@ -249,6 +249,10 @@ def sidekiq_config(opts) WITH_SIDEKIQ_7 ? ::Sidekiq::Config.new(opts) : SidekiqConfigMock.new(opts) end +def now_in_ms + ::Process.clock_gettime(::Process::CLOCK_REALTIME, :millisecond) +end + def execute_worker(processor, klass, **options) klass_options = klass.sidekiq_options_hash || {} # for Ruby < 2.6 @@ -260,7 +264,7 @@ def execute_worker(processor, klass, **options) timecop_delay = options.delete(:timecop_delay) if WITH_SIDEKIQ_8 - current_time_ms = (Time.now.to_f * 1000).to_i + current_time_ms = now_in_ms msg = Sidekiq.dump_json(created_at: current_time_ms, enqueued_at: current_time_ms, jid: jid, class: klass, args: [], **options) else msg = Sidekiq.dump_json(created_at: Time.now.to_f, enqueued_at: Time.now.to_f, jid: jid, class: klass, args: [], **options)