Class: NewRelic::Agent::Transaction::AbstractSegment

Inherits:
Object
  • Object
show all
Defined in:
lib/new_relic/agent/transaction/abstract_segment.rb

Direct Known Subclasses

Segment

Constant Summary collapse

CALLBACK =
:@callback
SEGMENT =
'segment'
INSPECT_IGNORE =
[:@transaction, :@transaction_state].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name = nil, start_time = nil) ⇒ AbstractSegment

Returns a new instance of AbstractSegment.



30
31
32
33
34
35
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/new_relic/agent/transaction/abstract_segment.rb', line 30

def initialize(name = nil, start_time = nil)
  @name = name
  @starting_segment_key = NewRelic::Agent::Tracer.current_segment_key
  @thread_id = NewRelic::Agent::TransactionTimeAggregator.current_execution_context_id
  @transaction_name = nil
  @transaction = nil
  @guid = NewRelic::Agent::GuidGenerator.generate_guid
  @parent = nil
  @params = nil
  @start_time = start_time if start_time
  @end_time = nil
  @duration = 0.0
  @exclusive_duration = 0.0
  @children_timings = []
  @children_time = 0.0
  @active_children = 0
  @range_recorded = false
  @concurrent_children = false
  @record_metrics = true
  @record_scoped_metric = true
  @record_on_finish = false
  @noticed_error = nil
  @code_filepath = nil
  @code_function = nil
  @code_lineno = nil
  @code_namespace = nil
  @span_kind = nil
  invoke_callback
end

Instance Attribute Details

#children_timeObject



23
24
25
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 23

def children_time
  @children_time
end

#durationObject (readonly)

This class is the base class for all segments. It is responsible for timing, naming, and defining lifecycle callbacks. One of the more complex responsibilities of this class is computing exclusive duration. One of the reasons for this complexity is that exclusive time will be computed using time ranges or by recording an aggregate value for a segments children time. The reason for this is that computing exclusive duration using time ranges is expensive and it's only necessary if a segment's children run concurrently, or a segment ends after its parent. We will use the optimized exclusive duration calculation in all other cases.



22
23
24
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 22

def duration
  @duration
end

#end_timeObject (readonly)

This class is the base class for all segments. It is responsible for timing, naming, and defining lifecycle callbacks. One of the more complex responsibilities of this class is computing exclusive duration. One of the reasons for this complexity is that exclusive time will be computed using time ranges or by recording an aggregate value for a segments children time. The reason for this is that computing exclusive duration using time ranges is expensive and it's only necessary if a segment's children run concurrently, or a segment ends after its parent. We will use the optimized exclusive duration calculation in all other cases.



22
23
24
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 22

def end_time
  @end_time
end

#exclusive_durationObject (readonly)

This class is the base class for all segments. It is responsible for timing, naming, and defining lifecycle callbacks. One of the more complex responsibilities of this class is computing exclusive duration. One of the reasons for this complexity is that exclusive time will be computed using time ranges or by recording an aggregate value for a segments children time. The reason for this is that computing exclusive duration using time ranges is expensive and it's only necessary if a segment's children run concurrently, or a segment ends after its parent. We will use the optimized exclusive duration calculation in all other cases.



22
23
24
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 22

def exclusive_duration
  @exclusive_duration
end

#guidObject (readonly)

This class is the base class for all segments. It is responsible for timing, naming, and defining lifecycle callbacks. One of the more complex responsibilities of this class is computing exclusive duration. One of the reasons for this complexity is that exclusive time will be computed using time ranges or by recording an aggregate value for a segments children time. The reason for this is that computing exclusive duration using time ranges is expensive and it's only necessary if a segment's children run concurrently, or a segment ends after its parent. We will use the optimized exclusive duration calculation in all other cases.



22
23
24
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 22

def guid
  @guid
end

#llm_eventObject



23
24
25
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 23

def llm_event
  @llm_event
end

#nameObject



23
24
25
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 23

def name
  @name
end

#noticed_errorObject (readonly)



25
26
27
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 25

def noticed_error
  @noticed_error
end

#parentObject



23
24
25
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 23

def parent
  @parent
end

