scout_apm 2.1.32 → 2.2.0.pre0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/CHANGELOG.markdown +2 -161
  4. data/Rakefile +2 -2
  5. data/ext/allocations/allocations.c +0 -6
  6. data/ext/allocations/extconf.rb +0 -1
  7. data/ext/stacks/extconf.rb +33 -0
  8. data/ext/stacks/scout_atomics.h +86 -0
  9. data/ext/stacks/stacks.c +744 -0
  10. data/lib/scout_apm.rb +16 -24
  11. data/lib/scout_apm/agent.rb +38 -93
  12. data/lib/scout_apm/agent/logging.rb +1 -6
  13. data/lib/scout_apm/agent/reporting.rb +6 -8
  14. data/lib/scout_apm/app_server_load.rb +10 -21
  15. data/lib/scout_apm/attribute_arranger.rb +2 -0
  16. data/lib/scout_apm/background_job_integrations/delayed_job.rb +1 -71
  17. data/lib/scout_apm/background_job_integrations/sidekiq.rb +27 -66
  18. data/lib/scout_apm/background_worker.rb +15 -19
  19. data/lib/scout_apm/capacity.rb +57 -0
  20. data/lib/scout_apm/config.rb +29 -135
  21. data/lib/scout_apm/context.rb +5 -9
  22. data/lib/scout_apm/deploy_integrations/capistrano_2.cap +12 -0
  23. data/lib/scout_apm/deploy_integrations/capistrano_2.rb +83 -0
  24. data/lib/scout_apm/deploy_integrations/capistrano_3.cap +12 -0
  25. data/lib/scout_apm/deploy_integrations/capistrano_3.rb +88 -0
  26. data/lib/scout_apm/environment.rb +15 -22
  27. data/lib/scout_apm/histogram.rb +2 -11
  28. data/lib/scout_apm/instant/assets/xmlhttp_instrumentation.html +2 -2
  29. data/lib/scout_apm/instant/middleware.rb +57 -198
  30. data/lib/scout_apm/instruments/action_controller_rails_2.rb +2 -1
  31. data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +59 -90
  32. data/lib/scout_apm/instruments/active_record.rb +5 -7
  33. data/lib/scout_apm/instruments/delayed_job.rb +57 -0
  34. data/lib/scout_apm/instruments/grape.rb +3 -4
  35. data/lib/scout_apm/instruments/middleware_detailed.rb +6 -4
  36. data/lib/scout_apm/instruments/middleware_summary.rb +1 -39
  37. data/lib/scout_apm/instruments/mongoid.rb +3 -24
  38. data/lib/scout_apm/instruments/net_http.rb +2 -7
  39. data/lib/scout_apm/instruments/percentile_sampler.rb +19 -36
  40. data/lib/scout_apm/instruments/process/process_cpu.rb +2 -3
  41. data/lib/scout_apm/instruments/process/process_memory.rb +3 -3
  42. data/lib/scout_apm/layaway.rb +33 -76
  43. data/lib/scout_apm/layer.rb +59 -16
  44. data/lib/scout_apm/layer_converters/converter_base.rb +0 -199
  45. data/lib/scout_apm/layer_converters/job_converter.rb +1 -1
  46. data/lib/scout_apm/layer_converters/metric_converter.rb +1 -1
  47. data/lib/scout_apm/layer_converters/slow_job_converter.rb +90 -15
  48. data/lib/scout_apm/layer_converters/slow_request_converter.rb +101 -13
  49. data/lib/scout_apm/metric_set.rb +1 -9
  50. data/lib/scout_apm/metric_stats.rb +8 -8
  51. data/lib/scout_apm/reporter.rb +15 -51
  52. data/lib/scout_apm/request_histograms.rb +0 -4
  53. data/lib/scout_apm/request_manager.rb +1 -2
  54. data/lib/scout_apm/scored_item_set.rb +0 -7
  55. data/lib/scout_apm/serializers/deploy_serializer.rb +16 -0
  56. data/lib/scout_apm/serializers/payload_serializer.rb +3 -9
  57. data/lib/scout_apm/serializers/payload_serializer_to_json.rb +5 -2
  58. data/lib/scout_apm/serializers/slow_jobs_serializer_to_json.rb +1 -2
  59. data/lib/scout_apm/server_integrations/puma.rb +2 -5
  60. data/lib/scout_apm/slow_item_set.rb +80 -0
  61. data/lib/scout_apm/slow_job_record.rb +1 -6
  62. data/lib/scout_apm/slow_transaction.rb +2 -20
  63. data/lib/scout_apm/store.rb +12 -50
  64. data/lib/scout_apm/trace_compactor.rb +311 -0
  65. data/lib/scout_apm/tracked_request.rb +37 -128
  66. data/lib/scout_apm/utils/backtrace_parser.rb +5 -7
  67. data/lib/scout_apm/utils/fake_stacks.rb +83 -0
  68. data/lib/scout_apm/version.rb +1 -1
  69. data/scout_apm.gemspec +4 -6
  70. data/test/test_helper.rb +0 -56
  71. data/test/unit/config_test.rb +9 -60
  72. data/test/unit/histogram_test.rb +0 -14
  73. data/test/unit/layaway_test.rb +16 -31
  74. data/test/unit/serializers/payload_serializer_test.rb +105 -3
  75. data/test/unit/slow_item_set_test.rb +94 -0
  76. data/test/unit/slow_job_policy_test.rb +49 -0
  77. data/test/unit/slow_request_policy_test.rb +5 -4
  78. data/test/unit/utils/backtrace_parser_test.rb +0 -19
  79. data/tester.rb +53 -0
  80. metadata +29 -124
  81. data/.rubocop.yml +0 -8
  82. data/Guardfile +0 -42
  83. data/ext/rusage/README.md +0 -26
  84. data/ext/rusage/extconf.rb +0 -5
  85. data/ext/rusage/rusage.c +0 -52
  86. data/lib/scout_apm/background_job_integrations/resque.rb +0 -85
  87. data/lib/scout_apm/background_recorder.rb +0 -43
  88. data/lib/scout_apm/debug.rb +0 -37
  89. data/lib/scout_apm/git_revision.rb +0 -51
  90. data/lib/scout_apm/instruments/action_view.rb +0 -49
  91. data/lib/scout_apm/instruments/resque.rb +0 -40
  92. data/lib/scout_apm/layer_children_set.rb +0 -77
  93. data/lib/scout_apm/limited_layer.rb +0 -122
  94. data/lib/scout_apm/rack.rb +0 -26
  95. data/lib/scout_apm/remote/message.rb +0 -23
  96. data/lib/scout_apm/remote/recorder.rb +0 -57
  97. data/lib/scout_apm/remote/router.rb +0 -49
  98. data/lib/scout_apm/remote/server.rb +0 -58
  99. data/lib/scout_apm/serializers/histograms_serializer_to_json.rb +0 -21
  100. data/lib/scout_apm/synchronous_recorder.rb +0 -26
  101. data/lib/scout_apm/utils/gzip_helper.rb +0 -24
  102. data/lib/scout_apm/utils/numbers.rb +0 -14
  103. data/lib/scout_apm/utils/scm.rb +0 -14
  104. data/test/unit/background_job_integrations/sidekiq_test.rb +0 -104
  105. data/test/unit/context_test.rb +0 -30
  106. data/test/unit/git_revision_test.rb +0 -15
  107. data/test/unit/instruments/net_http_test.rb +0 -21
  108. data/test/unit/instruments/percentile_sampler_test.rb +0 -137
  109. data/test/unit/layer_children_set_test.rb +0 -88
  110. data/test/unit/limited_layer_test.rb +0 -53
  111. data/test/unit/remote/test_message.rb +0 -13
  112. data/test/unit/remote/test_router.rb +0 -33
  113. data/test/unit/remote/test_server.rb +0 -15
  114. data/test/unit/store_test.rb +0 -89
  115. data/test/unit/test_tracked_request.rb +0 -87
  116. data/test/unit/utils/numbers_test.rb +0 -15
  117. data/test/unit/utils/scm.rb +0 -17
