newrelic_rpm 6.1.0.352 → 6.6.0.358

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +34 -96
  4. data/CHANGELOG.md +139 -0
  5. data/lib/new_relic/agent/agent.rb +51 -134
  6. data/lib/new_relic/agent/commands/agent_command_router.rb +2 -21
  7. data/lib/new_relic/agent/configuration/default_source.rb +53 -41
  8. data/lib/new_relic/agent/configuration/environment_source.rb +4 -2
  9. data/lib/new_relic/agent/configuration/event_harvest_config.rb +39 -0
  10. data/lib/new_relic/agent/configuration/high_security_source.rb +1 -0
  11. data/lib/new_relic/agent/configuration/manager.rb +13 -1
  12. data/lib/new_relic/agent/configuration/server_source.rb +34 -1
  13. data/lib/new_relic/agent/connect/request_builder.rb +69 -0
  14. data/lib/new_relic/agent/connect/response_handler.rb +61 -0
  15. data/lib/new_relic/agent/error_collector.rb +2 -2
  16. data/lib/new_relic/agent/error_event_aggregator.rb +2 -1
  17. data/lib/new_relic/agent/error_trace_aggregator.rb +1 -0
  18. data/lib/new_relic/agent/event_aggregator.rb +26 -32
  19. data/lib/new_relic/agent/hostname.rb +1 -1
  20. data/lib/new_relic/agent/inbound_request_monitor.rb +2 -2
  21. data/lib/new_relic/agent/instrumentation/action_cable_subscriber.rb +24 -42
  22. data/lib/new_relic/agent/instrumentation/action_controller_subscriber.rb +46 -74
  23. data/lib/new_relic/agent/instrumentation/action_view_subscriber.rb +70 -53
  24. data/lib/new_relic/agent/instrumentation/active_record_notifications.rb +168 -0
  25. data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +41 -48
  26. data/lib/new_relic/agent/instrumentation/active_storage_subscriber.rb +4 -4
  27. data/lib/new_relic/agent/instrumentation/grape.rb +2 -3
  28. data/lib/new_relic/agent/instrumentation/notifications_subscriber.rb +76 -0
  29. data/lib/new_relic/agent/instrumentation/{rails5 → rails_notifications}/action_cable.rb +5 -6
  30. data/lib/new_relic/agent/instrumentation/{rails5 → rails_notifications}/action_controller.rb +3 -3
  31. data/lib/new_relic/agent/instrumentation/{rails4 → rails_notifications}/action_view.rb +3 -3
  32. data/lib/new_relic/agent/javascript_instrumentor.rb +1 -1
  33. data/lib/new_relic/agent/new_relic_service.rb +0 -4
  34. data/lib/new_relic/agent/new_relic_service/json_marshaller.rb +0 -1
  35. data/lib/new_relic/agent/parameter_filtering.rb +18 -5
  36. data/lib/new_relic/agent/priority_sampled_buffer.rb +2 -0
  37. data/lib/new_relic/agent/span_event_aggregator.rb +2 -5
  38. data/lib/new_relic/agent/threading/backtrace_service.rb +3 -3
  39. data/lib/new_relic/agent/threading/thread_profile.rb +9 -23
  40. data/lib/new_relic/agent/transaction.rb +0 -2
  41. data/lib/new_relic/agent/transaction/trace.rb +3 -8
  42. data/lib/new_relic/agent/transaction/trace_builder.rb +0 -1
  43. data/lib/new_relic/agent/transaction_event_recorder.rb +3 -3
  44. data/lib/new_relic/agent/transaction_sampler.rb +1 -5
  45. data/lib/new_relic/agent/transaction_time_aggregator.rb +19 -4
  46. data/lib/new_relic/control/class_methods.rb +7 -1
  47. data/lib/new_relic/control/frameworks/{rails5.rb → rails_notifications.rb} +1 -1
  48. data/lib/new_relic/rack/browser_monitoring.rb +10 -8
  49. data/lib/new_relic/version.rb +1 -1
  50. data/lib/tasks/config.rake +1 -2
  51. data/newrelic_rpm.gemspec +2 -9
  52. data/test/agent_helper.rb +18 -5
  53. metadata +18 -51
  54. data/lib/new_relic/agent/commands/xray_session.rb +0 -55
  55. data/lib/new_relic/agent/commands/xray_session_collection.rb +0 -161
  56. data/lib/new_relic/agent/instrumentation/active_record_4.rb +0 -42
  57. data/lib/new_relic/agent/instrumentation/active_record_5.rb +0 -41
  58. data/lib/new_relic/agent/instrumentation/evented_subscriber.rb +0 -104
  59. data/lib/new_relic/agent/instrumentation/rails4/action_controller.rb +0 -32
  60. data/lib/new_relic/agent/instrumentation/rails5/action_view.rb +0 -27
  61. data/lib/new_relic/agent/transaction/xray_sample_buffer.rb +0 -64
  62. data/lib/new_relic/control/frameworks/rails6.rb +0 -14
