Module: Datadog::Tracing::Contrib::ActiveSupport::Cache::Events::Cache

Includes:
Datadog::Tracing::Contrib::ActiveSupport::Cache::Event
Defined in:
lib/datadog/tracing/contrib/active_support/cache/events/cache.rb

Overview

Defines instrumentation for instantiation.active_record event

Constant Summary collapse

MAPPING =

DEV: Look for other uses of ActiveSupport::Cache::Store#instrument, to find other useful event keys.

{
  'cache_delete.active_support' => {resource: Ext::RESOURCE_CACHE_DELETE},
  'cache_read.active_support' => {resource: Ext::RESOURCE_CACHE_GET},
  'cache_read_multi.active_support' => {resource: Ext::RESOURCE_CACHE_MGET, multi_key: true},
  'cache_write.active_support' => {resource: Ext::RESOURCE_CACHE_SET},
  'cache_write_multi.active_support' => {resource: Ext::RESOURCE_CACHE_MSET, multi_key: true}
}.freeze

Class Method Summary collapse

Methods included from Datadog::Tracing::Contrib::ActiveSupport::Cache::Event

included

Class Method Details

.cache_backend(store) ⇒ Object

The name of the store is never saved by Rails. ActiveSupport looks up stores by converting a symbol into a 'require' path, then "camelizing" it for a const_get call:

require "active_support/cache/#{store}"
ActiveSupport::Cache.const_get(store.to_s.camelize)

We can reverse engineer the original symbol by converting the class name to snake case: e.g. ActiveSupport::Cache::RedisStore -> active_support/cache/redis_store In this case, redis_store is the store name.

Because there's no API retrieve only the class name (only RedisStore, and not ActiveSupport::Cache::RedisStore) the easiest way to retrieve the store symbol is to convert the fully qualified name using the Rails-provided method #underscore, which is the reverse of #camelize, then extracting the last part of it.

Also, this method caches the store name, given this value will be retrieve multiple times and involves string manipulation.



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/datadog/tracing/contrib/active_support/cache/events/cache.rb', line 136

def cache_backend(store)
  # Cache the backend name to avoid the expensive string manipulation required to calculate it.
  # DEV: We can't store it directly in the `store` object because it is a frozen String.
  if (name = @cache_backend[store])
    return name
  end

  # DEV: #underscore is available through ActiveSupport, and is
  # DEV: the exact reverse operation to `#camelize`.
  # DEV: #demodulize is available through ActiveSupport, and is
  # DEV: used to remove the module ('*::') part of a constant name.
  name = ::ActiveSupport::Inflector.demodulize(store)
  name = ::ActiveSupport::Inflector.underscore(name)

  # Despite a given application only ever having 1-3 store types,
  # we limit the size of the `@cache_backend` just in case, because
  # the user can create custom Cache store classes themselves.
  @cache_backend[store] = name if @cache_backend.size < 50

  name
end

.event_nameObject



25
26
27
# File 'lib/datadog/tracing/contrib/active_support/cache/events/cache.rb', line 25

def event_name
  /\Acache_(?:delete|read|read_multi|write|write_multi)\.active_support\z/
end

.on_start(span, event, _id, payload) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/datadog/tracing/contrib/active_support/cache/events/cache.rb', line 70

def on_start(span, event, _id, payload)
  # Since Rails 8, `dd_original_keys` contains the denormalized key provided by the user.
  # In previous versions, the denormalized key is stored in the official `key` attribute.
  # We fall back to `key`, even in Rails 8, as a defensive measure.
  key = payload[:dd_original_keys] || payload[:key]
  store = payload[:store]

  mapping = MAPPING.fetch(event)

  span.service = configuration[:cache_service]
  span.resource = mapping[:resource]

  span.set_tag(Tracing::::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
  span.set_tag(Tracing::::Ext::TAG_OPERATION, Ext::TAG_OPERATION_CACHE)

  if span.service != Datadog.configuration.service
    span.set_tag(Tracing::Contrib::Ext::::TAG_BASE_SERVICE, Datadog.configuration.service)
  end

  span.set_tag(Ext::TAG_CACHE_BACKEND, cache_backend(store))

  span.set_tag('EVENT', event)

  if Datadog.configuration.tracing[:active_support][:cache_key].enabled
    set_cache_key(span, key, mapping[:multi_key])
  end
rescue => e
  Datadog.logger.error(e.message)
  Datadog::Core::Telemetry::Logger.report(e)
end

.set_cache_key(span, key, multi_key) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/datadog/tracing/contrib/active_support/cache/events/cache.rb', line 101

def set_cache_key(span, key, multi_key)
  if multi_key
    keys = key.is_a?(Hash) ? key.keys : key # `write`s use Hashes, while `read`s use Arrays
    resolved_key = keys.map { |k| ::ActiveSupport::Cache.expand_cache_key(k) }
    cache_key = Core::Utils.truncate(resolved_key, Ext::QUANTIZE_CACHE_MAX_KEY_SIZE)
    span.set_tag(Ext::TAG_CACHE_KEY_MULTI, cache_key)
  else
    resolved_key = ::ActiveSupport::Cache.expand_cache_key(key)
    cache_key = Core::Utils.truncate(resolved_key, Ext::QUANTIZE_CACHE_MAX_KEY_SIZE)
    span.set_tag(Ext::TAG_CACHE_KEY, cache_key)
  end
end

.span_nameObject



29
30
31
# File 'lib/datadog/tracing/contrib/active_support/cache/events/cache.rb', line 29

def span_name
  Ext::SPAN_CACHE
end

.span_optionsObject



33
34
35
36
37
# File 'lib/datadog/tracing/contrib/active_support/cache/events/cache.rb', line 33

def span_options
  {
    type: Ext::SPAN_TYPE_CACHE
  }
end

.subscribe!Object

Acts as this module's initializer.



20
21
22
23
# File 'lib/datadog/tracing/contrib/active_support/cache/events/cache.rb', line 20

def subscribe!
  @cache_backend = {}
  super
end

.trace?(event, payload) ⇒ Boolean

Returns:

  • (Boolean)


48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/datadog/tracing/contrib/active_support/cache/events/cache.rb', line 48

def trace?(event, payload)
  return false if !Tracing.enabled? || !configuration.enabled

  if (cache_store = configuration[:cache_store])
    store = cache_backend(payload[:store])

    return false unless cache_store.include?(store)
  end

  # DEV-3.0: Backwards compatibility code for the 2.x gem series.
  # DEV-3.0: See documentation at {Datadog::Tracing::Contrib::ActiveSupport::Cache::Instrumentation}
  # DEV-3.0: for the complete information about this backwards compatibility code.
  case event
  when 'cache_read.active_support'
    !ActiveSupport::Cache::Instrumentation.nested_read?
  when 'cache_read_multi.active_support'
    !ActiveSupport::Cache::Instrumentation.nested_multiread?
  else
    true
  end
end