@@ -57,7 +57,7 @@ module ScoutApm
57
57
  walker.walk do |layer|
58
58
  next if layer == job_layer
59
59
  next if layer == queue_layer
60
- next if skip_layer?(layer)
60
+ next if layer.annotations[:ignorable]
61
61
 
62
62
  # we don't need to use the full metric name for scoped metrics as we
63
63
  # only display metrics aggregrated by type, just use "ActiveRecord"
@@ -21,7 +21,7 @@ module ScoutApm
21
21
  metric_hash = Hash.new
22
22
 
23
23
  walker.walk do |layer|
24
- next if skip_layer?(layer)
24
+ next if layer.annotations[:ignorable]
25
25
 
26
26
  meta_options = if layer == scope_layer # We don't scope the controller under itself
27
27
  {}
@@ -2,6 +2,7 @@ module ScoutApm
2
2
  module LayerConverters
3
3
  class SlowJobConverter < ConverterBase
4
4
  def initialize(*)
5
+ @backtraces = []
5
6
  super
6
7
 
7
8
  # After call to super, so @request is populated
@@ -10,8 +11,6 @@ module ScoutApm
10
11
  else
11
12
  -1
12
13
  end
13
-
14
- setup_subscopable_callbacks
15
14
  end
16
15
 
17
16
  def name
