Class: DependencyDetection::Dependent

Inherits:
Object
  • Object
show all
Defined in:
lib/new_relic/dependency_detection.rb

Constant Summary collapse

VALID_CONFIG_VALUES =
[:auto, :disabled, :prepend, :chain]
AUTO_CONFIG_VALUE =

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeDependent

Returns a new instance of Dependent.



53
54
55
56
57
58
59
# File 'lib/new_relic/dependency_detection.rb', line 53

def initialize
  @dependencies = []
  @executes = []
  @prepend_conflicts = []
  @name = nil
  @config_name = nil
end

Instance Attribute Details

#config_nameObject



49
50
51
# File 'lib/new_relic/dependency_detection.rb', line 49

def config_name
  @config_name || @name
end

#dependenciesObject (readonly)

Returns the value of attribute dependencies.



42
43
44
# File 'lib/new_relic/dependency_detection.rb', line 42

def dependencies
  @dependencies
end

#executedObject (readonly)

Returns the value of attribute executed.



39
40
41
# File 'lib/new_relic/dependency_detection.rb', line 39

def executed
  @executed
end

#nameObject

Returns the value of attribute name.



40
41
42
# File 'lib/new_relic/dependency_detection.rb', line 40

def name
  @name
end

#prepend_conflictsObject (readonly)

Returns the value of attribute prepend_conflicts.



43
44
45
# File 'lib/new_relic/dependency_detection.rb', line 43

def prepend_conflicts
  @prepend_conflicts
end

Instance Method Details

#allowed_by_config?Boolean

Returns:

  • (Boolean)


143
144
145
# File 'lib/new_relic/dependency_detection.rb', line 143

def allowed_by_config?
  !(disabled_configured? || deprecated_disabled_configured?)
end

#chain_instrument(instrumenting_module, supportability_name = nil) ⇒ Object



100
101
102
103
104
# File 'lib/new_relic/dependency_detection.rb', line 100

def chain_instrument(instrumenting_module, supportability_name = nil)
  log_and_instrument('MethodChaining', instrumenting_module, supportability_name) do
    instrumenting_module.instrument!
  end
end

#chain_instrument_target(target, instrumenting_module, supportability_name = nil) ⇒ Object



106
107
108
109
110
111
# File 'lib/new_relic/dependency_detection.rb', line 106

def chain_instrument_target(target, instrumenting_module, supportability_name = nil)
  NewRelic::Agent.logger.info("Installing deferred #{target} instrumentation")
  log_and_instrument('MethodChaining', instrumenting_module, supportability_name) do
    instrumenting_module.instrument!(target)
  end
end

#check_dependenciesObject



126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/new_relic/dependency_detection.rb', line 126

def check_dependencies
  return false unless allowed_by_config? && dependencies

  dependencies.all? do |dep|
    begin
      dep.call
    rescue => err
      NewRelic::Agent.logger.error("Error while detecting #{self.name}:", err)
      false
    end
  end
end

#config_keyObject



164
165
166
167
168
# File 'lib/new_relic/dependency_detection.rb', line 164

def config_key
  return nil if self.config_name.nil?

  @config_key ||= "instrumentation.#{self.config_name}".to_sym
end

#config_valueObject



202
203
204
205
206
# File 'lib/new_relic/dependency_detection.rb', line 202

def config_value
  return AUTO_CONFIG_VALUE unless config_key

  fetch_config_value(config_key)
end

#configure_as_unsatisfiedObject



65
66
67
68
69
70
71
72
73
74
# File 'lib/new_relic/dependency_detection.rb', line 65

def configure_as_unsatisfied
  # TODO: currently using :unsatisfied for Padrino will clobber the value
  #       already set for Sinatra, so skip Padrino and circle back with a
  #       new Padrino specific solution in the future.
  #
  #       https://github.com/newrelic/newrelic-ruby-agent/issues/2912
  return if name == :padrino

  NewRelic::Agent.config.instance_variable_get(:@cache)[config_key] = :unsatisfied
end

#configure_with(new_config_name) ⇒ Object



212
213
214
# File 'lib/new_relic/dependency_detection.rb', line 212

def configure_with(new_config_name)
  self.config_name = new_config_name
end

#conflicts_with_prepend(&block) ⇒ Object



220
221
222
# File 'lib/new_relic/dependency_detection.rb', line 220

def conflicts_with_prepend(&block)
  @prepend_conflicts << block if block
end

#dependencies_satisfied?Boolean

Returns:

  • (Boolean)


61
62
63
# File 'lib/new_relic/dependency_detection.rb', line 61

def dependencies_satisfied?
  !executed and check_dependencies
end

#depends_on(&block) ⇒ Object



139
140
141
# File 'lib/new_relic/dependency_detection.rb', line 139

def depends_on(&block)
  @dependencies << block if block
end

#deprecated_disabled_configured?Boolean

TODO: MAJOR VERSION will only return true if a disabled key is found and is truthy

Returns:

  • (Boolean)


149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/new_relic/dependency_detection.rb', line 149

