Module: Mmtrix::Agent::Datastores

Defined in:
lib/mmtrix/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, scoped_metric, elapsed) ⇒ Object

Note:

THERE ARE SECURITY CONCERNS WHEN CAPTURING QUERY TEXT! Mmtrix’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.

Mmtrix::Agent::Datastores.notice_sql(query, metrics, elapsed)

Parameters:

  • query (String)

    the SQL text to be captured. Note that depending on user settings, this string will be run through obfuscation, but some dialects of SQL (or non-SQL queries) are not guaranteed to be properly obfuscated by these routines!

  • scoped_metric (String)

    The most specific metric relating to this query. Typically the result of Mmtrix::Agent::Datastores::MetricHelper#metrics_for

  • elapsed (Float)

    the elapsed time during query execution



149
150
151
152
153
154
# File 'lib/mmtrix/agent/datastores.rb', line 149

def self.notice_sql(query, scoped_metric, elapsed)
  agent = Mmtrix::Agent.instance
  agent.transaction_sampler.notice_sql(query, nil, elapsed)
  agent.sql_sampler.notice_sql(query, scoped_metric, nil, elapsed)
  nil
end

.notice_statement(statement, elapsed) ⇒ 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 Mmtrix.

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 Mmtrix, so please ensure that user information is obfuscated if the agent setting ‘transaction_tracer.record_sql` is set to `obfuscated`

Mmtrix::Agent::Datastores.notice_statement("key", elapsed)

Parameters:

  • statement (String)

    text of the statement to capture.

  • elapsed (Float)

    the elapsed time during query execution



179
180
181
182
183
184
185
186
187
# File 'lib/mmtrix/agent/datastores.rb', line 179

def self.notice_statement(statement, elapsed)
  # Settings may change eventually, but for now we follow the same
  # capture rules as SQL for non-SQL statements.
  return unless Mmtrix::Agent::Database.should_record_sql?

  agent = Mmtrix::Agent.instance
  agent.transaction_sampler.notice_nosql_statement(statement, elapsed)
  nil
end

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

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

Parameters:

  • clazz (Class)

    the class to instrument

  • method_name (String, Symbol)

    the name of instance method to instrument

  • product (String)

    name of your datastore for use in metric naming, e.g. “Redis”

  • operation (optional, String) (defaults to: method_name)

    the name of operation if different than the instrumented method name



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/mmtrix/agent/datastores.rb', line 36

def self.trace(clazz, method_name, product, operation = method_name)
  clazz.class_eval do
    method_name_without_mmtrix = "#{method_name}_without_mmtrix"

    if Mmtrix::Helper.instance_methods_include?(clazz, method_name) &&
       !Mmtrix::Helper.instance_methods_include?(clazz, method_name_without_mmtrix)

      visibility = Mmtrix::Helper.instance_method_visibility(clazz, method_name)

      alias_method method_name_without_mmtrix, method_name

      define_method(method_name) do |*args, &blk|
        metrics = MetricHelper.metrics_for(product, operation)
        Mmtrix::Agent::MethodTracer.trace_execution_scoped(metrics) do
          send(method_name_without_mmtrix, *args, &blk)
        end
      end

      send visibility, method_name
      send visibility, method_name_without_mmtrix
    end
  end
end

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

Note:

THERE ARE SECURITY CONCERNS WHEN CAPTURING QUERY TEXT! Mmtrix’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 Mmtrix 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 Mmtrix::Agent::Datastores.trace can’t properly handle.

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

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

Parameters:

  • product (String)

    the datastore name for use in metric naming, e.g. “FauxDB”

  • operation (String, Symbol)

    the name of operation (e.g. “select”), often named after the method that’s being instrumented.

  • collection (optional, String) (defaults to: nil)

    the collection name for use in statement-level metrics (i.e. table or model name)

  • callback (Proc, #call) (defaults to: nil)

    proc or other callable to invoke after running the datastore block. Receives three arguments: result of the yield, the most specific (scoped) metric name, and elapsed time of the call. An example use is attaching SQL to Transaction Traces at the end of a wrapped datastore call.

    callback = Proc.new do |result, metrics, elapsed|
      Mmtrix::Agent::Datastores.notice_sql(query, metrics, elapsed)
    end
    
    Mmtrix::Agent::Datastores.wrap("FauxDB", "find", "items", callback) do
      FauxDB.find(query)
    end
    


103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/mmtrix/agent/datastores.rb', line 103

def self.wrap(product, operation, collection = nil, callback = nil)
  return yield unless operation

  metrics = MetricHelper.metrics_for(product, operation, collection)
  scoped_metric = metrics.first
  Mmtrix::Agent::MethodTracer.trace_execution_scoped(metrics) do
    t0 = Time.now
    begin
      result = yield
    ensure
      if callback
        elapsed_time = (Time.now - t0).to_f
        callback.call(result, scoped_metric, elapsed_time)
      end
    end
  end
end