@@ -33,7 +32,6 @@ module ScoutApm
33
32
  mem_delta = ScoutApm::Instruments::Process::ProcessMemory.rss_to_mb(request.capture_mem_delta!)
34
33
 
35
34
  timing_metrics, allocation_metrics = create_metrics
36
-
37
35
  unless ScoutApm::Instruments::Allocations::ENABLED
38
36
  allocation_metrics = {}
39
37
  end
@@ -49,9 +47,7 @@ module ScoutApm
49
47
  allocation_metrics,
50
48
  mem_delta,
51
49
  job_layer.total_allocations,
52
- score,
53
- limited?
54
- )
50
+ score)
55
51
  end
56
52
 
57
53
  def queue_layer
@@ -62,29 +58,108 @@ module ScoutApm
62
58
  @job_layer ||= find_first_layer_of_type("Job")
63
59
  end
64
60
 
65
- def skip_layer?(layer)
66
- super(layer) || layer == queue_layer
67
- end
68
-
69
61
  def create_metrics
70
62
  metric_hash = Hash.new
71
63
  allocation_metric_hash = Hash.new
72
64
 
65
+ # Keep a list of subscopes, but only ever use the front one. The rest
66
+ # get pushed/popped in cases when we have many levels of subscopable
67
+ # layers. This lets us push/pop without otherwise keeping track very closely.
68
+ subscope_layers = []
69
+
70
+ walker.before do |layer|
71
+ if layer.subscopable?
72
+ subscope_layers.push(layer)
73
+ end
74
+ end
75
+
76
+ walker.after do |layer|
77
+ if layer.subscopable?
78
+ subscope_layers.pop
79
+ end
80
+ end
81
+
73
82
  walker.walk do |layer|
74
- next if skip_layer?(layer)
83
+ # Sometimes we start capturing a layer without knowing if we really
84
+ # want to make an entry for it. See ActiveRecord instrumentation for
85
+ # an example. We start capturing before we know if a query is cached
86
+ # or not, and want to skip any cached queries.
87
+ next if layer.annotations[:ignorable]
75
88
 
76
89
  # The queue_layer is useful to capture for other reasons, but doesn't
77
90
  # create a MetricMeta/Stat of its own
78
91
  next if layer == queue_layer
79
92
 