@@ -1,87 +1,104 @@
1
1
  # encoding: utf-8
2
2
  # This file is distributed under New Relic's license terms.
3
3
  # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
- require 'new_relic/agent/instrumentation/evented_subscriber'
4
+ require 'new_relic/agent/instrumentation/notifications_subscriber'
5
5
 
6
6
  # Listen for ActiveSupport::Notifications events for ActionView render
7
7
  # events. Write metric data and transaction trace nodes for each event.
8
8
  module NewRelic
9
9
  module Agent
10
10
  module Instrumentation
11
- class ActionViewSubscriber < EventedSubscriber
11
+ class ActionViewSubscriber < NotificationsSubscriber
12
12
 
13
13
  def start(name, id, payload) #THREAD_LOCAL_ACCESS
14
- event = RenderEvent.new(name, Time.now, nil, id, payload)
15
- push_event(event)
16
- if state.is_execution_traced? && event.recordable?
17
- event.segment = NewRelic::Agent::Tracer.start_segment name: event.metric_name
14
+ parent = segment_stack[id].last
15
+ metric_name = format_metric_name(name, payload, parent)
16
+
17
+ event = ActionViewEvent.new(metric_name, payload[:identifier])
18
+ if state.is_execution_traced? && recordable?(name, metric_name)
19
+ event.finishable = Tracer.start_segment(name: metric_name)
18
20
  end
21
+ push_segment id, event
19
22
  rescue => e
20
23
  log_notification_error(e, name, 'start')
21
24
  end
22
25
 
23
26
  def finish(name, id, payload) #THREAD_LOCAL_ACCESS
24
- event = pop_event(id)
25
- event.segment.finish if event.segment
27
+ event = pop_segment(id)
28
+ event.finish if event
26
29
  rescue => e
27
30
  log_notification_error(e, name, 'finish')
28
31
  end
29
32
 
30
- class RenderEvent < Event
31
- attr_accessor :segment
32
-
33
- RENDER_TEMPLATE_EVENT_NAME = 'render_template.action_view'.freeze
34
- RENDER_PARTIAL_EVENT_NAME = 'render_partial.action_view'.freeze
35
- RENDER_COLLECTION_EVENT_NAME = 'render_collection.action_view'.freeze
33
+ def format_metric_name(event_name, payload, parent)
34
+ return parent.name if parent \
35
+ && (payload[:virtual_path] \
36
+ || (parent.identifier =~ /template$/))
36
37
 
37
- # Nearly every "render_blah.action_view" event has a child
38
- # in the form of "!render_blah.action_view". The children
39
- # are the ones we want to record. There are a couple
40
- # special cases of events without children.
41
- def recordable?
42
- name[0] == '!' ||
43
- metric_name == 'View/text template/Rendering' ||
44
- metric_name == "View/#{::NewRelic::Agent::UNKNOWN_METRIC}/Partial"
38
+ if payload.key?(:virtual_path)
39
+ identifier = payload[:virtual_path]
40
+ else
41
+ identifier = payload[:identifier]
45
42
  end
46
43
 
