Class: NewRelic::TransactionSample

Inherits:
Object
  • Object
show all
Includes:
TransactionAnalysis
Defined in:
lib/new_relic/transaction_sample.rb

Defined Under Namespace

Classes: CompositeSegment, FakeSegment, IDGenerator, Segment, SummarySegment

Constant Summary collapse

EMPTY_ARRAY =
[].freeze
@@start_time =
Time.now

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from TransactionAnalysis

#breakdown_data, #database_time, #render_time, #sql_segments

Constructor Details

#initialize(time = Time.now.to_f, sample_id = nil) ⇒ TransactionSample

Returns a new instance of TransactionSample.



377
378
379
380
381
382
383
# File 'lib/new_relic/transaction_sample.rb', line 377

def initialize(time = Time.now.to_f, sample_id = nil)
  @sample_id = sample_id || object_id
  @start_time = time
  @root_segment = create_segment 0.0, "ROOT"
  @params = {}
  @params[:request_params] = {}
end

Instance Attribute Details

#paramsObject

Returns the value of attribute params.



374
375
376
# File 'lib/new_relic/transaction_sample.rb', line 374

def params
  @params
end

#profileObject

Returns the value of attribute profile.



372
373
374
# File 'lib/new_relic/transaction_sample.rb', line 372

def profile
  @profile
end

#root_segmentObject

Returns the value of attribute root_segment.



373
374
375
# File 'lib/new_relic/transaction_sample.rb', line 373

def root_segment
  @root_segment
end

#sample_idObject (readonly)

Returns the value of attribute sample_id.



375
376
377
# File 'lib/new_relic/transaction_sample.rb', line 375

def sample_id
  @sample_id
end

Class Method Details

.close_connectionsObject



358
359
360
361
362
363
364
365
366
367
368
# File 'lib/new_relic/transaction_sample.rb', line 358

def close_connections
  @@connections ||= {}
  @@connections.values.each do |connection|
    begin
      connection.disconnect!
    rescue
    end
  end
  
  @@connections = {}
end

.from_json(json) ⇒ Object



416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
# File 'lib/new_relic/transaction_sample.rb', line 416

def self.from_json(json)
  json = ActiveSupport::JSON.decode(json) if json.is_a?(String)
  
  if json.is_a?(Array)
    start_time = json[0].to_f / 1000
    custom_params = HashWithIndifferentAccess.new(json[2])
    params = {:request_params => HashWithIndifferentAccess.new(json[1]), 
          :custom_params => custom_params}
    cpu_time = custom_params.delete(:cpu_time)
    sample_id = nil
    params[:cpu_time] = cpu_time.to_f / 1000 if cpu_time
    root = json[3]
  else
    start_time = json["start_time"].to_f 
    sample_id = json["sample_id"].to_i
    params = json["params"] 
    root = json["root_segment"]
  end
  
  sample = TransactionSample.new(start_time, sample_id)
  
  if params
    sample.send :params=, HashWithIndifferentAccess.new(params)
  end
  if root
    sample.send :root_segment=, Segment.from_json(root, IDGenerator.new)
  end
  sample
end

.get_connection(config) ⇒ Object



341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'lib/new_relic/transaction_sample.rb', line 341

def get_connection(config)
  @@connections ||= {}
  
  connection = @@connections[config]
  
  return connection if connection
  
  begin
    connection = ActiveRecord::Base.send("#{config[:adapter]}_connection", config)
    @@connections[config] = connection
  rescue => e
    NewRelic::Agent.agent.log.error("Caught exception #{e} trying to get connection to DB for explain. Control: #{config}")
    NewRelic::Agent.agent.log.error(e.backtrace.join("\n"))
    nil
  end
end

.obfuscate_sql(sql) ⇒ Object



336
337
338
# File 'lib/new_relic/transaction_sample.rb', line 336

def obfuscate_sql(sql)
  NewRelic::Agent.instance.obfuscator.call(sql) 
end

Instance Method Details

#analyzeObject



539
540
541
542
543
544
545
546
547
548
549
550
551
# File 'lib/new_relic/transaction_sample.rb', line 539

def analyze
  sample = self
  original_path_string = nil
  loop do
    original_path_string = sample.path_string.to_s
    new_sample = sample.dup
    new_sample.root_segment = sample.root_segment.dup
    new_sample.root_segment.called_segments = analyze_called_segments(root_segment.called_segments)
    sample = new_sample
    return sample if sample.path_string.to_s == original_path_string
  end
  
end

#count_segmentsObject



385
386
387
# File 'lib/new_relic/transaction_sample.rb', line 385

def count_segments
  @root_segment.count_segments - 1    # don't count the root segment
end

#create_segment(relative_timestamp, metric_name, segment_id = nil) ⇒ Object

Raises:

  • (TypeError)