80
- store_specific_metric(layer, metric_hash, allocation_metric_hash)
81
- store_aggregate_metric(layer, metric_hash, allocation_metric_hash)
82
- end
93
+ meta_options = if subscope_layers.first && layer != subscope_layers.first # Don't scope under ourself.
94
+ subscope_name = subscope_layers.first.legacy_metric_name
95
+ {:scope => subscope_name}
96
+ elsif layer == job_layer # We don't scope the controller under itself
97
+ {}
98
+ else
99
+ {:scope => job_layer.legacy_metric_name}
100
+ end
101
+
102
+ # Specific Metric
103
+ meta_options.merge!(:desc => layer.desc.to_s) if layer.desc
104
+ meta = MetricMeta.new(layer.legacy_metric_name, meta_options)
105
+ meta.extra.merge!(layer.annotations)
106
+
107
+ if layer.backtrace
108
+ bt = ScoutApm::Utils::BacktraceParser.new(layer.backtrace).call
109
+ if bt.any? # we could walk thru the call stack and not find in-app code
110
+ meta.backtrace = bt
111
+ # Why not just call meta.backtrace and call it done? The walker could access a later later that generates the same MetricMeta but doesn't have a backtrace. This could be
112
+ # lost in the metric_hash if it is replaced by the new key.
113
+ @backtraces << meta
114
+ else
115
+ ScoutApm::Agent.instance.logger.debug { "Unable to capture an app-specific backtrace for #{meta.inspect}\n#{layer.backtrace}" }
116
+ end
117
+ end
118
+
119
+ metric_hash[meta] ||= MetricStats.new( meta_options.has_key?(:scope) )
120
+ allocation_metric_hash[meta] ||= MetricStats.new( meta_options.has_key?(:scope) )
121
+ stat = metric_hash[meta]
122
+ stat.update!(layer.total_call_time, layer.total_exclusive_time)
123
+ stat = allocation_metric_hash[meta]
124
+ stat.update!(layer.total_allocations, layer.total_exclusive_allocations)
125
+
126
+ # Merged Metric (no specifics, just sum up by type)
127
+ meta = MetricMeta.new("#{layer.type}/all")
128
+ metric_hash[meta] ||= MetricStats.new(false)
129
+ allocation_metric_hash[meta] ||= MetricStats.new(false)
130
+ stat = metric_hash[meta]
131
+ stat.update!(layer.total_call_time, layer.total_exclusive_time)
132
+ stat = allocation_metric_hash[meta]
133
+ stat.update!(layer.total_allocations, layer.total_exclusive_allocations)
134
+
135
+ stat.add_traces(layer.traces.as_json)
136
+
137
+ # Debug logging for scoutprof traces
138
+ if ScoutApm::Agent.instance.config.value('profile')
139
+ if layer.type =~ %r{^(Controller|Queue|Job)$}.freeze
140
+ ScoutApm::Agent.instance.logger.debug do
141
+ traces_inspect = layer.traces.inspect
142
+ "****** Slow Request #{layer.type} Traces (#{layer.name}, tet: #{layer.total_exclusive_time}, tct: #{layer.total_call_time}), total raw traces: #{layer.traces.cube.total_count}, total clean traces: #{layer.traces.total_count}:\n#{traces_inspect}"
143
+ end
144
+ end
145
+ else
146
+ if layer.type =~ %r{^(Controller|Queue|Job)$}.freeze
147
+ ScoutApm::Agent.instance.logger.debug "****** Slow Request #{layer.type} Traces: Scoutprof is not enabled"
148
+ end
149
+ end
150
+ end # walker.walk
83
151
 
84
152
  metric_hash = attach_backtraces(metric_hash)
85
153
  allocation_metric_hash = attach_backtraces(allocation_metric_hash)
86
154
 
87
- [metric_hash, allocation_metric_hash]
155
+ [metric_hash,allocation_metric_hash]
156
+ end
157
+
158
+ def attach_backtraces(metric_hash)
159
+ @backtraces.each do |meta_with_backtrace|
160
+ metric_hash.keys.find { |k| k == meta_with_backtrace }.backtrace = meta_with_backtrace.backtrace
161
+ end
162
+ metric_hash
88
163
  end
89
164
  end
90
165
  end