47
- def metric_name
48
- if parent && (payload[:virtual_path] ||
49
- (parent.payload[:identifier] =~ /template$/))
50
- return parent.metric_name
51
- elsif payload.key?(:virtual_path)
52
- identifier = payload[:virtual_path]
53
- else
54
- identifier = payload[:identifier]
55
- end
44
+ "View/#{metric_path(event_name, identifier)}/#{metric_action(event_name)}"
45
+ end
56
46
 
57
- # memoize
58
- @metric_name ||= "View/#{metric_path(name, identifier)}/#{metric_action(name)}"
59
- @metric_name
60
- end
47
+ # Nearly every "render_blah.action_view" event has a child
48
+ # in the form of "!render_blah.action_view". The children
49
+ # are the ones we want to record. There are a couple
50
+ # special cases of events without children.
51
+ def recordable?(event_name, metric_name)
52
+ event_name[0] == '!' \
53
+ || metric_name == 'View/text template/Rendering' \
54
+ || metric_name == "View/#{::NewRelic::Agent::UNKNOWN_METRIC}/Partial"
55
+ end
56
+
57
+ RENDER_TEMPLATE_EVENT_NAME = 'render_template.action_view'.freeze
58
+ RENDER_PARTIAL_EVENT_NAME = 'render_partial.action_view'.freeze
59
+ RENDER_COLLECTION_EVENT_NAME = 'render_collection.action_view'.freeze
61
60
 
62
- def metric_path(name, identifier)
63
- # Rails 5 sets identifier to nil for empty collections,
64
- # so do not mistake rendering a collection for rendering a file.
65
- if identifier == nil && name != RENDER_COLLECTION_EVENT_NAME
66
- 'file'
67
- elsif identifier =~ /template$/
68
- identifier
69
- elsif identifier && (parts = identifier.split('/')).size > 1
70
- parts[-2..-1].join('/')
71
- else
72
- ::NewRelic::Agent::UNKNOWN_METRIC
73
- end
61
+ def metric_action(name)
62
+ case name
63
+ when /#{RENDER_TEMPLATE_EVENT_NAME}$/ then 'Rendering'
64
+ when RENDER_PARTIAL_EVENT_NAME then 'Partial'
65
+ when RENDER_COLLECTION_EVENT_NAME then 'Partial'
74
66
  end
67
+ end
75
68
 
76
- def metric_action(name)
77
- case name
78
- when /#{RENDER_TEMPLATE_EVENT_NAME}$/ then 'Rendering'
79
- when RENDER_PARTIAL_EVENT_NAME then 'Partial'
80
- when RENDER_COLLECTION_EVENT_NAME then 'Partial'
81
- end
69
+ def metric_path(name, identifier)
70
+ # Rails 5 sets identifier to nil for empty collections,
71
+ # so do not mistake rendering a collection for rendering a file.
72
+ if identifier.nil? && name != RENDER_COLLECTION_EVENT_NAME
73
+ 'file'
74
+ elsif identifier =~ /template$/
75
+ identifier
76
+ elsif identifier && (parts = identifier.split('/')).size > 1
77
+ parts[-2..-1].join('/')
78
+ else
79
+ ::NewRelic::Agent::UNKNOWN_METRIC
82
80
  end
83
81
  end
84
82
  end
83
+
84
+ # This class holds state information between calls to `start`
85
+ # and `finish` for ActiveSupport events that we do not want to track
86
+ # as a transaction or segment.
87
+ class ActionViewEvent
88
+ attr_reader :name, :identifier
89
+ attr_accessor :finishable
90
+
91
+ def initialize(name, identifier)
92
+ @name = name
93
+ @identifier = identifier
94
+ @finishable = nil
95
+ end
96
+
97
+ def finish
98
+ @finishable.finish if @finishable
99
+ end
100
+ end
85
101
  end
102
+
86
103
  end
87
104
  end