def deprecated_disabled_configured?
  return false if self.name.nil?

  key = "disable_#{self.name}".to_sym
  return false unless ::NewRelic::Agent.config[key] == true

  ::NewRelic::Agent.logger.debug("Not installing #{self.name} instrumentation because of configuration #{key}")
  ::NewRelic::Agent.logger.debug( \
    "[DEPRECATED] configuration #{key} for #{self.name} will be removed in the next major release. " \
    "Use `#{config_key}` with one of `#{VALID_CONFIG_VALUES.map(&:to_s).inspect}`"
  )

  return true
end

#executeObject



113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/new_relic/dependency_detection.rb', line 113

def execute
  @executes.each do |x|
    begin
      x.call
    rescue => err
      NewRelic::Agent.logger.error("Error while installing #{self.name} instrumentation:", err)
      break
    end
  end
ensure
  executed!
end

#executed!Object



45
46
47
# File 'lib/new_relic/dependency_detection.rb', line 45

def executed!
  @executed = true
end

#executes(&block) ⇒ Object



216
217
218
# File 'lib/new_relic/dependency_detection.rb', line 216

def executes(&block)
  @executes << block if block
end

#extract_supportability_name(instrumenting_module) ⇒ Object

Extracts the instrumented library name from the instrumenting module’s name Given “NewRelic::Agent::Instrumentation::NetHTTP::Prepend” Will extract “NetHTTP” which is in the 2nd to last spot



83
84
85
# File 'lib/new_relic/dependency_detection.rb', line 83

def extract_supportability_name(instrumenting_module)
  instrumenting_module.to_s.split('::')[-2]
end

#fetch_config_value(key) ⇒ Object

fetches and transform potentially invalid value given to one of the valid config values logs the resolved value during debug mode.



187
188
189
190
191
# File 'lib/new_relic/dependency_detection.rb', line 187

def fetch_config_value(key)
  valid_value = valid_config_value(::NewRelic::Agent.config[key].to_s.to_sym)
  ::NewRelic::Agent.logger.debug("Using #{valid_value} configuration value for #{self.name} to configure instrumentation")
  return valid_value
end

#log_and_instrument(method, instrumenting_module, supportability_name) ⇒ Object



87
88
89
90
91
92
# File 'lib/new_relic/dependency_detection.rb', line 87

def log_and_instrument(method, instrumenting_module, supportability_name)
  supportability_name ||= extract_supportability_name(instrumenting_module)
  NewRelic::Agent.logger.info("Installing New Relic supported #{supportability_name} instrumentation using #{method}")
  NewRelic::Agent.record_metric("Supportability/Instrumentation/#{supportability_name}/#{method}", 0.0)
  yield
end

#named(new_name) ⇒ Object



208
209
210
# File 'lib/new_relic/dependency_detection.rb', line 208

def named(new_name)
  self.name = new_name
end

#prepend_conflicts?Boolean

Returns:

  • (Boolean)


228
229
230
231
232
233
234
235
236
237
# File 'lib/new_relic/dependency_detection.rb', line 228

def prepend_conflicts?
  @prepend_conflicts.any? do |conflict|
    begin
      conflict.call
    rescue => err
      NewRelic::Agent.logger.error("Error while checking prepend conflicts #{self.name}:", err)
      false # assumes no conflicts exist since `prepend` is preferred method of instrumenting
    end
  end
end

#prepend_instrument(target_class, instrumenting_module, supportability_name = nil) ⇒ Object



94
95
96
97
98
# File 'lib/new_relic/dependency_detection.rb', line 94

def prepend_instrument(target_class, instrumenting_module, supportability_name = nil)
  log_and_instrument('Prepend', instrumenting_module, supportability_name) do
    target_class.send(:prepend, instrumenting_module)
  end
end

#source_location_for(klass, method_name) ⇒ Object



76
77
78
# File 'lib/new_relic/dependency_detection.rb', line 76

def source_location_for(klass, method_name)
  Object.instance_method(:method).bind(klass.allocate).call(method_name).source_location.to_s
end

#update_config_value(use_prepend) ⇒ Object

update any :auto config value to be either :prepend or :chain after auto determination has selected one of those to use



195
196
197
198
199
200
# File 'lib/new_relic/dependency_detection.rb', line 195

def update_config_value(use_prepend)
  if config_key && auto_configured?
    NewRelic::Agent.config.instance_variable_get(:@cache)[config_key] = use_prepend ? :prepend : :chain
  end
  use_prepend
end

#use_prepend?Boolean

Returns:

  • (Boolean)


224
225
226
# File 'lib/new_relic/dependency_detection.rb', line 224

def use_prepend?
  update_config_value(prepend_configured? || (auto_configured? && !prepend_conflicts?))
end

#valid_config_value(retrieved_value) ⇒ Object

returns only a valid value for instrumentation configuration If user uses “enabled” it’s converted to “auto”



181
182
183
# File 'lib/new_relic/dependency_detection.rb', line 181

def valid_config_value(retrieved_value)
  VALID_CONFIG_VALUES.include?(retrieved_value) ? retrieved_value : AUTO_CONFIG_VALUE
end