@@ -2,6 +2,7 @@ module ScoutApm
2
2
  module LayerConverters
3
3
  class SlowRequestConverter < ConverterBase
4
4
  def initialize(*)
5
+ @backtraces = [] # An Array of MetricMetas that have a backtrace
5
6
  super
6
7
 
7
8
  # After call to super, so @request is populated
@@ -10,8 +11,6 @@ module ScoutApm
10
11
  else
11
12
  -1
12
13
  end
13
-
14
- setup_subscopable_callbacks
15
14
  end
16
15
 
17
16
  def name
@@ -25,8 +24,8 @@ module ScoutApm
25
24
  # Unconditionally attempts to convert this into a SlowTransaction object.
26
25
  # Can return nil if the request didn't have any scope_layer.
27
26
  def call
28
- return nil unless request.web?
29
- return nil unless scope_layer
27
+ scope = scope_layer
28
+ return nil unless scope
30
29
 
31
30
  ScoutApm::Agent.instance.slow_request_policy.stored!(request)
32
31
 
@@ -36,13 +35,12 @@ module ScoutApm
36
35
  uri = request.annotations[:uri] || ""
37
36
 
38
37
  timing_metrics, allocation_metrics = create_metrics
39
-
40
38
  unless ScoutApm::Instruments::Allocations::ENABLED
41
39
  allocation_metrics = {}
42
40
  end
43
41
 
44
42
  SlowTransaction.new(uri,
45
- scope_layer.legacy_metric_name,
43
+ scope.legacy_metric_name,
46
44
  root_layer.total_call_time,
47
45
  timing_metrics,
48
46
  allocation_metrics,
@@ -51,29 +49,119 @@ module ScoutApm
51
49
  [], # stackprof, now unused.
52
50
  mem_delta,
53
51
  root_layer.total_allocations,
54
- @points,
55
- limited?)
52
+ @points)
53
+ end
54
+
55
+ # Iterates over the TrackedRequest's MetricMetas that have backtraces and attaches each to correct MetricMeta in the Metric Hash.
56
+ def attach_backtraces(metric_hash)
57
+ @backtraces.each do |meta_with_backtrace|
58
+ metric_hash.keys.find { |k| k == meta_with_backtrace }.backtrace = meta_with_backtrace.backtrace
59
+ end
60
+ metric_hash
56
61
  end
57
62
 
58
63
  # Full metrics from this request. These get stored permanently in a SlowTransaction.
59
64
  # Some merging of metrics will happen here, so if a request calls the same
60
65
  # ActiveRecord or View repeatedly, it'll get merged.
61
- #
66
+ #
62
67
  # This returns a 2-element of Metric Hashes (the first element is timing metrics, the second element is allocation metrics)
63
68
  def create_metrics
64
69
  metric_hash = Hash.new
65
70
  allocation_metric_hash = Hash.new
66
71
 
72
+ # Keep a list of subscopes, but only ever use the front one. The rest
73
+ # get pushed/popped in cases when we have many levels of subscopable
74
+ # layers. This lets us push/pop without otherwise keeping track very closely.
75
+ subscope_layers = []
76
+
77
+ walker.before do |layer|
78
+ if layer.subscopable?
79
+ subscope_layers.push(layer)
80
+ end
81
+ end
82
+
83
+ walker.after do |layer|
84
+ if layer.subscopable?
85
+ subscope_layers.pop
86
+ end
87
+ end
88
+
67
89
  walker.walk do |layer|