454
455
456
457
# File 'lib/new_relic/transaction_sample.rb', line 454

def create_segment(relative_timestamp, metric_name, segment_id = nil)
  raise TypeError.new("Frozen Transaction Sample") if frozen?
  NewRelic::TransactionSample::Segment.new(relative_timestamp, metric_name, segment_id)    
end

#durationObject



464
465
466
# File 'lib/new_relic/transaction_sample.rb', line 464

def duration
  root_segment.duration
end

#each_segment(&block) ⇒ Object



468
469
470
# File 'lib/new_relic/transaction_sample.rb', line 468

def each_segment(&block)
  @root_segment.each_segment(&block)
end

#find_segment(id) ⇒ Object



476
477
478
# File 'lib/new_relic/transaction_sample.rb', line 476

def find_segment(id)
  @root_segment.find_segment(id)
end

#freezeObject



459
460
461
462
# File 'lib/new_relic/transaction_sample.rb', line 459

def freeze
  @root_segment.freeze
  super
end

#omit_segments_with(regex) ⇒ Object

return a new transaction sample that treats segments with the given regular expression in their name as if they were never called at all. This allows us to strip out segments from traces captured in development environment that would not normally show up in production (like Rails/Application Code Loading)



505
506
507
508
509
510
511
512
513
514
515
516
# File 'lib/new_relic/transaction_sample.rb', line 505

def omit_segments_with(regex)
  regex = Regexp.new(regex)
  
  sample = TransactionSample.new(@start_time, sample_id)
  
  params.each {|k,v| sample.params[k] = v}
    
  delta = build_segment_with_omissions(sample, 0.0, @root_segment, sample.root_segment, regex)
  sample.root_segment.end_trace(@root_segment.exit_timestamp - delta)
  sample.profile = self.profile
  sample.freeze
end

#path_stringObject



450
451
452
# File 'lib/new_relic/transaction_sample.rb', line 450

def path_string
  @root_segment.path_string
end

#prepare_to_send(options = {}) ⇒ Object

return a new transaction sample that can be sent to the RPM service. this involves potentially one or more of the following options

:explain_sql : run EXPLAIN on all queries whose response times equal the value for this key
    (for example :explain_sql => 2.0 would explain everything over 2 seconds.  0.0 would explain everything.)
:keep_backtraces : keep backtraces, significantly increasing size of trace (off by default)
:obfuscate_sql : clear sql fields of potentially sensitive values (higher overhead, better security)


524
525
526
527
528
529
530
531
532
533
534
535
536
537
# File 'lib/new_relic/transaction_sample.rb', line 524

def prepare_to_send(options={})
  sample = TransactionSample.new(@start_time, sample_id)
  
  sample.params.merge! self.params
  
  begin
    build_segment_for_transfer(sample, @root_segment, sample.root_segment, options)
  ensure
    self.class.close_connections
  end
  
  sample.root_segment.end_trace(@root_segment.exit_timestamp) 
  sample.freeze
end

#start_timeObject



446
447
448
# File 'lib/new_relic/transaction_sample.rb', line 446

def start_time
  Time.at(@start_time)
end

#timestampObject

offset from start of app



402
403
404
# File 'lib/new_relic/transaction_sample.rb', line 402

def timestamp
  @start_time - @@start_time.to_f
end

#to_json(options = {}) ⇒ Object



406
407
408
409
410
411
412
413
414
# File 'lib/new_relic/transaction_sample.rb', line 406

def to_json(options = {})
  map = {:sample_id => @sample_id,
    :start_time => @start_time,
    :root_segment => @root_segment}
  if @params && !@params.empty?
    map[:params] = @params  
  end
  map.to_json
end

#to_sObject



480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
# File 'lib/new_relic/transaction_sample.rb', line 480

def to_s
  s = "Transaction Sample collected at #{start_time}\n"
  s << "  {\n"
  s << "  Path: #{params[:path]} \n"
  
  params.each do |k,v|
    next if k == :path
    s << "  #{k}: " <<
    case v
      when Enumerable then v.map(&:to_s).sort.join("; ")
      when String then v
      when Float then '%6.3s' % v 
    else
      raise "unexpected value type for #{k}: '#{v}' (#{v.class})"
    end << "\n"
  end
  s << "  }\n\n"
  s <<  @root_segment.to_debug_str(0)
end

#to_s_compactObject



472
473
474
# File 'lib/new_relic/transaction_sample.rb', line 472

def to_s_compact
  @root_segment.to_s_compact
end

#truncate(max) ⇒ Object



389
390
391
392
393
394
395
396
397
398
399
# File 'lib/new_relic/transaction_sample.rb', line 389

def truncate(max)
  original_count = count_segments
  
  return if original_count <= max
  
  @root_segment.truncate(max-1)
  
  if params[:segment_count].nil?
    params[:segment_count] = original_count
  end
end