@@ -0,0 +1,168 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
+
5
+ require 'new_relic/agent/instrumentation/active_record_subscriber'
6
+ require 'new_relic/agent/instrumentation/active_record_prepend'
7
+
8
+
9
+ # Provides a way to send :connection through ActiveSupport notifications to avoid
10
+ # looping through connection handlers to locate a connection by connection_id
11
+ # This is not needed in Rails 6+: https://github.com/rails/rails/pull/34602
12
+ module NewRelic
13
+ module Agent
14
+ module Instrumentation
15
+ module ActiveRecordNotifications
16
+ SQL_ACTIVE_RECORD = 'sql.active_record'.freeze
17
+
18
+ module BaseExtensions4x
19
+ # https://github.com/rails/rails/blob/4-1-stable/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb#L371
20
+ def log(sql, name = "SQL", binds = [], statement_name = nil)
21
+ @instrumenter.instrument(
22
+ SQL_ACTIVE_RECORD,
23
+ :sql => sql,
24
+ :name => name,
25
+ :connection_id => object_id,
26
+ :connection => self,
27
+ :statement_name => statement_name,
28
+ :binds => binds) { yield }
29
+ rescue => e
30
+ # The translate_exception_class method got introduced in 4.1
31
+ if ::ActiveRecord::VERSION::MINOR == 0
32
+ raise translate_exception(e, sql)
33
+ else
34
+ raise translate_exception_class(e, sql)
35
+ end
36
+ end
37
+ end
38
+
39
+ module BaseExtensions50
40
+ # https://github.com/rails/rails/blob/5-0-stable/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb#L582
41
+ def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil)
42
+ @instrumenter.instrument(
43
+ SQL_ACTIVE_RECORD,
44
+ sql: sql,
45
+ name: name,
46
+ binds: binds,
47
+ type_casted_binds: type_casted_binds,
48
+ statement_name: statement_name,
49
+ connection_id: object_id,
50
+ connection: self) { yield }
51
+ rescue => e
52
+ raise translate_exception_class(e, sql)
53
+ end
54
+ end
55
+
56
+ module BaseExtensions51
57
+ # https://github.com/rails/rails/blob/5-1-stable/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb#L603
58
+ def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil) # :doc:
59
+ @instrumenter.instrument(
60
+ SQL_ACTIVE_RECORD,
61
+ sql: sql,
62
+ name: name,
63
+ binds: binds,
64
+ type_casted_binds: type_casted_binds,
65
+ statement_name: statement_name,
66
+ connection_id: object_id,
67
+ connection: self) do
68
+ @lock.synchronize do
69
+ yield
70
+ end
71
+ end
72
+ rescue => e
73
+ raise translate_exception_class(e, sql)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ DependencyDetection.defer do
82
+ named :active_record_notifications
83
+
84
+ depends_on do
85
+ defined?(::ActiveRecord) && defined?(::ActiveRecord::Base) &&
86
+ defined?(::ActiveRecord::VERSION) &&
87
+ ::ActiveRecord::VERSION::MAJOR.to_i >= 4
88
+ end
89
+
90
+ depends_on do
91
+ !NewRelic::Agent.config[:disable_activerecord_instrumentation] &&
92
+ !NewRelic::Agent::Instrumentation::ActiveRecordSubscriber.subscribed?
93
+ end
94
+
95
+
96
+ depends_on do
97
+ # If the deprecated :disable_active_record_4 setting is true, and
98
+ # the active record version is four, disable!
99
+ NewRelic::Agent.config[:disable_active_record_4] == false \
100
+ || ::ActiveRecord::VERSION::MAJOR.to_i != 4
101
+ end
102
+
103
+ depends_on do
104
+ # If the deprecated :disable_active_record_5 setting is true, and
105
+ # the active record version is five, disable!
106
+ NewRelic::Agent.config[:disable_active_record_5] == false \
107
+ || ::ActiveRecord::VERSION::MAJOR.to_i != 5
108
+ end
109
+
110
+
111
+ executes do
112
+ ::NewRelic::Agent.logger.info 'Installing notifications based Active Record instrumentation'
113
+ end
114
+
115
+ executes do
116
+ ActiveSupport::Notifications.subscribe('sql.active_record',
117
+ NewRelic::Agent::Instrumentation::ActiveRecordSubscriber.new)
118
+ end
119
+
120
+ executes do
121
+ ActiveSupport.on_load(:active_record) do
122
+ ::NewRelic::Agent::PrependSupportability.record_metrics_for(
123
+ ::ActiveRecord::Base,
124
+ ::ActiveRecord::Relation)
125
+
126
+ # Default to .prepending, unless the ActiveRecord version is <=4
127
+ # **AND** the :prepend_active_record_instrumentation config is false
128
+ if ::ActiveRecord::VERSION::MAJOR > 4 \
129
+ || ::NewRelic::Agent.config[:prepend_active_record_instrumentation]
130
+
131
+ ::ActiveRecord::Base.send(:prepend,
132
+ ::NewRelic::Agent::Instrumentation::ActiveRecordPrepend::BaseExtensions)
133
+ ::ActiveRecord::Relation.send(:prepend,
134
+ ::NewRelic::Agent::Instrumentation::ActiveRecordPrepend::RelationExtensions)
135
+ else
136
+ ::NewRelic::Agent::Instrumentation::ActiveRecordHelper.instrument_additional_methods
137
+ end
138
+ end
139
+ end
140
+
141
+ executes do
142
+ if NewRelic::Agent.config[:backport_fast_active_record_connection_lookup]
143
+ major_version = ::ActiveRecord::VERSION::MAJOR.to_i
144
+ minor_version = ::ActiveRecord::VERSION::MINOR.to_i
145
+
146
+ activerecord_extension = if major_version == 4
147
+ ::NewRelic::Agent::Instrumentation::ActiveRecordNotifications::BaseExtensions4x
148
+ elsif major_version == 5 && minor_version == 0
149
+ ::NewRelic::Agent::Instrumentation::ActiveRecordNotifications::BaseExtensions50
150
+ elsif major_version == 5 && minor_version == 1
151
+ ::NewRelic::Agent::Instrumentation::ActiveRecordNotifications::BaseExtensions51
152
+ end
153
+
154
+ unless activerecord_extension.nil?
155
+ ::ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:prepend, activerecord_extension)
156
+ end
157
+ end
158
+ end
159
+
160
+ executes do
161
+ if ::ActiveRecord::VERSION::MAJOR == 5 \
162
+ && ::ActiveRecord::VERSION::MINOR.to_i == 1 \
163
+ && ::ActiveRecord::VERSION::TINY.to_i >= 6
164
+
165
+ ::ActiveRecord::Base.prepend ::NewRelic::Agent::Instrumentation::ActiveRecordPrepend::BaseExtensions516
166
+ end
167
+ end
168
+ end
@@ -2,7 +2,7 @@
2
2
  # This file is distributed under New Relic's license terms.
