Skip to content

Commit

Permalink
Add ActiveSupport for cache module
Browse files Browse the repository at this point in the history
  • Loading branch information
frederikspang committed Aug 20, 2024
1 parent 6181b7b commit 10be4dd
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 0 deletions.
2 changes: 2 additions & 0 deletions sentry-rails/lib/sentry/rails/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
require "sentry/rails/tracing/action_view_subscriber"
require "sentry/rails/tracing/active_record_subscriber"
require "sentry/rails/tracing/active_storage_subscriber"
require "sentry/rails/tracing/active_support_subscriber"

module Sentry
class Configuration
Expand Down Expand Up @@ -162,6 +163,7 @@ def initialize
end
@tracing_subscribers = Set.new([
Sentry::Rails::Tracing::ActionViewSubscriber,
Sentry::Rails::Tracing::ActiveSupportSubscriber,
Sentry::Rails::Tracing::ActiveRecordSubscriber,
Sentry::Rails::Tracing::ActiveStorageSubscriber
])
Expand Down
63 changes: 63 additions & 0 deletions sentry-rails/lib/sentry/rails/tracing/active_support_subscriber.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
require "sentry/rails/tracing/abstract_subscriber"

module Sentry
module Rails
module Tracing
class ActiveSupportSubscriber < AbstractSubscriber
READ_EVENT_NAMES = %w[
cache_read.active_support
].freeze

WRITE_EVENT_NAMES = %w[
cache_write.active_support
cache_increment.active_support
cache_decrement.active_support
].freeze

REMOVE_EVENT_NAMES = %w[
cache_delete.active_support
].freeze

FLUSH_EVENT_NAMES = %w[
cache_prune.active_support
].freeze

EVENT_NAMES = READ_EVENT_NAMES + WRITE_EVENT_NAMES + REMOVE_EVENT_NAMES + FLUSH_EVENT_NAMES

SPAN_ORIGIN = "auto.cache.rails".freeze

def self.subscribe!
subscribe_to_event(EVENT_NAMES) do |event_name, duration, payload|
record_on_current_span(
op: operation_name(event_name),
origin: SPAN_ORIGIN,
start_timestamp: payload[START_TIMESTAMP_NAME],
description: payload[:service],
duration: duration
) do |span|
span.set_data('cache.key', [*payload[:key]])
span.set_data('cache.hit', payload[:hit] == true) # Handle nil case
end
end
end

private

def self.operation_name(event_name)
case
when READ_EVENT_NAMES.include?(event_name)
"cache.get"
when WRITE_EVENT_NAMES.include?(event_name)
"cache.put"
when REMOVE_EVENT_NAMES.include?(event_name)
"cache.remove"
when FLUSH_EVENT_NAMES.include?(event_name)
"cache.flush"
else
"other"
end
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
require "spec_helper"

RSpec.describe Sentry::Rails::Tracing::ActiveSupportSubscriber, :subscriber, type: :request do
let(:transport) do
Sentry.get_current_client.transport
end

context "when transaction is sampled" do
before do
make_basic_app do |config, app|
config.traces_sample_rate = 1.0
config.rails.tracing_subscribers = [described_class]

app.config.action_controller.perform_caching = true
app.config.cache_store = :memory_store
end
end

it "tracks cache write" do
transaction = Sentry::Transaction.new(sampled: true, hub: Sentry.get_current_hub)
Sentry.get_current_scope.set_span(transaction)

Rails.cache.write("my_cache_key", "my_cache_value")
transaction.finish

expect(transport.events.count).to eq(1)
read_cache_transaction = transport.events.first.to_hash
expect(read_cache_transaction[:type]).to eq("transaction")

expect(read_cache_transaction[:spans].count).to eq(1)
expect(read_cache_transaction[:spans][0][:op]).to eq("cache.put")
expect(read_cache_transaction[:spans][0][:origin]).to eq("auto.cache.rails")
end

it "tracks cache increment" do
Rails.cache.write("my_cache_key", 0)

transaction = Sentry::Transaction.new(sampled: true, hub: Sentry.get_current_hub)
Sentry.get_current_scope.set_span(transaction)
Rails.cache.increment("my_cache_key")

transaction.finish

expect(transport.events.count).to eq(1)
read_cache_transaction = transport.events.first.to_hash
expect(read_cache_transaction[:type]).to eq("transaction")
expect(read_cache_transaction[:spans].count).to eq(2)
expect(read_cache_transaction[:spans][1][:op]).to eq("cache.put")
expect(read_cache_transaction[:spans][1][:origin]).to eq("auto.cache.rails")
end

it "tracks cache decrement" do
Rails.cache.write("my_cache_key", 0)

transaction = Sentry::Transaction.new(sampled: true, hub: Sentry.get_current_hub)
Sentry.get_current_scope.set_span(transaction)
Rails.cache.decrement("my_cache_key")

transaction.finish

expect(transport.events.count).to eq(1)
read_cache_transaction = transport.events.first.to_hash
expect(read_cache_transaction[:type]).to eq("transaction")
expect(read_cache_transaction[:spans].count).to eq(2)
expect(read_cache_transaction[:spans][1][:op]).to eq("cache.put")
expect(read_cache_transaction[:spans][1][:origin]).to eq("auto.cache.rails")
end

it "tracks cache read" do
transaction = Sentry::Transaction.new(sampled: true, hub: Sentry.get_current_hub)
Sentry.get_current_scope.set_span(transaction)
Rails.cache.read("my_cache_key")

transaction.finish

expect(transport.events.count).to eq(1)
read_cache_transaction = transport.events.first.to_hash
expect(read_cache_transaction[:type]).to eq("transaction")
expect(read_cache_transaction[:spans].count).to eq(1)
expect(read_cache_transaction[:spans][0][:op]).to eq("cache.get")
expect(read_cache_transaction[:spans][0][:origin]).to eq("auto.cache.rails")
end

it "tracks sets cache hit" do
Rails.cache.write("my_cache_key", "my_cache_value")
transaction = Sentry::Transaction.new(sampled: true, hub: Sentry.get_current_hub)
Sentry.get_current_scope.set_span(transaction)
Rails.cache.read("my_cache_key")
Rails.cache.read("my_cache_key_non_existing")

transaction.finish

expect(transport.events.count).to eq(1)
read_cache_transaction = transport.events.first.to_hash
expect(read_cache_transaction[:type]).to eq("transaction")
expect(read_cache_transaction[:spans].count).to eq(2)
expect(read_cache_transaction[:spans][0][:op]).to eq("cache.get")
expect(read_cache_transaction[:spans][0][:origin]).to eq("auto.cache.rails")
expect(read_cache_transaction[:spans][0][:data]['cache.hit']).to eq(true)

expect(read_cache_transaction[:spans][1][:op]).to eq("cache.get")
expect(read_cache_transaction[:spans][1][:origin]).to eq("auto.cache.rails")
expect(read_cache_transaction[:spans][1][:data]['cache.hit']).to eq(false)
end
end

context "when transaction is not sampled" do
before do
make_basic_app
end

it "doesn't record spans" do
Rails.cache.write("my_cache_key", "my_cache_value")

expect(transport.events.count).to eq(0)
end
end
end

0 comments on commit 10be4dd

Please sign in to comment.