#record_metrics=(value) ⇒ Object



24
25
26
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 24

def record_metrics=(value)
  @record_metrics = value
end

#record_on_finish=(value) ⇒ Object (writeonly)



24
25
26
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 24

def record_on_finish=(value)
  @record_on_finish = value
end

#record_scoped_metric=(value) ⇒ Object (writeonly)



24
25
26
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 24

def record_scoped_metric=(value)
  @record_scoped_metric = value
end

#span_kindObject



23
24
25
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 23

def span_kind
  @span_kind
end

#start_timeObject (readonly)

This class is the base class for all segments. It is responsible for timing, naming, and defining lifecycle callbacks. One of the more complex responsibilities of this class is computing exclusive duration. One of the reasons for this complexity is that exclusive time will be computed using time ranges or by recording an aggregate value for a segments children time. The reason for this is that computing exclusive duration using time ranges is expensive and it's only necessary if a segment's children run concurrently, or a segment ends after its parent. We will use the optimized exclusive duration calculation in all other cases.



22
23
24
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 22

def start_time
  @start_time
end

#starting_segment_keyObject (readonly)

This class is the base class for all segments. It is responsible for timing, naming, and defining lifecycle callbacks. One of the more complex responsibilities of this class is computing exclusive duration. One of the reasons for this complexity is that exclusive time will be computed using time ranges or by recording an aggregate value for a segments children time. The reason for this is that computing exclusive duration using time ranges is expensive and it's only necessary if a segment's children run concurrently, or a segment ends after its parent. We will use the optimized exclusive duration calculation in all other cases.



22
23
24
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 22

def starting_segment_key
  @starting_segment_key
end

#thread_idObject (readonly)

This class is the base class for all segments. It is responsible for timing, naming, and defining lifecycle callbacks. One of the more complex responsibilities of this class is computing exclusive duration. One of the reasons for this complexity is that exclusive time will be computed using time ranges or by recording an aggregate value for a segments children time. The reason for this is that computing exclusive duration using time ranges is expensive and it's only necessary if a segment's children run concurrently, or a segment ends after its parent. We will use the optimized exclusive duration calculation in all other cases.



22
23
24
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 22

def thread_id
  @thread_id
end

#transactionObject



23
24
25
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 23

def transaction
  @transaction
end

#transaction_nameObject



23
24
25
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 23

def transaction_name
  @transaction_name
end

Class Method Details

.set_segment_callback(callback_proc) ⇒ Object

Setting and invoking a segment callback

Each individual segment class such as ExternalRequestSegment allows for exactly one instance of a Proc (meaning a proc or lambda) to be set as a callback. A callback can be set on a segment class by calling .set_segment_callback with a proc or lambda as the only argument. If set, the callback will be invoked with #call at segment class initialization time.

Example usage:

callback = -> { puts 'Hello, World! }
ExternalRequestSegment.set_segment_callback(callback)
ExternalRequestSegment.new(library, uri, procedure)

A callback set on a segment class will only be called when that specific segment class is initialized. Other segment classes will not be impacted.

Great caution should be taken in the defining of the callback block to not have the block perform anything too time consuming or resource intensive in order to keep the New Relic Ruby agent operating normally.

Given that callbacks are user defined, they must be set entirely at the user's own risk. It is recommended that each callback use conditional logic that only performs work for certain qualified segments. It is recommended that each callback be thoroughly tested in non-production environments before being introduced to production environments.



379
380
381
382
383
384
385
386
387
388
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 379

def self.set_segment_callback(callback_proc)
  unless callback_proc.is_a?(Proc)
    NewRelic::Agent.logger.error("#{self}.#{__method__}: expected an argument of type Proc, " \
                                 "got #{callback_proc.class}")
    return
  end

  NewRelic::Agent.record_api_supportability_metric(:set_segment_callback)
  instance_variable_set(CALLBACK, callback_proc)
end

Instance Method Details

#all_code_information_present?Boolean

Returns:

  • (Boolean)


130
131
132
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 130

def all_code_information_present?
  @code_filepath && @code_function && @code_lineno && @code_namespace
end

#children_time_ranges?Boolean

Returns:

  • (Boolean)


113
114
115
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 113

def children_time_ranges?
  !@children_timings.empty?
end

#code_attributesObject



134
135
136
137
138
139
140
141
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 134

def code_attributes
  return ::NewRelic::EMPTY_HASH unless all_code_information_present?

  @code_attributes ||= {'code.filepath' => @code_filepath,
                        'code.function' => @code_function,
                        'code.lineno' => @code_lineno,
                        'code.namespace' => @code_namespace}
end

#code_information=(info = {}) ⇒ Object



121
122
123
124
125
126
127
128
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 121

def code_information=(info = {})
  return unless info[:filepath]

  @code_filepath = info[:filepath]
  @code_function = info[:function]
  @code_lineno = info[:lineno]
  @code_namespace = info[:namespace]
end

#concurrent_children?Boolean

Returns:

  • (Boolean)


117
118
119
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 117

def concurrent_children?
  @concurrent_children
end

#finalizeObject



95
96
97
98
99
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 95

def finalize
  force_finish unless finished?
  record_exclusive_duration
  record_metrics if record_metrics?
end

#finishObject



67
68
69
70
71
72
73
74
75
76
77
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 67

def finish
  @end_time = Process.clock_gettime(Process::CLOCK_REALTIME)
  @duration = end_time - start_time

  return unless transaction

  run_complete_callbacks
  finalize if record_on_finish?
rescue => e
  NewRelic::Agent.logger.error("Exception finishing segment: #{name}", e)
end

#finished?Boolean

Returns:

  • (Boolean)


79
80
81
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 79

def finished?
  !!@end_time
end

#inspectObject



145
146
147
148
149
150
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 145

def inspect
  ivars = (instance_variables - INSPECT_IGNORE).inject([]) do |memo, var_name|
    memo << "#{var_name}=#{instance_variable_get(var_name).inspect}"
  end
  sprintf('#<%s:0x%x %s>', self.class.name, object_id, ivars.join(', '))
end

#notice_error(exception, options = {}) ⇒ Object



166
167
168
169
170
171
172
173
174
175
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 166

def notice_error(exception, options = {})
  if Agent.config[:high_security]
    NewRelic::Agent.logger.debug( \
      "Segment: #{name} ignores notice_error for " \
      "error: #{exception.inspect} because :high_security is enabled"
    )
  else
    NewRelic::Agent.instance.error_collector.notice_segment_error(self, exception, options)
  end
end

#noticed_error_attributesObject



177
178
179
180
181
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 177

def noticed_error_attributes
  return unless @noticed_error

  @noticed_error.attributes_from_notice_error
end

#paramsObject



101
102
103
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 101

def params
  @params ||= {}
end

#params?Boolean

Returns:

  • (Boolean)


105
106
107
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 105

def params?
  !!@params
end

#record_metrics?Boolean

Returns:

  • (Boolean)


83
84
85
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 83

def record_metrics?
  @record_metrics
end

#record_on_finish?Boolean

Returns:

  • (Boolean)


91
92
93
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 91

def record_on_finish?
  @record_on_finish
end

#record_scoped_metric?Boolean

Returns:

  • (Boolean)


87
88
89
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 87

def record_scoped_metric?
  @record_scoped_metric
end

#set_noticed_error(noticed_error) ⇒ Object



156
157
158
159
160
161
162
163
164
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 156

def set_noticed_error(noticed_error)
  if @noticed_error
    NewRelic::Agent.logger.debug( \
      "Segment: #{name} overwriting previously noticed " \
      "error: #{@noticed_error.inspect} with: #{noticed_error.inspect}"
    )
  end
  @noticed_error = noticed_error
end

#startObject



60
61
62
63
64
65
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 60

def start
  @start_time ||= Process.clock_gettime(Process::CLOCK_REALTIME)
  return unless transaction

  parent&.child_start(self)
end

#time_rangeObject



109
110
111
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 109

def time_range
  @start_time.to_f..@end_time.to_f
end

#transaction_assignedObject

callback for subclasses to override



153
154
# File 'lib/new_relic/agent/transaction/abstract_segment.rb', line 153

def transaction_assigned
end