3
3
  # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
4
  require 'new_relic/agent/instrumentation/active_record_helper'
5
- require 'new_relic/agent/instrumentation/evented_subscriber'
5
+ require 'new_relic/agent/instrumentation/notifications_subscriber'
6
6
 
7
7
  # Listen for ActiveSupport::Notifications events for ActiveRecord query
8
8
  # events. Write metric data, transaction trace nodes and slow sql
@@ -10,7 +10,7 @@ require 'new_relic/agent/instrumentation/evented_subscriber'
10
10
  module NewRelic
11
11
  module Agent
12
12
  module Instrumentation
13
- class ActiveRecordSubscriber < EventedSubscriber
13
+ class ActiveRecordSubscriber < NotificationsSubscriber
14
14
  CACHED_QUERY_NAME = 'CACHE'.freeze
15
15
 
16
16
  def initialize
@@ -31,7 +31,8 @@ module NewRelic
31
31
  # we don't expect this to be called more than once, but we're being
32
32
  # defensive.
33
33
  return if defined?(cached?)
34
- if ::ActiveRecord::VERSION::STRING >= "5.1.0"
34
+
35
+ if defined?(::ActiveRecord) && ::ActiveRecord::VERSION::STRING >= "5.1.0"
35
36
  def cached?(payload)
36
37
  payload.fetch(:cached, false)
37
38
  end
@@ -45,9 +46,10 @@ module NewRelic
45
46
  def start(name, id, payload) #THREAD_LOCAL_ACCESS
46
47
  return if cached?(payload)
47
48
  return unless NewRelic::Agent.tl_is_execution_traced?