68
- next if skip_layer?(layer)
69
- store_specific_metric(layer, metric_hash, allocation_metric_hash)
70
- store_aggregate_metric(layer, metric_hash, allocation_metric_hash)
90
+ # Sometimes we start capturing a layer without knowing if we really
91
+ # want to make an entry for it. See ActiveRecord instrumentation for
92
+ # an example. We start capturing before we know if a query is cached
93
+ # or not, and want to skip any cached queries.
94
+ if layer.annotations[:ignorable]
95
+ next
96
+ end
97
+
98
+ meta_options = if subscope_layers.first && layer != subscope_layers.first # Don't scope under ourself.
99
+ subscope_name = subscope_layers.first.legacy_metric_name
100
+ {:scope => subscope_name}
101
+ elsif layer == scope_layer # We don't scope the controller under itself
102
+ {}
103
+ else
104
+ {:scope => scope_layer.legacy_metric_name}
105
+ end
106
+
107
+ # Specific Metric
108
+ meta_options.merge!(:desc => layer.desc.to_s) if layer.desc
109
+ meta = MetricMeta.new(layer.legacy_metric_name, meta_options)
110
+ meta.extra.merge!(layer.annotations)
111
+ if layer.backtrace
112
+ bt = ScoutApm::Utils::BacktraceParser.new(layer.backtrace).call
113
+ if bt.any? # we could walk thru the call stack and not find in-app code
114
+ meta.backtrace = bt
115
+ # Why not just call meta.backtrace and call it done? The walker
116
+ # could access a later later that generates the same MetricMeta
117
+ # but doesn't have a backtrace. This could be lost in the
118
+ # metric_hash if it is replaced by the new key.
119
+ @backtraces << meta
120
+ else
121
+ ScoutApm::Agent.instance.logger.debug { "Unable to capture an app-specific backtrace for #{meta.inspect}\n#{layer.backtrace}" }
122
+ end
123
+ end
124
+ metric_hash[meta] ||= MetricStats.new( meta_options.has_key?(:scope) )
125
+ allocation_metric_hash[meta] ||= MetricStats.new( meta_options.has_key?(:scope) )
126
+ # timing
127
+ stat = metric_hash[meta]
128
+ stat.update!(layer.total_call_time, layer.total_exclusive_time)
129
+ stat.add_traces(layer.traces.as_json)
130
+
131
+ # Debug logging for scoutprof traces
132
+ if ScoutApm::Agent.instance.config.value('profile')
133
+ if layer.type =~ %r{^(Controller|Queue|Job)$}.freeze
134
+ ScoutApm::Agent.instance.logger.debug do
135
+ traces_inspect = layer.traces.inspect
136
+ "****** Slow Request #{layer.type} Traces (#{layer.name}, tet: #{layer.total_exclusive_time}, tct: #{layer.total_call_time}), total raw traces: #{layer.traces.cube.total_count}, total clean traces: #{layer.traces.total_count}:\n#{traces_inspect}"
137
+ end
138
+ end
139
+ else
140
+ if layer.type =~ %r{^(Controller|Queue|Job)$}.freeze
141
+ ScoutApm::Agent.instance.logger.debug "****** Slow Request #{layer.type} Traces: Scoutprof is not enabled"
142
+ end
143
+ end
144
+
145
+ # allocations
146
+ stat = allocation_metric_hash[meta]
147
+ stat.update!(layer.total_allocations, layer.total_exclusive_allocations)
148
+
149
+ # Merged Metric (no specifics, just sum up by type)
150
+ meta = MetricMeta.new("#{layer.type}/all")
151
+ metric_hash[meta] ||= MetricStats.new(false)
152
+ allocation_metric_hash[meta] ||= MetricStats.new(false)
153
+ # timing
154
+ stat = metric_hash[meta]
155
+ stat.update!(layer.total_call_time, layer.total_exclusive_time)
156
+ # allocations
157
+ stat = allocation_metric_hash[meta]
158
+ stat.update!(layer.total_allocations, layer.total_exclusive_allocations)
71
159
  end
72
160
 
73
161
  metric_hash = attach_backtraces(metric_hash)
74
162
  allocation_metric_hash = attach_backtraces(allocation_metric_hash)
75
163
 
76
- [metric_hash, allocation_metric_hash]
164
+ [metric_hash,allocation_metric_hash]
77
165
  end
78
166
  end
79
167
  end
@@ -1,8 +1,6 @@
1
1
  module ScoutApm
2
2
  class MetricSet
