Module: NewRelic::Agent::Datastores

Defined in:
lib/new_relic/agent/datastores.rb

Overview

This module contains helper methods to facilitate instrumentation of datastores not directly supported by the Ruby agent. It is intended to be primarily used by authors of 3rd-party datastore instrumentation.

Tracing query methods collapse

Capturing query / statement text collapse

Class Method Details

.notice_sql(query) ⇒ Object

Note:

THERE ARE SECURITY CONCERNS WHEN CAPTURING QUERY TEXT! New Relic’s Transaction Tracing and Slow SQL features will attempt to apply obfuscation to the passed queries, but it is possible for a query format to be unsupported and result in exposing user information embedded within captured queries.

Wrapper for simplifying attaching SQL queries during a transaction.

If you are recording non-SQL data, please use notice_statement instead.

NewRelic::Agent::Datastores.notice_sql(query)


160
161
162
163
164
165
166
167
# File 'lib/new_relic/agent/datastores.rb', line 160

def self.notice_sql(query)
  NewRelic::Agent.record_api_supportability_metric(:notice_sql)

  if (txn = Tracer.current_transaction) && (segment = txn.current_segment) && segment.respond_to?(:notice_sql)
    segment.notice_sql(query)
  end
  nil
end

.notice_statement(statement) ⇒ Object

Note:

THERE ARE SECURITY CONCERNS WHEN CAPTURING STATEMENTS! This method will properly ignore statements when the user has turned off capturing queries, but it is not able to obfuscate arbitrary data! To prevent exposing user information embedded in captured queries, please ensure all data passed to this method is safe to transmit to New Relic.

Wrapper for simplifying attaching non-SQL data statements to a transaction. For instance, Mongo or CQL queries, Memcached or Redis keys would all be appropriate data to attach as statements.

Data passed to this method is NOT obfuscated by New Relic, so please ensure that user information is obfuscated if the agent setting transaction_tracer.record_sql is set to obfuscated

NewRelic::Agent::Datastores.notice_statement("key")


190
191
192
193
194
195
196
197
198
199
# File 'lib/new_relic/agent/datastores.rb', line 190

def self.notice_statement(statement)
  NewRelic::Agent.record_api_supportability_metric(:notice_statement)

  # Settings may change eventually, but for now we follow the same
  # capture rules as SQL for non-SQL statements.
  if (txn = Tracer.current_transaction) && (segment = txn.current_segment) && segment.respond_to?(:notice_nosql_statement)
    segment.notice_nosql_statement(statement)
  end
  nil
end

.trace(klass, method_name, product, operation = method_name) ⇒ Object

Add Datastore tracing to a method. This properly generates the metrics for New Relic’s Datastore features. It does not capture the actual query content into Transaction Traces. Use wrap if you want to provide that functionality.



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/new_relic/agent/datastores.rb', line 35

def self.trace(klass, method_name, product, operation = method_name)
  NewRelic::Agent.record_api_supportability_metric(:trace)

  klass.class_eval do
    method_name_without_newrelic = "#{method_name}_without_newrelic"

    if NewRelic::Helper.instance_methods_include?(klass, method_name) &&
        !NewRelic::Helper.instance_methods_include?(klass, method_name_without_newrelic)

      visibility = NewRelic::Helper.instance_method_visibility(klass, method_name)

      alias_method(method_name_without_newrelic, method_name)

      define_method(method_name) do |*args, &blk|
        segment = NewRelic::Agent::Tracer.start_datastore_segment(
          product: product,
          operation: operation
        )
        begin
          send(method_name_without_newrelic, *args, &blk)
        ensure
          ::NewRelic::Agent::Transaction::Segment.finish(segment)
        end
      end

      send(visibility, method_name)
      send(visibility, method_name_without_newrelic)
    end
  end
end

.wrap(product, operation, collection = nil, callback = nil) ⇒ Object

Note:

THERE ARE SECURITY CONCERNS WHEN CAPTURING QUERY TEXT! New Relic’s Transaction Tracing and Slow SQL features will attempt to apply obfuscation to the passed queries, but it is possible for a query format to be unsupported and result in exposing user information embedded within captured queries.

Wrap a call to a datastore and record New Relic Datastore metrics. This method can be used when a collection (i.e. table or model name) is known at runtime to be included in the metric naming. It is intended for situations that the simpler NewRelic::Agent::Datastores.trace can’t properly handle.

To use this, wrap the datastore operation in the block passed to wrap.

NewRelic::Agent::Datastores.wrap("FauxDB", "find", "items") do
  FauxDB.find(query)
end


108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/new_relic/agent/datastores.rb', line 108

def self.wrap(product, operation, collection = nil, callback = nil)
  NewRelic::Agent.logger.warn('The NewRelic::Agent::Datastores.wrap method is changing. ' \
    'In a future major version, proc will only accept a single argument, the result of the yield. ' \
    'The scoped metric name and elapsed arguments will be removed, as they are being removed from the ' \
    'Datastores.notice_sql method. The scoped metric name and elapsed values are derived from the ' \
    'current segment when the wrap yields.')
  NewRelic::Agent.record_api_supportability_metric(:wrap)

  return yield unless operation

  segment = NewRelic::Agent::Tracer.start_datastore_segment(
    product: product,
    operation: operation,
    collection: collection
  )

  begin
    result = yield
  ensure
    begin
      if callback
        elapsed_time = Process.clock_gettime(Process::CLOCK_REALTIME) - segment.start_time
        callback.call(result, segment.name, elapsed_time)
      end
    ensure
      ::NewRelic::Agent::Transaction::Segment.finish(segment)
    end
  end
end