49
+
48
50
  config = active_record_config(payload)
49
- event = ActiveRecordEvent.new(name, Time.now, nil, id, payload, @explainer, config)
50
- push_event(event)
51
+ segment = start_segment(config, payload)
52
+ push_segment(id, segment)
51
53
  rescue => e
52
54
  log_notification_error(e, name, 'start')
53
55
  end
@@ -55,8 +57,9 @@ module NewRelic
55
57
  def finish(name, id, payload) #THREAD_LOCAL_ACCESS
56
58
  return if cached?(payload)
57
59
  return unless state.is_execution_traced?
58
- event = pop_event(id)
59
- event.finish
60
+
61
+ segment = pop_segment(id)
62
+ segment.finish if segment
60
63
  rescue => e
61
64
  log_notification_error(e, name, 'finish')
62
65
  end
@@ -78,6 +81,13 @@ module NewRelic
78
81
  def active_record_config(payload)
79
82
  return unless payload[:connection_id]
80
83
 
84
+ # handle if the notification payload provides the AR connection
85
+ # available in Rails 6+ & our ActiveRecordNotifications#log extension
86
+ if payload[:connection]
87
+ connection_config = payload[:connection].instance_variable_get(:@config)
88
+ return connection_config if connection_config
89
+ end
90
+
81
91
  connection = nil
82
92
  connection_id = payload[:connection_id]
83
93
 
@@ -92,50 +102,33 @@ module NewRelic
92
102
  connection.instance_variable_get(:@config) if connection
93
103
  end
94
104
 
95
- class ActiveRecordEvent < Event
96
- def initialize(name, start, ending, transaction_id, payload, explainer, config)
97
- super(name, start, ending, transaction_id, payload)
98
- @explainer = explainer
99
- @config = config
100
- @segment = start_segment
101
- end
102
-
103
- def start_segment
104
- product, operation, collection = ActiveRecordHelper.product_operation_collection_for(payload[:name],
105
- sql, @config && @config[:adapter])
106
-
107
- host = nil
108
- port_path_or_id = nil
109
- database = nil
110
-
111
- if ActiveRecordHelper::InstanceIdentification.supported_adapter?(@config)
112
- host = ActiveRecordHelper::InstanceIdentification.host(@config)
113
- port_path_or_id = ActiveRecordHelper::InstanceIdentification.port_path_or_id(@config)
114
- database = @config && @config[:database]
115
- end
116
-
117
- segment = Tracer.start_datastore_segment product: product,
118
- operation: operation,
119
- collection: collection,
120
- host: host,
121
- port_path_or_id: port_path_or_id,
122
- database_name: database
123
-
124
- segment._notice_sql sql, @config, @explainer, payload[:binds], payload[:name]
125
- segment
105
+ def start_segment(config, payload)
106
+ sql = Helper.correctly_encoded payload[:sql]
107
+ product, operation, collection = ActiveRecordHelper.product_operation_collection_for(
108
+ payload[:name],
109
+ sql,
110
+ config && config[:adapter]
111
+ )
112
+
113
+ host = nil
114
+ port_path_or_id = nil
115
+ database = nil
116
+
117
+ if ActiveRecordHelper::InstanceIdentification.supported_adapter?(config)
118
+ host = ActiveRecordHelper::InstanceIdentification.host(config)
119
+ port_path_or_id = ActiveRecordHelper::InstanceIdentification.port_path_or_id(config)
120
+ database = config && config[:database]
126
121
  end
127
122
 
128
- def finish
129
- @segment.finish if @segment
130
- end
131
-
132
- def state
133
- @state ||= NewRelic::Agent::Tracer.state
134
- end
123
+ segment = Tracer.start_datastore_segment(product: product,
124
+ operation: operation,
125
+ collection: collection,
126
+ host: host,
127
+ port_path_or_id: port_path_or_id,
128
+ database_name: database)
135
129
 
136
- def sql
137
- @sql ||= Helper.correctly_encoded payload[:sql]
138
- end
130
+ segment._notice_sql sql, config, @explainer, payload[:binds], payload[:name]
131
+ segment
139
132
  end
140
133
  end
141
134
  end