3
- # We can't aggregate a handful of things like samplers (CPU, Memory), or
4
- # Controller, and Percentiles so pass through these metrics directly
5
- #
3
+ # We can't aggregate CPU, Memory, Capacity, or Controller, so pass through these metrics directly
6
4
  # TODO: Figure out a way to not have this duplicate what's in Samplers, and also on server's ingest
7
5
  PASSTHROUGH_METRICS = ["CPU", "Memory", "Instance", "Controller", "SlowTransaction", "Percentile", "Job"]
8
6
 
@@ -51,11 +49,5 @@ module ScoutApm
51
49
  @combine_in_progress = false
52
50
  self
53
51
  end
54
-
55
-
56
- def eql?(other)
57
- metrics == other.metrics
58
- end
59
- alias :== :eql?
60
52
  end
61
53
  end
@@ -9,6 +9,7 @@ class MetricStats
9
9
  attr_accessor :sum_of_squares
10
10
  attr_accessor :queue
11
11
  attr_accessor :latency
12
+ attr_accessor :traces
12
13
 
13
14
  def initialize(scoped = false)
14
15
  @scoped = scoped
@@ -18,6 +19,7 @@ class MetricStats
18
19
  self.min_call_time = 0.0
19
20
  self.max_call_time = 0.0
20
21
  self.sum_of_squares = 0.0
22
+ self.traces = []
21
23
  end
22
24
 
23
25
  # Note, that you must include exclusive_time if you wish to set
@@ -39,6 +41,10 @@ class MetricStats
39
41
  self
40
42
  end
41
43
 
44
+ def add_traces(traces)
45
+ self.traces += Array(traces)
46
+ end
47
+
42
48
  # combines data from another MetricStats object
43
49
  def combine!(other)
44
50
  self.call_count += other.call_count
@@ -47,18 +53,12 @@ class MetricStats
47
53
  self.min_call_time = other.min_call_time if self.min_call_time.zero? or other.min_call_time < self.min_call_time
48
54
  self.max_call_time = other.max_call_time if other.max_call_time > self.max_call_time
49
55
  self.sum_of_squares += other.sum_of_squares
56
+ self.traces = Array(self.traces) + Array(other.traces)
50
57
  self
51
58
  end
52
59
 
53
- # To avoid conflicts with different JSON libaries handle JSON ourselves.
54
- # Time-based metrics are converted to milliseconds from seconds.
55
- def to_json(*a)
56
- %Q[{"total_exclusive_time":#{total_exclusive_time*1000},"min_call_time":#{min_call_time*1000},"call_count":#{call_count},"sum_of_squares":#{sum_of_squares*1000},"total_call_time":#{total_call_time*1000},"max_call_time":#{max_call_time*1000}}]
57
- end
58
-
59
60
  def as_json
60
- json_attributes = [:call_count, :total_call_time, :total_exclusive_time, :min_call_time, :max_call_time]
61
- # uri, context
61
+ json_attributes = [:call_count, :total_call_time, :total_exclusive_time, :min_call_time, :max_call_time, :traces]
62
62
  ScoutApm::AttributeArranger.call(self, json_attributes)
63
63
  end
64
64
  end
@@ -17,36 +17,31 @@ module ScoutApm
17
17
  @instant_key = instant_key
18
18
  end
19
19
 
20
+ # TODO: Parse & return a real response object, not the HTTP Response object
20
21
  def report(payload, headers = {})
21
- hosts = determine_hosts
22
+ # Some posts (typically ones under development) bypass the ingestion pipeline and go directly to the webserver. They use direct_host instead of host
23
+ hosts = [:deploy_hook, :instant_trace].include?(type) ? config.value('direct_host') : config.value('host')
22
24
 
23
- if config.value('compress_payload')
24
- original_payload_size = payload.length
25
-
26
- payload, compression_headers = compress_payload(payload)
27
- headers.merge!(compression_headers)
28
-
29
- compress_payload_size = payload.length
30
- ScoutApm::Agent.instance.logger.debug("Original Size: #{original_payload_size} Compressed Size: #{compress_payload_size}")
25
+ Array(hosts).each do |host|
26
+ full_uri = uri(host)
27
+ response = post(full_uri, payload, headers)
28
+ unless response && response.is_a?(Net::HTTPSuccess)
29
+ logger.warn "Error on checkin to #{full_uri.to_s}: #{response.inspect}"
30
+ end
31
31
  end
32
-
33
- post_payload(hosts, payload, headers)
34
32
  end
35
33
 
36
34
  def uri(host)
37
- encoded_app_name = CGI.escape(Environment.instance.application_name)
38
- key = config.value('key')
39
-
40
35
  case type
41
36
  when :checkin
42
- URI.parse("#{host}/apps/checkin.scout?key=#{key}&name=#{encoded_app_name}")
37
+ URI.parse("#{host}/apps/checkin.scout?key=#{config.value('key')}&name=#{CGI.escape(Environment.instance.application_name)}")
43
38
  when :app_server_load
44
- URI.parse("#{host}/apps/app_server_load.scout?key=#{key}&name=#{encoded_app_name}")
39
+ URI.parse("#{host}/apps/app_server_load.scout?key=#{config.value('key')}&name=#{CGI.escape(Environment.instance.application_name)}")
45
40
  when :deploy_hook
46
- URI.parse("#{host}/apps/deploy.scout?key=#{key}&name=#{encoded_app_name}")
41
+ URI.parse("#{host}/apps/deploy.scout?key=#{config.value('key')}&name=#{CGI.escape(config.value('name'))}")
47
42
  when :instant_trace
48
- URI.parse("#{host}/apps/instant_trace.scout?key=#{key}&name=#{encoded_app_name}&instant_key=#{instant_key}")
49
- end.tap { |u| logger.debug("Posting to #{u}") }
43
+ URI.parse("#{host}/apps/instant_trace.scout?key=#{config.value('key')}&name=#{CGI.escape(config.value('name'))}&instant_key=#{instant_key}")
44
+ end.tap{|u| logger.debug("Posting to #{u.to_s}")}
50
45
  end
51
46
 
52
47
  def can_report?
@@ -111,10 +106,7 @@ module ScoutApm
111
106
  # Net::HTTP::Proxy returns a regular Net::HTTP class if the first argument (host) is nil.
112
107
  def http(url)
113
108
  proxy_uri = URI.parse(config.value('proxy').to_s)
114
- http = Net::HTTP::Proxy(proxy_uri.host,
115
- proxy_uri.port,
116
- proxy_uri.user,
117
- proxy_uri.password).new(url.host, url.port)
109
+ http = Net::HTTP::Proxy(proxy_uri.host,proxy_uri.port,proxy_uri.user,proxy_uri.password).new(url.host, url.port)
118
110
  if url.is_a?(URI::HTTPS)
119
111
  http.use_ssl = true
120
112
  http.ca_file = CA_FILE
@@ -122,33 +114,5 @@ module ScoutApm
122
114
  end
123
115
  http
124
116
  end
125
-
126
- def compress_payload(payload)
127
- [
128
- ScoutApm::Utils::GzipHelper.new.deflate(payload),
129
- { 'Content-Encoding' => 'gzip' }
130
- ]
131
- end
132
-
133
- # Some posts (typically ones under development) bypass the ingestion
134
- # pipeline and go directly to the webserver. They use direct_host instead
135
- # of host
136
- def determine_hosts
137
- if [:deploy_hook, :instant_trace].include?(type)
138
- config.value('direct_host')
139
- else
140
- config.value('host')
141
- end
142
- end
143
-
144
- def post_payload(hosts, payload, headers)
145
- Array(hosts).each do |host|
146
- full_uri = uri(host)
147
- response = post(full_uri, payload, headers)
148
- unless response && response.is_a?(Net::HTTPSuccess)
149
- logger.warn "Error on checkin to #{full_uri}: #{response.inspect}"
150
- end
151
- end
152
- end
153
117
  end
154
118
  end